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

@codatum/embed

v1.0.0

Published

TypeScript library for Codatum signed embedding

Readme

@codatum/embed

Core TypeScript SDK for embedding Codatum Notebook with signed embed: create the iframe in the browser, pass a token from your backend, handle parameters and events. Server-side token issuance is out of scope.

Docs: Signed embed · Integration · Use cases. Wrappers: @codatum/embed-react, @codatum/embed-vue.

Installation

pnpm add @codatum/embed
# or
npm install @codatum/embed

Quick start

import { createEmbed } from '@codatum/embed';

const embed = createEmbed({
  container: '#dashboard',
  embedUrl: 'https://app.codatum.com/protected/workspace/xxx/notebook/yyy',
  tokenProvider: async () => {
    // Issue a token in your backend and fetch it here
    const res = await fetch('/api/codatum/token', { method: 'POST' });
    const data = await res.json();
    return { token: data.token };
  },
});
await embed.init();

// cleanup
embed.destroy();

Embed

EmbedOptions

| Property | Required | Description | |--------|----------|-------------| | container | Yes | HTMLElement or CSS selector where the iframe is inserted | | embedUrl | Yes | Signed embed URL from Codatum | | tokenProvider | Yes | See tokenProvider below | | iframeOptions | No | See IframeOptions below | | tokenOptions | No | See TokenOptions below | | displayOptions | No | See DisplayOptions below | | devOptions | No | See DevOptions below |

tokenProvider

Required callback that issues a token from your backend and returns it (and optionally params). Called on init(), reload(), and when the token is about to expire (unless tokenOptions.disableRefresh is true).

Signature: (context: TokenProviderContext) => Promise<{ token: string; params?: EncodedParam[] }>

  • context.trigger'INIT' | 'RELOAD' | 'REFRESH'.
  • context.markNonRetryable() — Call on failure to skip retries (ignores tokenOptions.retryCount).
  • params — Optional. If returned, sent to the embed with the token; use ParamMapper encode() to build.

Example:

tokenProvider: async (context) => {
  const res = await fetch('/api/codatum/token', {
    method: 'POST',
    body: JSON.stringify({ tenant_id: currentUser.tenantId }),
  });
  if (!res.ok) {
    if (res.status === 401) context.markNonRetryable();
    throw new Error(`Token failed: ${res.status}`);
  }
  const data = await res.json();
  const params = paramMapper.encode({ 
    store_id: currentUser.defaultStoreId,
    date_range: ['2025-01-01', '2025-01-31']
  });
  return { token: data.token, params };
}

IframeOptions

Options applied to the iframe element and passed to the embed via URL/search params.

| Property | Type | Default | Description | |----------|------|---------|-------------| | theme | 'LIGHT' | 'DARK'| 'SYSTEM' | 'SYSTEM' | UI theme of the embedded notebook | | locale | string | Browser's locale | Locale code (e.g. 'en', 'ja') for the embed UI | | className | string | - | CSS class name(s) applied to the iframe element | | style | object | {display: 'block', width: '100%', height: '100%', border: 'none'} | Inline styles for the iframe; overrides the default styles | | attrs | Record<string, string> | - | Additional HTML attributes for the iframe (e.g. { title: 'Dashboard', 'data-testid': 'embed-iframe' }) |

TokenOptions

Controls token lifetime, refresh behavior, and loading timeout.

| Property | Type | Default | Description | |----------|------|---------|-------------| | disableRefresh | boolean | false | When true, disables automatic token refresh before the token expires | | refreshBuffer | number | 60 | Number of seconds before the token expires when auto-refresh is triggered | | retryCount | number | 2 | Number of retries on token fetch failure; 0 = no retry | | loadingTimeout | number | 30 | Max wait in seconds for CONTENT_READY after entering INITIALIZING, RELOADING, or REFRESHING; 0 = no timeout | | onRefreshError | (error: EmbedError) => void | undefined | Callback invoked when token auto-refresh fails (due to tokenProvider failure or loading timeout) and does not recover after all retries |

DisplayOptions

Sent to the embed with the token.

| Property | Type | Default | Description | |----------|------|---------|-------------| | sqlDisplay | 'SHOW' | 'RESULT_ONLY' | 'HIDE' | 'SHOW' | Whether to show SQL Blocks, results only, or hide | | hideParamsForm | boolean | false | Hide the parameter form in the embed (e.g. when your app owns the filters) | | expandParamsFormByDefault | boolean | false | Whether the parameter form is expanded by default |

DevOptions

Development and testing only. Not intended for production.

| Property | Type | Default | Description | |----------|------|---------|-------------| | debug | boolean | false | When true, logs SDK lifecycle: postMessage (in/out), tokenProvider calls/refresh/retries, status transitions. | | disableValidateUrl | boolean | false | When true, skips embedUrl format validation (domain/path). Use for local or staging URLs that don't match the production pattern. | | mock | boolean | MockOptions | - | Enables mock mode: no real embed load, no network. iframe uses srcdoc with a styled placeholder. |

MockOptions (when mock is an object):

| Property | Type | Default | Description | |----------|------|---------|-------------| | label | string | embedUrl | Text shown in the mock iframe placeholder. Useful when multiple embeds are on the page. | | callTokenProvider | boolean | false | When true, calls tokenProvider on init(), reload(), and auto-refresh. | | loadingDelay | number | 0 | Delay in seconds before the embed is loaded. Useful for testing loading UI. |

Creating an embed instance

createEmbed(options: EmbedOptions): EmbedInstance

Creates an embed instance. Throws EmbedError if options are invalid. Call init() to create the iframe and start the token flow.

Instance methods

| Method | Description | |--------|-------------| | async init() | Creates the iframe, waits for it to be ready, calls tokenProvider, and sends token (and optional params) to the embed. Resolves when the embed sends CONTENT_READY. Rejects with EmbedError on failure. | | async reload() | Calls tokenProvider, sends the returned token and params via SET_TOKEN, and resolves when the embed sends CONTENT_READY. Rejects with EmbedError on failure. | | destroy() | Removes iframe, clears listeners and timers. No-op if already destroyed. |

Instance properties

| Property | Type | Description | |----------|------|-------------| | iframe | HTMLIFrameElement \| null | The embed iframe element. | | status | 'CREATED' \| 'INITIALIZING' \| 'RELOADING' \| 'REFRESHING' \| 'READY' \| 'DESTROYED' | Current instance state. |

Container attribute

The SDK sets a data-codatum-embed-status attribute on the container element, reflecting the current status. The attribute is added when init() is called and removed on destroy(). Use it for CSS-based styling or E2E test selectors.

| Status | When | |--------|------| | INITIALIZING | init() called, waiting for content | | READY | Content loaded and visible | | RELOADING | reload() called, waiting for content | | REFRESHING | Auto-refresh in progress | | DESTROYED | (attribute removed) |

Example: Custom loading UI

Show your own loading indicator during initialization while keeping the iframe hidden:

<div id="dashboard" style="position: relative;">
  <div class="my-loading">Loading…</div>
</div>
/* Hide iframe and show loading UI while loading */
[data-codatum-embed-status="INITIALIZING"] iframe,
[data-codatum-embed-status="RELOADING"] iframe,
[data-codatum-embed-status="REFRESHING"] iframe {
  visibility: hidden;
}
[data-codatum-embed-status="INITIALIZING"] .my-loading,
[data-codatum-embed-status="RELOADING"] .my-loading,
[data-codatum-embed-status="REFRESHING"] .my-loading {
  display: flex;
  position: absolute;
  inset: 0;
  align-items: center;
  justify-content: center;
}

/* Hide loading UI when ready */
[data-codatum-embed-status="READY"] .my-loading {
  display: none;
}

Without any custom CSS, the embed's built-in loading screen is shown as-is inside the iframe.

Events

Subscribe with on(event, handler) and off(event, handler).

| Event | Description | Payload | |-------|-------------|---------| | statusChanged | Instance status changed. | { type: 'STATUS_CHANGED', status: EmbedStatus, previousStatus: EmbedStatus } | | paramChanged | User changed parameters in the embed. | { type: 'PARAM_CHANGED', params: EncodedParam[] } | | executeSqlsTriggered | SQL execution was triggered in the embed. | { type: 'EXECUTE_SQLS_TRIGGERED', params: EncodedParam[] } | | executionSucceeded | SQL execution was completed successfully. | { type: 'EXECUTION_SUCCEEDED' } | | executionFailed | SQL execution was failed. | { type: 'EXECUTION_FAILED', errorMessage: string } |

Decode with ParamMapper.decode(payload.params). EncodedParam: see ParamMapper.

ParamMapper

The embed uses param_ids (IDs assigned per notebook parameter). Your app typically works with meaningful keys such as store_id or date_range. ParamMapper maps between your app's key–value pairs and Codatum's param_id + param_value in both directions.

Basic usage

import { createParamMapper } from '@codatum/embed';

const paramMapper = createParamMapper({
  store_id: '67a1b2c3d4e5f6a7b8c9d0e1',
  date_range: '67a1b2c3d4e5f6a7b8c9d0e2',
  product_category: '67a1b2c3d4e5f6a7b8c9d0e3',
});

const appState = {
  store_id: 'store_001',
  date_range: ['2025-01-01', '2025-01-31'],
  product_category: ['Electronics'],
};

// encode: app key:value → EncodedParam[] (use in tokenProvider return)
paramMapper.encode(appState);
// → [
//   { param_id: '67a1b2c3...', param_value: '"store_001"' },
//   { param_id: '67a1b2c3...', param_value: '["2025-01-01","2025-01-31"]' },
//   { param_id: '67a1b2c3...', param_value: '["Electronics"]' },
// ]

// decode: EncodedParam[] → app key:value (use in paramChanged / executeSqlsTriggered)
paramMapper.decode(payload.params);
// → { store_id: 'store_001', date_range: [...], product_category: ['Electronics'] }

Creating a mapper

createParamMapper(mapping, meta?)

| Argument | Type | Description | |----------|------|-------------| | mapping | Record<string, string> | Your app's key → Codatum param_id for each parameter. | | meta | Record<key, ParamMeta> (optional) | Same keys as mapping. Per-key options: hidden, required, datatype. |

ParamMeta (optional, per key):

| Property | Type | Description | |----------|------|-------------| | datatype | ParamDatatype | Enables validation and improves typing. See Param datatypes below. | | required | boolean | If true, encode and decode throw MISSING_REQUIRED_PARAM when the param is missing. | | hidden | boolean | If true, encoded params include is_hidden: true so the embed can hide them from the params form. |

Param datatypes

When meta[key].datatype is set, encode and decode validate values. Supported values:

| ParamDatatype | JS/TS type | Notes | |-----------------|------------|--------| | 'STRING' | string | — | | 'NUMBER' | number | Rejects NaN. | | 'BOOLEAN' | boolean | — | | 'DATE' | string | Must be YYYY-MM-DD. | | 'STRING[]' | string[] | Array of strings. | | '[DATE, DATE]' | [string, string] | Date range; both elements must be YYYY-MM-DD. |

Instance methods

| Method | Description | |--------|-------------| | encode(values, options?) | App key:value → EncodedParam[]. JSON-stringifies values. Use RESET_TO_DEFAULT as a value to reset that param to the notebook's default. | decode(params, options?) | EncodedParam[] → app key:value. Ignores params not in mapping. |

encode / decode options

Both methods accept an optional second argument:

| Option | Type | Description | |--------|------|-------------| | only | (keyof mapping)[] | Limit to these keys. Useful when the host sends only a subset (e.g. server sends some params; client sends the rest via encode with only). | | noValidate | boolean | When true, skips required and datatype validation. |

Type-first usage

When you define parameter definitions (e.g. with datatype) and want typed encode/decode, you can use the helper types.

import { createParamMapper, RESET_TO_DEFAULT, type DefineDecodedParams, type EncodedParam } from '@codatum/embed';

const paramDefs = {
  store_id: { datatype: 'STRING', required: true },
  date_range: { datatype: '[DATE, DATE]' },
  product_category: { datatype: 'STRING[]' },
} as const;

type ParamValues = DefineDecodedParams<typeof paramDefs>;
// → { store_id: string, date_range?: [string, string], product_category?: string[] }

const paramValues: ParamValues = {
  store_id: 'store_001',
  date_range: RESET_TO_DEFAULT,
  product_category: ['Electronics']
};

const paramMapper = createParamMapper({
  store_id: '67a1b2c3d4e5f6a7b8c9d0e1',
  date_range: '67a1b2c3d4e5f6a7b8c9d0e2',
  product_category: '67a1b2c3d4e5f6a7b8c9d0e3',
}, paramDefs);

// encode only the date_range and product_category params for client-side params
const clientParams = paramMapper.encode(paramValues, { only: ['date_range', 'product_category'] })
// → [
//   { param_id: '67a1b2c3d4e5f6a7b8c9d0e2', param_value: '["2025-01-01","2025-01-31"]' },
//   { param_id: '67a1b2c3d4e5f6a7b8c9d0e3', param_value: '["Electronics"]' },
// ]

const onParamChanged = (ev: { params: EncodedParam[] }) => {
  const values: ParamValues = paramMapper.decode(ev.params);
  console.log('Changed:', values);
};

Errors

All errors are thrown/rejected as EmbedError with a code property.

| Code | Thrown by | Description | |------|----------|-------------| | INVALID_OPTIONS | createEmbed | Options are invalid | | CONTAINER_NOT_FOUND | init | Container element not found | | LOADING_TIMEOUT | init / reload | CONTENT_READY not received within tokenOptions.loadingTimeout; auto-refresh reports via onRefreshError | | TOKEN_PROVIDER_FAILED | init / reload | tokenProvider threw | | MISSING_REQUIRED_PARAM | encode / decode | Required param missing | | INVALID_PARAM_VALUE | encode / decode | Value failed validation | | UNEXPECTED_ERROR | createEmbed / init / reload | Unexpected error |

Error handling

init() and reload() throw on failure — handle with try/catch. Auto-refresh errors are delivered via the tokenOptions.onRefreshError callback.

import { createEmbed, EmbedError } from '@codatum/embed';

try {
  const embed = createEmbed({
    container: '#dashboard',
    embedUrl: '...',
    tokenProvider: async () => { /* ... */ },
    tokenOptions: {
      onRefreshError: (error) => {
        // Token auto-refresh failed after all retries.
        // e.g. redirect to login, show a banner, etc.
        console.error('Refresh failed:', error);
      },
    },
  });
  await embed.init();

  // reload() also throws on failure
  await embed.reload();
} catch (error) {
  if (error instanceof EmbedError) {
      // error.cause holds the original error thrown by tokenProvider (if applicable)
    console.error(error.code, error.message);
  }
}

Usage examples

The following patterns demonstrate common integration scenarios as outlined in the Signed embed use cases.

Example A: Params form in embed (server validates store, client sends filters)

Embed shows the notebook's parameter form. The server validates store_id and encodes it in the token; the client sends current store_id on each token request and encodes only date_range and product_category as client-side params so the form and host stay in sync.

Client

import { createEmbed, createParamMapper, RESET_TO_DEFAULT } from '@codatum/embed';

const paramMapper = createParamMapper({...}, {
  store_id: { datatype: 'STRING' },  // server-side param
  date_range: { datatype: '[DATE, DATE]' },  // client-side param
  product_category: { datatype: 'STRING[]' },  // client-side param
});
let paramValues = { store_id: undefined, date_range: RESET_TO_DEFAULT, product_category: [] };

const embed = createEmbed({
  container: '#dashboard',
  embedUrl: '...',
  displayOptions: { expandParamsFormByDefault: true },
  tokenProvider: async () => {
    const res = await fetch('/api/codatum/token', {
      method: 'POST',
      body: JSON.stringify({ tokenUserId: userId, params: { store_id: paramValues.store_id } }),
    });
    const { token } = await res.json();
    const params = paramMapper.encode(paramValues, { only: ['date_range', 'product_category'] });
    return { token, params };
  },
});
await embed.init();

embed.on('paramChanged', (ev) => { paramValues = paramMapper.decode(ev.params); });
embed.on('executeSqlsTriggered', (ev) => { paramValues = paramMapper.decode(ev.params); });

Runnable: React, Vue

Server — Validate store_id against the tenant and encode it in the token:

import { createParamMapper } from '@codatum/embed';

// POST /token body: { tokenUserId, params?: { store_id } }
const paramMapper = createParamMapper({...}, {
  tenant_id: { datatype: 'STRING', required: true },  // server-side param
  store_id: { datatype: 'STRING', required: true },  // server-side param
});
const tenantId = await getTenantIdByUserId(tokenUserId);
const storeIdsForTenant = await getStoreIdsByTenantId(tenantId);
const storeId = params?.store_id ?? storeIdsForTenant[0];
if (!storeIdsForTenant.includes(storeId)) throw new Error('Invalid storeId');
const encoded = paramMapper.encode({ tenant_id: tenantId, store_id: storeId });
// issue token with encoded params

Runnable: Node.js

Example B: Client-side params only (SaaS owns filters, hide params form)

All parameters except tenant_id are managed by the host; the embed's parameter form is hidden. The token carries only tenant context, so it can be cached and reused. When the user changes filters in the host UI, call reload() so the token provider runs again with updated values.

Client

import { createEmbed, createParamMapper } from '@codatum/embed';

const paramMapper = createParamMapper({...}, {
  store_id: { datatype: 'STRING' },  // client-side param
  date_range: { datatype: '[DATE, DATE]' },  // client-side param
  product_category: { datatype: 'STRING[]' },  // client-side param
});
let paramValues = { store_id: 'store_001', date_range: ['2025-01-01', '2025-01-31'], product_category: [] };

const embed = createEmbed({
  container: '#dashboard',
  embedUrl: '...',
  displayOptions: { hideParamsForm: true },
  tokenProvider: async () => {
    const res = await fetch('/api/codatum/token', { method: 'POST', body: JSON.stringify({ tokenUserId: userId }) });
    const { token } = await res.json();
    const params = paramMapper.encode(paramValues);
    return { token, params };
  },
});
await embed.init();

embed.on('paramChanged', (ev) => { paramValues = paramMapper.decode(ev.params); });
embed.on('executeSqlsTriggered', (ev) => { paramValues = paramMapper.decode(ev.params); });

// When the user changes filters in the host UI, update paramValues and reload the embed
async function onDashboardFilterChange(newValues: ParamValues) {
  paramValues = newValues;
  await embed.reload();
}

Runnable: React, Vue

Server — Token needs only tenant scope (no param in body):

import { createParamMapper } from '@codatum/embed';

// POST /token body: { tokenUserId }
const paramMapper = createParamMapper({...}, {
  tenant_id: { datatype: 'STRING', required: true },  // server-side param
});
const tenantId = await getTenantIdByUserId(tokenUserId);
const encoded = paramMapper.encode({ tenant_id: tenantId });
// issue token with encoded params

Runnable: Node.js

Example C: Server-side store (token re-issue on store change)

store_id is fixed in the token and validated server-side; changing the store requires a new token. The params form is visible for date_range and product_category. Keep latest param values from paramChanged and pass store_id to the server on each token request; when the user switches store, reload so a new token is issued for the new store.

Client

import { createEmbed, createParamMapper, RESET_TO_DEFAULT } from '@codatum/embed';

const paramMapper = createParamMapper({...}, {
  store_id: { datatype: 'STRING' },  // server-side param
  date_range: { datatype: '[DATE, DATE]' },  // client-side param
  product_category: { datatype: 'STRING[]' },  // client-side param
});
let paramValues = { store_id: undefined, date_range: RESET_TO_DEFAULT, product_category: ['Electronics'] };

const embed = createEmbed({
  container: '#dashboard',
  embedUrl: '...',
  displayOptions: { expandParamsFormByDefault: true },
  tokenProvider: async () => {
    const res = await fetch('/api/codatum/token', {
      method: 'POST',
      body: JSON.stringify({ tokenUserId: userId, params: { store_id: paramValues.store_id } }),
    });
    const { token } = await res.json();
    const params = paramMapper.encode(paramValues, { only: ['date_range', 'product_category'] });
    return { token, params };
  },
});
await embed.init();

embed.on('paramChanged', (ev) => { paramValues = paramMapper.decode(ev.params); });
embed.on('executeSqlsTriggered', (ev) => { paramValues = paramMapper.decode(ev.params); });

async function onStoreSwitch(storeId: string) {
  paramValues.store_id = storeId;
  await embed.reload();
}

Runnable: React, Vue

Server — Same as Example A: accept params.store_id, validate, encode tenant_id + store_id in the token.

Runnable: Node.js

CDN

A separate IIFE build exposes a single global CodatumEmbed. Load the script and use CodatumEmbed.createEmbed then embed.init().

<script src="https://unpkg.com/@codatum/embed/dist/index.global.min.js"></script>
<script>
  (async function () {
    const embed = CodatumEmbed.createEmbed({ container: '#dashboard', embedUrl: '...', tokenProvider: ... });
    await embed.init();
  })();
</script>

See also