@objectstack/metadata 0.8.2 → 0.9.1

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 CHANGED
@@ -1,5 +1,15 @@
1
1
  # @objectstack/metadata
2
2
 
3
+ ## 0.9.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Patch release for maintenance and stability improvements. All packages updated with unified versioning.
8
+ - Updated dependencies
9
+ - @objectstack/spec@0.9.1
10
+ - @objectstack/core@0.9.1
11
+ - @objectstack/types@0.9.1
12
+
3
13
  ## 0.8.2
4
14
 
5
15
  ### Patch Changes
package/README.md CHANGED
@@ -196,6 +196,256 @@ The metadata package is designed as a Layer 3 package in the ObjectStack archite
196
196
  └── @objectstack/objectql (registry persistence)
197
197
  ```
198
198
 
199
+ ## Common Workflows
200
+
201
+ ### Development Workflow with File Watching
202
+
203
+ ```typescript
204
+ import { MetadataManager } from '@objectstack/metadata';
205
+
206
+ const manager = new MetadataManager({
207
+ rootDir: './metadata',
208
+ watch: true, // Enable file watching
209
+ cache: { enabled: true, ttl: 3600 }
210
+ });
211
+
212
+ // Watch for changes and reload
213
+ manager.watch('object', async (event) => {
214
+ if (event.type === 'modified' || event.type === 'added') {
215
+ console.log(`Reloading object: ${event.name}`);
216
+ const updated = await manager.load('object', event.name);
217
+
218
+ // Notify the system to reload
219
+ await objectQL.reloadObject(event.name, updated);
220
+ }
221
+ });
222
+
223
+ // Hot module replacement for development
224
+ console.log('Watching metadata files for changes...');
225
+ ```
226
+
227
+ ### Metadata Migration Workflow
228
+
229
+ ```typescript
230
+ import { MetadataManager } from '@objectstack/metadata';
231
+
232
+ async function migrateMetadata() {
233
+ const manager = new MetadataManager({
234
+ rootDir: './metadata'
235
+ });
236
+
237
+ // 1. Load all existing objects
238
+ const objects = await manager.loadMany('object');
239
+
240
+ // 2. Transform metadata (e.g., rename field)
241
+ const transformed = objects.map(obj => ({
242
+ ...obj,
243
+ fields: Object.entries(obj.fields).reduce((acc, [key, field]) => {
244
+ // Rename 'description' to 'notes'
245
+ const newKey = key === 'description' ? 'notes' : key;
246
+ acc[newKey] = field;
247
+ return acc;
248
+ }, {})
249
+ }));
250
+
251
+ // 3. Save with backup
252
+ for (const obj of transformed) {
253
+ await manager.save('object', obj.name, obj, {
254
+ format: 'typescript',
255
+ backup: true, // Create .bak file
256
+ prettify: true
257
+ });
258
+ }
259
+
260
+ console.log(`Migrated ${objects.length} objects`);
261
+ }
262
+ ```
263
+
264
+ ### Multi-Format Support Workflow
265
+
266
+ ```typescript
267
+ import { MetadataManager } from '@objectstack/metadata';
268
+
269
+ const manager = new MetadataManager({
270
+ rootDir: './metadata',
271
+ formats: ['typescript', 'json', 'yaml']
272
+ });
273
+
274
+ // Load from any format - manager auto-detects
275
+ const customer = await manager.load('object', 'customer');
276
+ // Tries: customer.object.ts, customer.object.json, customer.object.yaml
277
+
278
+ // Save in preferred format
279
+ await manager.save('object', 'customer', customer, {
280
+ format: 'typescript' // Convert to TypeScript
281
+ });
282
+
283
+ // Generate documentation from metadata
284
+ const allObjects = await manager.loadMany('object');
285
+ const docs = allObjects.map(obj => `
286
+ ## ${obj.label}
287
+
288
+ **Name:** ${obj.name}
289
+
290
+ **Fields:**
291
+ ${Object.entries(obj.fields).map(([name, field]) =>
292
+ `- **${field.label}** (\`${name}\`): ${field.type}`
293
+ ).join('\n')}
294
+ `).join('\n\n');
295
+
296
+ fs.writeFileSync('docs/objects.md', docs);
297
+ ```
298
+
299
+ ### Validation and Testing Workflow
300
+
301
+ ```typescript
302
+ import { MetadataManager } from '@objectstack/metadata';
303
+ import { ObjectSchema } from '@objectstack/spec/data';
304
+
305
+ async function validateAllMetadata() {
306
+ const manager = new MetadataManager({
307
+ rootDir: './metadata'
308
+ });
309
+
310
+ const objects = await manager.loadMany('object');
311
+ const errors = [];
312
+
313
+ for (const obj of objects) {
314
+ const result = ObjectSchema.safeParse(obj);
315
+
316
+ if (!result.success) {
317
+ errors.push({
318
+ name: obj.name,
319
+ issues: result.error.issues
320
+ });
321
+ }
322
+ }
323
+
324
+ if (errors.length > 0) {
325
+ console.error('Validation errors found:');
326
+ errors.forEach(({ name, issues }) => {
327
+ console.error(`\n${name}:`);
328
+ issues.forEach(issue => {
329
+ console.error(` - ${issue.path.join('.')}: ${issue.message}`);
330
+ });
331
+ });
332
+ process.exit(1);
333
+ }
334
+
335
+ console.log(`✅ All ${objects.length} objects validated successfully`);
336
+ }
337
+ ```
338
+
339
+ ### Metadata Versioning Workflow
340
+
341
+ ```typescript
342
+ import { MetadataManager } from '@objectstack/metadata';
343
+ import { execSync } from 'child_process';
344
+
345
+ async function versionMetadata() {
346
+ const manager = new MetadataManager({
347
+ rootDir: './metadata'
348
+ });
349
+
350
+ const objects = await manager.loadMany('object');
351
+
352
+ // Get git user name safely
353
+ let modifiedBy = 'unknown';
354
+ try {
355
+ modifiedBy = execSync('git config user.name', { encoding: 'utf-8' }).trim();
356
+ } catch (error) {
357
+ console.warn('Could not get git user name, using "unknown"');
358
+ }
359
+
360
+ // Add version metadata
361
+ const versioned = objects.map(obj => ({
362
+ ...obj,
363
+ metadata: {
364
+ ...obj.metadata,
365
+ version: '2.0.0',
366
+ lastModified: new Date().toISOString(),
367
+ modifiedBy
368
+ }
369
+ }));
370
+
371
+ // Save versioned metadata
372
+ for (const obj of versioned) {
373
+ await manager.save('object', obj.name, obj, {
374
+ format: 'typescript',
375
+ prettify: true
376
+ });
377
+ }
378
+
379
+ // Commit to version control (if git is available)
380
+ try {
381
+ execSync('git add metadata/', { stdio: 'inherit' });
382
+ execSync('git commit -m "Version bump to 2.0.0"', { stdio: 'inherit' });
383
+ } catch (error) {
384
+ console.warn('Git commit failed, changes are staged but not committed');
385
+ }
386
+ }
387
+ ```
388
+
389
+ ### Import/Export Workflow
390
+
391
+ ```typescript
392
+ import { MetadataManager } from '@objectstack/metadata';
393
+
394
+ async function exportToJSON() {
395
+ const manager = new MetadataManager({
396
+ rootDir: './metadata'
397
+ });
398
+
399
+ // Load all metadata
400
+ const [objects, views, apps] = await Promise.all([
401
+ manager.loadMany('object'),
402
+ manager.loadMany('view'),
403
+ manager.loadMany('app')
404
+ ]);
405
+
406
+ // Create unified export
407
+ const exportData = {
408
+ version: '1.0.0',
409
+ exported: new Date().toISOString(),
410
+ objects,
411
+ views,
412
+ apps
413
+ };
414
+
415
+ // Save as single JSON file
416
+ fs.writeFileSync(
417
+ 'export/metadata-export.json',
418
+ JSON.stringify(exportData, null, 2)
419
+ );
420
+
421
+ console.log('Export complete!');
422
+ }
423
+
424
+ async function importFromJSON(filePath: string) {
425
+ const manager = new MetadataManager({
426
+ rootDir: './metadata'
427
+ });
428
+
429
+ const importData = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
430
+
431
+ // Import objects
432
+ for (const obj of importData.objects) {
433
+ await manager.save('object', obj.name, obj, {
434
+ format: 'typescript'
435
+ });
436
+ }
437
+
438
+ // Import views
439
+ for (const view of importData.views) {
440
+ await manager.save('view', view.name, view, {
441
+ format: 'typescript'
442
+ });
443
+ }
444
+
445
+ console.log('Import complete!');
446
+ }
447
+ ```
448
+
199
449
  ## License
200
450
 
201
451
  MIT
package/dist/index.d.ts CHANGED
@@ -10,6 +10,7 @@ export { FilesystemLoader } from './loaders/filesystem-loader.js';
10
10
  export { type MetadataSerializer, type SerializeOptions } from './serializers/serializer-interface.js';
11
11
  export { JSONSerializer } from './serializers/json-serializer.js';
12
12
  export { YAMLSerializer } from './serializers/yaml-serializer.js';
13
+ export * as Migration from './migration/index.js';
13
14
  export { TypeScriptSerializer } from './serializers/typescript-serializer.js';
14
15
  export type { MetadataFormat, MetadataStats, MetadataLoadOptions, MetadataSaveOptions, MetadataExportOptions, MetadataImportOptions, MetadataLoadResult, MetadataSaveResult, MetadataWatchEvent, MetadataCollectionInfo, MetadataLoaderContract, MetadataManagerConfig, } from '@objectstack/spec/system';
15
16
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,eAAe,EAAE,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAG5E,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAG7C,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAGlE,OAAO,EAAE,KAAK,kBAAkB,EAAE,KAAK,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AACvG,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAC;AAG9E,YAAY,EACV,cAAc,EACd,aAAa,EACb,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,EACrB,qBAAqB,EACrB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,sBAAsB,EACtB,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,eAAe,EAAE,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAG5E,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAG7C,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAGlE,OAAO,EAAE,KAAK,kBAAkB,EAAE,KAAK,gBAAgB,EAAE,MAAM,uCAAuC,CAAC;AACvG,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAClE,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAC;AAG9E,YAAY,EACV,cAAc,EACd,aAAa,EACb,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,EACrB,qBAAqB,EACrB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,sBAAsB,EACtB,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,0BAA0B,CAAC"}
package/dist/index.js CHANGED
@@ -10,4 +10,5 @@ export { MetadataPlugin } from './plugin.js';
10
10
  export { FilesystemLoader } from './loaders/filesystem-loader.js';
11
11
  export { JSONSerializer } from './serializers/json-serializer.js';
12
12
  export { YAMLSerializer } from './serializers/yaml-serializer.js';
13
+ export * as Migration from './migration/index.js';
13
14
  export { TypeScriptSerializer } from './serializers/typescript-serializer.js';
@@ -0,0 +1,9 @@
1
+ import { System } from '@objectstack/spec';
2
+ import { ISchemaDriver } from '@objectstack/spec/contracts';
3
+ export declare class MigrationExecutor {
4
+ private driver;
5
+ constructor(driver: ISchemaDriver);
6
+ executeChangeSet(changeSet: System.ChangeSet): Promise<void>;
7
+ private executeOperation;
8
+ }
9
+ //# sourceMappingURL=executor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../src/migration/executor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAE5D,qBAAa,iBAAiB;IAChB,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,aAAa;IAEnC,gBAAgB,CAAC,SAAS,EAAE,MAAM,CAAC,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;YAapD,gBAAgB;CAgC/B"}
@@ -0,0 +1,49 @@
1
+ export class MigrationExecutor {
2
+ constructor(driver) {
3
+ this.driver = driver;
4
+ }
5
+ async executeChangeSet(changeSet) {
6
+ console.log(`Executing ChangeSet: ${changeSet.name} (${changeSet.id})`);
7
+ for (const op of changeSet.operations) {
8
+ try {
9
+ await this.executeOperation(op);
10
+ }
11
+ catch (e) {
12
+ console.error(`Failed to execute operation ${op.type}:`, e);
13
+ throw e;
14
+ }
15
+ }
16
+ }
17
+ async executeOperation(op) {
18
+ switch (op.type) {
19
+ case 'create_object':
20
+ console.log(` > Create Object: ${op.object.name}`);
21
+ await this.driver.createCollection(op.object.name, op.object);
22
+ break;
23
+ case 'add_field':
24
+ console.log(` > Add Field: ${op.objectName}.${op.fieldName}`);
25
+ await this.driver.addColumn(op.objectName, op.fieldName, op.field);
26
+ break;
27
+ case 'remove_field':
28
+ console.log(` > Remove Field: ${op.objectName}.${op.fieldName}`);
29
+ await this.driver.dropColumn(op.objectName, op.fieldName);
30
+ break;
31
+ case 'delete_object':
32
+ console.log(` > Delete Object: ${op.objectName}`);
33
+ await this.driver.dropCollection(op.objectName);
34
+ break;
35
+ case 'execute_sql':
36
+ console.log(` > Execute SQL`);
37
+ await this.driver.executeRaw(op.sql);
38
+ break;
39
+ case 'modify_field':
40
+ console.warn(` ! Modify Field: ${op.objectName}.${op.fieldName} (Not fully implemented)`);
41
+ break;
42
+ case 'rename_object':
43
+ console.warn(` ! Rename Object: ${op.oldName} -> ${op.newName} (Not fully implemented)`);
44
+ break;
45
+ default:
46
+ throw new Error(`Unknown operation type`);
47
+ }
48
+ }
49
+ }
@@ -0,0 +1,2 @@
1
+ export * from './executor.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migration/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC"}
@@ -0,0 +1 @@
1
+ export * from './executor.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@objectstack/metadata",
3
- "version": "0.8.2",
3
+ "version": "0.9.1",
4
4
  "description": "Metadata loading, saving, and persistence for ObjectStack",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -16,9 +16,9 @@
16
16
  "js-yaml": "^4.1.0",
17
17
  "chokidar": "^3.5.3",
18
18
  "zod": "^4.3.6",
19
- "@objectstack/core": "0.8.2",
20
- "@objectstack/spec": "0.8.2",
21
- "@objectstack/types": "0.8.2"
19
+ "@objectstack/core": "0.9.1",
20
+ "@objectstack/spec": "0.9.1",
21
+ "@objectstack/types": "0.9.1"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@types/js-yaml": "^4.0.9",
package/src/index.ts CHANGED
@@ -18,6 +18,7 @@ export { FilesystemLoader } from './loaders/filesystem-loader.js';
18
18
  export { type MetadataSerializer, type SerializeOptions } from './serializers/serializer-interface.js';
19
19
  export { JSONSerializer } from './serializers/json-serializer.js';
20
20
  export { YAMLSerializer } from './serializers/yaml-serializer.js';
21
+ export * as Migration from './migration/index.js';
21
22
  export { TypeScriptSerializer } from './serializers/typescript-serializer.js';
22
23
 
23
24
  // Re-export types from spec
@@ -0,0 +1,52 @@
1
+ import { System } from '@objectstack/spec';
2
+ import { ISchemaDriver } from '@objectstack/spec/contracts';
3
+
4
+ export class MigrationExecutor {
5
+ constructor(private driver: ISchemaDriver) {}
6
+
7
+ async executeChangeSet(changeSet: System.ChangeSet): Promise<void> {
8
+ console.log(`Executing ChangeSet: ${changeSet.name} (${changeSet.id})`);
9
+
10
+ for (const op of changeSet.operations) {
11
+ try {
12
+ await this.executeOperation(op);
13
+ } catch (e) {
14
+ console.error(`Failed to execute operation ${op.type}:`, e);
15
+ throw e;
16
+ }
17
+ }
18
+ }
19
+
20
+ private async executeOperation(op: System.MigrationOperation): Promise<void> {
21
+ switch (op.type) {
22
+ case 'create_object':
23
+ console.log(` > Create Object: ${op.object.name}`);
24
+ await this.driver.createCollection(op.object.name, op.object);
25
+ break;
26
+ case 'add_field':
27
+ console.log(` > Add Field: ${op.objectName}.${op.fieldName}`);
28
+ await this.driver.addColumn(op.objectName, op.fieldName, op.field);
29
+ break;
30
+ case 'remove_field':
31
+ console.log(` > Remove Field: ${op.objectName}.${op.fieldName}`);
32
+ await this.driver.dropColumn(op.objectName, op.fieldName);
33
+ break;
34
+ case 'delete_object':
35
+ console.log(` > Delete Object: ${op.objectName}`);
36
+ await this.driver.dropCollection(op.objectName);
37
+ break;
38
+ case 'execute_sql':
39
+ console.log(` > Execute SQL`);
40
+ await this.driver.executeRaw(op.sql);
41
+ break;
42
+ case 'modify_field':
43
+ console.warn(` ! Modify Field: ${op.objectName}.${op.fieldName} (Not fully implemented)`);
44
+ break;
45
+ case 'rename_object':
46
+ console.warn(` ! Rename Object: ${op.oldName} -> ${op.newName} (Not fully implemented)`);
47
+ break;
48
+ default:
49
+ throw new Error(`Unknown operation type`);
50
+ }
51
+ }
52
+ }
@@ -0,0 +1 @@
1
+ export * from './executor.js';