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

@dylibso/xtp

v0.0.0-rc12

Published

XTP for JavaScript

Downloads

139

Readme

@dylibso/xtp

The JS SDK for XTP.

QuickStart

The first step to integrating with XTP is to create a Client:

import createClient from '@dylibso/xtp'

const client = await createClient({
  appId: process.env.APP_ID, // looks like: 'app_xxxxxxxxxxxxxxxxxxxxxxxx',
  token: process.env.XTP_TOKEN, // looks like: 'xtp0_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
  logger: console,
  useWasi: true
})

You'll need your appId and a token.

Note: XTP_TOKEN is a secret and is sensitive. Don't store the TOKEN in source code.

The client instance holds the connection to the API and any plugins you may want to run. For this reason you probably want to share the instance across your whole application. If you need to share this across multiple modules, you should use a singleton constructor:

// xtpClient.js

let xtpClient = null;
export async function getXtpClient() {
  if (!xtpClient) {
    xtpClient = await createClient({
      // your client options here
    })
  }
  return xtpClient
}


// mymodule.js
import { getXtpClient } from './xtpClient'
const xtpClient = await getXtpClient()

If your plug-in developers may be installing multiple plug-ins on an extension-point, you'll need a binding name. If not you can skip this step. You can ask which plugins are bound to an extension-point with the listAvailablePlugins

const GUEST_KEY = 'acme-corp'
const EXT_NAME = 'MyExtensionPoint'

const pluginNames = await xtpClient.listAvailablePlugins(EXT_NAME, GUEST_KEY)
// ['plugin1', 'plugin2']

Now you can actually execute the code. This next step will do a few things for you:

  1. Fetch the latest plug-in from XTP
  2. Cache it on disk
  3. Compile and load it
  4. Cache it in memory
  5. Execute the function
// assuming your extension point has an export `myExport`

const result = await client.extensionPoints.MyExtensionPoint.myExport(
  GUEST_KEY,
  'input data', // depends on what plug-in expects
  {
    bindingName: 'plugin1', // optional item from the list of names we got in the following step
    default: 'default value' // can be an object, Uint8Array, or string
  }
)

console.log(result)

API

fn createClient(opts: XTPClientOptions): Promise<Client>

Create a client with the provided options. On instantiation, the Client fetches the available list of extension points from the XTP API. If this call fails the promise returned by createClient will reject.

interface XTPClientOptions

  • baseUrl: defaults to https://xtp.dylibso.com.
  • token: An XTP API Token.
  • functions: A {[string]: {[string]: CallableFunction}} map to be exposed to modules.
  • logger: A pino-compatible logger.
  • keepResidentMs: The number of milliseconds to keep plugins "live" after a call. This means the plugin is in-memory and subsequent calls to the same extension point and guest tag will re-use the plugin instance. Resets after each call. Defaults to environment variable XTP_KEEP_RESIDENT_MS, or 5000ms.
  • refetchAfterMs: The number of milliseconds to treat local cached plugin data as "fresh"; after which the client will "re-fetch" the installed plugin for the given extension point and tag to revalidate the cache.
  • useWasi: boolean, defaults to true -- whether or not to enable WASIp1 for guests.
  • storage: Any ExtensionStorage interface. Uses localStorage on Deno, cacache on Node.
  • fetch: Any fetch-compatible function for making requests.

class Client

fn client.clone(opts: Partial<XTPClientOptions>): Client

Clone an existing client, bypassing the "load extension points" call during typical startup. Options for the client can be partially overridden.

fn close(): Promise<void>

Close all "live" plugins. May be called repeatedly.

fn inviteGuest(opts: RegisterGuest): Promise<Json>

Invite a guest to develop and install plugins on your XTP application.

interface RegisterGuest

  • email: string: The guest's email address.
  • name: string: The human-readable name of the guest.
  • guestKey: string: A unique string held by your application to identify the guest.

prop extensionPoints

fn extensionPoints[extName: string][exportName: string](guestKey: string, param: T, defaultValue:T): Promise<T>

const result = await client.extensionPoints.foo.bar('my guest key', {hello: 'world'}, { bindingName: 'my-plugin', default: 'default value' })

Call a plugin export installed by a guest (identified by guestKey) at an extension point.

opts:

Use the bindingName to idenfity the named plugin to return. Defaults to "default". Use optional default to return a default value


interface ExtensionStorage

Implement your own extension storage for fun and profit! Reach for this interface if the default storage options aren't working for you -- if you'd like to store plugins in a database, for example.

fn getByExtIdGuestKey(extId: string, guestKey: string): Promise<StoredPlugin | null>

Fetch plugin content based on an extId/guestKey pair. Must refer to the same StoredPlugin content as returned by getByETag.

Return null if not present.

fn getByETag(etag: string): Promise<StoredPlugin | null>

Fetch plugin content based on the ETag value returned by fetching installations. Must refer to the same StoredPlugin content as returned by getByExtIdGuestKey.

Return null if not present.

fn store(extId: string, guestKey: string, etag: string, meta: Record<string, string>, content: Uint8Array): Promise<void>

Store plugin content, indexed by both (extId, guestKey) and etag.

interface StoredPlugin

  • metadata: Record<string, string>: Metadata about the record. Should include etag, content-type, and last (a number representing the milliseconds since unix epoch at which the plugin content was last stored.)
  • data: Uint8Array: The plugin content, suitable for passing to WebAssembly.compile.
  • size: number: The size of the stored data.