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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@nothinq/marketplace

v1.4.0

Published

Nothinq Marketplace Index

Readme

Nothinq SDK

A TypeScript SDK for building extensions that integrate with the Nothinq platform. This SDK provides a communication bridge between extensions and the Nothinq host application.

Table of Contents

Installation

npm install @nothinq/sdk
# or
yarn add @nothinq/sdk
# or
bun add @nothinq/sdk

Quick Start

import { createSDK } from "@nothinq/sdk";

// Create SDK instance with your config type
const sdk = createSDK<MyConfigType>();

// Wait for SDK to be ready
sdk.onready(() => {
  console.log("Extension is ready!");

  // Notify host that extension has loaded
  sdk.loaded();
});

// Check if running inside Nothinq app
if (sdk.inapp()) {
  console.log("Running inside Nothinq");
}

Extension Manifest

Every Nothinq extension requires a manifest.json file that defines its metadata, configuration schema, and capabilities.

Basic Structure

{
  "identifier": "com.example.myextension",
  "author": "Your Name",
  "version": "1.0.0",
  "meta": {
    "description": "A brief description of your extension",
    "logo": "https://example.com/logo.png",
    "tags": ["productivity", "ai"]
  },
  "ui": {
    "url": "https://example.com/extension"
  }
}

Manifest Properties

Required Fields

  • identifier (string): Unique identifier for your extension (e.g., com.company.extension)
  • author (string): Extension author name

Optional Fields

  • version (string): Extension version following semver (e.g., 1.0.0)
  • system (string): System identifier if extension is system-level
  • scope (string): Extension scope - "workspace" or "user" (default: "user")
  • overview (string): Detailed overview or documentation URL

Metadata (meta)

{
  "meta": {
    "description": "Extension description shown in the store",
    "logo": "https://example.com/logo.png",
    "tags": ["category1", "category2"]
  }
}

UI Configuration (ui)

Define the extension's user interface:

{
  "ui": {
    "url": "https://example.com/extension-ui"
  }
}

Configuration Schema (config_schema)

Define user-configurable settings with validation:

{
  "config_schema": {
    "required": ["apiKey"],
    "properties": {
      "apiKey": {
        "key": "apiKey",
        "type": "string",
        "description": "Your API key"
      },
      "endpoint": {
        "key": "endpoint",
        "type": "string",
        "description": "API endpoint URL",
        "defaultValue": "https://api.example.com"
      },
      "enabled": {
        "key": "enabled",
        "type": "boolean",
        "description": "Enable the extension",
        "defaultValue": true
      },
      "maxRetries": {
        "key": "maxRetries",
        "type": "number",
        "description": "Maximum retry attempts",
        "defaultValue": 3
      },
      "mode": {
        "key": "mode",
        "type": "select",
        "description": "Operation mode",
        "defaultValue": "production",
        "options": [
          { "value": "development", "label": "Development" },
          { "value": "production", "label": "Production" }
        ]
      },
      "accessToken": {
        "key": "accessToken",
        "type": "auth",
        "description": "OAuth access token"
      }
    }
  }
}

Field Types:

  • string: Text input

    {
      "type": "string",
      "defaultValue": "default text"
    }
  • number: Numeric input

    {
      "type": "number",
      "defaultValue": 42
    }
  • boolean: Checkbox/toggle

    {
      "type": "boolean",
      "defaultValue": true
    }
  • select: Dropdown selection

    {
      "type": "select",
      "defaultValue": "option1",
      "options": [
        { "value": "option1", "label": "Option 1" },
        { "value": "option2", "label": "Option 2" }
      ]
    }
  • auth: OAuth/authentication token

    {
      "type": "auth",
      "description": "Authentication token"
    }

Field Properties:

  • key (required): Property key matching the config object
  • type (required): Field type (see above)
  • description: Help text shown to users
  • defaultValue: Default value for the field
  • dependencies: Comma-separated list of dependent field keys

Authentication (auth)

Configure OAuth or API key authentication:

OAuth 2.0 / OAuth 2.1:

{
  "auth": {
    "type": "oauth2",
    "authorization_endpoint": "https://provider.com/oauth/authorize",
    "token_endpoint": "https://provider.com/oauth/token",
    "refresh_endpoint": "https://provider.com/oauth/refresh",
    "scopes_supported": ["read", "write", "admin"]
  }
}

API Key:

{
  "auth": {
    "type": "apiKey"
  }
}

MCP Server Configuration (server)

Integrate with Model Context Protocol servers:

{
  "server": {
    "type": "stdio",
    "command": "node",
    "args": ["server.js"],
    "env": {
      "API_KEY": "{{config.apiKey}}"
    }
  }
}

Server Types:

  • stdio: Standard input/output communication

    {
      "type": "stdio",
      "command": "node",
      "args": ["server.js"],
      "env": { "KEY": "value" }
    }
  • http: HTTP-based communication

    {
      "type": "http",
      "url": "https://api.example.com/mcp",
      "headers": {
        "Authorization": "Bearer {{config.apiKey}}"
      }
    }
  • sse: Server-Sent Events

    {
      "type": "sse",
      "url": "https://api.example.com/events",
      "headers": {
        "Authorization": "Bearer {{config.apiKey}}"
      }
    }

Themes (themes)

Provide custom themes for the Nothinq interface:

{
  "themes": [
    {
      "id": "dark-theme",
      "name": "Dark Theme",
      "url": "https://example.com/themes/dark.json",
      "type": "dark"
    },
    {
      "id": "light-theme",
      "name": "Light Theme",
      "url": "https://example.com/themes/light.json",
      "type": "light"
    }
  ]
}

Commands (commands)

Define commands that can be triggered (future support):

{
  "commands": ["command.id1", "command.id2"]
}

Complete Example

{
  "identifier": "com.example.stripe",
  "author": "Example Corp",
  "version": "1.2.0",
  "scope": "workspace",
  "overview": "https://docs.example.com/stripe-extension",
  "meta": {
    "description": "Stripe payment integration for Nothinq",
    "logo": "https://example.com/stripe-logo.png",
    "tags": ["payments", "stripe", "commerce"]
  },
  "ui": {
    "url": "https://extension.example.com/stripe"
  },
  "auth": {
    "type": "oauth2",
    "authorization_endpoint": "https://connect.stripe.com/oauth/authorize",
    "token_endpoint": "https://connect.stripe.com/oauth/token",
    "scopes_supported": ["read_write"]
  },
  "config_schema": {
    "required": ["apiKey", "mode"],
    "properties": {
      "apiKey": {
        "key": "apiKey",
        "type": "auth",
        "description": "Stripe API key"
      },
      "mode": {
        "key": "mode",
        "type": "select",
        "description": "Operating mode",
        "defaultValue": "test",
        "options": [
          { "value": "test", "label": "Test Mode" },
          { "value": "live", "label": "Live Mode" }
        ]
      },
      "webhookSecret": {
        "key": "webhookSecret",
        "type": "string",
        "description": "Webhook signing secret",
        "dependencies": "mode"
      },
      "enableLogging": {
        "key": "enableLogging",
        "type": "boolean",
        "description": "Enable detailed logging",
        "defaultValue": false
      },
      "timeout": {
        "key": "timeout",
        "type": "number",
        "description": "Request timeout in seconds",
        "defaultValue": 30
      }
    }
  },
  "server": {
    "type": "http",
    "url": "https://api.stripe.com/mcp",
    "headers": {
      "Authorization": "Bearer {{config.apiKey}}"
    }
  },
  "themes": [
    {
      "id": "stripe-dark",
      "name": "Stripe Dark",
      "url": "https://extension.example.com/themes/dark.json",
      "type": "dark"
    }
  ]
}

Publishing Your Extension

  1. Create manifest.json in your extension root
  2. Validate the manifest against the schema
  3. Test locally using the Nothinq development tools
  4. Submit for review through the Nothinq extension store

Testing Manifest

To test your manifest locally:

import { createSDK } from "@nothinq/sdk";

const sdk = createSDK<YourConfigType>();

sdk.onready(async () => {
  // Get the config defined in your manifest
  const config = await sdk.config.get();
  console.log("Config:", config);

  // Config will match your config_schema structure
  console.log("API Key:", config.apiKey);
  console.log("Mode:", config.mode);
});

Best Practices

  1. Use semantic versioning for your extension version
  2. Provide clear descriptions for all config fields
  3. Set sensible defaults for optional configuration
  4. Request minimal permissions needed for functionality
  5. Validate required fields in your config_schema
  6. Use dependencies to show/hide conditional fields
  7. Test with different configurations before publishing
  8. Keep your manifest up to date with code changes

Core Concepts

SDK Initialization

The SDK automatically handles the connection handshake between your extension and the Nothinq host application. It uses a two-way ready state system to ensure both sides are properly initialized before allowing communication.

const sdk = createSDK<ConfigType>();

sdk.onready(() => {
  // Both parent and child are ready
  // Safe to make API calls
});

Message Communication

The SDK provides a message router for bidirectional communication:

// Listen to specific message types
const unsubscribe = sdk.listen("custom.event", async (message) => {
  console.log("Received:", message);
  return { success: true };
});

// Listen to all messages
sdk.listen(async (message) => {
  console.log("Global handler:", message);
});

// Dispatch messages to host
await sdk.dispatch({
  type: "custom.action",
  data: { foo: "bar" },
});

// Cleanup
unsubscribe();

API Reference

SDK Interface

inapp(): boolean

Check if the extension is running inside the Nothinq application.

if (sdk.inapp()) {
  // Extension is running in Nothinq
}

onready(handler: () => void): void

Register a callback that fires when both the extension and host are ready. If already ready, the handler is called immediately.

sdk.onready(() => {
  console.log("Ready to communicate!");
});

loaded(): void

Notify the host application that the extension has finished loading.

sdk.onready(() => {
  // Initialize your extension
  sdk.loaded();
});

listen(handler: MessageHandler): () => void

listen(type: string, handler: MessageHandler): () => void

Listen for messages from the host application. Returns an unsubscribe function.

// Type-specific listener
const unsubscribe = sdk.listen("user.action", async (message) => {
  // Handle message
  return { status: "processed" };
});

// Global listener
sdk.listen(async (message) => {
  // Handle all messages
});

dispatch(message: BaseMessage): Promise<void>

Send a message to the host application.

await sdk.dispatch({
  type: "extension.event",
  payload: { data: "value" },
});

Configuration API

config.get(): Promise<T>

Retrieve the extension's configuration from the host.

interface MyConfig {
  apiKey: string;
  endpoint: string;
}

const config = await sdk.config.get();
console.log(config.apiKey);

config.set(config: ExtraConfig<T>): Promise<void>

Update the extension's configuration. Can optionally include credential information.

await sdk.config.set({
  apiKey: "new-key",
  endpoint: "https://api.example.com",
  credential: {
    accessToken: "token",
    refreshToken: "refresh",
    expiresIn: 3600,
  },
});

Theme API

theme.get(): Promise<Theme>

Get the current theme settings from the host application.

const theme = await sdk.theme.get();
console.log(theme.type); // 'dark' | 'light'
console.log(theme.background);
console.log(theme.foreground);
console.log(theme.border);
console.log(theme.primary);

Environment API

env.set(env: Record<string, any>): Promise<void>

Set environment variables in the host application.

await sdk.env.set({
  API_KEY: "secret",
  DEBUG: "true",
});

File System API

fs.read(path: string): Promise<string | void>

Read a file from the host's file system.

const content = await sdk.fs.read("/path/to/file.txt");
console.log(content);

fs.write(path: string, data: string): Promise<void>

Write data to a file in the host's file system.

await sdk.fs.write("/path/to/file.txt", "Hello, World!");

AI API

ai.insertContextInput(context: PromptContext): Promise<void>

Insert context into the AI prompt input.

// Insert custom text context
await sdk.ai.insertContextInput({
  type: "custom",
  id: "ctx-1",
  name: "User Data",
  value: "Some context text",
});

// Insert file attachment
await sdk.ai.insertContextInput({
  type: "attachment",
  id: "file-1",
  name: "document.pdf",
  value: {
    name: "document.pdf",
    url: "https://example.com/doc.pdf",
    contentType: "application/pdf",
    size: 1024000,
  },
});

Sandbox API

sandbox.host(): Promise<string | void>

Get the sandbox host URL.

const host = await sdk.sandbox.host();
console.log("Sandbox host:", host);

TypeScript Types

Theme

interface Theme {
  type: "dark" | "light";
  background: string;
  foreground: string;
  border: string;
  primary: string;
}

PromptContext

interface PromptContext {
  type?: "custom" | "attachment";
  value: string | Attachment;
  id: string;
  name: string;
  loading?: boolean;
}

Attachment

interface Attachment {
  name: string;
  url: string;
  contentType: string;
  size: number;
}

ExtensionCredential

interface ExtensionCredential {
  id: string;
  accessToken: string;
  refreshToken?: string;
  tokenType?: string;
  expiresIn?: number;
  expiresAt?: number;
  createdAt?: number;
  name?: string;
}

BaseMessage

interface BaseMessage {
  type: string;
  [key: string]: any;
}

Message Router

The SDK includes a message router for handling incoming messages:

import { MessageRouter, MessageHandler } from "@nothinq/sdk";

const router = new MessageRouter();

// Set handler for specific message type
router.setHandler("custom.event", async (message) => {
  return { processed: true };
});

// Set global handler
router.setGlobalHandler(async (message) => {
  console.log("All messages:", message);
});

// Handle a message
await router.handleMessage({ type: "custom.event", data: "test" });

// Remove handlers
router.removeHandler("custom.event");
router.removeGlobalHandler();

Advanced Usage

Custom Configuration Type

Define your extension's configuration interface:

interface StripeConfig {
  apiKey: string;
  webhookSecret: string;
  mode: "test" | "live";
}

const sdk = createSDK<StripeConfig>();

sdk.onready(async () => {
  const config = await sdk.config.get();
  // config is typed as StripeConfig
  console.log(config.apiKey);
});

Handling Credentials

Store and retrieve OAuth tokens or API credentials:

await sdk.config.set({
  apiKey: "my-key",
  credential: {
    accessToken: "access-token",
    refreshToken: "refresh-token",
    expiresIn: 3600,
    expiresAt: Date.now() + 3600000,
    tokenType: "Bearer",
  },
});

Lifecycle Management

const sdk = createSDK<ConfigType>();

// 1. Wait for connection
sdk.onready(async () => {
  // 2. Initialize your extension
  await initializeExtension();

  // 3. Set up message listeners
  sdk.listen("user.action", handleUserAction);

  // 4. Notify host that loading is complete
  sdk.loaded();
});

Deprecated APIs

The following APIs are deprecated and should not be used in new code:

  • deprecated_expose() - Use the new SDK initialization instead
  • deprecated_connect() - Use the new SDK initialization instead

For disconnecting proxies, use:

import { disconnect } from "@nothinq/sdk";

disconnect(proxy);

Best Practices

  1. Always wait for ready state: Use sdk.onready() before making API calls
  2. Call loaded() when ready: Notify the host when your extension is fully initialized
  3. Type your configuration: Define a TypeScript interface for your config
  4. Handle errors: Wrap SDK calls in try-catch blocks
  5. Clean up listeners: Store unsubscribe functions and call them when needed
  6. Check inapp() status: Gracefully handle running outside Nothinq

Example Extension

import { createSDK, PromptContext } from "@nothinq/sdk";

interface MyExtensionConfig {
  apiEndpoint: string;
  enabled: boolean;
}

const sdk = createSDK<MyExtensionConfig>();

// Initialize
sdk.onready(async () => {
  try {
    // Get configuration
    const config = await sdk.config.get();

    // Get theme for UI styling
    const theme = await sdk.theme.get();
    document.body.style.background = theme.background;

    // Listen for custom events
    sdk.listen("data.request", async (message) => {
      const data = await fetchData(config.apiEndpoint);
      return { data };
    });

    // Notify host we're ready
    sdk.loaded();
  } catch (error) {
    console.error("Initialization failed:", error);
  }
});

// Send custom events
async function sendNotification(text: string) {
  await sdk.dispatch({
    type: "extension.notification",
    message: text,
  });
}

License

MIT