@objectstack/metadata 4.1.1 → 5.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/dist/index.d.cts CHANGED
@@ -7,6 +7,8 @@ import { MetadataTypeRegistryEntry, MetadataQuery, MetadataQueryResult, Metadata
7
7
  export { MetadataBulkResult, MetadataDependency, MetadataPluginConfig, MetadataPluginManifest, MetadataQuery, MetadataQueryResult, MetadataType, MetadataTypeRegistryEntry, MetadataValidationResult } from '@objectstack/spec/kernel';
8
8
  import { Logger, Plugin, PluginContext } from '@objectstack/core';
9
9
  import { z } from 'zod';
10
+ import { MetadataRepository } from '@objectstack/metadata-core';
11
+ export { HistoryOptions, MetaRef, MetadataEvent, MetadataItem, MetadataItemHeader, MetadataRepository, WatchFilter } from '@objectstack/metadata-core';
10
12
  export { SysMetadataHistoryObject, SysMetadataObject } from '@objectstack/platform-objects/metadata';
11
13
 
12
14
  /**
@@ -159,6 +161,9 @@ declare class MetadataManager implements IMetadataService {
159
161
  private listCache;
160
162
  private static readonly LIST_CACHE_TTL_MS;
161
163
  private realtimeService?;
164
+ protected repository?: MetadataRepository;
165
+ private repoWatchIter?;
166
+ private repoWatchClosed;
162
167
  constructor(config: MetadataManagerOptions);
163
168
  /**
164
169
  * Set the type registry for metadata type discovery.
@@ -327,6 +332,18 @@ declare class MetadataManager implements IMetadataService {
327
332
  * Returns a handle for unsubscribing.
328
333
  */
329
334
  watchService(type: string, callback: MetadataWatchCallback): MetadataWatchHandle;
335
+ /**
336
+ * Subscribe to raw metadata watch events for a given type.
337
+ *
338
+ * Unlike `watchService` (which maps to the IMetadataService contract and
339
+ * drops fields like `path`/`timestamp`), this returns the raw
340
+ * `MetadataWatchEvent` produced by the underlying watcher — useful for
341
+ * developer-facing tooling such as the HMR SSE endpoint that wants the
342
+ * source file path and original timestamp.
343
+ *
344
+ * @returns An unsubscribe function.
345
+ */
346
+ subscribe(type: string, callback: WatchCallback): () => void;
330
347
  /**
331
348
  * Export metadata as a portable bundle
332
349
  */
@@ -388,6 +405,34 @@ declare class MetadataManager implements IMetadataService {
388
405
  * Stop all watching
389
406
  */
390
407
  stopWatching(): Promise<void>;
408
+ /**
409
+ * Attach a {@link MetadataRepository} as a supplementary event source.
410
+ *
411
+ * The manager subscribes to `repo.watch({})` and re-emits each event
412
+ * through {@link notifyWatchers} as a legacy `MetadataWatchEvent`.
413
+ * Each event also invalidates the in-memory registry entry and the
414
+ * `list()` cache for the affected type so subsequent reads see fresh
415
+ * data.
416
+ *
417
+ * No write-through. `register()` / `unregister()` / `save()` are
418
+ * untouched in this PR (deferred to ADR-0008 M0 PR-10).
419
+ *
420
+ * Call {@link dispose} (or {@link stopRepositoryWatch}) to detach.
421
+ */
422
+ setRepository(repo: MetadataRepository): void;
423
+ /** Return the attached repository, if any. */
424
+ getRepository(): MetadataRepository | undefined;
425
+ /** Stop the active repo.watch() loop (best-effort). */
426
+ stopRepositoryWatch(): Promise<void>;
427
+ /**
428
+ * Best-effort cleanup. Stops the FS watcher (if any), drains the
429
+ * repository watch loop, and clears registry caches. Safe to call
430
+ * multiple times.
431
+ */
432
+ dispose(): Promise<void>;
433
+ private startRepositoryWatch;
434
+ /** Translate a repo event to the legacy MetadataWatchEvent + invalidate caches. */
435
+ private applyRepoEvent;
391
436
  protected notifyWatchers(type: string, event: MetadataWatchEvent): void;
392
437
  /**
393
438
  * Get the database loader for history operations.
@@ -416,7 +461,30 @@ declare class MetadataManager implements IMetadataService {
416
461
 
417
462
  interface MetadataPluginOptions {
418
463
  rootDir?: string;
464
+ /**
465
+ * When `true`, NodeMetadataManager scans `rootDir` for source-file metadata
466
+ * (yaml/json/ts/js loaders) AND attaches a chokidar watcher to react to
467
+ * filesystem changes. In **artifact-mode** (this is the normal path when
468
+ * a `defineStack()` config is compiled into `dist/objectstack.json`) this
469
+ * filesystem scan is redundant and expensive — leave `watch: false`.
470
+ *
471
+ * The artifact-file HMR watcher is controlled separately by
472
+ * {@link artifactWatch} so that the cheap, single-file polling watcher
473
+ * can be enabled in dev without paying the cost of scanning the entire
474
+ * project root.
475
+ *
476
+ * Default: `false` (post PR-10e — was previously `true`).
477
+ */
419
478
  watch?: boolean;
479
+ /**
480
+ * When `true` AND `artifactSource.mode === 'local-file'`, attach a
481
+ * polling chokidar watcher to the artifact file so the server reloads
482
+ * metadata when the CLI recompiles `dist/objectstack.json` in dev mode.
483
+ * Independent of {@link watch} (which controls the source-file scanner).
484
+ *
485
+ * Default: `true` when `artifactSource` is set, otherwise `false`.
486
+ */
487
+ artifactWatch?: boolean;
420
488
  config?: Partial<MetadataPluginConfig>;
421
489
  /** Organization ID for metadata-scoped consumers; MetadataPlugin itself does not persist runtime metadata. */
422
490
  organizationId?: string;
@@ -455,9 +523,13 @@ declare class MetadataPlugin implements Plugin {
455
523
  version: string;
456
524
  private manager;
457
525
  private options;
526
+ private repository?;
527
+ /** Chokidar watcher on the artifact file (local-file mode) — ADR-0008 PR-8. */
528
+ private artifactWatcher?;
458
529
  constructor(options?: MetadataPluginOptions);
459
530
  init: (ctx: PluginContext) => Promise<void>;
460
531
  start: (ctx: PluginContext) => Promise<void>;
532
+ stop: (ctx: PluginContext) => Promise<void>;
461
533
  /**
462
534
  * Fetch JSON content from a URL with configurable timeout.
463
535
  */
@@ -614,7 +686,13 @@ interface DatabaseLoaderOptions {
614
686
  historyTableName?: string;
615
687
  /** Organization ID for multi-tenant isolation */
616
688
  organizationId?: string;
617
- /** Project ID — null = platform-global, set = project-scoped */
689
+ /**
690
+ * @deprecated since ADR-0008 §0 amendment (branch/project removal).
691
+ * The metadata layer is keyed by organization only. This option is
692
+ * accepted for back-compat but ignored — writes do not set
693
+ * `project_id` and filters do not constrain on it. Will be removed
694
+ * in the next major release.
695
+ */
618
696
  projectId?: string;
619
697
  /** Enable history tracking (default: true) */
620
698
  trackHistory?: boolean;
@@ -639,7 +717,6 @@ declare class DatabaseLoader implements MetadataLoader {
639
717
  private tableName;
640
718
  private historyTableName;
641
719
  private organizationId?;
642
- private projectId?;
643
720
  private trackHistory;
644
721
  private schemaReady;
645
722
  private historySchemaReady;
@@ -675,6 +752,13 @@ declare class DatabaseLoader implements MetadataLoader {
675
752
  private _create;
676
753
  private _update;
677
754
  private _delete;
755
+ /**
756
+ * Compute the next per-org `event_seq` for `sys_metadata_history`.
757
+ * Reads `MAX(event_seq) + 1` for the configured `organization_id`.
758
+ * Legacy path — not transactional, so concurrent writes can collide.
759
+ * The canonical (transactional) producer is `SysMetadataRepository`.
760
+ */
761
+ private nextEventSeq;
678
762
  /**
679
763
  * Ensure the metadata table exists.
680
764
  * Uses IDataDriver.syncSchema with the SysMetadataObject definition
@@ -688,8 +772,9 @@ declare class DatabaseLoader implements MetadataLoader {
688
772
  private ensureHistorySchema;
689
773
  /**
690
774
  * Build base filter conditions for queries.
691
- * Filters by organizationId when configured; project_id when projectId is set,
692
- * or null (platform-global) when not set.
775
+ * Filters by organizationId when configured. `projectId` is accepted
776
+ * for back-compat but no longer constrains the query — see
777
+ * ADR-0008 §0 (branch/project removal).
693
778
  */
694
779
  private baseFilter;
695
780
  /**
package/dist/index.d.ts CHANGED
@@ -7,6 +7,8 @@ import { MetadataTypeRegistryEntry, MetadataQuery, MetadataQueryResult, Metadata
7
7
  export { MetadataBulkResult, MetadataDependency, MetadataPluginConfig, MetadataPluginManifest, MetadataQuery, MetadataQueryResult, MetadataType, MetadataTypeRegistryEntry, MetadataValidationResult } from '@objectstack/spec/kernel';
8
8
  import { Logger, Plugin, PluginContext } from '@objectstack/core';
9
9
  import { z } from 'zod';
10
+ import { MetadataRepository } from '@objectstack/metadata-core';
11
+ export { HistoryOptions, MetaRef, MetadataEvent, MetadataItem, MetadataItemHeader, MetadataRepository, WatchFilter } from '@objectstack/metadata-core';
10
12
  export { SysMetadataHistoryObject, SysMetadataObject } from '@objectstack/platform-objects/metadata';
11
13
 
12
14
  /**
@@ -159,6 +161,9 @@ declare class MetadataManager implements IMetadataService {
159
161
  private listCache;
160
162
  private static readonly LIST_CACHE_TTL_MS;
161
163
  private realtimeService?;
164
+ protected repository?: MetadataRepository;
165
+ private repoWatchIter?;
166
+ private repoWatchClosed;
162
167
  constructor(config: MetadataManagerOptions);
163
168
  /**
164
169
  * Set the type registry for metadata type discovery.
@@ -327,6 +332,18 @@ declare class MetadataManager implements IMetadataService {
327
332
  * Returns a handle for unsubscribing.
328
333
  */
329
334
  watchService(type: string, callback: MetadataWatchCallback): MetadataWatchHandle;
335
+ /**
336
+ * Subscribe to raw metadata watch events for a given type.
337
+ *
338
+ * Unlike `watchService` (which maps to the IMetadataService contract and
339
+ * drops fields like `path`/`timestamp`), this returns the raw
340
+ * `MetadataWatchEvent` produced by the underlying watcher — useful for
341
+ * developer-facing tooling such as the HMR SSE endpoint that wants the
342
+ * source file path and original timestamp.
343
+ *
344
+ * @returns An unsubscribe function.
345
+ */
346
+ subscribe(type: string, callback: WatchCallback): () => void;
330
347
  /**
331
348
  * Export metadata as a portable bundle
332
349
  */
@@ -388,6 +405,34 @@ declare class MetadataManager implements IMetadataService {
388
405
  * Stop all watching
389
406
  */
390
407
  stopWatching(): Promise<void>;
408
+ /**
409
+ * Attach a {@link MetadataRepository} as a supplementary event source.
410
+ *
411
+ * The manager subscribes to `repo.watch({})` and re-emits each event
412
+ * through {@link notifyWatchers} as a legacy `MetadataWatchEvent`.
413
+ * Each event also invalidates the in-memory registry entry and the
414
+ * `list()` cache for the affected type so subsequent reads see fresh
415
+ * data.
416
+ *
417
+ * No write-through. `register()` / `unregister()` / `save()` are
418
+ * untouched in this PR (deferred to ADR-0008 M0 PR-10).
419
+ *
420
+ * Call {@link dispose} (or {@link stopRepositoryWatch}) to detach.
421
+ */
422
+ setRepository(repo: MetadataRepository): void;
423
+ /** Return the attached repository, if any. */
424
+ getRepository(): MetadataRepository | undefined;
425
+ /** Stop the active repo.watch() loop (best-effort). */
426
+ stopRepositoryWatch(): Promise<void>;
427
+ /**
428
+ * Best-effort cleanup. Stops the FS watcher (if any), drains the
429
+ * repository watch loop, and clears registry caches. Safe to call
430
+ * multiple times.
431
+ */
432
+ dispose(): Promise<void>;
433
+ private startRepositoryWatch;
434
+ /** Translate a repo event to the legacy MetadataWatchEvent + invalidate caches. */
435
+ private applyRepoEvent;
391
436
  protected notifyWatchers(type: string, event: MetadataWatchEvent): void;
392
437
  /**
393
438
  * Get the database loader for history operations.
@@ -416,7 +461,30 @@ declare class MetadataManager implements IMetadataService {
416
461
 
417
462
  interface MetadataPluginOptions {
418
463
  rootDir?: string;
464
+ /**
465
+ * When `true`, NodeMetadataManager scans `rootDir` for source-file metadata
466
+ * (yaml/json/ts/js loaders) AND attaches a chokidar watcher to react to
467
+ * filesystem changes. In **artifact-mode** (this is the normal path when
468
+ * a `defineStack()` config is compiled into `dist/objectstack.json`) this
469
+ * filesystem scan is redundant and expensive — leave `watch: false`.
470
+ *
471
+ * The artifact-file HMR watcher is controlled separately by
472
+ * {@link artifactWatch} so that the cheap, single-file polling watcher
473
+ * can be enabled in dev without paying the cost of scanning the entire
474
+ * project root.
475
+ *
476
+ * Default: `false` (post PR-10e — was previously `true`).
477
+ */
419
478
  watch?: boolean;
479
+ /**
480
+ * When `true` AND `artifactSource.mode === 'local-file'`, attach a
481
+ * polling chokidar watcher to the artifact file so the server reloads
482
+ * metadata when the CLI recompiles `dist/objectstack.json` in dev mode.
483
+ * Independent of {@link watch} (which controls the source-file scanner).
484
+ *
485
+ * Default: `true` when `artifactSource` is set, otherwise `false`.
486
+ */
487
+ artifactWatch?: boolean;
420
488
  config?: Partial<MetadataPluginConfig>;
421
489
  /** Organization ID for metadata-scoped consumers; MetadataPlugin itself does not persist runtime metadata. */
422
490
  organizationId?: string;
@@ -455,9 +523,13 @@ declare class MetadataPlugin implements Plugin {
455
523
  version: string;
456
524
  private manager;
457
525
  private options;
526
+ private repository?;
527
+ /** Chokidar watcher on the artifact file (local-file mode) — ADR-0008 PR-8. */
528
+ private artifactWatcher?;
458
529
  constructor(options?: MetadataPluginOptions);
459
530
  init: (ctx: PluginContext) => Promise<void>;
460
531
  start: (ctx: PluginContext) => Promise<void>;
532
+ stop: (ctx: PluginContext) => Promise<void>;
461
533
  /**
462
534
  * Fetch JSON content from a URL with configurable timeout.
463
535
  */
@@ -614,7 +686,13 @@ interface DatabaseLoaderOptions {
614
686
  historyTableName?: string;
615
687
  /** Organization ID for multi-tenant isolation */
616
688
  organizationId?: string;
617
- /** Project ID — null = platform-global, set = project-scoped */
689
+ /**
690
+ * @deprecated since ADR-0008 §0 amendment (branch/project removal).
691
+ * The metadata layer is keyed by organization only. This option is
692
+ * accepted for back-compat but ignored — writes do not set
693
+ * `project_id` and filters do not constrain on it. Will be removed
694
+ * in the next major release.
695
+ */
618
696
  projectId?: string;
619
697
  /** Enable history tracking (default: true) */
620
698
  trackHistory?: boolean;
@@ -639,7 +717,6 @@ declare class DatabaseLoader implements MetadataLoader {
639
717
  private tableName;
640
718
  private historyTableName;
641
719
  private organizationId?;
642
- private projectId?;
643
720
  private trackHistory;
644
721
  private schemaReady;
645
722
  private historySchemaReady;
@@ -675,6 +752,13 @@ declare class DatabaseLoader implements MetadataLoader {
675
752
  private _create;
676
753
  private _update;
677
754
  private _delete;
755
+ /**
756
+ * Compute the next per-org `event_seq` for `sys_metadata_history`.
757
+ * Reads `MAX(event_seq) + 1` for the configured `organization_id`.
758
+ * Legacy path — not transactional, so concurrent writes can collide.
759
+ * The canonical (transactional) producer is `SysMetadataRepository`.
760
+ */
761
+ private nextEventSeq;
678
762
  /**
679
763
  * Ensure the metadata table exists.
680
764
  * Uses IDataDriver.syncSchema with the SysMetadataObject definition
@@ -688,8 +772,9 @@ declare class DatabaseLoader implements MetadataLoader {
688
772
  private ensureHistorySchema;
689
773
  /**
690
774
  * Build base filter conditions for queries.
691
- * Filters by organizationId when configured; project_id when projectId is set,
692
- * or null (platform-global) when not set.
775
+ * Filters by organizationId when configured. `projectId` is accepted
776
+ * for back-compat but no longer constrains the query — see
777
+ * ADR-0008 §0 (branch/project removal).
693
778
  */
694
779
  private baseFilter;
695
780
  /**