Today Canvas Kit
SDK packages

@todayai-labs/tck-bundler

Compile a .tck.tsx widget source into a distributable .tckb bundle. CLI + programmatic.

The widget-side build tool. Takes a .tck.tsx source, runs esbuild + Tailwind v4 over it, and emits a .tckb envelope ready to ship to a host. CLI for one-shot builds; programmatic for higher-level pipelines.

tck-bundler calls @todayai-labs/tck-bundle-format's packTckb to construct the envelope — tck-bundler derives the manifest, compiles the JS, scopes the CSS; the format package owns the envelope bytes.

CLI

# Bundle a single widget
pnpm exec tck-bundle widgets/counter/widget.tck.tsx --out widgets/counter/dist/widget.tckb

# Bundle every widget in the workspace
pnpm exec tck-bundle --all

# Print the manifest JSON Schema (the authoring contract: fields,
# defaults, constraints) to stdout
pnpm exec tck-bundle schema

tck-bundle schema [--json] prints the manifest JSON Schema — the authoring contract's fields, their defaults, and their constraints — to stdout. It's the canonical reference for what a WidgetManifestInput accepts and how sizes / defaultSize / defaultState default. The schema is generated from the zod single source of truth (packages/tck/src/manifest/schema.ts); see @todayai-labs/tck.

Programmatic surface

import { bundleWidget, readWidgetSource, type BundleOptions } from '@todayai-labs/tck-bundler'

const result = await bundleWidget({
  entry: 'widgets/counter/widget.tck.tsx',
  outFile: 'widgets/counter/dist/widget.tckb',
  // ... see BundleOptions
})
ExportWhat it does
bundleWidget(options)Compile + scope CSS + pack .tckb. Returns the BundleResult.
BundleErrorThrown when a widget imports a bare specifier outside TCK_EXTERNAL_SPECIFIERS, exceeds maxSizeBytes, or fails manifest validation.
readWidgetSource(path)Read a .tck.tsx file as UTF-8 text. Useful for tests and dev tools that want the source before invoking esbuild.
buildWidgetCss, scopeCssThe CSS scoping pipeline. Lifted into its own export for tooling that wants to lint or pre-process scoped CSS.

Constraints

  • Manifest authoring belongs to widget source. The entry directory and an existing cwd bound the source files whose defineManifest, defineSlots, TCK_ABI_VERSION, and TCK_MANIFEST_VERSION imports are pinned into the bundle. Installed dependencies under node_modules must not call or re-export those APIs. The bundler resolves symlinks before checking containment, so a link whose target escapes the authoring roots is not inspected as widget source.
  • Pinning preserves module semantics. Named and namespace imports, namespace aliases/destructuring, standalone version constants, and local re-exports all use build-time manifest helpers. Runtime hooks and components remain external.
  • SDK checks follow bindings and the actual manifest. All JavaScript/TypeScript module extensions are scanned. Unknown exports fail before manifest evaluation; Feed restrictions use the normalized exported manifest. Comments and unrelated type: 'feed' objects cannot select the mode. Namespace shadowing is resolved by the TypeScript binder; arbitrary aliases and dynamic property keys are outside the static check.
  • A missing cwd is only a resolution base. An absolute entry still supplies its own authoring root, but a non-existent working directory cannot own source and is not added to the boundary. Derived source roots are validated and fail closed instead of widening the scan.
  • Externals whitelist enforced at build time. BundleError: bundle imports "<spec>", which is not on the TCK externals whitelist — fail-fast at CI, not at load time.
  • Soft cap 256 KB on widget.mjs. Configurable via maxSizeBytes. Exceeding it is a build error, not a load error.
  • JSX: automatic transform throughout. Classic-transform output (banner-injected import React) is internal — widget source itself uses react-jsx.

Source

On this page