# Bundles

Compose multiple artifacts into a distributable package

Canonical URL: https://docs.paradoc.dev/guides/bundles/

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.



**Object Pattern**

```ts
import { p } from '@paradoc/sdk'

// Lease Agreement Form
const lease = p.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', partyType: 'person', signature: { required: true } },
    tenant: { label: 'Tenant', partyType: 'person', signature: { required: true } },
  },
  defaultLayer: 'markdown',
  layers: {
    markdown: { kind: 'inline', mimeType: 'text/markdown', text: '# Lease\n\n{{fields.address}}' },
  },
})

// Lead Paint Disclosure Document
const disclosure = p.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 = p.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' },
  ],
  defaultLayer: 'text',
  layers: { text: { kind: 'inline', mimeType: 'text/plain', text: 'Move-In Checklist' } },
})
```

**Builder Pattern**

```ts
import { p } from '@paradoc/sdk'

// Lease Agreement Form
const lease = p
  .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: p.party().label('Landlord').partyType('person').signature({ required: true }),
    tenant: p.party().label('Tenant').partyType('person').signature({ required: true }),
  })
  .defaultLayer('markdown')
  .inlineLayer('markdown', { mimeType: 'text/markdown', text: '# Lease\n\n{{fields.address}}' })
  .build()

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

// Move-In Checklist
const moveInChecklist = p
  .checklist()
  .name('move-in-checklist')
  .version('1.0.0')
  .title('Move-In Checklist')
  .item({ id: 'keys', title: 'Keys Provided' })
  .item({ id: 'utilities', title: 'Utilities Transferred' })
  .item({ id: 'inspection', title: 'Walk-Through Complete' })
  .inlineLayer('text', { mimeType: 'text/plain', text: 'Move-In Checklist' })
  .defaultLayer('text')
  .build()
```

## Compose the Bundle

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



**Object Pattern**

```ts
const leaseBundle = p.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 }) },
  ],
})
```

**Builder Pattern**

```ts
const leaseBundle = p
  .bundle()
  .name('lease-transaction')
  .version('1.0.0')
  .title('Complete Lease Package')
  .inline('lease', lease)
  .inline('disclosure', disclosure)
  .inline('checklist', moveInChecklist)
  .build()
```

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:

```ts
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:

```ts
// 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-0', name: 'Jane Smith' },
    tenant: { id: 'tenant-0', name: 'John Doe' },
  },
})

// Assemble the bundle
const assembled = await leaseBundle.assemble({
  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}`)
}
```

Bundle assembly selects the built-in renderer for each layer MIME type. Pass a
`renderers` map only when a MIME type needs a custom override.

Output:

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

## Save the Artifact

Export the bundle definition to JSON or YAML:

```ts
const json = leaseBundle.toJSON() // or leaseBundle.toYAML()
console.log(JSON.stringify(json, null, 2))
```



**JSON**

```json
{
  "$schema": "https://schema.paradoc.dev/2026-10-02.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", "..." } }
  ]
}
```

**YAML**

```yaml
# yaml-language-server: $schema=https://schema.paradoc.dev/2026-10-02.json
$schema: https://schema.paradoc.dev/2026-10-02.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:

| Type       | Description                                                                               |
| ---------- | ----------------------------------------------------------------------------------------- |
| `inline`   | Artifact embedded directly in the bundle                                                  |
| `path`     | Reference to a local file path                                                            |
| `registry` | Reference to a registry artifact by coordinate (e.g., `@org/repo/artifact/2024-03@1.0.0`) |

```ts
const bundle = p
  .bundle()
  .name('mixed-bundle')
  .inline('embedded', someForm)           // Embedded
  .path('local', './forms/other.yaml')    // File reference
  .registry('shared', '@acme/forms/disclosure/2024-03@1.0.0') // Registry reference
  .build()
```
