npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@wnfs-wg/nest

v2.0.0

Published

A utility layer around the `wnfs` package.

Downloads

11

Readme

Nest 🪺

npm (scoped)

A layer around the wnfs package that provides a FileSystem class, a root tree, mounts, transactions and some other essentials.

Features

  • A file system class that allows for an easy-to-use mutable API.
  • A root tree, holding references to all the needed individual parts (public fs, private forest, exchange, etc)
  • A mounting system for private nodes, mount specific paths.
  • A unix-fs compatibility layer for the public file system (allows for public files to be viewed through, for example, IPFS gateways)
  • Private data sharing + helpers
  • Provides a transaction system, rewinding the state if an error occurs.
  • Creates a private forest automatically with a RSA modulus using the Web Crypto API (supported on multiple platforms)
  • In addition to the default symmetric key, use an RSA-OAEP asymmetric key to mount a private node (essentially sharing to self). Can be used to load a private directory, or file, using a passkey + the PRF extension.
  • Ability to verify commits to the file system. If a commit, aka. modification, is not verified, it will result in a no-op.
  • And more: typed paths, events, path helpers, data casting, …

Installation

pnpm install @wnfs-wg/nest

Usage

Scenario 1: 🚀 Create a new file system, create a new file and read it back.

import { FileSystem, Path } from '@wnfs-wg/nest'

// Provide some block store of the `Blockstore` type from the `interface-blockstore` package
import { IDBBlockstore } from 'blockstore-idb'

const blockstore = new IDBBlockstore('path/to/store')
await blockstore.open()

const fs = await FileSystem.create({
  blockstore,
})

// Create the private node of which we'll keep the encryption key around.
const { capsuleKey } = await fs.createPrivateNode({
  path: Path.root(), // ie. root private directory
})

// Write & Read
await fs.write(['private', 'file'], 'utf8', '🪺')

const contents = await fs.read(['private', 'file'], 'utf8')

Scenario 2: 🛰️ Listen to commit and/or publish events.

A commit is a (optionally verified) modification to the file system, and publishes are the debounced events resulting from the commits.

This will allow us the store the latest state of our file system, for this we need what we call the data root. This is the top-level CID of our root tree, the pointer to our file system.

let fsPointer: CID = await fs.calculateDataRoot()

// When we make a modification to the file system a verification is performed.
await fs.write(['private', 'file'], 'utf8', '🪺')

// If the commit is approved, the changes are reflected in the file system and
// the `commit` and `publish` events are emitted.
fs.on('commit', ({ dataRoot, modifications }) => {
  // Commit approved and performed ✅
})

fs.on('publish', ({ dataRoot }) => {
  // Commit approved and performed ✅
  // Debounced and delayed ✅
  fsPointer = dataRoot
})

Scenario 3: 🧳 Load a file system from a previous pointer.

// `blockstore` from scenario 1
// `fsPointer` from scenario 2
const fs = await FileSystem.fromCID(fsPointer, { blockstore })

// `capsuleKey` from scenario 1
await fs.mountPrivateNode({
  path: Path.root(),
  capsuleKey,
})

Actions

Queries

fs.exists
fs.listDirectory // alias: fs.ls
fs.read
fs.size

Mutations

fs.copy // alias: fs.cp
fs.move // alias: fs.mv
fs.createDirectory
fs.createFile
fs.ensureDirectory // alias: fs.mkdir
fs.remove // alias: fs.rm
fs.rename
fs.write

Identifier

fs.identifier()
fs.assignIdentifier('did')

Private Data Sharing

Flow:

  1. The receiver of a share register their exchange key. An app could do this automatically when the app starts, or at some other time.
  2. The data root of the receiver is passed to the sharer. Ideally this is done through some naming system. For example, you use DNS to map a username to the data root (eg. TXT file-system.tokono.ma could resolve to the data root, a CID). That said, this could also be done without a naming system, maybe by presenting a QR code.
  3. Make sure the sharer's file system has an identity assigned.
  4. The sharer creates the share.
  5. This step is the reverse of step 2, where we pass the sharer's data root to the receiver.
  6. Use the shared item.
// Step 1 & 2 (Receiver)
const { dataRoot } = await fs.registerExchangeKey('key-id', publicKey)
const receiverDataRoot = dataRoot

// Step 3, 4 & 5 (Sharer)
await fs.assignIdentifier('did')

const { dataRoot } = await fs.share(pathToPrivateItem, receiverDataRoot)
const sharerDataRoot = dataRoot

// Step 6 (Receiver)
const share = await fs.receive(sharerDataRoot, { publicKey, privateKey })

await share.read('utf8')

Manage private node using exchange key pair

Instead of keeping the (symmetric) capsule key around we can use an (asymmetric) exchange key pair to mount a private node. This basically creates a share for ourselves.

// 🚀 Create & mount
await fs.createPrivateNode({
  path: Path.root(),
  exchangeKeyPair: { publicKey, privateKey }, // 🔑 Pass in key pair here
})

// 🧳 Load
await fs.mountPrivateNode({
  path: Path.root(),
  exchangeKeyPair: { publicKey, privateKey },
})

Transactions

const result: Promise<
  | { modifications: Modification[]; dataRoot: CID }
  | 'no-op'
> = fs.transaction(t => {
  t.write(…)
  t.read(…)
  t.write(…)
  // You can use all the same methods as with the `fs` interface
})

Commit verification

This exists so you can approve modifications to the file system.

import { Modification } from '@wnfs-wg/nest'

const fs = FileSystem.create({
  blockstore,
  onCommit: async (
    modifications: Modification[]
  ): Promise<{ commit: boolean }> => {
    // For example, check if I have access to all paths.
    const satisfied = modifications.every((m) =>
      ALLOWED_PATHS.includes(Path.toPosix(m.path))
    )
    if (satisfied) return { commit: true }
    else return { commit: false }
  },
})

When you make a modification through the transaction method and the commit ends up not being approved, this will result in a "no-op" string. In the case of using a regular mutation method such as write it will produce an error.

Docs

Check https://wnfs-wg.github.io/nest

Contributing

Read contributing guidelines here.

Open in GitHub Codespaces

License

This project is licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.