Skip to content

Commit 2a8cd68

Browse files
authored
Merge pull request #665 from objectstack-ai/copilot/update-metadata-documentation
2 parents 291775e + 2cc3b28 commit 2a8cd68

File tree

2 files changed

+461
-6
lines changed

2 files changed

+461
-6
lines changed

packages/metadata/README.md

Lines changed: 238 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,245 @@
11
# @objectstack/metadata
22

3-
The **Model Definition** layer. It provides classes and utilities for parsing and validating ObjectStack metadata schemas.
3+
> **Metadata Loading, Persistence & Customization Layer for ObjectStack.**
44
5-
## Features
5+
`@objectstack/metadata` is the central service responsible for loading, validating, persisting and watching all metadata definitions (Objects, Views, Flows, Apps, Agents, etc.) in the ObjectStack platform.
66

7-
- **Schema Loading**: Load metadata from JSON/YAML or TypeScript files.
8-
- **Reference Resolution**: Resolves cross-object references and inheritance.
9-
- **Validation**: Strict validation against the Zod schemas in `@objectstack/spec`.
7+
It implements the **`IMetadataService`** contract from `@objectstack/spec` and acts as the single source of truth that all other packages depend on.
8+
9+
## Architecture Overview
10+
11+
```
12+
┌─────────────────────────────────────────────────────────────┐
13+
│ IMetadataService │
14+
│ (Contract: @objectstack/spec) │
15+
├─────────────────────────────────────────────────────────────┤
16+
│ MetadataManager │
17+
│ (Orchestrator: this package) │
18+
│ │
19+
│ ┌─────────────┐ ┌──────────────┐ ┌───────────────────┐ │
20+
│ │ In-Memory │ │ Overlay │ │ Type Registry │ │
21+
│ │ Registry │ │ System │ │ & Dependencies │ │
22+
│ └─────────────┘ └──────────────┘ └───────────────────┘ │
23+
├─────────────────────────────────────────────────────────────┤
24+
│ Loader Layer │
25+
│ ┌─────────────┐ ┌──────────────┐ ┌───────────────────┐ │
26+
│ │ Filesystem │ │ Remote │ │ Memory │ │
27+
│ │ Loader │ │ Loader │ │ Loader │ │
28+
│ │ (files) │ │ (HTTP) │ │ (test) │ │
29+
│ └─────────────┘ └──────────────┘ └───────────────────┘ │
30+
│ ┌──────────────────────────────────────────────────────┐ │
31+
│ │ DatabaseLoader (planned — datasource-backed storage) │ │
32+
│ └──────────────────────────────────────────────────────┘ │
33+
├─────────────────────────────────────────────────────────────┤
34+
│ Serializer Layer │
35+
│ ┌──────────┐ ┌──────────┐ ┌──────────────────────────┐ │
36+
│ │ JSON │ │ YAML │ │ TypeScript/JavaScript │ │
37+
│ └──────────┘ └──────────┘ └──────────────────────────┘ │
38+
└─────────────────────────────────────────────────────────────┘
39+
```
40+
41+
## Core Concepts
42+
43+
### 1. Metadata Sources (Three-Scope Model)
44+
45+
ObjectStack adopts a three-scope layered model for metadata:
46+
47+
| Scope | Storage | Mutability | Description |
48+
|:-----------|:-------------|:-------------|:-------------------------------------------|
49+
| `system` | Filesystem | Read-only | Defined in code, shipped with packages |
50+
| `platform` | Database | Admin-editable | Created/modified by admins via UI |
51+
| `user` | Database | User-editable | Personal customizations per user |
52+
53+
Resolution order: **system** ← merge(**platform**) ← merge(**user**).
54+
55+
### 2. Loaders
56+
57+
Loaders are pluggable data sources that know how to read/write metadata from different backends. Each loader declares a `MetadataLoaderContract` with name, protocol, and capabilities:
58+
59+
| Loader | Protocol | Read | Write | Watch | Status |
60+
|:--------------------|:---------------|:-----|:------|:------|:-------------|
61+
| `FilesystemLoader` | `file:` |||| Implemented |
62+
| `MemoryLoader` | `memory:` |||| Implemented |
63+
| `RemoteLoader` | `http:` |||| Implemented |
64+
| `DatabaseLoader` | `datasource:` |||| Planned |
65+
66+
### 3. Serializers
67+
68+
Serializers convert metadata objects to/from different file formats:
69+
70+
- **JSONSerializer**`.json` files with optional key sorting
71+
- **YAMLSerializer**`.yaml`/`.yml` files (JSON_SCHEMA for security)
72+
- **TypeScriptSerializer**`.ts`/`.js` module exports (for `defineObject()`, `defineView()`, etc.)
73+
74+
### 4. Overlay / Customization System
75+
76+
The overlay system enables non-destructive customizations on top of package-delivered (system) metadata, following a delta-based approach (JSON Merge Patch):
77+
78+
- **getOverlay** / **saveOverlay** / **removeOverlay** — manage customization deltas
79+
- **getEffective** — returns the merged result of base + platform overlay + user overlay
80+
- Overlays never modify the base definition — they are additive patches
81+
82+
### 5. MetadataManager (IMetadataService Implementation)
83+
84+
The `MetadataManager` is the main orchestrator. It provides:
85+
86+
- **Core CRUD**: `register`, `get`, `list`, `unregister`, `exists`, `listNames`
87+
- **Convenience**: `getObject`, `listObjects`
88+
- **Package Management**: `unregisterPackage` — unload all metadata from a package
89+
- **Query / Search**: `query` with filtering, pagination, sorting by type/scope/state/tags
90+
- **Bulk Operations**: `bulkRegister`, `bulkUnregister` with error handling
91+
- **Import / Export**: `exportMetadata`, `importMetadata` with conflict resolution (skip/overwrite/merge)
92+
- **Validation**: `validate` — structural validation of metadata items
93+
- **Type Registry**: `getRegisteredTypes`, `getTypeInfo` — discover available metadata types
94+
- **Dependency Tracking**: `getDependencies`, `getDependents` — cross-reference analysis
95+
- **Watch / Subscribe**: `watchService` — observe metadata changes in real-time
96+
- **Loader Delegation**: `load`, `loadMany`, `save` — delegate I/O to registered loaders
97+
98+
### 6. NodeMetadataManager
99+
100+
Extends `MetadataManager` with Node.js-specific capabilities:
101+
102+
- Auto-configures `FilesystemLoader` for local development
103+
- File watching via **chokidar** for hot-reload during development
104+
- Detects file add/change/delete events and notifies subscribers
105+
106+
### 7. MetadataPlugin
107+
108+
Integrates with the ObjectStack kernel plugin system:
109+
110+
- Registers as the primary `IMetadataService` provider
111+
- Auto-loads all metadata types from the filesystem on startup (sorted by `loadOrder`)
112+
- Supports YAML, JSON, TypeScript, and JavaScript metadata formats
113+
114+
## Metadata Types
115+
116+
The platform supports **26 built-in metadata types** across 6 protocol domains:
117+
118+
| Domain | Types |
119+
|:-------------|:----------------------------------------------------------------------------|
120+
| **Data** | `object`, `field`, `datasource`, `validation` |
121+
| **UI** | `view`, `app`, `dashboard`, `report`, `action`, `theme` |
122+
| **Automation** | `flow`, `workflow`, `trigger`, `schedule` |
123+
| **System** | `manifest`, `translation`, `api`, `permission_set`, `role`, `profile` |
124+
| **Security** | `permission_set`, `role` |
125+
| **AI** | `agent`, `rag_pipeline`, `model`, `prompt`, `tool` |
126+
127+
Each type has a defined `loadOrder` (dependencies load before dependents), file patterns (e.g. `**/*.object.{ts,json,yaml}`), and overlay support flag.
128+
129+
## Spec Protocol References
130+
131+
This package depends on schemas and contracts defined in `@objectstack/spec`:
132+
133+
| Spec Module | What It Defines |
134+
|:---------------------------------|:----------------------------------------------------|
135+
| `spec/contracts/metadata-service` | `IMetadataService` — the async service interface |
136+
| `spec/kernel/metadata-loader` | Loader contract, load/save/watch schemas, `MetadataManagerConfig` |
137+
| `spec/kernel/metadata-plugin` | Type registry, plugin manifest, capabilities |
138+
| `spec/kernel/metadata-customization` | Overlay, merge strategy, customization policy |
139+
| `spec/system/metadata-persistence` | `MetadataRecord` — DB persistence envelope |
140+
| `spec/data/datasource` | `DatasourceSchema`, `DriverDefinition`, capabilities |
141+
| `spec/contracts/data-driver` | `IDataDriver` — database driver interface |
142+
143+
## Installation
144+
145+
```bash
146+
pnpm add @objectstack/metadata
147+
```
10148

11149
## Usage
12150

13-
Internal package used by `@objectstack/runtime` to process configuration.
151+
### Basic (Browser-Compatible)
152+
153+
```typescript
154+
import { MetadataManager, MemoryLoader } from '@objectstack/metadata';
155+
156+
const manager = new MetadataManager({
157+
formats: ['json'],
158+
loaders: [new MemoryLoader()],
159+
});
160+
161+
// Register metadata
162+
await manager.register('object', 'account', { name: 'account', label: 'Account', fields: {} });
163+
164+
// Retrieve
165+
const obj = await manager.get('object', 'account');
166+
167+
// Query
168+
const result = await manager.query({ types: ['object'], search: 'account' });
169+
```
170+
171+
### Node.js (with Filesystem)
172+
173+
```typescript
174+
import { NodeMetadataManager, MetadataPlugin } from '@objectstack/metadata/node';
175+
176+
const manager = new NodeMetadataManager({
177+
rootDir: './src',
178+
formats: ['typescript', 'json', 'yaml'],
179+
watch: true,
180+
});
181+
182+
// Load all objects from filesystem
183+
const objects = await manager.loadMany('object');
184+
185+
// Watch for changes
186+
manager.watchService('object', (event) => {
187+
console.log(`Object ${event.name} was ${event.type}`);
188+
});
189+
```
190+
191+
### With Kernel Plugin
192+
193+
```typescript
194+
import { MetadataPlugin } from '@objectstack/metadata/node';
195+
196+
const plugin = MetadataPlugin({
197+
rootDir: './src',
198+
watch: process.env.NODE_ENV === 'development',
199+
});
200+
// Register with ObjectStack kernel
201+
kernel.use(plugin);
202+
```
203+
204+
## Package Structure
205+
206+
```
207+
packages/metadata/
208+
├── src/
209+
│ ├── index.ts # Main exports (browser-compatible)
210+
│ ├── node.ts # Node.js exports (filesystem, watching)
211+
│ ├── metadata-manager.ts # MetadataManager (IMetadataService impl)
212+
│ ├── node-metadata-manager.ts # NodeMetadataManager (+ file watching)
213+
│ ├── plugin.ts # MetadataPlugin (kernel integration)
214+
│ ├── loaders/
215+
│ │ ├── loader-interface.ts # MetadataLoader contract
216+
│ │ ├── filesystem-loader.ts # File I/O with glob, cache, ETag
217+
│ │ ├── memory-loader.ts # In-memory store (tests/overrides)
218+
│ │ └── remote-loader.ts # HTTP API loader with auth
219+
│ ├── serializers/
220+
│ │ ├── serializer-interface.ts # MetadataSerializer contract
221+
│ │ ├── json-serializer.ts # JSON format
222+
│ │ ├── yaml-serializer.ts # YAML format
223+
│ │ └── typescript-serializer.ts # TS/JS module format
224+
│ └── migration/
225+
│ ├── index.ts # Barrel export
226+
│ └── executor.ts # ChangeSet executor (DDL operations)
227+
├── package.json
228+
├── tsconfig.json
229+
├── vitest.config.ts
230+
├── README.md # This file
231+
└── ROADMAP.md # Development roadmap
232+
```
233+
234+
## Related Packages
235+
236+
| Package | Relationship |
237+
|:------------------------|:-------------------------------------------------|
238+
| `@objectstack/spec` | Protocol definitions (schemas, contracts, types) |
239+
| `@objectstack/core` | Logger, service registry, kernel utilities |
240+
| `@objectstack/runtime` | Uses this package to bootstrap metadata |
241+
| `apps/studio` | Visual metadata editor (consumes IMetadataService)|
242+
243+
## License
244+
245+
Apache-2.0 — see [LICENSE](../../LICENSE) for details.

0 commit comments

Comments
 (0)