@objectstack/metadata 4.0.5 → 4.1.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/dist/index.d.cts CHANGED
@@ -156,6 +156,8 @@ declare class MetadataManager implements IMetadataService {
156
156
  private overlays;
157
157
  private typeRegistry;
158
158
  private dependencies;
159
+ private listCache;
160
+ private static readonly LIST_CACHE_TTL_MS;
159
161
  private realtimeService?;
160
162
  constructor(config: MetadataManagerOptions);
161
163
  /**
@@ -209,6 +211,9 @@ declare class MetadataManager implements IMetadataService {
209
211
  * List all metadata items of a given type
210
212
  */
211
213
  list(type: string): Promise<unknown[]>;
214
+ private cacheListResult;
215
+ /** Internal helper: drop the cached `list()` result for a type. */
216
+ private invalidateListCache;
212
217
  /**
213
218
  * Unregister/remove a metadata item by type and name.
214
219
  * Deletes from database-backed loaders only (same rationale as register()).
@@ -425,14 +430,17 @@ interface MetadataPluginOptions {
425
430
  artifactSource?: {
426
431
  mode: 'local-file';
427
432
  path: string;
433
+ fetchTimeoutMs?: number;
428
434
  } | {
429
435
  mode: 'artifact-api';
430
436
  url: string;
437
+ token?: string;
438
+ commitId?: string;
439
+ fetchTimeoutMs?: number;
431
440
  };
432
441
  /**
433
- * Register the queryable system metadata-storage objects
434
- * (`sys_object`, `sys_view`, `sys_flow`, `sys_agent`, `sys_tool`) on this
435
- * kernel. Default `true` for backward compatibility.
442
+ * Register the `sys_metadata` + `sys_metadata_history` storage objects
443
+ * on this kernel. Default `true` for backward compatibility.
436
444
  *
437
445
  * Set to `false` for **per-project** kernels: in cloud / project mode the
438
446
  * control plane is the sole owner of metadata storage tables — exposing
@@ -450,89 +458,21 @@ declare class MetadataPlugin implements Plugin {
450
458
  constructor(options?: MetadataPluginOptions);
451
459
  init: (ctx: PluginContext) => Promise<void>;
452
460
  start: (ctx: PluginContext) => Promise<void>;
453
- private _loadFromLocalFile;
454
- private _loadFromFileSystem;
455
- }
456
-
457
- /**
458
- * Metadata Projection Service
459
- *
460
- * Implements the dual-table architecture pattern:
461
- * - sys_metadata: Source of truth for package management, versioning
462
- * - Type-specific tables (sys_object, sys_view, etc.): Queryable projections
463
- *
464
- * When metadata is saved to sys_metadata, this service projects it into
465
- * the appropriate type-specific table so Studio can query it via Object Protocol.
466
- */
467
-
468
- /**
469
- * Configuration for the MetadataProjector
470
- */
471
- interface MetadataProjectorOptions {
472
- /** The IDataDriver instance to use for database operations */
473
- driver?: IDataDriver;
474
- /** The IDataEngine (ObjectQL) instance — preferred over raw driver */
475
- engine?: IDataEngine;
476
- /** Organization ID for multi-tenant isolation */
477
- organizationId?: string;
478
- /** Project ID — null = platform-global, set = project-scoped */
479
- projectId?: string;
480
- }
481
- /**
482
- * MetadataProjector
483
- *
484
- * Handles projection from sys_metadata to type-specific tables.
485
- */
486
- declare class MetadataProjector {
487
- private driver?;
488
- private engine?;
489
- /** Reserved for future multi-tenant projection scoping */
490
- readonly scope: {
491
- organizationId?: string;
492
- projectId?: string;
493
- };
494
- private readonly typeTableMap;
495
- constructor(options: MetadataProjectorOptions);
496
- /**
497
- * Project metadata to type-specific table
498
- */
499
- project(type: string, name: string, data: any): Promise<void>;
500
- /**
501
- * Delete projection from type-specific table
502
- */
503
- deleteProjection(type: string, name: string): Promise<void>;
504
461
  /**
505
- * Transform metadata into projection record
462
+ * Fetch JSON content from a URL with configurable timeout.
506
463
  */
507
- private transformToProjection;
464
+ private _fetchJson;
508
465
  /**
509
- * Project object metadata to sys_object
466
+ * Parse raw artifact JSON (envelope or bare definition) and register all
467
+ * metadata items into the MetadataManager.
510
468
  */
511
- private projectObject;
512
- /**
513
- * Project view metadata to sys_view
514
- */
515
- private projectView;
516
- /**
517
- * Project agent metadata to sys_agent
518
- */
519
- private projectAgent;
520
- /**
521
- * Project tool metadata to sys_tool
522
- */
523
- private projectTool;
524
- /**
525
- * Project flow metadata to sys_flow
526
- */
527
- private projectFlow;
528
- private _findOne;
529
- private _create;
530
- private _update;
531
- private _delete;
469
+ private _parseAndRegisterArtifact;
470
+ private _loadFromLocalFile;
532
471
  /**
533
- * Generate a simple unique ID
472
+ * P2: Load metadata from the cloud artifact API endpoint.
534
473
  */
535
- private generateId;
474
+ private _loadFromArtifactApi;
475
+ private _loadFromFileSystem;
536
476
  }
537
477
 
538
478
  /**
@@ -578,6 +518,52 @@ declare class RemoteLoader implements MetadataLoader {
578
518
  save(type: string, name: string, data: any, _options?: MetadataSaveOptions): Promise<MetadataSaveResult>;
579
519
  }
580
520
 
521
+ /**
522
+ * Generic LRU (Least Recently Used) cache with optional TTL.
523
+ *
524
+ * Implementation notes:
525
+ * - Backed by a `Map`, which preserves insertion order. We promote entries on
526
+ * read by deleting and re-inserting, so the oldest entry is always
527
+ * `map.keys().next()`.
528
+ * - TTL is checked lazily on `get` / `has`. Expired entries are evicted on
529
+ * access; we do not run a background sweeper to keep the implementation
530
+ * side-effect free in serverless / edge runtimes.
531
+ * - Set `maxSize <= 0` to disable the size cap; set `ttl <= 0` (or omit) to
532
+ * disable expiration.
533
+ *
534
+ * Designed for `DatabaseLoader` read-path caching — see
535
+ * `packages/metadata/src/loaders/database-loader.ts`.
536
+ */
537
+ interface LRUCacheOptions {
538
+ /** Maximum number of entries; when exceeded, the LRU entry is evicted. */
539
+ maxSize?: number;
540
+ /** Time-to-live in milliseconds. Zero or undefined disables TTL. */
541
+ ttl?: number;
542
+ }
543
+ declare class LRUCache<K, V> {
544
+ private readonly map;
545
+ private readonly maxSize;
546
+ private readonly ttl;
547
+ private hits;
548
+ private misses;
549
+ constructor(options?: LRUCacheOptions);
550
+ get(key: K): V | undefined;
551
+ set(key: K, value: V): void;
552
+ has(key: K): boolean;
553
+ delete(key: K): boolean;
554
+ clear(): void;
555
+ get size(): number;
556
+ /** Diagnostic counters — useful for `metrics` endpoints. */
557
+ stats(): {
558
+ size: number;
559
+ hits: number;
560
+ misses: number;
561
+ hitRate: number;
562
+ };
563
+ /** Resets hit/miss counters without dropping cached entries. */
564
+ resetStats(): void;
565
+ }
566
+
581
567
  /**
582
568
  * Database Metadata Loader
583
569
  *
@@ -587,6 +573,28 @@ declare class RemoteLoader implements MetadataLoader {
587
573
  * MetadataRecordSchema envelope defined in @objectstack/spec.
588
574
  */
589
575
 
576
+ /**
577
+ * Cache configuration for `DatabaseLoader`.
578
+ *
579
+ * The cache sits in front of `load()`, `loadMany()`, `exists()`, `stat()`,
580
+ * and `list()` so that hot read paths (REST `/meta/*`, ObjectQL plan
581
+ * resolution, runtime overlay merges) do not hit the database on every
582
+ * request. All write paths (`save`, `delete`, `registerRollback`) invalidate
583
+ * the relevant entries.
584
+ *
585
+ * Defaults are conservative: 500 entries, 60s TTL — chosen so that single-
586
+ * tenant Studio usage does not burn memory and so that an external write
587
+ * (out-of-band SQL update) becomes visible within a minute even without
588
+ * realtime invalidation.
589
+ */
590
+ interface DatabaseLoaderCacheOptions {
591
+ /** Whether the cache is active. Default: `true`. */
592
+ enabled?: boolean;
593
+ /** Max number of cached `(type, name)` entries. Default: `500`. */
594
+ maxSize?: number;
595
+ /** TTL in milliseconds. Set to `0` to disable expiry. Default: `60_000`. */
596
+ ttl?: number;
597
+ }
590
598
  /**
591
599
  * Configuration for the DatabaseLoader.
592
600
  *
@@ -610,8 +618,12 @@ interface DatabaseLoaderOptions {
610
618
  projectId?: string;
611
619
  /** Enable history tracking (default: true) */
612
620
  trackHistory?: boolean;
613
- /** Enable metadata projection to type-specific tables (default: true) */
614
- enableProjection?: boolean;
621
+ /**
622
+ * Read-through cache configuration. Pass `{ enabled: false }` to disable
623
+ * caching outright (useful in tests or when the caller wants the loader to
624
+ * always read fresh from the database).
625
+ */
626
+ cache?: DatabaseLoaderCacheOptions;
615
627
  }
616
628
  /**
617
629
  * DatabaseLoader — Datasource-backed metadata persistence.
@@ -631,9 +643,32 @@ declare class DatabaseLoader implements MetadataLoader {
631
643
  private trackHistory;
632
644
  private schemaReady;
633
645
  private historySchemaReady;
634
- private enableProjection;
635
- private projector?;
646
+ /** (type, name) → metadata payload — primes `load()` */
647
+ private readonly loadCache?;
648
+ /** type → array of payloads — primes `loadMany()` */
649
+ private readonly loadManyCache?;
650
+ /** type → list of names — primes `list()` */
651
+ private readonly listCache?;
652
+ /** (type, name) → MetadataStats — primes `stat()` */
653
+ private readonly statCache?;
636
654
  constructor(options: DatabaseLoaderOptions);
655
+ private cacheKey;
656
+ /**
657
+ * Invalidate all cached entries for a specific (type, name) pair plus
658
+ * the type-level aggregates (`loadMany`, `list`). Called from every write
659
+ * path (`save`, `delete`, `registerRollback`).
660
+ */
661
+ private invalidate;
662
+ /** Drop the entire cache — useful after bulk imports or schema changes. */
663
+ invalidateAll(): void;
664
+ /** Diagnostic: aggregated cache statistics for `metrics` endpoints. */
665
+ getCacheStats(): {
666
+ enabled: boolean;
667
+ load: ReturnType<LRUCache<string, unknown>['stats']> | null;
668
+ loadMany: ReturnType<LRUCache<string, unknown>['stats']> | null;
669
+ list: ReturnType<LRUCache<string, unknown>['stats']> | null;
670
+ stat: ReturnType<LRUCache<string, unknown>['stats']> | null;
671
+ };
637
672
  private _find;
638
673
  private _findOne;
639
674
  private _count;
@@ -887,4 +922,4 @@ declare class TypeScriptSerializer implements MetadataSerializer {
887
922
  getFormat(): MetadataFormat;
888
923
  }
889
924
 
890
- export { DatabaseLoader, type DatabaseLoaderOptions, HistoryCleanupManager, JSONSerializer, MemoryLoader, type MetadataLoader, MetadataManager, type MetadataManagerOptions, MetadataPlugin, MetadataProjector, type MetadataProjectorOptions, type MetadataSerializer, index as Migration, RemoteLoader, type SerializeOptions, TypeScriptSerializer, type WatchCallback, YAMLSerializer, calculateChecksum, generateDiffSummary, generateSimpleDiff, registerMetadataHistoryRoutes };
925
+ export { DatabaseLoader, type DatabaseLoaderOptions, HistoryCleanupManager, JSONSerializer, MemoryLoader, type MetadataLoader, MetadataManager, type MetadataManagerOptions, MetadataPlugin, type MetadataSerializer, index as Migration, RemoteLoader, type SerializeOptions, TypeScriptSerializer, type WatchCallback, YAMLSerializer, calculateChecksum, generateDiffSummary, generateSimpleDiff, registerMetadataHistoryRoutes };
package/dist/index.d.ts CHANGED
@@ -156,6 +156,8 @@ declare class MetadataManager implements IMetadataService {
156
156
  private overlays;
157
157
  private typeRegistry;
158
158
  private dependencies;
159
+ private listCache;
160
+ private static readonly LIST_CACHE_TTL_MS;
159
161
  private realtimeService?;
160
162
  constructor(config: MetadataManagerOptions);
161
163
  /**
@@ -209,6 +211,9 @@ declare class MetadataManager implements IMetadataService {
209
211
  * List all metadata items of a given type
210
212
  */
211
213
  list(type: string): Promise<unknown[]>;
214
+ private cacheListResult;
215
+ /** Internal helper: drop the cached `list()` result for a type. */
216
+ private invalidateListCache;
212
217
  /**
213
218
  * Unregister/remove a metadata item by type and name.
214
219
  * Deletes from database-backed loaders only (same rationale as register()).
@@ -425,14 +430,17 @@ interface MetadataPluginOptions {
425
430
  artifactSource?: {
426
431
  mode: 'local-file';
427
432
  path: string;
433
+ fetchTimeoutMs?: number;
428
434
  } | {
429
435
  mode: 'artifact-api';
430
436
  url: string;
437
+ token?: string;
438
+ commitId?: string;
439
+ fetchTimeoutMs?: number;
431
440
  };
432
441
  /**
433
- * Register the queryable system metadata-storage objects
434
- * (`sys_object`, `sys_view`, `sys_flow`, `sys_agent`, `sys_tool`) on this
435
- * kernel. Default `true` for backward compatibility.
442
+ * Register the `sys_metadata` + `sys_metadata_history` storage objects
443
+ * on this kernel. Default `true` for backward compatibility.
436
444
  *
437
445
  * Set to `false` for **per-project** kernels: in cloud / project mode the
438
446
  * control plane is the sole owner of metadata storage tables — exposing
@@ -450,89 +458,21 @@ declare class MetadataPlugin implements Plugin {
450
458
  constructor(options?: MetadataPluginOptions);
451
459
  init: (ctx: PluginContext) => Promise<void>;
452
460
  start: (ctx: PluginContext) => Promise<void>;
453
- private _loadFromLocalFile;
454
- private _loadFromFileSystem;
455
- }
456
-
457
- /**
458
- * Metadata Projection Service
459
- *
460
- * Implements the dual-table architecture pattern:
461
- * - sys_metadata: Source of truth for package management, versioning
462
- * - Type-specific tables (sys_object, sys_view, etc.): Queryable projections
463
- *
464
- * When metadata is saved to sys_metadata, this service projects it into
465
- * the appropriate type-specific table so Studio can query it via Object Protocol.
466
- */
467
-
468
- /**
469
- * Configuration for the MetadataProjector
470
- */
471
- interface MetadataProjectorOptions {
472
- /** The IDataDriver instance to use for database operations */
473
- driver?: IDataDriver;
474
- /** The IDataEngine (ObjectQL) instance — preferred over raw driver */
475
- engine?: IDataEngine;
476
- /** Organization ID for multi-tenant isolation */
477
- organizationId?: string;
478
- /** Project ID — null = platform-global, set = project-scoped */
479
- projectId?: string;
480
- }
481
- /**
482
- * MetadataProjector
483
- *
484
- * Handles projection from sys_metadata to type-specific tables.
485
- */
486
- declare class MetadataProjector {
487
- private driver?;
488
- private engine?;
489
- /** Reserved for future multi-tenant projection scoping */
490
- readonly scope: {
491
- organizationId?: string;
492
- projectId?: string;
493
- };
494
- private readonly typeTableMap;
495
- constructor(options: MetadataProjectorOptions);
496
- /**
497
- * Project metadata to type-specific table
498
- */
499
- project(type: string, name: string, data: any): Promise<void>;
500
- /**
501
- * Delete projection from type-specific table
502
- */
503
- deleteProjection(type: string, name: string): Promise<void>;
504
461
  /**
505
- * Transform metadata into projection record
462
+ * Fetch JSON content from a URL with configurable timeout.
506
463
  */
507
- private transformToProjection;
464
+ private _fetchJson;
508
465
  /**
509
- * Project object metadata to sys_object
466
+ * Parse raw artifact JSON (envelope or bare definition) and register all
467
+ * metadata items into the MetadataManager.
510
468
  */
511
- private projectObject;
512
- /**
513
- * Project view metadata to sys_view
514
- */
515
- private projectView;
516
- /**
517
- * Project agent metadata to sys_agent
518
- */
519
- private projectAgent;
520
- /**
521
- * Project tool metadata to sys_tool
522
- */
523
- private projectTool;
524
- /**
525
- * Project flow metadata to sys_flow
526
- */
527
- private projectFlow;
528
- private _findOne;
529
- private _create;
530
- private _update;
531
- private _delete;
469
+ private _parseAndRegisterArtifact;
470
+ private _loadFromLocalFile;
532
471
  /**
533
- * Generate a simple unique ID
472
+ * P2: Load metadata from the cloud artifact API endpoint.
534
473
  */
535
- private generateId;
474
+ private _loadFromArtifactApi;
475
+ private _loadFromFileSystem;
536
476
  }
537
477
 
538
478
  /**
@@ -578,6 +518,52 @@ declare class RemoteLoader implements MetadataLoader {
578
518
  save(type: string, name: string, data: any, _options?: MetadataSaveOptions): Promise<MetadataSaveResult>;
579
519
  }
580
520
 
521
+ /**
522
+ * Generic LRU (Least Recently Used) cache with optional TTL.
523
+ *
524
+ * Implementation notes:
525
+ * - Backed by a `Map`, which preserves insertion order. We promote entries on
526
+ * read by deleting and re-inserting, so the oldest entry is always
527
+ * `map.keys().next()`.
528
+ * - TTL is checked lazily on `get` / `has`. Expired entries are evicted on
529
+ * access; we do not run a background sweeper to keep the implementation
530
+ * side-effect free in serverless / edge runtimes.
531
+ * - Set `maxSize <= 0` to disable the size cap; set `ttl <= 0` (or omit) to
532
+ * disable expiration.
533
+ *
534
+ * Designed for `DatabaseLoader` read-path caching — see
535
+ * `packages/metadata/src/loaders/database-loader.ts`.
536
+ */
537
+ interface LRUCacheOptions {
538
+ /** Maximum number of entries; when exceeded, the LRU entry is evicted. */
539
+ maxSize?: number;
540
+ /** Time-to-live in milliseconds. Zero or undefined disables TTL. */
541
+ ttl?: number;
542
+ }
543
+ declare class LRUCache<K, V> {
544
+ private readonly map;
545
+ private readonly maxSize;
546
+ private readonly ttl;
547
+ private hits;
548
+ private misses;
549
+ constructor(options?: LRUCacheOptions);
550
+ get(key: K): V | undefined;
551
+ set(key: K, value: V): void;
552
+ has(key: K): boolean;
553
+ delete(key: K): boolean;
554
+ clear(): void;
555
+ get size(): number;
556
+ /** Diagnostic counters — useful for `metrics` endpoints. */
557
+ stats(): {
558
+ size: number;
559
+ hits: number;
560
+ misses: number;
561
+ hitRate: number;
562
+ };
563
+ /** Resets hit/miss counters without dropping cached entries. */
564
+ resetStats(): void;
565
+ }
566
+
581
567
  /**
582
568
  * Database Metadata Loader
583
569
  *
@@ -587,6 +573,28 @@ declare class RemoteLoader implements MetadataLoader {
587
573
  * MetadataRecordSchema envelope defined in @objectstack/spec.
588
574
  */
589
575
 
576
+ /**
577
+ * Cache configuration for `DatabaseLoader`.
578
+ *
579
+ * The cache sits in front of `load()`, `loadMany()`, `exists()`, `stat()`,
580
+ * and `list()` so that hot read paths (REST `/meta/*`, ObjectQL plan
581
+ * resolution, runtime overlay merges) do not hit the database on every
582
+ * request. All write paths (`save`, `delete`, `registerRollback`) invalidate
583
+ * the relevant entries.
584
+ *
585
+ * Defaults are conservative: 500 entries, 60s TTL — chosen so that single-
586
+ * tenant Studio usage does not burn memory and so that an external write
587
+ * (out-of-band SQL update) becomes visible within a minute even without
588
+ * realtime invalidation.
589
+ */
590
+ interface DatabaseLoaderCacheOptions {
591
+ /** Whether the cache is active. Default: `true`. */
592
+ enabled?: boolean;
593
+ /** Max number of cached `(type, name)` entries. Default: `500`. */
594
+ maxSize?: number;
595
+ /** TTL in milliseconds. Set to `0` to disable expiry. Default: `60_000`. */
596
+ ttl?: number;
597
+ }
590
598
  /**
591
599
  * Configuration for the DatabaseLoader.
592
600
  *
@@ -610,8 +618,12 @@ interface DatabaseLoaderOptions {
610
618
  projectId?: string;
611
619
  /** Enable history tracking (default: true) */
612
620
  trackHistory?: boolean;
613
- /** Enable metadata projection to type-specific tables (default: true) */
614
- enableProjection?: boolean;
621
+ /**
622
+ * Read-through cache configuration. Pass `{ enabled: false }` to disable
623
+ * caching outright (useful in tests or when the caller wants the loader to
624
+ * always read fresh from the database).
625
+ */
626
+ cache?: DatabaseLoaderCacheOptions;
615
627
  }
616
628
  /**
617
629
  * DatabaseLoader — Datasource-backed metadata persistence.
@@ -631,9 +643,32 @@ declare class DatabaseLoader implements MetadataLoader {
631
643
  private trackHistory;
632
644
  private schemaReady;
633
645
  private historySchemaReady;
634
- private enableProjection;
635
- private projector?;
646
+ /** (type, name) → metadata payload — primes `load()` */
647
+ private readonly loadCache?;
648
+ /** type → array of payloads — primes `loadMany()` */
649
+ private readonly loadManyCache?;
650
+ /** type → list of names — primes `list()` */
651
+ private readonly listCache?;
652
+ /** (type, name) → MetadataStats — primes `stat()` */
653
+ private readonly statCache?;
636
654
  constructor(options: DatabaseLoaderOptions);
655
+ private cacheKey;
656
+ /**
657
+ * Invalidate all cached entries for a specific (type, name) pair plus
658
+ * the type-level aggregates (`loadMany`, `list`). Called from every write
659
+ * path (`save`, `delete`, `registerRollback`).
660
+ */
661
+ private invalidate;
662
+ /** Drop the entire cache — useful after bulk imports or schema changes. */
663
+ invalidateAll(): void;
664
+ /** Diagnostic: aggregated cache statistics for `metrics` endpoints. */
665
+ getCacheStats(): {
666
+ enabled: boolean;
667
+ load: ReturnType<LRUCache<string, unknown>['stats']> | null;
668
+ loadMany: ReturnType<LRUCache<string, unknown>['stats']> | null;
669
+ list: ReturnType<LRUCache<string, unknown>['stats']> | null;
670
+ stat: ReturnType<LRUCache<string, unknown>['stats']> | null;
671
+ };
637
672
  private _find;
638
673
  private _findOne;
639
674
  private _count;
@@ -887,4 +922,4 @@ declare class TypeScriptSerializer implements MetadataSerializer {
887
922
  getFormat(): MetadataFormat;
888
923
  }
889
924
 
890
- export { DatabaseLoader, type DatabaseLoaderOptions, HistoryCleanupManager, JSONSerializer, MemoryLoader, type MetadataLoader, MetadataManager, type MetadataManagerOptions, MetadataPlugin, MetadataProjector, type MetadataProjectorOptions, type MetadataSerializer, index as Migration, RemoteLoader, type SerializeOptions, TypeScriptSerializer, type WatchCallback, YAMLSerializer, calculateChecksum, generateDiffSummary, generateSimpleDiff, registerMetadataHistoryRoutes };
925
+ export { DatabaseLoader, type DatabaseLoaderOptions, HistoryCleanupManager, JSONSerializer, MemoryLoader, type MetadataLoader, MetadataManager, type MetadataManagerOptions, MetadataPlugin, type MetadataSerializer, index as Migration, RemoteLoader, type SerializeOptions, TypeScriptSerializer, type WatchCallback, YAMLSerializer, calculateChecksum, generateDiffSummary, generateSimpleDiff, registerMetadataHistoryRoutes };