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

@oddjs/odd

v0.37.2

Published

ODD SDK

Downloads

2,490

Readme

ODD SDK

NPM License Built by FISSION Discord Discourse

The ODD SDK empowers developers to build fully distributed web applications without needing a complex back-end. The SDK provides:

  • User accounts via the browser's Web Crypto API or by using a blockchain wallet as a ODD plugin.
  • Authorization using UCAN.
  • Encrypted file storage using WNFS backed by IPLD.
  • Key management using websockets and a two-factor auth-like flow.

ODD applications work offline and store data encrypted for the user by leveraging the power of the web platform. You can read more about the ODD SDK in Fission's ODD SDK Guide. There's also an API reference which can be found at api.odd.dev

Installation

The ts-odd package is published on yarn, pnpm and npm as @oddjs/odd:

npm

npm install @oddjs/odd

pnpm

pnpm install @oddjs/odd

yarn

yarn add @oddjs/odd

Getting started

// ESM
import * as odd from "@oddjs/odd"

// Browser/UMD build
const odd = globalThis.oddjs

Creating a Program

An ODD program is an assembly of components that make up a distributed web application. Several of the components can be customized. Let's stick with the default components for now, which means we'll be using the Web Crypto API.

const program = await odd.program({
  // Can also be a string, used as an identifier for caches.
  // If you're developing multiple apps on the same localhost port,
  // make sure these differ.
  namespace: { creator: "Nullsoft", name: "Winamp" }

}).catch(error => {
  switch (error) {
    case odd.ProgramError.InsecureContext:
      // The ODD SDK requires HTTPS
      break;
    case odd.ProgramError.UnsupportedBrowser:
      break;
  }

})

odd.program returns a Program object, which can create a new user session or reuse an existing session. There are two ways to create a user session, either by using an authentication strategy or by requesting access from another app through the "capabilities" system. Let's start with the default authentication strategy.

let session

// Do we have an existing session?
if (program.session) {
  session = program.session

// If not, let's authenticate.
// (a) new user, register a new Fission account
} else if (userChoseToRegister) {
  const { success } = await program.auth.register({ username: "llama" })
  session = success ? program.auth.session() : null

// (b) existing user, link a new device
} else {
  // On device with existing session:
  const producer = await program.auth.accountProducer(program.session.username)

  producer.on("challenge", challenge => {
    // Either show `challenge.pin` or have the user input a PIN and see if they're equal.
    if (userInput === challenge.pin) challenge.confirmPin()
    else challenge.rejectPin()
  })

  producer.on("link", ({ approved }) => {
    if (approved) console.log("Link device successfully")
  })

  // On device without session:
  //     Somehow you'll need to get ahold of the username.
  //     Few ideas: URL query param, QR code, manual input.
  const consumer = await program.auth.accountConsumer(username)

  consumer.on("challenge", ({ pin }) => {
    showPinOnUI(pin)
  })

  consumer.on("link", async ({ approved, username }) => {
    if (approved) {
      console.log(`Successfully authenticated as ${username}`)
      session = await program.auth.session()
    }
  })
}

Alternatively you can use the "capabilities" system when you want partial access to a file system. At the moment of writing, capabilities are only supported through the "Fission auth lobby", which is an ODD app that uses the auth strategy shown above.

This Fission auth lobby flow works as follows:

  1. You get redirected to the Fission lobby from your app.
  2. Here you create an account like in the normal auth strategy flow shown above.
  3. The lobby shows what your app wants to access in your file system.
  4. You approve or deny these permissions and get redirected back to your app.
  5. Your app collects the encrypted information (UCANs & file system secrets).
  6. Your app can create a user session.
// We define a `Permissions` object,
// this represents what permissions to ask the user.
const permissions = {
  // Ask permission to write to and read from the directory:
  // private/Apps/Nullsoft/Winamp
  app: { creator: "Nullsoft", name: "Winamp" }
}

// We need to pass this object to our program
const program = await odd.program({
  namespace: { creator: "Nullsoft", name: "Winamp" },
  permissions
})

// (a) Whenever you are ready to redirect to the lobby, call this:
program.capabilities.request()

// (b) When you get redirected back and your program is ready,
// you will have access to your user session.
session = program.session

Once you have your Session, you have access to your file system 🎉

const fs = session.fs

Notes:

  • You can use alternative authentication strategies, such as odd-walletauth.
  • You can remove all traces of the user using await session.destroy()
  • You can load the file system separately if you're using a web worker. This is done using the combination of configuration.fileSystem.loadImmediately = false and program.fileSystem.load()
  • You can recover a file system if you've downloaded a Recovery Kit by calling program.fileSystem.recover({ newUsername, oldUsername, readKey }). The oldUsername and readKey can be parsed from the uploaded Recovery Kit and the newUsername can be generated before calling the function. Please refer to this example from Fission's ODD App Template. Additionally, if you would like to see how to generate a Recovery Kit, you can reference this example

Working with the file system

The Webnative File System (WNFS) is a file system built on top of IPLD. It supports operations similar to your macOS, Windows, or Linux desktop file system. It consists of a public and private branch: The public branch is "live" and publicly accessible on the Internet. The private branch is encrypted so that only the owner can see the contents. Read more about it here.

const { Branch } = odd.path

// List the user's private files
await fs.ls(
  odd.path.directory(Branch.Private)
)

// Create a sub directory and add some content
const contentPath = odd.file(
  Branch.Private, "Sub Directory", "hello.txt"
)

await fs.write(
  contentPath,
  new TextEncoder().encode("👋") // Uint8Array
)

// Persist changes and announce them to your other devices
await fs.publish()

// Read the file
const content = new TextDecoder().decode(
  await fs.read(contentPath)
)

That's it, you have successfully created an ODD app! 🚀

POSIX Interface

WNFS exposes a familiar POSIX-style interface:

  • exists: check if a file or directory exists
  • ls: list a directory
  • mkdir: create a directory
  • mv: move a file or directory
  • read: read from a file
  • rm: remove a file or directory
  • write: write to a file

Versioning

Each file and directory has a history property, which you can use to get an earlier version of that item. We use the delta variable as the order index. Primarily because the timestamps can be slightly out of sequence, due to device inconsistencies.

const file = await fs.get(odd.path.file("private", "Blog Posts", "article.md"))

file.history.list()
// { delta: -1, timestamp: 1606236743 }
// { delta: -2, timestamp: 1606236532 }

// Get the previous version
file.history.back()

// Go back two versions
const delta = -2
file.history.back(delta)

// Get the first version (ie. delta -2)
// by providing a timestamp
file.history.prior(1606236743)

Sharing Private Data

https://docs.odd.dev/sharing-private-data

Migration

Some versions of the ODD SDK require apps to migrate their codebase to address breaking changes. Please see our migration guide for help migrating your apps to the latest ODD SDK version.

Debugging

Debugging mode can be enable by setting debug to true in your configuration object that you pass to your Program. By default this will add your programs to the global context object (eg. window) under globalThis.__odd.programs (can be disabled, see API docs).

const appInfo = { creator: "Nullsoft", name: "Winamp" }

await odd.program({
  namespace: appInfo,
  debug: true
})

// Automatically exposed Program in debug mode
const program = globalThis.__odd[ odd.namespace(appInfo) ] // namespace: "Nullsoft/Winamp"