@objectstack/metadata 0.9.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/README.md +7 -452
- package/dist/loaders/filesystem-loader.d.ts.map +1 -1
- package/dist/loaders/filesystem-loader.js +8 -1
- package/dist/plugin.d.ts +2 -2
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +53 -53
- package/package.json +5 -4
- package/src/loaders/filesystem-loader.ts +9 -2
- package/src/plugin.ts +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @objectstack/metadata
|
|
2
2
|
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- Major version release for ObjectStack Protocol v1.0.
|
|
8
|
+
- Stabilized Protocol Definitions
|
|
9
|
+
- Enhanced Runtime Plugin Support
|
|
10
|
+
- Fixed Type Compliance across Monorepo
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- Updated dependencies
|
|
15
|
+
- @objectstack/spec@1.0.0
|
|
16
|
+
- @objectstack/core@1.0.0
|
|
17
|
+
- @objectstack/types@1.0.0
|
|
18
|
+
|
|
3
19
|
## 0.9.2
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1,458 +1,13 @@
|
|
|
1
1
|
# @objectstack/metadata
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The **Model Definition** layer. It provides classes and utilities for parsing and validating ObjectStack metadata schemas.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Features
|
|
6
6
|
|
|
7
|
-
|
|
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`.
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
- **File System Operations** - Load, save, and watch metadata files
|
|
11
|
-
- **Validation** - Integration with Zod schemas from `@objectstack/spec`
|
|
12
|
-
- **Caching** - ETag-based caching for performance optimization
|
|
13
|
-
- **File Watching** - Development mode with automatic reload on file changes
|
|
11
|
+
## Usage
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
**Role**: Metadata IO & Persistence
|
|
18
|
-
**Usage**:
|
|
19
|
-
- Use `MetadataManager` to read/write `.object.ts`, `.view.yaml` files.
|
|
20
|
-
- Handles format loading (TS, JSON, YAML).
|
|
21
|
-
|
|
22
|
-
## Installation
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
pnpm add @objectstack/metadata
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
## Quick Start
|
|
29
|
-
|
|
30
|
-
```typescript
|
|
31
|
-
import { MetadataManager } from '@objectstack/metadata';
|
|
32
|
-
import type { ServiceObject } from '@objectstack/spec/data';
|
|
33
|
-
|
|
34
|
-
// Create manager
|
|
35
|
-
const manager = new MetadataManager({
|
|
36
|
-
rootDir: './metadata',
|
|
37
|
-
formats: ['typescript', 'json', 'yaml'],
|
|
38
|
-
cache: { enabled: true, ttl: 3600 },
|
|
39
|
-
watch: process.env.NODE_ENV === 'development',
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
// Load metadata
|
|
43
|
-
const customer = await manager.load<ServiceObject>('object', 'customer');
|
|
44
|
-
|
|
45
|
-
// Save metadata
|
|
46
|
-
await manager.save('object', 'project', projectObject, {
|
|
47
|
-
format: 'typescript',
|
|
48
|
-
prettify: true,
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
// Load multiple items
|
|
52
|
-
const objects = await manager.loadMany<ServiceObject>('object', {
|
|
53
|
-
patterns: ['**/*.object.ts', '**/*.object.json'],
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
// Watch for changes
|
|
57
|
-
manager.watch('object', (event) => {
|
|
58
|
-
console.log(`Object ${event.type}:`, event.name);
|
|
59
|
-
});
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
## API
|
|
63
|
-
|
|
64
|
-
### MetadataManager
|
|
65
|
-
|
|
66
|
-
Main class for metadata operations.
|
|
67
|
-
|
|
68
|
-
#### Constructor
|
|
69
|
-
|
|
70
|
-
```typescript
|
|
71
|
-
new MetadataManager(config: MetadataManagerConfig)
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
**Config options:**
|
|
75
|
-
- `rootDir` - Root directory for metadata files
|
|
76
|
-
- `formats` - Enabled serialization formats (default: `['typescript', 'json', 'yaml']`)
|
|
77
|
-
- `cache` - Cache configuration with `enabled` and `ttl` options
|
|
78
|
-
- `watch` - Enable file watching (default: `false`)
|
|
79
|
-
- `watchOptions` - File watcher options (`ignored`, `persistent`, `ignoreInitial`)
|
|
80
|
-
|
|
81
|
-
#### Methods
|
|
82
|
-
|
|
83
|
-
**load<T>(type: string, name: string, options?: MetadataLoadOptions): Promise<T | null>**
|
|
84
|
-
|
|
85
|
-
Load a single metadata item.
|
|
86
|
-
|
|
87
|
-
```typescript
|
|
88
|
-
const customer = await manager.load<ServiceObject>('object', 'customer');
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
**loadMany<T>(type: string, options?: MetadataLoadOptions): Promise<T[]>**
|
|
92
|
-
|
|
93
|
-
Load multiple metadata items matching patterns.
|
|
94
|
-
|
|
95
|
-
```typescript
|
|
96
|
-
const objects = await manager.loadMany<ServiceObject>('object', {
|
|
97
|
-
patterns: ['**/*.object.ts'],
|
|
98
|
-
limit: 100,
|
|
99
|
-
});
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
**save<T>(type: string, name: string, data: T, options?: MetadataSaveOptions): Promise<MetadataSaveResult>**
|
|
103
|
-
|
|
104
|
-
Save metadata to disk.
|
|
105
|
-
|
|
106
|
-
```typescript
|
|
107
|
-
await manager.save('object', 'customer', customerObject, {
|
|
108
|
-
format: 'typescript',
|
|
109
|
-
prettify: true,
|
|
110
|
-
backup: true,
|
|
111
|
-
});
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
**exists(type: string, name: string): Promise<boolean>**
|
|
115
|
-
|
|
116
|
-
Check if metadata item exists.
|
|
117
|
-
|
|
118
|
-
```typescript
|
|
119
|
-
const exists = await manager.exists('object', 'customer');
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
**list(type: string): Promise<string[]>**
|
|
123
|
-
|
|
124
|
-
List all items of a type.
|
|
125
|
-
|
|
126
|
-
```typescript
|
|
127
|
-
const objectNames = await manager.list('object');
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
**watch(type: string, callback: WatchCallback): void**
|
|
131
|
-
|
|
132
|
-
Watch for metadata changes.
|
|
133
|
-
|
|
134
|
-
```typescript
|
|
135
|
-
manager.watch('object', (event) => {
|
|
136
|
-
if (event.type === 'added') {
|
|
137
|
-
console.log('New object:', event.name);
|
|
138
|
-
}
|
|
139
|
-
});
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
**stopWatching(): Promise<void>**
|
|
143
|
-
|
|
144
|
-
Stop all file watching.
|
|
145
|
-
|
|
146
|
-
## Serialization Formats
|
|
147
|
-
|
|
148
|
-
### JSON
|
|
149
|
-
|
|
150
|
-
```json
|
|
151
|
-
{
|
|
152
|
-
"name": "customer",
|
|
153
|
-
"label": "Customer",
|
|
154
|
-
"fields": {
|
|
155
|
-
"name": { "type": "text", "label": "Name" }
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
### YAML
|
|
161
|
-
|
|
162
|
-
```yaml
|
|
163
|
-
name: customer
|
|
164
|
-
label: Customer
|
|
165
|
-
fields:
|
|
166
|
-
name:
|
|
167
|
-
type: text
|
|
168
|
-
label: Name
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
### TypeScript
|
|
172
|
-
|
|
173
|
-
```typescript
|
|
174
|
-
import type { ServiceObject } from '@objectstack/spec/data';
|
|
175
|
-
|
|
176
|
-
export const metadata: ServiceObject = {
|
|
177
|
-
name: 'customer',
|
|
178
|
-
label: 'Customer',
|
|
179
|
-
fields: {
|
|
180
|
-
name: { type: 'text', label: 'Name' },
|
|
181
|
-
},
|
|
182
|
-
};
|
|
183
|
-
|
|
184
|
-
export default metadata;
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
## Architecture
|
|
188
|
-
|
|
189
|
-
The metadata package is designed as a Layer 3 package in the ObjectStack architecture:
|
|
190
|
-
|
|
191
|
-
```
|
|
192
|
-
@objectstack/metadata (Layer 3)
|
|
193
|
-
├── Dependencies:
|
|
194
|
-
│ ├── @objectstack/spec (validation)
|
|
195
|
-
│ ├── @objectstack/core (logging, DI)
|
|
196
|
-
│ ├── @objectstack/types (shared types)
|
|
197
|
-
│ ├── glob (file pattern matching)
|
|
198
|
-
│ ├── js-yaml (YAML support)
|
|
199
|
-
│ └── chokidar (file watching)
|
|
200
|
-
└── Used By:
|
|
201
|
-
├── @objectstack/cli (code generation)
|
|
202
|
-
├── @objectstack/runtime (manifest loading)
|
|
203
|
-
└── @objectstack/objectql (registry persistence)
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
## Common Workflows
|
|
207
|
-
|
|
208
|
-
### Development Workflow with File Watching
|
|
209
|
-
|
|
210
|
-
```typescript
|
|
211
|
-
import { MetadataManager } from '@objectstack/metadata';
|
|
212
|
-
|
|
213
|
-
const manager = new MetadataManager({
|
|
214
|
-
rootDir: './metadata',
|
|
215
|
-
watch: true, // Enable file watching
|
|
216
|
-
cache: { enabled: true, ttl: 3600 }
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
// Watch for changes and reload
|
|
220
|
-
manager.watch('object', async (event) => {
|
|
221
|
-
if (event.type === 'modified' || event.type === 'added') {
|
|
222
|
-
console.log(`Reloading object: ${event.name}`);
|
|
223
|
-
const updated = await manager.load('object', event.name);
|
|
224
|
-
|
|
225
|
-
// Notify the system to reload
|
|
226
|
-
await objectQL.reloadObject(event.name, updated);
|
|
227
|
-
}
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
// Hot module replacement for development
|
|
231
|
-
console.log('Watching metadata files for changes...');
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
### Metadata Migration Workflow
|
|
235
|
-
|
|
236
|
-
```typescript
|
|
237
|
-
import { MetadataManager } from '@objectstack/metadata';
|
|
238
|
-
|
|
239
|
-
async function migrateMetadata() {
|
|
240
|
-
const manager = new MetadataManager({
|
|
241
|
-
rootDir: './metadata'
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
// 1. Load all existing objects
|
|
245
|
-
const objects = await manager.loadMany('object');
|
|
246
|
-
|
|
247
|
-
// 2. Transform metadata (e.g., rename field)
|
|
248
|
-
const transformed = objects.map(obj => ({
|
|
249
|
-
...obj,
|
|
250
|
-
fields: Object.entries(obj.fields).reduce((acc, [key, field]) => {
|
|
251
|
-
// Rename 'description' to 'notes'
|
|
252
|
-
const newKey = key === 'description' ? 'notes' : key;
|
|
253
|
-
acc[newKey] = field;
|
|
254
|
-
return acc;
|
|
255
|
-
}, {})
|
|
256
|
-
}));
|
|
257
|
-
|
|
258
|
-
// 3. Save with backup
|
|
259
|
-
for (const obj of transformed) {
|
|
260
|
-
await manager.save('object', obj.name, obj, {
|
|
261
|
-
format: 'typescript',
|
|
262
|
-
backup: true, // Create .bak file
|
|
263
|
-
prettify: true
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
console.log(`Migrated ${objects.length} objects`);
|
|
268
|
-
}
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
### Multi-Format Support Workflow
|
|
272
|
-
|
|
273
|
-
```typescript
|
|
274
|
-
import { MetadataManager } from '@objectstack/metadata';
|
|
275
|
-
|
|
276
|
-
const manager = new MetadataManager({
|
|
277
|
-
rootDir: './metadata',
|
|
278
|
-
formats: ['typescript', 'json', 'yaml']
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
// Load from any format - manager auto-detects
|
|
282
|
-
const customer = await manager.load('object', 'customer');
|
|
283
|
-
// Tries: customer.object.ts, customer.object.json, customer.object.yaml
|
|
284
|
-
|
|
285
|
-
// Save in preferred format
|
|
286
|
-
await manager.save('object', 'customer', customer, {
|
|
287
|
-
format: 'typescript' // Convert to TypeScript
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
// Generate documentation from metadata
|
|
291
|
-
const allObjects = await manager.loadMany('object');
|
|
292
|
-
const docs = allObjects.map(obj => `
|
|
293
|
-
## ${obj.label}
|
|
294
|
-
|
|
295
|
-
**Name:** ${obj.name}
|
|
296
|
-
|
|
297
|
-
**Fields:**
|
|
298
|
-
${Object.entries(obj.fields).map(([name, field]) =>
|
|
299
|
-
`- **${field.label}** (\`${name}\`): ${field.type}`
|
|
300
|
-
).join('\n')}
|
|
301
|
-
`).join('\n\n');
|
|
302
|
-
|
|
303
|
-
fs.writeFileSync('docs/objects.md', docs);
|
|
304
|
-
```
|
|
305
|
-
|
|
306
|
-
### Validation and Testing Workflow
|
|
307
|
-
|
|
308
|
-
```typescript
|
|
309
|
-
import { MetadataManager } from '@objectstack/metadata';
|
|
310
|
-
import { ObjectSchema } from '@objectstack/spec/data';
|
|
311
|
-
|
|
312
|
-
async function validateAllMetadata() {
|
|
313
|
-
const manager = new MetadataManager({
|
|
314
|
-
rootDir: './metadata'
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
const objects = await manager.loadMany('object');
|
|
318
|
-
const errors = [];
|
|
319
|
-
|
|
320
|
-
for (const obj of objects) {
|
|
321
|
-
const result = ObjectSchema.safeParse(obj);
|
|
322
|
-
|
|
323
|
-
if (!result.success) {
|
|
324
|
-
errors.push({
|
|
325
|
-
name: obj.name,
|
|
326
|
-
issues: result.error.issues
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
if (errors.length > 0) {
|
|
332
|
-
console.error('Validation errors found:');
|
|
333
|
-
errors.forEach(({ name, issues }) => {
|
|
334
|
-
console.error(`\n${name}:`);
|
|
335
|
-
issues.forEach(issue => {
|
|
336
|
-
console.error(` - ${issue.path.join('.')}: ${issue.message}`);
|
|
337
|
-
});
|
|
338
|
-
});
|
|
339
|
-
process.exit(1);
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
console.log(`✅ All ${objects.length} objects validated successfully`);
|
|
343
|
-
}
|
|
344
|
-
```
|
|
345
|
-
|
|
346
|
-
### Metadata Versioning Workflow
|
|
347
|
-
|
|
348
|
-
```typescript
|
|
349
|
-
import { MetadataManager } from '@objectstack/metadata';
|
|
350
|
-
import { execSync } from 'child_process';
|
|
351
|
-
|
|
352
|
-
async function versionMetadata() {
|
|
353
|
-
const manager = new MetadataManager({
|
|
354
|
-
rootDir: './metadata'
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
const objects = await manager.loadMany('object');
|
|
358
|
-
|
|
359
|
-
// Get git user name safely
|
|
360
|
-
let modifiedBy = 'unknown';
|
|
361
|
-
try {
|
|
362
|
-
modifiedBy = execSync('git config user.name', { encoding: 'utf-8' }).trim();
|
|
363
|
-
} catch (error) {
|
|
364
|
-
console.warn('Could not get git user name, using "unknown"');
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
// Add version metadata
|
|
368
|
-
const versioned = objects.map(obj => ({
|
|
369
|
-
...obj,
|
|
370
|
-
metadata: {
|
|
371
|
-
...obj.metadata,
|
|
372
|
-
version: '2.0.0',
|
|
373
|
-
lastModified: new Date().toISOString(),
|
|
374
|
-
modifiedBy
|
|
375
|
-
}
|
|
376
|
-
}));
|
|
377
|
-
|
|
378
|
-
// Save versioned metadata
|
|
379
|
-
for (const obj of versioned) {
|
|
380
|
-
await manager.save('object', obj.name, obj, {
|
|
381
|
-
format: 'typescript',
|
|
382
|
-
prettify: true
|
|
383
|
-
});
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
// Commit to version control (if git is available)
|
|
387
|
-
try {
|
|
388
|
-
execSync('git add metadata/', { stdio: 'inherit' });
|
|
389
|
-
execSync('git commit -m "Version bump to 2.0.0"', { stdio: 'inherit' });
|
|
390
|
-
} catch (error) {
|
|
391
|
-
console.warn('Git commit failed, changes are staged but not committed');
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
```
|
|
395
|
-
|
|
396
|
-
### Import/Export Workflow
|
|
397
|
-
|
|
398
|
-
```typescript
|
|
399
|
-
import { MetadataManager } from '@objectstack/metadata';
|
|
400
|
-
|
|
401
|
-
async function exportToJSON() {
|
|
402
|
-
const manager = new MetadataManager({
|
|
403
|
-
rootDir: './metadata'
|
|
404
|
-
});
|
|
405
|
-
|
|
406
|
-
// Load all metadata
|
|
407
|
-
const [objects, views, apps] = await Promise.all([
|
|
408
|
-
manager.loadMany('object'),
|
|
409
|
-
manager.loadMany('view'),
|
|
410
|
-
manager.loadMany('app')
|
|
411
|
-
]);
|
|
412
|
-
|
|
413
|
-
// Create unified export
|
|
414
|
-
const exportData = {
|
|
415
|
-
version: '1.0.0',
|
|
416
|
-
exported: new Date().toISOString(),
|
|
417
|
-
objects,
|
|
418
|
-
views,
|
|
419
|
-
apps
|
|
420
|
-
};
|
|
421
|
-
|
|
422
|
-
// Save as single JSON file
|
|
423
|
-
fs.writeFileSync(
|
|
424
|
-
'export/metadata-export.json',
|
|
425
|
-
JSON.stringify(exportData, null, 2)
|
|
426
|
-
);
|
|
427
|
-
|
|
428
|
-
console.log('Export complete!');
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
async function importFromJSON(filePath: string) {
|
|
432
|
-
const manager = new MetadataManager({
|
|
433
|
-
rootDir: './metadata'
|
|
434
|
-
});
|
|
435
|
-
|
|
436
|
-
const importData = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
437
|
-
|
|
438
|
-
// Import objects
|
|
439
|
-
for (const obj of importData.objects) {
|
|
440
|
-
await manager.save('object', obj.name, obj, {
|
|
441
|
-
format: 'typescript'
|
|
442
|
-
});
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
// Import views
|
|
446
|
-
for (const view of importData.views) {
|
|
447
|
-
await manager.save('view', view.name, view, {
|
|
448
|
-
format: 'typescript'
|
|
449
|
-
});
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
console.log('Import complete!');
|
|
453
|
-
}
|
|
454
|
-
```
|
|
455
|
-
|
|
456
|
-
## License
|
|
457
|
-
|
|
458
|
-
MIT
|
|
13
|
+
Internal package used by `@objectstack/runtime` to process configuration.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filesystem-loader.d.ts","sourceRoot":"","sources":["../../src/loaders/filesystem-loader.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,KAAK,EACV,mBAAmB,EACnB,kBAAkB,EAClB,aAAa,EACb,sBAAsB,EACtB,cAAc,EACf,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wCAAwC,CAAC;AAEjF,qBAAa,gBAAiB,YAAW,cAAc;
|
|
1
|
+
{"version":3,"file":"filesystem-loader.d.ts","sourceRoot":"","sources":["../../src/loaders/filesystem-loader.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,OAAO,KAAK,EACV,mBAAmB,EACnB,kBAAkB,EAClB,aAAa,EACb,sBAAsB,EACtB,cAAc,EACf,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wCAAwC,CAAC;AAEjF,qBAAa,gBAAiB,YAAW,cAAc;IAmBnD,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,MAAM,CAAC;IApBjB,QAAQ,CAAC,QAAQ,EAAE,sBAAsB,CAavC;IAEF,OAAO,CAAC,KAAK,CAAqE;gBAGxE,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,GAAG,CAAC,cAAc,EAAE,kBAAkB,CAAC,EACpD,MAAM,CAAC,EAAE,MAAM,YAAA;IAGnB,IAAI,CACR,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,kBAAkB,CAAC;IA8FxB,QAAQ,CAAC,CAAC,GAAG,GAAG,EACpB,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,mBAAmB,GAC5B,OAAO,CAAC,CAAC,EAAE,CAAC;IAwDT,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKpD,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IA+B/D,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAwB3C;;OAEG;YACW,QAAQ;IAkBtB;;OAEG;IACH,OAAO,CAAC,YAAY;IAkBpB;;OAEG;IACH,OAAO,CAAC,aAAa;IAIrB;;;;OAIG;IACH,OAAO,CAAC,YAAY;CAIrB"}
|
|
@@ -14,6 +14,13 @@ export class FilesystemLoader {
|
|
|
14
14
|
this.logger = logger;
|
|
15
15
|
this.contract = {
|
|
16
16
|
name: 'filesystem',
|
|
17
|
+
protocol: 'file',
|
|
18
|
+
capabilities: {
|
|
19
|
+
read: true,
|
|
20
|
+
write: true,
|
|
21
|
+
watch: true,
|
|
22
|
+
list: true,
|
|
23
|
+
},
|
|
17
24
|
supportedFormats: ['json', 'yaml', 'typescript', 'javascript'],
|
|
18
25
|
supportsWatch: true,
|
|
19
26
|
supportsWrite: true,
|
|
@@ -82,7 +89,7 @@ export class FilesystemLoader {
|
|
|
82
89
|
if (useCache) {
|
|
83
90
|
this.cache.set(cacheKey, {
|
|
84
91
|
data,
|
|
85
|
-
etag: stats.etag,
|
|
92
|
+
etag: stats.etag || '',
|
|
86
93
|
timestamp: Date.now(),
|
|
87
94
|
});
|
|
88
95
|
}
|
package/dist/plugin.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export declare class MetadataPlugin implements Plugin {
|
|
|
9
9
|
private manager;
|
|
10
10
|
private options;
|
|
11
11
|
constructor(options?: MetadataPluginOptions);
|
|
12
|
-
init(ctx: PluginContext)
|
|
13
|
-
start(ctx: PluginContext)
|
|
12
|
+
init: (ctx: PluginContext) => Promise<void>;
|
|
13
|
+
start: (ctx: PluginContext) => Promise<void>;
|
|
14
14
|
}
|
|
15
15
|
//# sourceMappingURL=plugin.d.ts.map
|
package/dist/plugin.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAI1D,MAAM,WAAW,qBAAqB;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,qBAAa,cAAe,YAAW,MAAM;IACzC,IAAI,SAA8B;IAClC,OAAO,SAAW;IAElB,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,OAAO,CAAwB;gBAE3B,OAAO,GAAE,qBAA0B;
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAI1D,MAAM,WAAW,qBAAqB;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,qBAAa,cAAe,YAAW,MAAM;IACzC,IAAI,SAA8B;IAClC,OAAO,SAAW;IAElB,OAAO,CAAC,OAAO,CAAkB;IACjC,OAAO,CAAC,OAAO,CAAwB;gBAE3B,OAAO,GAAE,qBAA0B;IAe/C,IAAI,GAAU,KAAK,aAAa,mBAM/B;IAED,KAAK,GAAU,KAAK,aAAa,mBAgDhC;CACJ"}
|
package/dist/plugin.js
CHANGED
|
@@ -4,6 +4,59 @@ export class MetadataPlugin {
|
|
|
4
4
|
constructor(options = {}) {
|
|
5
5
|
this.name = 'com.objectstack.metadata';
|
|
6
6
|
this.version = '1.0.0';
|
|
7
|
+
this.init = async (ctx) => {
|
|
8
|
+
ctx.logger.info('Initializing Metadata Manager', { root: this.options.rootDir || process.cwd() });
|
|
9
|
+
// Register Metadata Manager as a service
|
|
10
|
+
// This allows other plugins to query raw metadata or listen to changes
|
|
11
|
+
ctx.registerService('metadata', this.manager);
|
|
12
|
+
};
|
|
13
|
+
this.start = async (ctx) => {
|
|
14
|
+
ctx.logger.info('Loading metadata...');
|
|
15
|
+
// Define metadata types directly from the Protocol Definition
|
|
16
|
+
// This ensures the loader is always in sync with the Spec
|
|
17
|
+
const metadataTypes = Object.keys(ObjectStackDefinitionSchema.shape)
|
|
18
|
+
.filter(key => key !== 'manifest'); // Manifest is handled separately
|
|
19
|
+
for (const type of metadataTypes) {
|
|
20
|
+
try {
|
|
21
|
+
// Try to load metadata of this type
|
|
22
|
+
const items = await this.manager.loadMany(type, {
|
|
23
|
+
recursive: true
|
|
24
|
+
});
|
|
25
|
+
if (items.length > 0) {
|
|
26
|
+
ctx.logger.info(`Loaded ${items.length} ${type}`);
|
|
27
|
+
// Helper: Register with ObjectQL Registry
|
|
28
|
+
const ql = ctx.getService('objectql');
|
|
29
|
+
if (ql && ql.registry) {
|
|
30
|
+
items.forEach((item) => {
|
|
31
|
+
// Determine key field (id or name)
|
|
32
|
+
const keyField = item.id ? 'id' : 'name';
|
|
33
|
+
// Map plural type to singular/registry type if needed
|
|
34
|
+
// For now, we use the singular form for standard types:
|
|
35
|
+
// objects -> object, apps -> app, etc.
|
|
36
|
+
// But Registry seems to accept arbitrary strings.
|
|
37
|
+
// To match Protocol standard, we might want to normalize.
|
|
38
|
+
// Let's use the directory name (plural) as the type for now,
|
|
39
|
+
// OR map 'objects' -> 'object' specifically.
|
|
40
|
+
let registryType = type;
|
|
41
|
+
if (type === 'objects')
|
|
42
|
+
registryType = 'object';
|
|
43
|
+
if (type === 'apps')
|
|
44
|
+
registryType = 'app';
|
|
45
|
+
if (type === 'plugins')
|
|
46
|
+
registryType = 'plugin';
|
|
47
|
+
if (type === 'functions')
|
|
48
|
+
registryType = 'function';
|
|
49
|
+
ql.registry.registerItem(registryType, item, keyField);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
catch (e) {
|
|
55
|
+
// Ignore missing directories or errors
|
|
56
|
+
// ctx.logger.debug(`No metadata found for type: ${type}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
};
|
|
7
60
|
this.options = {
|
|
8
61
|
watch: true,
|
|
9
62
|
...options
|
|
@@ -15,57 +68,4 @@ export class MetadataPlugin {
|
|
|
15
68
|
formats: ['yaml', 'json', 'typescript', 'javascript']
|
|
16
69
|
});
|
|
17
70
|
}
|
|
18
|
-
async init(ctx) {
|
|
19
|
-
ctx.logger.info('Initializing Metadata Manager', { root: this.options.rootDir || process.cwd() });
|
|
20
|
-
// Register Metadata Manager as a service
|
|
21
|
-
// This allows other plugins to query raw metadata or listen to changes
|
|
22
|
-
ctx.registerService('metadata', this.manager);
|
|
23
|
-
}
|
|
24
|
-
async start(ctx) {
|
|
25
|
-
ctx.logger.info('Loading metadata...');
|
|
26
|
-
// Define metadata types directly from the Protocol Definition
|
|
27
|
-
// This ensures the loader is always in sync with the Spec
|
|
28
|
-
const metadataTypes = Object.keys(ObjectStackDefinitionSchema.shape)
|
|
29
|
-
.filter(key => key !== 'manifest'); // Manifest is handled separately
|
|
30
|
-
for (const type of metadataTypes) {
|
|
31
|
-
try {
|
|
32
|
-
// Try to load metadata of this type
|
|
33
|
-
const items = await this.manager.loadMany(type, {
|
|
34
|
-
recursive: true
|
|
35
|
-
});
|
|
36
|
-
if (items.length > 0) {
|
|
37
|
-
ctx.logger.info(`Loaded ${items.length} ${type}`);
|
|
38
|
-
// Helper: Register with ObjectQL Registry
|
|
39
|
-
const ql = ctx.getService('objectql');
|
|
40
|
-
if (ql && ql.registry) {
|
|
41
|
-
items.forEach((item) => {
|
|
42
|
-
// Determine key field (id or name)
|
|
43
|
-
const keyField = item.id ? 'id' : 'name';
|
|
44
|
-
// Map plural type to singular/registry type if needed
|
|
45
|
-
// For now, we use the singular form for standard types:
|
|
46
|
-
// objects -> object, apps -> app, etc.
|
|
47
|
-
// But Registry seems to accept arbitrary strings.
|
|
48
|
-
// To match Protocol standard, we might want to normalize.
|
|
49
|
-
// Let's use the directory name (plural) as the type for now,
|
|
50
|
-
// OR map 'objects' -> 'object' specifically.
|
|
51
|
-
let registryType = type;
|
|
52
|
-
if (type === 'objects')
|
|
53
|
-
registryType = 'object';
|
|
54
|
-
if (type === 'apps')
|
|
55
|
-
registryType = 'app';
|
|
56
|
-
if (type === 'plugins')
|
|
57
|
-
registryType = 'plugin';
|
|
58
|
-
if (type === 'functions')
|
|
59
|
-
registryType = 'function';
|
|
60
|
-
ql.registry.registerItem(registryType, item, keyField);
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
catch (e) {
|
|
66
|
-
// Ignore missing directories or errors
|
|
67
|
-
// ctx.logger.debug(`No metadata found for type: ${type}`);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
71
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectstack/metadata",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"license": "Apache-2.0",
|
|
4
5
|
"description": "Metadata loading, saving, and persistence for ObjectStack",
|
|
5
6
|
"main": "src/index.ts",
|
|
6
7
|
"types": "src/index.ts",
|
|
@@ -16,9 +17,9 @@
|
|
|
16
17
|
"js-yaml": "^4.1.0",
|
|
17
18
|
"chokidar": "^3.5.3",
|
|
18
19
|
"zod": "^4.3.6",
|
|
19
|
-
"@objectstack/core": "0.
|
|
20
|
-
"@objectstack/spec": "0.
|
|
21
|
-
"@objectstack/types": "0.
|
|
20
|
+
"@objectstack/core": "1.0.0",
|
|
21
|
+
"@objectstack/spec": "1.0.0",
|
|
22
|
+
"@objectstack/types": "1.0.0"
|
|
22
23
|
},
|
|
23
24
|
"devDependencies": {
|
|
24
25
|
"@types/js-yaml": "^4.0.9",
|
|
@@ -22,6 +22,13 @@ import type { MetadataSerializer } from '../serializers/serializer-interface.js'
|
|
|
22
22
|
export class FilesystemLoader implements MetadataLoader {
|
|
23
23
|
readonly contract: MetadataLoaderContract = {
|
|
24
24
|
name: 'filesystem',
|
|
25
|
+
protocol: 'file',
|
|
26
|
+
capabilities: {
|
|
27
|
+
read: true,
|
|
28
|
+
write: true,
|
|
29
|
+
watch: true,
|
|
30
|
+
list: true,
|
|
31
|
+
},
|
|
25
32
|
supportedFormats: ['json', 'yaml', 'typescript', 'javascript'],
|
|
26
33
|
supportsWatch: true,
|
|
27
34
|
supportsWrite: true,
|
|
@@ -99,7 +106,7 @@ export class FilesystemLoader implements MetadataLoader {
|
|
|
99
106
|
|
|
100
107
|
// Load and deserialize
|
|
101
108
|
const content = await fs.readFile(filePath, 'utf-8');
|
|
102
|
-
const serializer = this.getSerializer(stats.format);
|
|
109
|
+
const serializer = this.getSerializer(stats.format!);
|
|
103
110
|
|
|
104
111
|
if (!serializer) {
|
|
105
112
|
throw new Error(`No serializer found for format: ${stats.format}`);
|
|
@@ -111,7 +118,7 @@ export class FilesystemLoader implements MetadataLoader {
|
|
|
111
118
|
if (useCache) {
|
|
112
119
|
this.cache.set(cacheKey, {
|
|
113
120
|
data,
|
|
114
|
-
etag: stats.etag,
|
|
121
|
+
etag: stats.etag || '',
|
|
115
122
|
timestamp: Date.now(),
|
|
116
123
|
});
|
|
117
124
|
}
|
package/src/plugin.ts
CHANGED
|
@@ -29,7 +29,7 @@ export class MetadataPlugin implements Plugin {
|
|
|
29
29
|
});
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
async
|
|
32
|
+
init = async (ctx: PluginContext) => {
|
|
33
33
|
ctx.logger.info('Initializing Metadata Manager', { root: this.options.rootDir || process.cwd() });
|
|
34
34
|
|
|
35
35
|
// Register Metadata Manager as a service
|
|
@@ -37,7 +37,7 @@ export class MetadataPlugin implements Plugin {
|
|
|
37
37
|
ctx.registerService('metadata', this.manager);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
async
|
|
40
|
+
start = async (ctx: PluginContext) => {
|
|
41
41
|
ctx.logger.info('Loading metadata...');
|
|
42
42
|
|
|
43
43
|
// Define metadata types directly from the Protocol Definition
|