OpenForm

bundle

Last updated on

Create Bundle artifacts to group multiple artifacts together

Create Bundle artifacts to group multiple documents, forms, and checklists into a single distributable package. Bundles support conditional includes and can reference content inline, by file path, or by registry slug.

Examples

Creating a bundle

// Object pattern
const bundle = open.bundle({
  name: 'lease-package',
  version: '1.0.0',
  title: 'Residential Lease Package',
  logic: { hasPets: 'data.pets.length > 0' },
  contents: [
    { type: 'registry', key: 'lease', slug: 'residential-lease@1.0.0' },
    { type: 'path', key: 'addendum', path: './pet-addendum.json', include: 'hasPets' },
    { type: 'inline', key: 'disclosure', artifact: disclosureDoc }
  ]
})

// Builder pattern
const bundle = open.bundle()
  .name('lease-package')
  .version('1.0.0')
  .title('Residential Lease Package')
  .expr('hasPets', 'data.pets.length > 0')
  .registry('lease', 'residential-lease@1.0.0')
  .path('addendum', './pet-addendum.json', 'hasPets')
  .inline('disclosure', disclosureDoc)
  .build()

Loading from external data

// Parse and validate unknown input (throws on error)
const bundle = open.bundle.from(jsonData)

// Safe parsing (returns result object)
const result = open.bundle.safeFrom(jsonData)
if (result.success) {
  const bundle = result.data
}

Assembling a bundle

Bundles can be assembled to resolve all contents and optionally render forms:

// Basic assembly
const assembled = await bundle.assemble({
  resolver,
  includeDocumentBytes: true,
})

// Assembly with form rendering
const assembled = await bundle.assemble({
  resolver,
  formData: {
    lease: leaseFormData,
    addendum: petAddendumData,
  },
  renderForm: async (form, data, resolver) => ({
    content: await renderFormToPDF(form, data, resolver),
    filename: `${form.name}.pdf`,
    mimeType: 'application/pdf',
  }),
})

// Access assembled items
for (const item of assembled.items) {
  if (item.kind === 'form' && item.rendered) {
    // item.rendered contains the PDF bytes
    // item.filename is the suggested filename
  }
}

API

Object Pattern

open.bundle(input: BundleInput): BundleInstance

Parameters

name: string
Unique identifier; must follow slug constraints
version?: string
Artifact version (semantic versioning)
title?: string
Human-friendly name presented to end users
description?: string
Long-form description or context
code?: string
Internal code or reference number
releaseDate?: string
ISO date string indicating when the artifact was released
metadata?: Metadata
Custom metadata map (keys must be alphanumeric with hyphens)
logic?: LogicSection
Named expressions for conditional includes
contents: BundleContentItem[]
Ordered list of bundle content items

Returns

Returns a BundleInstance with the following properties and methods:

kind: 'bundle'
Artifact discriminator
name: string
Bundle name
version: string | undefined
Semantic version
title: string | undefined
Human-readable title
description: string | undefined
Description text
code: string | undefined
Internal code or reference number
releaseDate: string | undefined
ISO date string
metadata: Metadata | undefined
Custom metadata map
logic: LogicSection | undefined
Named logic expressions
contents: BundleContentItem[]
Bundle content items
Methods
validate: (options?: ValidateOptions) => StandardSchemaV1.Result
Validate the bundle definition
isValid: (options?: ValidateOptions) => boolean
Check if bundle is valid
toJSON: (options?: SerializationOptions) => object
Serialize to JSON (includes $schema by default)
toYAML: (options?: SerializationOptions) => string
Serialize to YAML
clone: () => BundleInstance
Deep clone the instance
assemble: (options: BundleAssemblyOptions) => Promise<AssembledBundle>
Resolve contents and render runtime instances
prepare: (contents?: RuntimeBundleContents) => DraftBundle
Create runtime bundle in draft phase

Builder Pattern

Chain methods to build a bundle incrementally:

All methods return BundleBuilder and are chainable.

open.bundle()
name: (value: string) => BundleBuilder
Set bundle name (required)
version: (value: string) => BundleBuilder
Set semantic version
title: (value: string) => BundleBuilder
Set human-readable title
description: (value: string) => BundleBuilder
Set description
code: (value: string) => BundleBuilder
Set external reference code
releaseDate: (value: string) => BundleBuilder
Set release date (ISO format)
metadata: (value: Metadata) => BundleBuilder
Set custom metadata
logic: (logicDef: LogicSection) => BundleBuilder
Set all logic expressions
expr: (name: string, expression: string) => BundleBuilder
Add a named logic expression
registry: (key: string, slug: string, include?: CondExpr) => BundleBuilder
Add registry reference
path: (key: string, path: string, include?: CondExpr) => BundleBuilder
Add path reference
inline: (key: string, artifact: Artifact) => BundleBuilder
Add inline artifact
contents: (items: BundleContentItem[]) => BundleBuilder
Set all contents at once
removeContent: (predicate: (item, index) => boolean) => BundleBuilder
Remove matching content
clearContents: () => BundleBuilder
Remove all contents
build: () => BundleInstance
Build and validate

Static Methods

Parse bundles from unknown data sources:

from: (input: unknown) => BundleInstance
Parse unknown input (throws on error)
safeFrom: (input: unknown) => Result<BundleInstance>
Parse unknown input (returns result object)

Bundle Content Types

Bundles can contain content from three sources:

Adding content

Use builder methods to add content:

// Registry reference - published artifacts
.registry('lease', 'residential-lease@1.0.0')

// Path reference - local files
.path('addendum', './pet-addendum.yaml', 'hasPets')

// Inline - embedded artifacts
.inline('disclosure', open.document({...}))

Content Item Types

InlineBundleItem

Embed an artifact directly in the bundle:

type: 'inline'
Discriminator for inline bundle item
key: string
Unique key for this item within the bundle
artifact: Document | Form | Checklist | Bundle
The embedded artifact

PathBundleItem

Reference an artifact by file path:

type: 'path'
Discriminator for path-based bundle item
key: string
Unique key for this item within the bundle
path: string
File path to the artifact definition
include?: CondExpr
Conditional expression for including this item

RegistryBundleItem

Reference a published artifact by slug:

type: 'registry'
Discriminator for registry-based bundle item
key: string
Unique key for this item within the bundle
slug: string
Registry slug identifying the artifact
include?: CondExpr
Conditional expression for including this item

Conditional Includes

Path and registry items support conditional inclusion using logic expressions:

const bundle = open.bundle()
  .name('lease-package')
  // Define logic expressions
  .expr('hasPets', 'data.pets.length > 0')
  .expr('hasVehicles', 'data.vehicles.length > 0')
  // Always include
  .registry('lease', 'residential-lease@1.0.0')
  // Include only if hasPets evaluates to true
  .path('pet-addendum', './pet-addendum.yaml', 'hasPets')
  // Include only if hasVehicles evaluates to true
  .path('parking-addendum', './parking.yaml', 'hasVehicles')
  .build()

Assembly Options

The assemble() method accepts the following options:

resolver: Resolver | ArtifactResolver
Resolver for loading files and artifacts
formData?: Record<string, Data>
Data to use when rendering forms, keyed by content key
renderForm?: (form, data, resolver) => Promise<RenderResult>
Function to render a form with its data
includeDocumentBytes?: boolean
Whether to include document layer bytes (default: true)
assembleNestedBundles?: boolean
Whether to recursively assemble nested bundles (default: true)

Lifecycle Types

Bundles follow a three-phase lifecycle similar to forms (since bundles can contain forms that require signatures):

TypePhaseDescription
DraftBundleDraftMutable contents, configure signers
SignableBundleSignableFrozen data, capture signatures
ExecutedBundleExecutedFully frozen, ready for archival
// Prepare bundle with runtime contents (DraftBundle)
const draft = bundle.prepare({
  lease: filledLeaseForm,
  disclosure: preparedDocument,
  checklist: filledChecklist,
})

// Prepare for signing (SignableBundle)
const signable = draft.prepareForSigning()

// Capture signatures across forms
const signed = signable.captureSignature('lease', 'tenant', 'tenant-0', 'john', 'sig-1')

// Finalize (ExecutedBundle)
const executed = signed.finalize()

// executed.executedAt === ISO timestamp

DraftBundle

Created by bundle.prepare(). Contains runtime instances of all artifacts.

phase: 'draft'
Phase discriminator
bundle: Bundle
The underlying bundle definition
executedAt: undefined
Execution timestamp (always undefined for draft)
Methods
getContentKeys: () => string[]
Get all content keys
getContent: (key: string) => RuntimeInstance | undefined
Get runtime instance by key
hasContent: (key: string) => boolean
Check if content exists
getAllContents: () => RuntimeBundleContents
Get all contents
setContent: (key: string, instance: RuntimeInstance) => DraftBundle
Set content instance
removeContent: (key: string) => DraftBundle
Remove content by key
updateContents: (contents: RuntimeBundleContents) => DraftBundle
Update multiple contents
prepareForSigning: () => SignableBundle
Transition to signable phase
render: (options: RuntimeBundleRenderOptions) => Promise<RuntimeBundleRendered>
Render all contents
toJSON: () => RuntimeBundleJSON
Serialize to JSON
clone: () => DraftBundle
Create exact copy

SignableBundle

Created by draft.prepareForSigning(). Contains signable instances.

phase: 'signable'
Phase discriminator
bundle: Bundle
The underlying bundle definition
executedAt: undefined
Execution timestamp (always undefined for signable)
Methods
getContentKeys: () => string[]
Get all content keys
getContent: (key: string) => RuntimeInstance | undefined
Get runtime instance by key
hasContent: (key: string) => boolean
Check if content exists
getAllContents: () => RuntimeBundleContents
Get all contents
updateContent: (key: string, instance: RuntimeInstance) => SignableBundle
Update content (for signature capture)
finalize: () => ExecutedBundle
Transition to executed phase
render: (options: RuntimeBundleRenderOptions) => Promise<RuntimeBundleRendered>
Render all contents
toJSON: () => RuntimeBundleJSON
Serialize to JSON
clone: () => SignableBundle
Create exact copy

ExecutedBundle

Created by signable.finalize(). Fully frozen.

phase: 'executed'
Phase discriminator
bundle: Bundle
The underlying bundle definition
executedAt: string
ISO timestamp of execution
Methods
getContentKeys: () => string[]
Get all content keys
getContent: (key: string) => RuntimeInstance | undefined
Get runtime instance by key
hasContent: (key: string) => boolean
Check if content exists
getAllContents: () => RuntimeBundleContents
Get all contents
render: (options: RuntimeBundleRenderOptions) => Promise<RuntimeBundleRendered>
Render all contents
toJSON: () => RuntimeBundleJSON
Serialize to JSON
clone: () => ExecutedBundle
Create exact copy

Related

On this page