Reorganize packages: move seed data to entity-type folders

- Move all seed data from packages/*/seed/ to packages/*/[entity-type]/
- Establish clear entity-type folder structure (page-config, workflow, etc.)
- Update metadata.json files to be entity-specific, not package-level
- Reorganize all 12 bootstrap packages to new structure:
  - ui_home, ui_header, ui_footer, ui_auth, ui_login
  - dashboard, user_manager, role_editor, admin_dialog
  - package_manager, database_manager, schema_editor

Update seed orchestration in /dbal/development/src/seeds/index.ts:
- Look for packages/[packageId]/page-config/ folders instead of seed/
- Load all JSON files in entity folders (not just specific references)
- Maintains idempotency and error handling

Update documentation:
- CLAUDE.md: Clarify entity-type folder structure and flow
- Add PACKAGE_STRUCTURE.md: Complete guide for organizing packages
- Update Task 2 with step-by-step entity folder setup

Benefits:
- Clear navigation: instantly see what each package provides
- Prevents AI from overthinking: structure is obvious
- One folder per entity type: simple and consistent
- Scalable: easy to add new entity types

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-16 14:40:24 +00:00
parent f47f5a6671
commit aaf4b6e9d0
28 changed files with 308 additions and 233 deletions

View File

@@ -286,12 +286,13 @@ See `seed/packages/core-packages.yaml` for full order.
- `package_permissions.yaml` - Permission/access control seed data
- Loaded during `POST /api/bootstrap` via `seedDatabase()`
2. **Package Seed Data** (`/packages/[packageId]/seed/`)
- `metadata.json` - **Required**. Describes the package and references data files
- `[entity-type].json` - **Optional**. Data files for specific entities (page-config, workflow, etc.)
2. **Package Entity Seed Data** (`/packages/[packageId]/[entity-type]/`)
- One folder per entity type: `page-config/`, `workflow/`, `credential/`, etc.
- `metadata.json` - Describes the entity folder (entity type, packageId, description)
- `*.json` - Data files (e.g., `home.json`, `dashboard.json` in page-config folder)
- Simple JSON arrays, not code or schemas
- Loaded automatically for packages listed in `installed_packages.yaml`
- Examples: ui_home (has page-config.json), others ready to add as needed
- Examples: ui_home/page-config/, dashboard/page-config/, etc.
3. **DBAL** (Phase 2: TypeScript, Phase 3: C++)
- **OWNS:** Database schema (YAML source of truth)
@@ -718,34 +719,37 @@ export async function GET(request: Request) {
### Task 2: Add Seed Data to a Package
Each package that needs bootstrap data gets a simple seed folder:
Each package organizes seed data by entity type in subfolders:
```
/packages/my-package/seed/
├── metadata.json [Required - describes package]
└── page-config.json [Optional - entity data]
/packages/my-package/
├── page-config/ [If package defines routes]
│ ├── metadata.json
│ └── home.json
├── workflow/ [If package defines workflows]
│ ├── metadata.json
│ └── sync.json
└── package.json
```
**Step 1: Create metadata.json**
**Step 1: Create entity folder**
```bash
mkdir -p /packages/my-package/page-config
```
**Step 2: Create metadata.json**
```json
{
"$schema": "https://metabuilder.dev/schemas/package-metadata.schema.json",
"entity": "page-config",
"packageId": "my_package",
"name": "My Package",
"version": "1.0.0",
"description": "Seed data for my_package",
"author": "MetaBuilder Contributors",
"category": "ui",
"minLevel": 0,
"seed": {
"schema": "page-config.json"
}
"description": "Page routes for my_package",
"version": "1.0.0"
}
```
**Step 2: Create entity data file (if needed)**
**Step 3: Create data files**
```json
// /packages/my-package/seed/page-config.json
// /packages/my-package/page-config/home.json
[
{
"id": "page_my_pkg_home",
@@ -759,10 +763,10 @@ Each package that needs bootstrap data gets a simple seed folder:
]
```
**Step 3: Package must be listed in bootstrap**
**Step 4: Package must be listed in bootstrap**
Add to `/dbal/shared/seeds/database/installed_packages.yaml` if it's a core system package.
See `/packages/SEED_FORMAT.md` for complete seed data specification.
See `/packages/PACKAGE_STRUCTURE.md` for complete package organization guide.
### Task 3: Initialize Database for Tests
```typescript

View File

@@ -10,7 +10,7 @@
* 1. /dbal/shared/seeds/database - Base system bootstrap data (YAML format)
* 2. /dbal/shared/seeds/config - Bootstrap configuration (YAML format)
* 3. /dbal/shared/seeds/packages - Package installation order (YAML format)
* 4. packages/[packageId]/seed/metadata.json - Package-specific seed data
* 4. packages/[packageId]/[entity-type]/ - Entity-specific seed data (page-config, workflow, etc.)
*/
import fs from 'fs'
@@ -80,63 +80,60 @@ export async function seedDatabase(dbal: DBALClient): Promise<void> {
}
}
// 3. Load PageConfig entries from package seed/page-config.json files
// 3. Load PageConfig entries from packages/[packageId]/page-config/ folders
for (const pkg of packagesData.records) {
const seedMetadataPath = path.join(packagesDir, pkg.packageId, 'seed', 'metadata.json')
const pageConfigDir = path.join(packagesDir, pkg.packageId, 'page-config')
// Check if this package has seed data
if (fs.existsSync(seedMetadataPath)) {
const seedMetadata = JSON.parse(fs.readFileSync(seedMetadataPath, 'utf8'))
// Check if this package has page-config folder
if (fs.existsSync(pageConfigDir)) {
// Load all JSON files in page-config folder
const files = fs.readdirSync(pageConfigDir)
const jsonFiles = files.filter(f => f.endsWith('.json') && f !== 'metadata.json')
// Get the reference to the actual seed data file (e.g., page-config.json)
const seedFile = seedMetadata.seed?.schema
if (seedFile) {
const seedDataPath = path.join(packagesDir, pkg.packageId, 'seed', seedFile)
for (const file of jsonFiles) {
const seedDataPath = path.join(pageConfigDir, file)
const seedData = JSON.parse(fs.readFileSync(seedDataPath, 'utf8'))
if (fs.existsSync(seedDataPath)) {
const seedData = JSON.parse(fs.readFileSync(seedDataPath, 'utf8'))
// seedData should be an array of PageConfig entries
const pages = Array.isArray(seedData) ? seedData : []
// seedData should be an array of PageConfig entries
const pages = Array.isArray(seedData) ? seedData : []
for (const page of pages) {
// Check if page already exists
const existing = await prisma.pageConfig.findFirst({
where: { path: page.path }
})
for (const page of pages) {
// Check if page already exists
const existing = await prisma.pageConfig.findFirst({
where: { path: page.path }
})
if (!existing) {
try {
await prisma.pageConfig.create({
data: {
id: page.id || `page_${pkg.packageId}_${page.path.replace(/\//g, '_')}`,
tenantId: page.tenantId || null,
packageId: pkg.packageId,
path: page.path,
title: page.title,
description: page.description || null,
icon: page.icon || null,
component: page.component,
componentTree: page.componentTree || '{}',
level: page.level || 0,
requiresAuth: page.requiresAuth === true,
requiredRole: page.requiredRole || null,
parentPath: page.parentPath || null,
sortOrder: page.sortOrder || 0,
isPublished: page.isPublished === true,
params: page.params || null,
meta: page.meta || null,
createdAt: BigInt(Date.now()),
updatedAt: BigInt(Date.now())
}
})
console.log(`Created PageConfig for: ${page.path}`)
} catch (error) {
if ((error as any).code === 'P2002') {
console.log(`PageConfig ${page.path} already exists, skipping`)
} else {
throw error
if (!existing) {
try {
await prisma.pageConfig.create({
data: {
id: page.id || `page_${pkg.packageId}_${page.path.replace(/\//g, '_')}`,
tenantId: page.tenantId || null,
packageId: pkg.packageId,
path: page.path,
title: page.title,
description: page.description || null,
icon: page.icon || null,
component: page.component,
componentTree: page.componentTree || '{}',
level: page.level || 0,
requiresAuth: page.requiresAuth === true,
requiredRole: page.requiredRole || null,
parentPath: page.parentPath || null,
sortOrder: page.sortOrder || 0,
isPublished: page.isPublished === true,
params: page.params || null,
meta: page.meta || null,
createdAt: BigInt(Date.now()),
updatedAt: BigInt(Date.now())
}
})
console.log(`Created PageConfig for: ${page.path}`)
} catch (error) {
if ((error as any).code === 'P2002') {
console.log(`PageConfig ${page.path} already exists, skipping`)
} else {
throw error
}
}
}

View File

@@ -0,0 +1,161 @@
# Package Structure Guide
## Quick Reference
Every MetaBuilder package follows this simple structure:
```
packages/my-package/
├── page-config/ [If package defines routes/pages]
│ ├── metadata.json
│ └── *.json [Page configuration data]
├── workflow/ [If package defines workflows]
│ ├── metadata.json
│ └── *.json [Workflow definitions]
├── credential/ [If package needs API credentials]
│ ├── metadata.json
│ └── *.json [Credential templates]
├── notification/ [If package sends notifications]
│ ├── metadata.json
│ └── *.json [Notification templates]
├── component/ [If package defines reusable components]
│ ├── metadata.json
│ └── *.json [Component definitions]
├── package.json [Standard NPM package file]
├── README.md [Documentation]
└── ...other files... [Source, tests, etc.]
```
## Entity Folders
Each entity folder contains:
| Folder | Purpose | When to Use |
|--------|---------|-------------|
| **page-config** | Routes/pages exposed by this package | If package has public pages/routes |
| **workflow** | Automation workflows | If package provides workflows |
| **credential** | API credentials or secrets | If package integrates with external APIs |
| **notification** | Notification templates | If package sends notifications |
| **component** | Reusable component definitions | If package defines UI components |
| **ui_component** | UI-specific components | Alternative for UI packages |
## File Organization
Inside each entity folder:
### metadata.json (Required)
Describes what's in this folder:
```json
{
"entity": "page-config",
"packageId": "my_package",
"description": "Page routes defined by my_package",
"version": "1.0.0"
}
```
### Data Files (Optional)
Named by what they define:
```
page-config/
├── metadata.json
├── home.json [Route /my-package/home]
└── dashboard.json [Route /my-package/dashboard]
workflow/
├── metadata.json
├── user_onboard.json [Workflow: onboard users]
└── sync_data.json [Workflow: sync data]
credential/
├── metadata.json
└── api_keys.json [API credential template]
```
## Rules
1. **One folder per entity type** - Don't mix entities
2. **Only data files** - No code, scripts, or utilities
3. **Always include metadata.json** - Describes the folder
4. **Keep it minimal** - Only what the package actually needs
## Examples
### Package with Pages Only
```
packages/ui_home/
├── page-config/
│ ├── metadata.json
│ └── home.json
└── package.json
```
### Package with Multiple Entity Types
```
packages/dashboard/
├── page-config/
│ ├── metadata.json
│ └── dashboard.json
├── workflow/
│ ├── metadata.json
│ └── daily_sync.json
└── package.json
```
### Package with Just Components
```
packages/form_builder/
├── component/
│ ├── metadata.json
│ └── form_field.json
└── package.json
```
## Navigation
When you see a package folder, immediately know what it provides:
- See `page-config/` → This package defines routes
- See `workflow/` → This package provides workflows
- See `credential/` → This package needs API keys
- See `notification/` → This package sends alerts
- See `component/` → This package provides components
**No guessing. No overthinking. Just look at the folders.**
## Anti-Patterns (What NOT to Do)
```
❌ DON'T create other folders:
packages/my-package/
├── seed/ ← Put this in page-config/, workflow/, etc.
├── seeds/ ← Wrong location
├── data/ ← Wrong location
├── schemas/ ← Wrong location
└── utils/ ← Wrong location
❌ DON'T put code files here:
packages/my-package/
├── page-config/
│ ├── loader.ts ← NO: TypeScript code
│ └── utils.js ← NO: Utilities
```
## Adding to Your Package
To add entity data to an existing package:
1. Create the entity folder: `packages/my-package/page-config/`
2. Add metadata.json describing what's in it
3. Add data files for each entity (home.json, dashboard.json, etc.)
4. Done - the system will find and load it
See `/packages/SEED_FORMAT.md` for detailed seed data specification.
## See Also
- `/packages/SEED_FORMAT.md` - Complete seed data documentation
- `/packages/PACKAGE_AUDIT.md` - Analysis of all 51 packages
- `/dbal/shared/api/schema/entities/` - Entity definitions (source of truth)

View File

@@ -0,0 +1,6 @@
{
"entity": "page-config",
"packageId": "admin_dialog",
"description": "Page routes for admin_dialog",
"version": "1.0.0"
}

View File

@@ -1,13 +0,0 @@
{
"$schema": "https://metabuilder.dev/schemas/package-metadata.schema.json",
"packageId": "admin_dialog",
"name": "Admin Dialog",
"version": "1.0.0",
"description": "Seed data for admin_dialog package",
"author": "MetaBuilder Contributors",
"license": "MIT",
"category": "ui",
"minLevel": 4,
"primary": false,
"keywords": ["admin", "dialog", "ui", "seed-data"]
}

View File

@@ -0,0 +1,6 @@
{
"entity": "page-config",
"packageId": "dashboard",
"description": "Page routes for dashboard",
"version": "1.0.0"
}

View File

@@ -1,13 +0,0 @@
{
"$schema": "https://metabuilder.dev/schemas/package-metadata.schema.json",
"packageId": "dashboard",
"name": "Dashboard",
"version": "1.0.0",
"description": "Seed data for dashboard package",
"author": "MetaBuilder Contributors",
"license": "MIT",
"category": "ui",
"minLevel": 1,
"primary": true,
"keywords": ["dashboard", "user", "ui", "seed-data"]
}

View File

@@ -0,0 +1,6 @@
{
"entity": "page-config",
"packageId": "database_manager",
"description": "Page routes for database_manager",
"version": "1.0.0"
}

View File

@@ -1,13 +0,0 @@
{
"$schema": "https://metabuilder.dev/schemas/package-metadata.schema.json",
"packageId": "database_manager",
"name": "Database Manager",
"version": "1.0.0",
"description": "Seed data for database_manager package",
"author": "MetaBuilder Contributors",
"license": "MIT",
"category": "admin",
"minLevel": 5,
"primary": false,
"keywords": ["database", "admin", "system", "seed-data"]
}

View File

@@ -0,0 +1,6 @@
{
"entity": "page-config",
"packageId": "package_manager",
"description": "Page routes for package_manager",
"version": "1.0.0"
}

View File

@@ -1,13 +0,0 @@
{
"$schema": "https://metabuilder.dev/schemas/package-metadata.schema.json",
"packageId": "package_manager",
"name": "Package Manager",
"version": "1.0.0",
"description": "Seed data for package_manager system package",
"author": "MetaBuilder Contributors",
"license": "MIT",
"category": "system",
"minLevel": 4,
"primary": true,
"keywords": ["package", "management", "system", "seed-data"]
}

View File

@@ -0,0 +1,6 @@
{
"entity": "page-config",
"packageId": "role_editor",
"description": "Page routes for role_editor",
"version": "1.0.0"
}

View File

@@ -1,13 +0,0 @@
{
"$schema": "https://metabuilder.dev/schemas/package-metadata.schema.json",
"packageId": "role_editor",
"name": "Role Editor",
"version": "1.0.0",
"description": "Seed data for role_editor package",
"author": "MetaBuilder Contributors",
"license": "MIT",
"category": "admin",
"minLevel": 4,
"primary": false,
"keywords": ["roles", "permissions", "editor", "admin", "seed-data"]
}

View File

@@ -0,0 +1,6 @@
{
"entity": "page-config",
"packageId": "schema_editor",
"description": "Page routes for schema_editor",
"version": "1.0.0"
}

View File

@@ -1,13 +0,0 @@
{
"$schema": "https://metabuilder.dev/schemas/package-metadata.schema.json",
"packageId": "schema_editor",
"name": "Schema Editor",
"version": "1.0.0",
"description": "Seed data for schema_editor package",
"author": "MetaBuilder Contributors",
"license": "MIT",
"category": "admin",
"minLevel": 5,
"primary": false,
"keywords": ["schema", "editor", "admin", "system", "seed-data"]
}

View File

@@ -0,0 +1,6 @@
{
"entity": "page-config",
"packageId": "ui_auth",
"description": "Page routes for ui_auth",
"version": "1.0.0"
}

View File

@@ -1,13 +0,0 @@
{
"$schema": "https://metabuilder.dev/schemas/package-metadata.schema.json",
"packageId": "ui_auth",
"name": "UI Auth",
"version": "1.0.0",
"description": "Seed data for ui_auth package",
"author": "MetaBuilder Contributors",
"license": "MIT",
"category": "ui",
"minLevel": 0,
"primary": true,
"keywords": ["authentication", "auth", "ui", "seed-data"]
}

View File

@@ -0,0 +1,6 @@
{
"entity": "page-config",
"packageId": "ui_footer",
"description": "Page routes for ui_footer",
"version": "1.0.0"
}

View File

@@ -1,13 +0,0 @@
{
"$schema": "https://metabuilder.dev/schemas/package-metadata.schema.json",
"packageId": "ui_footer",
"name": "UI Footer",
"version": "1.0.0",
"description": "Seed data for ui_footer package",
"author": "MetaBuilder Contributors",
"license": "MIT",
"category": "ui",
"minLevel": 0,
"primary": true,
"keywords": ["footer", "ui", "seed-data"]
}

View File

@@ -0,0 +1,6 @@
{
"entity": "page-config",
"packageId": "ui_header",
"description": "Page routes for ui_header",
"version": "1.0.0"
}

View File

@@ -1,13 +0,0 @@
{
"$schema": "https://metabuilder.dev/schemas/package-metadata.schema.json",
"packageId": "ui_header",
"name": "UI Header",
"version": "1.0.0",
"description": "Seed data for ui_header package",
"author": "MetaBuilder Contributors",
"license": "MIT",
"category": "ui",
"minLevel": 0,
"primary": true,
"keywords": ["header", "navigation", "ui", "seed-data"]
}

View File

@@ -0,0 +1,6 @@
{
"entity": "page-config",
"packageId": "ui_home",
"description": "Home page routes",
"version": "1.0.0"
}

View File

@@ -1,16 +0,0 @@
{
"$schema": "https://metabuilder.dev/schemas/package-metadata.schema.json",
"packageId": "ui_home",
"name": "Home Page",
"version": "1.0.0",
"description": "Seed data for ui_home package",
"author": "MetaBuilder Contributors",
"license": "MIT",
"category": "ui",
"minLevel": 0,
"primary": true,
"keywords": ["home", "landing", "seed-data"],
"seed": {
"schema": "page-config.json"
}
}

View File

@@ -0,0 +1,6 @@
{
"entity": "page-config",
"packageId": "ui_login",
"description": "Page routes for ui_login",
"version": "1.0.0"
}

View File

@@ -1,13 +0,0 @@
{
"$schema": "https://metabuilder.dev/schemas/package-metadata.schema.json",
"packageId": "ui_login",
"name": "UI Login",
"version": "1.0.0",
"description": "Seed data for ui_login package",
"author": "MetaBuilder Contributors",
"license": "MIT",
"category": "ui",
"minLevel": 0,
"primary": true,
"keywords": ["login", "authentication", "ui", "seed-data"]
}

View File

@@ -0,0 +1,6 @@
{
"entity": "page-config",
"packageId": "user_manager",
"description": "Page routes for user_manager",
"version": "1.0.0"
}

View File

@@ -1,13 +0,0 @@
{
"$schema": "https://metabuilder.dev/schemas/package-metadata.schema.json",
"packageId": "user_manager",
"name": "User Manager",
"version": "1.0.0",
"description": "Seed data for user_manager package",
"author": "MetaBuilder Contributors",
"license": "MIT",
"category": "admin",
"minLevel": 3,
"primary": true,
"keywords": ["users", "management", "admin", "seed-data"]
}