@codatum/embed

中文文档
1.0.0发布于 2026/03/03Apache-2.0TypeScriptESM + CJS零依赖构建来源已验证近 30 天下载 1.1万

TypeScript library for Codatum signed embedding

原始使用文档
文档目录22 节
  1. Installation
  2. Quick start
  3. Embed
  4. EmbedOptions
  5. Creating an embed instance
  6. Instance methods
  7. Instance properties
  8. Container attribute
  9. Events
  10. ParamMapper
  11. Basic usage
  12. Creating a mapper
  13. Instance methods
  14. Type-first usage
  15. Errors
  16. Error handling
  17. Usage examples
  18. Example A: Params form in embed (server validates store, cl…
  19. Example B: Client-side params only (SaaS owns filters, hide…
  20. Example C: Server-side store (token re-issue on store chang…
  21. CDN
  22. See also

@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

PropertyRequiredDescription
containerYesHTMLElement or CSS selector where the iframe is inserted
embedUrlYesSigned embed URL from Codatum
tokenProviderYesSee tokenProvider below
iframeOptionsNoSee IframeOptions below
tokenOptionsNoSee TokenOptions below
displayOptionsNoSee DisplayOptions below
devOptionsNoSee 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.

PropertyTypeDefaultDescription
theme'LIGHT' | 'DARK'| 'SYSTEM''SYSTEM'UI theme of the embedded notebook
localestringBrowser's localeLocale code (e.g. 'en', 'ja') for the embed UI
classNamestring-CSS class name(s) applied to the iframe element
styleobject{display: 'block', width: '100%', height: '100%', border: 'none'}Inline styles for the iframe; overrides the default styles
attrsRecord<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.

PropertyTypeDefaultDescription
disableRefreshbooleanfalseWhen true, disables automatic token refresh before the token expires
refreshBuffernumber60Number of seconds before the token expires when auto-refresh is triggered
retryCountnumber2Number of retries on token fetch failure; 0 = no retry
loadingTimeoutnumber30Max wait in seconds for CONTENT_READY after entering INITIALIZING, RELOADING, or REFRESHING; 0 = no timeout
onRefreshError(error: EmbedError) => voidundefinedCallback 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.

PropertyTypeDefaultDescription
sqlDisplay'SHOW' | 'RESULT_ONLY' | 'HIDE''SHOW'Whether to show SQL Blocks, results only, or hide
hideParamsFormbooleanfalseHide the parameter form in the embed (e.g. when your app owns the filters)
expandParamsFormByDefaultbooleanfalseWhether the parameter form is expanded by default

DevOptions

Development and testing only. Not intended for production.

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

MockOptions (when mock is an object):

PropertyTypeDefaultDescription
labelstringembedUrlText shown in the mock iframe placeholder. Useful when multiple embeds are on the page.
callTokenProviderbooleanfalseWhen true, calls tokenProvider on init(), reload(), and auto-refresh.
loadingDelaynumber0Delay 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

MethodDescription
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

PropertyTypeDescription
iframeHTMLIFrameElement | nullThe 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.

StatusWhen
INITIALIZINGinit() called, waiting for content
READYContent loaded and visible
RELOADINGreload() called, waiting for content
REFRESHINGAuto-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).

EventDescriptionPayload
statusChangedInstance status changed.{ type: 'STATUS_CHANGED', status: EmbedStatus, previousStatus: EmbedStatus }
paramChangedUser changed parameters in the embed.{ type: 'PARAM_CHANGED', params: EncodedParam[] }
executeSqlsTriggeredSQL execution was triggered in the embed.{ type: 'EXECUTE_SQLS_TRIGGERED', params: EncodedParam[] }
executionSucceededSQL execution was completed successfully.{ type: 'EXECUTION_SUCCEEDED' }
executionFailedSQL 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?)

ArgumentTypeDescription
mappingRecord<string, string>Your app's key → Codatum param_id for each parameter.
metaRecord<key, ParamMeta> (optional)Same keys as mapping. Per-key options: hidden, required, datatype.

ParamMeta (optional, per key):

PropertyTypeDescription
datatypeParamDatatypeEnables validation and improves typing. See Param datatypes below.
requiredbooleanIf true, encode and decode throw MISSING_REQUIRED_PARAM when the param is missing.
hiddenbooleanIf 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:

ParamDatatypeJS/TS typeNotes
'STRING'string—
'NUMBER'numberRejects NaN.
'BOOLEAN'boolean—
'DATE'stringMust be YYYY-MM-DD.
'STRING[]'string[]Array of strings.
'[DATE, DATE]'[string, string]Date range; both elements must be YYYY-MM-DD.

Instance methods

MethodDescription
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:

OptionTypeDescription
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).
noValidatebooleanWhen 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.

CodeThrown byDescription
INVALID_OPTIONScreateEmbedOptions are invalid
CONTAINER_NOT_FOUNDinitContainer element not found
LOADING_TIMEOUTinit / reloadCONTENT_READY not received within tokenOptions.loadingTimeout; auto-refresh reports via onRefreshError
TOKEN_PROVIDER_FAILEDinit / reloadtokenProvider threw
MISSING_REQUIRED_PARAMencode / decodeRequired param missing
INVALID_PARAM_VALUEencode / decodeValue failed validation
UNEXPECTED_ERRORcreateEmbed / init / reloadUnexpected 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

版本与发布记录

@codatum/embed 共发布 10 个版本,首次发布于 2026/02/20;过去 12 个月发布了 10 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
1.0.0latest2026/03/038 个月前
0.5.22026/03/038 个月前
0.5.12026/03/038 个月前
0.5.02026/03/038 个月前
0.4.02026/03/028 个月前
0.3.02026/02/248 个月前

关于 @codatum/embed 的常见问题

@codatum/embed 是什么?

TypeScript library for Codatum signed embedding

如何安装 @codatum/embed?

在项目目录运行 npm install @codatum/embed,或使用 pnpm add / yarn add / bun add。

@codatum/embed 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@codatum/embed 支持 ESM 还是 CommonJS?

同时提供 ES Module 与 CommonJS 入口,import 和 require() 都可以使用,打包工具会自动选择合适的版本。

@codatum/embed 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20.12。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@codatum/embed 可以商用吗?

@codatum/embed 使用 Apache-2.0 许可。宽松许可:允许商业使用、修改和再分发,并附带明确的专利授权;分发时需保留许可与 NOTICE 文件,修改过的文件要注明改动。以上为概括,不构成法律意见,请以许可原文为准。

@codatum/embed 的下载量有多少?

根据 npm 官方下载统计,@codatum/embed 在 2026/10/10 – 2026/11/08 这 30 天内被下载 10,834 次,日均约 361 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@codatum/embed 还在维护吗?

最新版本 1.0.0 发布于 2026/03/03(8 个月前);至今共发布 10 个版本,其中过去 12 个月发布了 10 个;首次发布于 2026/02/20。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。