Files
metabuilder/docs/architecture/packages.md

9.6 KiB

Package System Architecture

MetaBuilder's modular package system enables self-contained feature modules that can be independently developed, tested, distributed, and installed.

Overview

The package system allows developers to:

  • Create isolated feature packages
  • Bundle components, data, and Lua scripts
  • Import and export packages
  • Share packages between installations
  • Version and manage dependencies

Package Structure

packages/
└── my-feature/
    ├── package.json              # Package metadata
    ├── package.md                # Package documentation
    ├── seed/
    │   ├── index.ts              # Main seed data
    │   ├── workflows.ts          # Workflow definitions
    │   ├── components.ts         # Component configurations
    │   ├── schemas.ts            # Data schemas
    │   └── scripts/
    │       ├── initialize.lua    # Lua scripts
    │       └── helpers.lua
    ├── src/
    │   ├── components/           # React components
    │   ├── lib/                  # Utilities
    │   └── hooks/                # Custom hooks
    ├── styles/
    │   └── styles.scss           # Component styles
    └── static_content/           # Assets
        ├── images/
        └── templates/

Package Metadata

package.json

{
  "name": "@metabuilder/my-feature",
  "version": "1.0.0",
  "description": "Description of the feature",
  "author": "Author Name",
  "license": "MIT",
  "keywords": ["feature", "dashboard"],
  "metabuilder": {
    "type": "feature",
    "minVersion": "1.0.0",
    "permissions": ["level3"],
    "components": [
      {
        "name": "MyComponent",
        "path": "src/components/MyComponent.tsx",
        "type": "widget"
      }
    ],
    "scripts": [
      {
        "name": "setup",
        "path": "seed/scripts/initialize.lua"
      }
    ],
    "exports": {
      "./components": "./src/components/index.ts",
      "./hooks": "./src/hooks/index.ts"
    }
  }
}

package.md

# My Feature Package

Brief description of what the package provides.

## Features

- Feature 1
- Feature 2

## Installation

## Usage

## Components

### MyComponent

Description of the component and how to use it.

## Lua Scripts

### setup

Description of what the script does.

Creating a Package

Step 1: Create Directory

mkdir -p packages/my-feature/seed/scripts
mkdir -p packages/my-feature/src/{components,lib,hooks}
mkdir -p packages/my-feature/styles
mkdir -p packages/my-feature/static_content

Step 2: Create package.json

{
  "name": "@metabuilder/my-feature",
  "version": "1.0.0",
  "description": "My awesome feature",
  "metabuilder": {
    "type": "feature"
  }
}

Step 3: Add Seed Data

// packages/my-feature/seed/index.ts
export const packageSeed = {
  name: '@metabuilder/my-feature',
  version: '1.0.0',
  components: [
    // Component configurations
  ],
  workflows: [
    // Workflow definitions
  ],
}

Step 4: Create Components

// packages/my-feature/src/components/MyComponent.tsx
export const MyComponent = () => {
  return <div>My Feature Component</div>
}

Step 5: Add Documentation

# My Feature Package

Documentation for the package...

Package Loading

Initialization

// In app initialization
import { initializePackageSystem } from '@/lib/package-loader'
import { packageSeed as myFeature } from '@packages/my-feature/seed'

await initializePackageSystem()

Runtime Loading

// Load package dynamically
const packageData = await importPackage('@metabuilder/my-feature')

Package Dependencies

Declaring Dependencies

{
  "metabuilder": {
    "dependencies": {
      "@metabuilder/core": ">=1.0.0",
      "@metabuilder/ui": "^2.0.0"
    }
  }
}

Dependency Resolution

Dependencies are resolved during package installation with version constraints:

  • * - Any version
  • 1.0.0 - Exact version
  • >=1.0.0 - Minimum version
  • ^1.0.0 - Compatible with 1.x.x (caret)
  • ~1.0.0 - Compatible with 1.0.x (tilde)

Publishing Packages

Create Distribution

# Create package distribution file
npm run package:pack my-feature

# Output: my-feature-1.0.0.zip

Package Contents

The ZIP file contains:

my-feature-1.0.0/
├── package.json
├── package.md
├── seed/
├── src/
├── styles/
└── static_content/

Share Package

  • Upload to package repository
  • Share via GitHub releases
  • Distribute through npm
  • Share directly with team

Installing Packages

From File

npm run package:install ./my-feature-1.0.0.zip

From Repository

npm run package:install @metabuilder/my-feature@1.0.0

From GitHub

npm run package:install github:username/repo#main

Seed Data

Seed data allows packages to pre-populate database with configurations:

// seed/index.ts
export const packageSeed = {
  name: '@metabuilder/my-feature',
  models: [
    {
      name: 'CustomModel',
      fields: [
        { name: 'title', type: 'string' },
        { name: 'data', type: 'json' },
      ],
    },
  ],
  components: [
    {
      id: 'custom-widget',
      name: 'Custom Widget',
      type: 'widget',
      config: { /* ... */ },
    },
  ],
  workflows: [
    {
      id: 'my-workflow',
      name: 'My Workflow',
      steps: [ /* ... */ ],
    },
  ],
}

Lua Scripts

Packages can include Lua scripts for server-side logic:

-- seed/scripts/initialize.lua
function initializePackage(context)
  -- context contains:
  -- - db: database access
  -- - user: current user
  -- - config: package configuration
  
  print("Initializing package...")
  return { success = true }
end

Using Lua Scripts

import { executeLua } from '@/lib/lua-sandbox'

const result = await executeLua(luaCode, {
  db: database,
  user: currentUser,
  config: packageConfig,
})

Component Exports

Export components for use in other packages:

// src/components/index.ts
export { MyComponent } from './MyComponent'
export { MyDialog } from './MyDialog'
export type { MyComponentProps } from './MyComponent'

Importing from Packages

// In another component
import { MyComponent } from '@metabuilder/my-feature/components'

Styling

Include SCSS that integrates with the theme:

// styles/styles.scss
@import '@/styles/variables';

.my-feature {
  background-color: var(--primary-color);
  font-family: var(--font-body);
  
  .component {
    padding: var(--spacing-md);
    border-radius: var(--radius);
  }
}

Asset Management

Include static assets in the package:

static_content/
├── images/
│   ├── logo.png
│   └── banner.jpg
├── templates/
│   └── email-template.html
└── data/
    └── initial-data.json

Accessing Assets

import logoImage from '@metabuilder/my-feature/static_content/images/logo.png'

export const Component = () => {
  return <img src={logoImage} />
}

Testing Packages

Unit Tests

// src/components/__tests__/MyComponent.spec.tsx
import { render } from '@testing-library/react'
import { MyComponent } from '../MyComponent'

describe('MyComponent', () => {
  it('should render', () => {
    const { getByText } = render(<MyComponent />)
    expect(getByText(/content/i)).toBeInTheDocument()
  })
})

Run Package Tests

npm run test -- packages/my-feature

Best Practices

Do:

  • Keep packages focused on single feature
  • Document all exports
  • Include example usage
  • Version packages semantically
  • Test package functionality
  • Include seed data for initial setup
  • Declare all dependencies
  • Use consistent naming

Don't:

  • Create overly large packages
  • Skip documentation
  • Use hardcoded paths
  • Assume specific database structure
  • Mix multiple concerns in one package
  • Forget version constraints
  • Include secrets in packages

Publishing to Registry

Register Package

# Create account on registry
npm run registry:login

# Publish package
npm run package:publish

Version Updates

# Increment version
npm version patch  # 1.0.0 -> 1.0.1
npm version minor  # 1.0.0 -> 1.1.0
npm version major  # 1.0.0 -> 2.0.0

# Republish
npm run package:publish

Multi-Tenant Packages

Ensure packages respect tenant isolation:

// Always filter by tenant ID
const data = await prisma.customModel.findMany({
  where: {
    tenantId: context.user.tenantId,  // Tenant isolation
  },
})

Examples

See /packages/ directory for example packages:

  • dashboard/ - Dashboard widgets
  • form_builder/ - Form creation tools
  • data_table/ - Data table component
  • admin_dialog/ - Admin utilities

Troubleshooting

Package Not Loading

# Verify package structure
npm run package:validate my-feature

# Check permissions
npm run package:check-perms my-feature

Seed Data Not Applied

# Manually run seed
npm run package:seed my-feature

# Clear and reseed
npm run db:reset && npm run seed

TODO: Development guide link should point to docs/guides/getting-started.md (current file does not exist).

See /docs/ for comprehensive guides on package development.