OpenForm

Bundles

Last updated on

Compose multiple artifacts into a distributable package

Bundles group forms, documents, and checklists into a single package. This guide walks through creating a lease transaction bundle.

Define the Artifacts

A lease transaction needs multiple artifacts. Start with the form from the previous guide, then add a disclosure document and a move-in checklist.

import { open } from '@open-form/core'

// Lease Agreement Form
const lease = open.form({
  name: 'lease-agreement',
  version: '1.0.0',
  title: 'Residential Lease Agreement',
  fields: {
    address: { type: 'address', label: 'Property Address', required: true },
    monthlyRent: { type: 'money', label: 'Monthly Rent', required: true },
    startDate: { type: 'date', label: 'Start Date', required: true },
    endDate: { type: 'date', label: 'End Date', required: true },
  },
  parties: {
    landlord: { label: 'Landlord', signature: { required: true } },
    tenant: { label: 'Tenant', signature: { required: true } },
  },
  defaultLayer: 'markdown',
  layers: {
    markdown: { kind: 'inline', mimeType: 'text/markdown', text: '# Lease\n\n{{address}}' },
  },
})

// Lead Paint Disclosure Document
const disclosure = open.document({
  name: 'lead-paint-disclosure',
  version: '1.0.0',
  title: 'Lead Paint Disclosure',
  code: 'EPA-747-K-12-001',
  defaultLayer: 'text',
  layers: {
    text: { kind: 'inline', mimeType: 'text/plain', text: 'Lead Paint Disclosure Notice...' },
  },
})

// Move-In Checklist
const moveInChecklist = open.checklist({
  name: 'move-in-checklist',
  version: '1.0.0',
  title: 'Move-In Checklist',
  items: [
    { id: 'keys', title: 'Keys Provided' },
    { id: 'utilities', title: 'Utilities Transferred' },
    { id: 'inspection', title: 'Walk-Through Complete' },
  ],
})

Compose the Bundle

Use .inline() to embed artifacts directly in the bundle:

const leaseBundle = open.bundle({
  name: 'lease-transaction',
  version: '1.0.0',
  title: 'Complete Lease Package',
  contents: [
    { type: 'inline', key: 'lease', artifact: lease.toJSON({ includeSchema: false }) },
    { type: 'inline', key: 'disclosure', artifact: disclosure.toJSON({ includeSchema: false }) },
    { type: 'inline', key: 'checklist', artifact: moveInChecklist.toJSON({ includeSchema: false }) },
  ],
})

Each content item has a key that identifies it within the bundle.

Validate the Bundle

Check that the bundle and all its contents are valid:

if (leaseBundle.isValid()) {
  console.log('Bundle is valid')
  console.log(`Contains ${leaseBundle.contents.length} artifacts`)
} else {
  const result = leaseBundle.validate()
  if ('errors' in result) {
    console.log('Validation errors:', result.errors)
  }
}

Output:

Bundle is valid
Contains 3 artifacts

Assemble and Render

Fill the artifacts and assemble the bundle for rendering:

import { textRenderer } from '@open-form/renderer-text'

// Fill the lease form
const filledLease = lease.fill({
  fields: {
    address: { line1: '123 Main St', locality: 'Portland', region: 'OR', postalCode: '97201', country: 'USA' },
    monthlyRent: { amount: 1500, currency: 'USD' },
    startDate: '2025-02-01',
    endDate: '2026-01-31',
  },
  parties: {
    landlord: { id: 'landlord-1', fullName: 'Jane Smith' },
    tenant: { id: 'tenant-1', fullName: 'John Doe' },
  },
})

// Assemble the bundle
const assembled = await leaseBundle.assemble({
  renderers: {
    'text/markdown': textRenderer(),
    'text/plain': textRenderer(),
  },
  contents: {
    lease: filledLease,
    disclosure: disclosure.prepare(),
    checklist: moveInChecklist.fill({ keys: true, utilities: true, inspection: false }),
  },
})

// Access rendered outputs
for (const [key, output] of Object.entries(assembled.outputs)) {
  console.log(`${key}: ${output.mimeType}`)
}

Output:

lease: text/markdown
disclosure: text/plain
checklist: text/plain

Save the Artifact

Export the bundle definition to JSON or YAML:

const json = leaseBundle.toJSON() // or leaseBundle.toYAML()
console.log(JSON.stringify(json, null, 2))
{
  "$schema": "https://schemas.open-form.dev/schema.json",
  "kind": "bundle",
  "name": "lease-transaction",
  "version": "1.0.0",
  "title": "Complete Lease Package",
  "contents": [
    { "type": "inline", "key": "lease", "artifact": { "kind": "form", "..." } },
    { "type": "inline", "key": "disclosure", "artifact": { "kind": "document", "..." } },
    { "type": "inline", "key": "checklist", "artifact": { "kind": "checklist", "..." } }
  ]
}

Content Types

Bundles support three content types:

TypeDescription
inlineArtifact embedded directly in the bundle
pathReference to a local file path
registryReference to a registry slug (e.g., @org/artifact)
const bundle = open
  .bundle()
  .name('mixed-bundle')
  .inline('embedded', someForm)           // Embedded
  .path('local', './forms/other.yaml')    // File reference
  .registry('shared', '@acme/disclosure') // Registry reference
  .build()

On this page