Packs

A pack is the vocabulary: which node types exist and how each one renders. Where a frame decides what document an image becomes — exclusive, one per render — a pack is additive. Hand a runtime two packs and you get the union of their types, with a card from one sitting beside a sparkline from another.

createIsomerRuntime({ packs: [componentsPack, chartsPack] });
		

Packs are built by definePrimitivePack in the SDK, not here — see the pack contract. This page is about what a runtime does with them.

Contribution Used for
primitives The definitions the dispatcher, validator, parser, and schema are built from
types Duplicate detection across packs
surfaces Which optional surfaces every primitive implements (only slack)
enhancements Progressive enhancements the HTML surface may apply, by id
slackAssetTypes Which node types are pictures, so Slack uploads them instead of approximating them
styleAdapter Optional. Its HTML CSS; at most one composed pack declares one
styleCollector Optional. The collector shape its collectStyles hooks expect
TTheme The palette its snapshot renderers require, as definePrimitivePack<T>'s type argument

The last facts are declared by the pack because they are facts about a vocabulary, and a host composing packs it did not write cannot be expected to know them. The runtime unions slackAssetTypes and enhancements across packs.

Two rules, both enforced when the runtime is built:

  • No node type may be registered by two packs. The dispatcher is keyed on type and nothing else, so a duplicate makes dispatch ambiguous.
  • No enhancement id may be registered by two packs, for the same reason one level up — the HTML surface flattens enhancements runtime-wide.

Everything else composes silently, with one bound to respect: a pack declares the palette its snapshot renderers read, and every pack in a runtime — plus every frame in its map — has to agree on it. A pack that declares snapshot and one that does not can still sit together; nodes from the second simply produce a validation warning if you try to draw them. Packs whose renderers want different palettes belong in different runtimes, which is what Frame covers.

Vocabulary and document are independent. Splitting them makes a pack purely additive. Nothing about a primitive decides the frame, so any pack's nodes can be drawn inside any frame.

What replaced frame ownership is a type-level bound rather than a runtime check — a pack states the palette it needs, and the compiler pairs it with a frame that supplies one.

A pack ships its own styleAdapter, and a runtime styles with one: the one composed pack's adapter serves every pack, so a pack without one is styled by it. Two packs declaring one throw AMBIGUOUS_STYLE_ADAPTER, naming both, and the host chooses by passing styleAdapter on the runtime.

A pack is also CSS-bearing when any of its primitives declare collectStyles; construction throws if such a pack has no adapter at all, naming both the pack and the primitive.

A CSS-bearing pack should also declare styleCollector, naming the collector shape its hooks mutate — the slides pack's Distillate adapter publishes 'distillate'. Adapters publish the same tag, and construction throws when they disagree. Without it the mismatch surfaces as a corrupted collector at render instead.

A host that wants a CSS-bearing pack on text or Slack only supplies a no-op adapter, which replaces the pack's; see Surfaces.

Frame · Renderer overrides · The pack contract