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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@nanograph/sdk

v0.1.23

Published

Official Volted Node.js SDK

Readme

Nano SDK for NodeJS

This package provides the NodeJS implementation of the Nano SDK, which allows you to create node servers that can communicate with the Nano orchestrator.

Installation

npm i @nanograph/sdk

Usage

Creating a Server

import { NanoSDK } from '@nanograph/sdk'

// Initialize SDK
const sdk = new NanoSDK({
  serverName: 'My Node Server',
  serverUid: 'my-node-server',
  domain: 'local-nodeserver.nanograph',
  port: 3016,
  nodesPath: 'src/nodes'
})

// Start the server
async function main() {
  await sdk.start()
  console.log('Server started')
}

// Handle shutdown
sdk.onShutdown(() => {
  console.log('Server is shutting down')
})

// Graceful shutdown
process.on('SIGINT', () => sdk.stop())
process.on('SIGTERM', () => sdk.stop())

// Run the server
main().catch(console.error)

Creating Nodes

import { NanoSDK, NodeDefinition, NodeInstance } from '@nanograph/sdk'

// Define the node
const nodeDef: NodeDefinition = {
  uid: 'my-unique-node-id',
  name: 'My Node',
  category: 'Processing',
  version: '1.0.0',
  type: 'server',
  description: 'Description of my node',
  inputs: [
    {
      name: 'input1',
      type: 'string',
      description: 'First input'
    }
  ],
  outputs: [
    {
      name: 'output1',
      type: 'string',
      description: 'First output'
    }
  ],
  parameters: [
    {
      name: 'param1',
      type: 'boolean',
      value: true,
      default: true,
      label: 'Parameter 1',
      description: 'Description of parameter 1'
    }
  ]
}

// Register the node
const myNode: NodeInstance = NanoSDK.registerNode(nodeDef)

// Implement the execution logic
myNode.execute = async ({ inputs, parameters, context }) => {
  // Get input values (inputs are now arrays)
  const input1 = (inputs.input1 && inputs.input1[0]) || ''
  
  // Send status update
  context.sendStatus({ type: 'running', message: 'Processing...' })
  
  // Check for abort
  if (context.isAborted()) {
    throw new Error('Execution aborted')
  }
  
  // Process the inputs
  const output1 = `Processed: ${input1}`
  
  // Return the outputs (outputs must be arrays)
  return { output1: [output1] }
}

export default myNode

Multiple nodes can be defined in a single file, or you can organize your nodes in a directory structure.

Folder Structure

Recommended project structure:

my-node-server/
├── src/
│   ├── index.ts          # Entry point
│   └── nodes/            # Nodes directory (automatically scanned)
│       ├── processing/   # Category directory
│       │   ├── TextUppercase.ts  # Single node file
│       │   └── complex/   # Complex node with multiple files
│       │       ├── index.ts      # Main node definition
│       │       └── utils.ts      # Helper functions
│       └── math/         # Another category
└── package.json         # Dependencies

Configuration

The SDK accepts the following configuration options:

| Key | Type | Default | Description | |-----------------------|-----------|-----------|--------------------------------------------------------------------| | serverName | string | (required) | Human-readable name of your server | | serverUid | string | (required) | Unique identifier for your server (no spaces or special chars) | | domain | string | (required) | Domain to group servers (e.g., "local-nodeserver.nanograph") | | port | number | 3016 | HTTP port for server to listen on | | nodesPath | string | "nodes" | Directory path containing node definition files | | autoWatch | boolean | true | Automatically reload nodes when files change | | watchDebounceTime | number | 500 | Debounce time (ms) for file change detection |

Example configuration:

const sdk = new NanoSDK({
  serverName: 'My Processing Server',
  serverUid: 'my-processing-server',
  domain: 'local-processing.nanograph',
  port: 3016,
  nodesPath: 'src/nodes',
  autoWatch: true,
  watchDebounceTime: 1000
})

API Reference

NanoSDK

The main SDK class.

constructor(config: NanoSDKConfig)

Create a new SDK instance.

static registerNode(definition: NodeDefinition): NodeInstance

Register a node with the SDK.

async start(): Promise<void>

Start the node server.

async stop(): Promise<void>

Stop the node server.

onShutdown(handler: () => void | Promise<void>): void

Register a handler to be called when the server is shutting down.

NodeInstance

Represents a registered node.

definition: NodeDefinition

The node definition.

execute: ExecuteFunction

The function to execute when the node is called.

ExecuteFunction

type ExecuteFunction = (ctx: ExecutionContext) => Promise<NodeOutputs> | NodeOutputs

ExecutionContext

interface ExecutionContext {
  inputs: NodeInputs
  parameters: Parameter[]
  context: {
    sendStatus: (status: NodeStatus) => void
    isAborted: () => boolean
    graphNode: GraphNode
    instanceId: string
  }
}

NodeStatus Reference

The NodeStatus object is used to communicate the current status, progress, or result of a node execution back to the orchestrator. You send it using context.sendStatus(status) from within your node's execute function.

NodeStatus fields:

| Field | Type | Description | |------------|---------------------|----------------------------------------------------------------------| | type | string | One of: 'idle', 'running', 'complete', 'error', 'missing' | | message | string (optional) | Human-readable status or error message | | progress | object (optional) | Progress info, e.g. { step: 2, total: 5 } | | outputs | object (optional) | Output values (only for 'complete' status) |

Example: Sending progress updates from a node

myNode.execute = async ({ inputs, parameters, context }) => {
  const totalSteps = 5
  for (let step = 1; step <= totalSteps; step++) {
    if (context.isAborted()) {
      throw new Error('Aborted!')
    }

    // Simulate work
    await new Promise(res => setTimeout(res, 1000))
    // Send progress update
    context.sendStatus({
      type: 'running',
      message: `Processing step ${step}/${totalSteps}`,
      progress: { step, total: totalSteps }
    })
  }
  // Just return the outputs as arrays; the SDK will send the 'complete' status automatically
  return { result: ['done'] }
}

Note: You do not need to manually send a 'complete' status at the end. The SDK will automatically send a 'complete' status with the outputs you return from your execute function.

Asset Management (NanoCore integration)

Configuration: Set NANOCORE_HTTP_ENDPOINT (e.g., https://host:3001) and NANOCORE_TOKEN in your environment; the SDK uses these to communicate with NanoCore.

Asset Reference Format

Assets are referenced using a canonical URI scheme:

nanocore://<domain>/asset/<uuid>
  • <domain>: Logical domain of the NanoCore server/cluster.
  • <uuid>: The unique asset identifier in NanoCore.

To resolve/download, clients use the server’s HTTP endpoint (nanocoreHttpEndpoint, e.g., https://host:3001). In this SDK, configure it via the NANOCORE_HTTP_ENDPOINT environment variable.

Node Output Convention

Nodes should output asset references (not blobs/base64):

return { output1: [ "nanocore://stairs-need-actor/asset/123e4567-e89b-12d3-a456-426614174000" ] }

1. Uploading an Asset

Use the uploadAsset function to upload a file (from a file path, Buffer, or stream) to NanoCore.

import { uploadAsset } from '@nanograph/sdk';

// Upload from a file path
const result = await uploadAsset('/path/to/myfile.png', {
  type: 'image', // optional
  meta: { description: 'My image' } // optional
});

// Result:
console.log(result.uri);   // Asset URI (e.g. nanocore://.../asset/...)
console.log(result.uuid);  // Asset UUID

You can also upload a Buffer or a stream:

const buffer = Buffer.from(...);
await uploadAsset(buffer, { type: 'image' });

2. Downloading an Asset

To download an asset from its URI:

import { resolveAsset } from '@nanograph/sdk';

const ref = 'nanocore://stairs-need-actor/asset/123e4567-e89b-12d3-a456-426614174000';
const stream = await resolveAsset(ref);
// `stream` is a Node.js Readable Stream

// To get the content as a Buffer (for small files only):
const buffer = await resolveAsset(ref, { asBuffer: true });

3. Get the Direct Download URL (Backend Use)

If you need the direct download URL (with backend authentication):

import { getAssetDownloadUrl } from '@nanograph/sdk';

const url = getAssetDownloadUrl(ref);
// url = 'https://.../assets/<uuid>/download'

4. Generate a Temporary Signed URL (Frontend Use)

To allow a client (e.g. browser) to download an asset without exposing your API token, generate a temporary signed URL:

import { getAssetPresignedUrl } from '@nanograph/sdk';

const { url, expiresIn } = await getAssetPresignedUrl(ref);
// `url` is a temporary direct-access URL to the asset (valid for `expiresIn` seconds)

This URL can be safely sent to the frontend, which can download the asset without any further authentication.


5. Asset Reference Format

The asset reference format is always:

nanocore://<domain>/asset/<uuid>

Resolution uses the configured NANOCORE_HTTP_ENDPOINT (NodeServer nanocoreHttpEndpoint).


Note:
All SDK functions automatically use the configuration provided by NanoCore.
No sensitive information (such as API tokens) is ever exposed to the frontend, thanks to the use of signed URLs.

License

MIT