Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,19 @@ ObjectStack is organized as a **monorepo** with distinct package layers:

**Dependencies**: `@objectstack/core`, `@objectstack/spec`, `@objectstack/types`

#### `@objectstack/metadata`
**Location**: `packages/metadata/`
**Role**: Metadata Loading & Persistence

- Metadata serialization/deserialization (JSON, YAML, TypeScript)
- File system operations (load, save, watch)
- Validation using Zod schemas
- ETag-based caching
- Import/export tools
- SchemaRegistry persistence bridge

**Dependencies**: `@objectstack/spec`, `@objectstack/core`, `@objectstack/types`, `glob`, `js-yaml`, `chokidar`

#### `@objectstack/runtime`
**Location**: `packages/runtime/`
**Role**: Runtime Utilities & Plugins
Expand Down Expand Up @@ -409,6 +422,12 @@ interface PluginCapabilityManifest {
│ │ ↑
│ │ └── @objectstack/plugin-msw
│ │
│ ├── @objectstack/metadata (Metadata I/O)
│ │ ↑
│ │ ├── @objectstack/runtime (Uses for manifest loading)
│ │ ├── @objectstack/cli (Uses for code generation)
│ │ └── @objectstack/objectql (Uses for registry persistence)
│ │
│ ├── @objectstack/runtime (Plugins & HTTP)
│ │ ↑
│ │ └── (Used by server plugins)
Expand Down
21 changes: 20 additions & 1 deletion PACKAGE-DEPENDENCIES.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,16 @@ This is the foundation layer. All other packages depend on `@objectstack/spec`.
│ └── @objectstack/types
└── Role: ObjectQL query engine

@objectstack/metadata
├── Dependencies:
│ ├── @objectstack/core
│ ├── @objectstack/spec
│ ├── @objectstack/types
│ ├── glob
│ ├── js-yaml
│ └── chokidar (optional)
└── Role: Metadata loading, saving, and persistence

@objectstack/runtime
├── Dependencies:
│ ├── @objectstack/core
Expand Down Expand Up @@ -131,6 +141,12 @@ This is the foundation layer. All other packages depend on `@objectstack/spec`.
│ │ ↑
│ │ └── @objectstack/plugin-msw (Layer 5)
│ │
│ ├── @objectstack/metadata (Layer 3)
│ │ ↑
│ │ ├── @objectstack/runtime (Layer 3)
│ │ ├── @objectstack/cli (Layer 6)
│ │ └── (Future integrations)
│ │
│ ├── @objectstack/runtime (Layer 3)
│ │ ↑
│ │ └── (Used by server plugins)
Expand All @@ -156,6 +172,7 @@ This is the foundation layer. All other packages depend on `@objectstack/spec`.
| `@objectstack/types` | `@objectstack/spec` |
| `@objectstack/core` | `@objectstack/spec`, `pino`, `zod` |
| `@objectstack/objectql` | `@objectstack/core`, `@objectstack/spec`, `@objectstack/types` |
| `@objectstack/metadata` | `@objectstack/core`, `@objectstack/spec`, `@objectstack/types`, `glob`, `js-yaml`, `chokidar` |
| `@objectstack/runtime` | `@objectstack/core`, `@objectstack/spec`, `@objectstack/types` |
| `@objectstack/client` | `@objectstack/core`, `@objectstack/spec` |
| `@objectstack/client-react` | `@objectstack/client`, `@objectstack/core`, `@objectstack/spec`, `react` (peer) |
Expand All @@ -181,6 +198,7 @@ pnpm --filter @objectstack/core build

# Level 3
pnpm --filter @objectstack/objectql build
pnpm --filter @objectstack/metadata build
pnpm --filter @objectstack/runtime build

# Level 4
Expand Down Expand Up @@ -253,7 +271,8 @@ Use peer dependencies for:
|---------|---------------|-------------------|
| `@objectstack/spec` | Define protocols | Runtime behavior |
| `@objectstack/core` | Manage plugin lifecycle | Query execution, HTTP handling |
| `@objectstack/objectql` | Execute queries | HTTP routing, UI rendering |
| `@objectstack/objectql` | Execute queries | HTTP routing, UI rendering, Metadata persistence |
| `@objectstack/metadata` | Load/save metadata | Query execution, Plugin lifecycle |
| `@objectstack/runtime` | Provide plugin utilities | Execute queries directly |

## External Dependencies
Expand Down
7 changes: 1 addition & 6 deletions content/docs/references/meta.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
{
"title": "Protocol Reference",
"pages": [
"packages",
"data",
"ui",
"automation",
"system",
"permission",
"ai",
"api",
"driver",
"hub",
"integration",
"auth",
"shared"
"driver"
]
}
1 change: 1 addition & 0 deletions content/docs/references/system/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ This section contains all protocol schemas for the system layer of ObjectStack.
<Card href="./manifest" title="Manifest" description="Source: packages/spec/src/system/manifest.zod.ts" />
<Card href="./masking" title="Masking" description="Source: packages/spec/src/system/masking.zod.ts" />
<Card href="./message-queue" title="Message Queue" description="Source: packages/spec/src/system/message-queue.zod.ts" />
<Card href="./metadata-loader" title="Metadata Loader" description="Source: packages/spec/src/system/metadata-loader.zod.ts" />
<Card href="./metrics" title="Metrics" description="Source: packages/spec/src/system/metrics.zod.ts" />
<Card href="./notification" title="Notification" description="Source: packages/spec/src/system/notification.zod.ts" />
<Card href="./object-storage" title="Object Storage" description="Source: packages/spec/src/system/object-storage.zod.ts" />
Expand Down
1 change: 1 addition & 0 deletions content/docs/references/system/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"manifest",
"masking",
"message-queue",
"metadata-loader",
"metrics",
"notification",
"object-storage",
Expand Down
4 changes: 2 additions & 2 deletions examples/msw-react-crud/src/components/TaskList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ export function TaskList({ client, onEdit, refreshTrigger }: TaskListProps) {
sort: ['priority', '-created_at']
});

// Handle { value: [] } (OData), { records: [] } (Protocol) and [] (Raw) formats
const rawValues = Array.isArray(result) ? result : (result.value || result.records || []);
// Handle { value: [] } (PaginatedResult) and [] (Raw) formats
const rawValues = Array.isArray(result) ? result : (result.value || []);
const fetchedTasks = [...rawValues] as Task[];

// Client-side sort fallback (since InMemoryDriver has limited sort support)
Expand Down
201 changes: 201 additions & 0 deletions packages/metadata/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
# @objectstack/metadata

Metadata loading, saving, and persistence for ObjectStack.

## Overview

The `@objectstack/metadata` package provides a unified interface for managing metadata across the ObjectStack ecosystem. It handles:

- **Metadata Serialization/Deserialization** - Support for JSON, YAML, TypeScript, and JavaScript formats
- **File System Operations** - Load, save, and watch metadata files
- **Validation** - Integration with Zod schemas from `@objectstack/spec`
- **Caching** - ETag-based caching for performance optimization
- **File Watching** - Development mode with automatic reload on file changes

## Installation

```bash
pnpm add @objectstack/metadata
```

## Quick Start

```typescript
import { MetadataManager } from '@objectstack/metadata';
import type { ServiceObject } from '@objectstack/spec/data';

// Create manager
const manager = new MetadataManager({
rootDir: './metadata',
formats: ['typescript', 'json', 'yaml'],
cache: { enabled: true, ttl: 3600 },
watch: process.env.NODE_ENV === 'development',
});

// Load metadata
const customer = await manager.load<ServiceObject>('object', 'customer');

// Save metadata
await manager.save('object', 'project', projectObject, {
format: 'typescript',
prettify: true,
});

// Load multiple items
const objects = await manager.loadMany<ServiceObject>('object', {
patterns: ['**/*.object.ts', '**/*.object.json'],
});

// Watch for changes
manager.watch('object', (event) => {
console.log(`Object ${event.type}:`, event.name);
});
```

## API

### MetadataManager

Main class for metadata operations.

#### Constructor

```typescript
new MetadataManager(config: MetadataManagerConfig)
```

**Config options:**
- `rootDir` - Root directory for metadata files
- `formats` - Enabled serialization formats (default: `['typescript', 'json', 'yaml']`)
- `cache` - Cache configuration with `enabled` and `ttl` options
- `watch` - Enable file watching (default: `false`)
- `watchOptions` - File watcher options (`ignored`, `persistent`, `ignoreInitial`)

#### Methods

**load<T>(type: string, name: string, options?: MetadataLoadOptions): Promise<T | null>**

Load a single metadata item.

```typescript
const customer = await manager.load<ServiceObject>('object', 'customer');
```

**loadMany<T>(type: string, options?: MetadataLoadOptions): Promise<T[]>**

Load multiple metadata items matching patterns.

```typescript
const objects = await manager.loadMany<ServiceObject>('object', {
patterns: ['**/*.object.ts'],
limit: 100,
});
```

**save<T>(type: string, name: string, data: T, options?: MetadataSaveOptions): Promise<MetadataSaveResult>**

Save metadata to disk.

```typescript
await manager.save('object', 'customer', customerObject, {
format: 'typescript',
prettify: true,
backup: true,
});
```

**exists(type: string, name: string): Promise<boolean>**

Check if metadata item exists.

```typescript
const exists = await manager.exists('object', 'customer');
```

**list(type: string): Promise<string[]>**

List all items of a type.

```typescript
const objectNames = await manager.list('object');
```

**watch(type: string, callback: WatchCallback): void**

Watch for metadata changes.

```typescript
manager.watch('object', (event) => {
if (event.type === 'added') {
console.log('New object:', event.name);
}
});
```

**stopWatching(): Promise<void>**

Stop all file watching.

## Serialization Formats

### JSON

```json
{
"name": "customer",
"label": "Customer",
"fields": {
"name": { "type": "text", "label": "Name" }
}
}
```

### YAML

```yaml
name: customer
label: Customer
fields:
name:
type: text
label: Name
```

### TypeScript

```typescript
import type { ServiceObject } from '@objectstack/spec/data';

export const metadata: ServiceObject = {
name: 'customer',
label: 'Customer',
fields: {
name: { type: 'text', label: 'Name' },
},
};

export default metadata;
```

## Architecture

The metadata package is designed as a Layer 3 package in the ObjectStack architecture:

```
@objectstack/metadata (Layer 3)
├── Dependencies:
│ ├── @objectstack/spec (validation)
│ ├── @objectstack/core (logging, DI)
│ ├── @objectstack/types (shared types)
│ ├── glob (file pattern matching)
│ ├── js-yaml (YAML support)
│ └── chokidar (file watching)
└── Used By:
├── @objectstack/cli (code generation)
├── @objectstack/runtime (manifest loading)
└── @objectstack/objectql (registry persistence)
```

## License

MIT
37 changes: 37 additions & 0 deletions packages/metadata/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"name": "@objectstack/metadata",
"version": "0.6.1",
"description": "Metadata loading, saving, and persistence for ObjectStack",
"main": "src/index.ts",
"types": "src/index.ts",
"scripts": {
"build": "tsc",
"dev": "tsc --watch",
"clean": "rm -rf dist",
"test": "vitest run",
"test:watch": "vitest",
"test:coverage": "vitest run --coverage"
},
"keywords": [
"objectstack",
"metadata",
"persistence",
"serialization",
"loader"
],
"dependencies": {
"@objectstack/core": "workspace:*",
"@objectstack/spec": "workspace:*",
"@objectstack/types": "workspace:*",
"glob": "^10.3.10",
"js-yaml": "^4.1.0",
"chokidar": "^3.5.3",
"zod": "^3.22.4"
},
"devDependencies": {
"@types/js-yaml": "^4.0.9",
"@types/node": "^20.0.0",
"typescript": "^5.0.0",
"vitest": "^1.0.0"
}
}
Loading
Loading