@objectstack/objectql 9.3.0 → 9.4.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.mts CHANGED
@@ -99,19 +99,21 @@ interface SchemaRegistryOptions {
99
99
  */
100
100
  multiTenant?: boolean;
101
101
  /**
102
- * Policy for cross-package base-layer metadata collisions (ADR-0048) — two
103
- * different code packages registering a bare-named generic item under the
104
- * same `(type, name)`.
102
+ * Policy for the install-time namespace gate (ADR-0048 Phase 1) — installing
103
+ * a package whose `manifest.namespace` is already owned by a *different*
104
+ * installed package.
105
105
  *
106
- * - `'error'` (default): throw {@link MetadataCollisionError} at registration
107
- * time, naming both packages and the type/name. Makes the otherwise-silent
108
- * last-write-wins shadowing a loud, actionable failure.
109
- * - `'warn'`: log a warning and let the registration proceed. For deliberate
110
- * migrations where a collision is temporarily expected.
106
+ * - `'error'` (default): throw {@link NamespaceConflictError} at install time,
107
+ * naming both packages. Makes the namespace land-grab a loud, early failure
108
+ * instead of a mid-install `CREATE TABLE` blow-up.
109
+ * - `'warn'`: log a warning and let the install proceed. For deliberate
110
+ * migrations where the conflict is temporarily expected.
111
111
  *
112
112
  * Sourced from `OS_METADATA_COLLISION` (`warn` to downgrade) when not set
113
- * explicitly. Legitimate runtime/DB overlays and same-package reloads are
114
- * never treated as collisions regardless of this setting.
113
+ * explicitly. Same-package reinstall and shareable platform namespaces
114
+ * (`base`/`system`/`sys`) are never treated as conflicts. (The per-item
115
+ * cross-package collision throw was retired in ADR-0048 §3.4 — distinct
116
+ * package ids are always disambiguable by package-scoped resolution.)
115
117
  */
116
118
  collisionPolicy?: 'error' | 'warn';
117
119
  }
@@ -276,15 +278,6 @@ declare class SchemaRegistry {
276
278
  * Universal Register Method for non-object metadata.
277
279
  */
278
280
  registerItem<T>(type: string, item: T, keyField?: keyof T, packageId?: string): void;
279
- /**
280
- * Find a code package OTHER than `incoming` that already owns `baseName` in
281
- * `collection` (ADR-0048 cross-package collision detection). Scans the live
282
- * collection — like {@link getItem} / {@link unregisterItem} — so it always
283
- * reflects current state with no parallel index to drift across
284
- * reset/unregister. Returns the conflicting owner's package id, or undefined
285
- * when the name is free or only held by the same package / a runtime overlay.
286
- */
287
- private findOtherPackageOwner;
288
281
  /**
289
282
  * Validate Metadata against Spec Zod Schemas
290
283
  */
@@ -294,9 +287,23 @@ declare class SchemaRegistry {
294
287
  */
295
288
  unregisterItem(type: string, name: string): void;
296
289
  /**
297
- * Universal Get Method
290
+ * Universal Get Method.
291
+ *
292
+ * ADR-0048 §3.3 — *package-scoped* resolution. When `currentPackageId` is
293
+ * given (the package the caller is resolving within — known from the route /
294
+ * `activeApp._packageId`), a bare name resolves to *that package's* item
295
+ * before any cross-package fallback, so two packages shipping e.g.
296
+ * `page/home` no longer resolve by registration order (first-match-wins).
297
+ * Because package ids are globally unique this is unambiguous. Omitting
298
+ * `currentPackageId` preserves the legacy resolution exactly (backward
299
+ * compatible) and is best-effort: it returns the first match.
300
+ *
301
+ * Precedence (highest first):
302
+ * 1. bare-key runtime/DB overlay (ADR-0005 sanctioned override) — unchanged
303
+ * 2. the `currentPackageId` composite entry (prefer-local)
304
+ * 3. first composite match (legacy first-registered-wins fallback)
298
305
  */
299
- getItem<T>(type: string, name: string): T | undefined;
306
+ getItem<T>(type: string, name: string, currentPackageId?: string): T | undefined;
300
307
  /**
301
308
  * Artifact-only lookup (ADR-0010 §3.3). Unlike {@link getItem} — which
302
309
  * returns the plain-key entry first, so a runtime/DB-rehydrated row
@@ -311,7 +318,7 @@ declare class SchemaRegistry {
311
318
  * it (that masking is exactly the "registry pollution" bug where a
312
319
  * locked app's `_lock` read back as undefined after a PUT+GET).
313
320
  */
314
- getArtifactItem<T>(type: string, name: string): T | undefined;
321
+ getArtifactItem<T>(type: string, name: string, currentPackageId?: string): T | undefined;
315
322
  /**
316
323
  * Remove a plain-key runtime shadow so the packaged artifact registered
317
324
  * under a composite key becomes the visible value again. Used by the
@@ -349,7 +356,7 @@ declare class SchemaRegistry {
349
356
  enablePackage(id: string): InstalledPackage | undefined;
350
357
  disablePackage(id: string): InstalledPackage | undefined;
351
358
  registerApp(app: any, packageId?: string): void;
352
- getApp(name: string): any;
359
+ getApp(name: string, currentPackageId?: string): any;
353
360
  getAllApps(): any[];
354
361
  /**
355
362
  * Register a navigation contribution — a package injecting nav items into
@@ -1829,6 +1836,7 @@ declare class SysMetadataRepository implements MetadataRepository {
1829
1836
  */
1830
1837
  get(ref: MetaRef, opts?: {
1831
1838
  state?: OverlayState;
1839
+ packageId?: string | null;
1832
1840
  }): Promise<MetadataItem | null>;
1833
1841
  /**
1834
1842
  * Resolve a historical version by content hash (ADR-0009).
@@ -2975,9 +2983,15 @@ declare class MetadataFacade {
2975
2983
  */
2976
2984
  register(type: string, name: string, data: any): Promise<void>;
2977
2985
  /**
2978
- * Get a metadata item by type and name
2986
+ * Get a metadata item by type and name.
2987
+ *
2988
+ * `currentPackageId` (ADR-0048) opts into package-scoped resolution: when two
2989
+ * installed packages ship an item of the same `type`/`name`, the registry
2990
+ * prefers the one owned by `currentPackageId` (composite key
2991
+ * `${packageId}:${name}`) before falling back to first-match. Omit it for the
2992
+ * legacy context-free lookup.
2979
2993
  */
2980
- get(type: string, name: string): Promise<any>;
2994
+ get(type: string, name: string, currentPackageId?: string): Promise<any>;
2981
2995
  /**
2982
2996
  * Get the raw entry (with metadata wrapper)
2983
2997
  */
@@ -3055,6 +3069,28 @@ interface ObjectQLPluginOptions {
3055
3069
  * code change.
3056
3070
  */
3057
3071
  skipSchemaSync?: boolean;
3072
+ /**
3073
+ * Hydrate the SchemaRegistry from this kernel's local `sys_metadata`
3074
+ * even when `environmentId` is set.
3075
+ *
3076
+ * By default Phase-2 hydration in `start()` is gated on
3077
+ * `environmentId === undefined`, because the original multi-environment
3078
+ * model assumed project kernels source metadata from a remote artifact /
3079
+ * control-plane proxy and have NO local `sys_metadata` to read. That is
3080
+ * NOT true for an isolated, proxy-free project kernel that persists its
3081
+ * OWN `sys_metadata` locally (e.g. the cloud single-env tenant runtime on
3082
+ * Turso): objects CREATED AT RUNTIME there — not present in the boot
3083
+ * artifact manifest — would otherwise never re-enter the registry after a
3084
+ * restart, so `registry.getObject(name)` returns nothing for them and any
3085
+ * registry consumer (the unknown-`$select` guard, hooks, relationships)
3086
+ * silently degrades.
3087
+ *
3088
+ * Set this ONLY when the kernel's registry is per-instance isolated AND
3089
+ * `sys_metadata` lives on the kernel's own local driver (no control-plane
3090
+ * proxy) — hydrating a proxied kernel would read the wrong database.
3091
+ * Safe to leave unset: hydration tolerates a missing table.
3092
+ */
3093
+ hydrateMetadataFromDb?: boolean;
3058
3094
  }
3059
3095
  declare class ObjectQLPlugin implements Plugin {
3060
3096
  name: string;
@@ -3070,6 +3106,7 @@ declare class ObjectQLPlugin implements Plugin {
3070
3106
  private hostContext?;
3071
3107
  private environmentId?;
3072
3108
  private skipSchemaSync;
3109
+ private hydrateMetadataFromDb;
3073
3110
  /** Unsubscribe handles for metadata-event subscriptions (ADR-0008 PR-7). */
3074
3111
  private metadataUnsubscribes;
3075
3112
  constructor(qlOrOptions?: ObjectQL | ObjectQLPluginOptions, hostContext?: Record<string, any>);
package/dist/index.d.ts CHANGED
@@ -99,19 +99,21 @@ interface SchemaRegistryOptions {
99
99
  */
100
100
  multiTenant?: boolean;
101
101
  /**
102
- * Policy for cross-package base-layer metadata collisions (ADR-0048) — two
103
- * different code packages registering a bare-named generic item under the
104
- * same `(type, name)`.
102
+ * Policy for the install-time namespace gate (ADR-0048 Phase 1) — installing
103
+ * a package whose `manifest.namespace` is already owned by a *different*
104
+ * installed package.
105
105
  *
106
- * - `'error'` (default): throw {@link MetadataCollisionError} at registration
107
- * time, naming both packages and the type/name. Makes the otherwise-silent
108
- * last-write-wins shadowing a loud, actionable failure.
109
- * - `'warn'`: log a warning and let the registration proceed. For deliberate
110
- * migrations where a collision is temporarily expected.
106
+ * - `'error'` (default): throw {@link NamespaceConflictError} at install time,
107
+ * naming both packages. Makes the namespace land-grab a loud, early failure
108
+ * instead of a mid-install `CREATE TABLE` blow-up.
109
+ * - `'warn'`: log a warning and let the install proceed. For deliberate
110
+ * migrations where the conflict is temporarily expected.
111
111
  *
112
112
  * Sourced from `OS_METADATA_COLLISION` (`warn` to downgrade) when not set
113
- * explicitly. Legitimate runtime/DB overlays and same-package reloads are
114
- * never treated as collisions regardless of this setting.
113
+ * explicitly. Same-package reinstall and shareable platform namespaces
114
+ * (`base`/`system`/`sys`) are never treated as conflicts. (The per-item
115
+ * cross-package collision throw was retired in ADR-0048 §3.4 — distinct
116
+ * package ids are always disambiguable by package-scoped resolution.)
115
117
  */
116
118
  collisionPolicy?: 'error' | 'warn';
117
119
  }
@@ -276,15 +278,6 @@ declare class SchemaRegistry {
276
278
  * Universal Register Method for non-object metadata.
277
279
  */
278
280
  registerItem<T>(type: string, item: T, keyField?: keyof T, packageId?: string): void;
279
- /**
280
- * Find a code package OTHER than `incoming` that already owns `baseName` in
281
- * `collection` (ADR-0048 cross-package collision detection). Scans the live
282
- * collection — like {@link getItem} / {@link unregisterItem} — so it always
283
- * reflects current state with no parallel index to drift across
284
- * reset/unregister. Returns the conflicting owner's package id, or undefined
285
- * when the name is free or only held by the same package / a runtime overlay.
286
- */
287
- private findOtherPackageOwner;
288
281
  /**
289
282
  * Validate Metadata against Spec Zod Schemas
290
283
  */
@@ -294,9 +287,23 @@ declare class SchemaRegistry {
294
287
  */
295
288
  unregisterItem(type: string, name: string): void;
296
289
  /**
297
- * Universal Get Method
290
+ * Universal Get Method.
291
+ *
292
+ * ADR-0048 §3.3 — *package-scoped* resolution. When `currentPackageId` is
293
+ * given (the package the caller is resolving within — known from the route /
294
+ * `activeApp._packageId`), a bare name resolves to *that package's* item
295
+ * before any cross-package fallback, so two packages shipping e.g.
296
+ * `page/home` no longer resolve by registration order (first-match-wins).
297
+ * Because package ids are globally unique this is unambiguous. Omitting
298
+ * `currentPackageId` preserves the legacy resolution exactly (backward
299
+ * compatible) and is best-effort: it returns the first match.
300
+ *
301
+ * Precedence (highest first):
302
+ * 1. bare-key runtime/DB overlay (ADR-0005 sanctioned override) — unchanged
303
+ * 2. the `currentPackageId` composite entry (prefer-local)
304
+ * 3. first composite match (legacy first-registered-wins fallback)
298
305
  */
299
- getItem<T>(type: string, name: string): T | undefined;
306
+ getItem<T>(type: string, name: string, currentPackageId?: string): T | undefined;
300
307
  /**
301
308
  * Artifact-only lookup (ADR-0010 §3.3). Unlike {@link getItem} — which
302
309
  * returns the plain-key entry first, so a runtime/DB-rehydrated row
@@ -311,7 +318,7 @@ declare class SchemaRegistry {
311
318
  * it (that masking is exactly the "registry pollution" bug where a
312
319
  * locked app's `_lock` read back as undefined after a PUT+GET).
313
320
  */
314
- getArtifactItem<T>(type: string, name: string): T | undefined;
321
+ getArtifactItem<T>(type: string, name: string, currentPackageId?: string): T | undefined;
315
322
  /**
316
323
  * Remove a plain-key runtime shadow so the packaged artifact registered
317
324
  * under a composite key becomes the visible value again. Used by the
@@ -349,7 +356,7 @@ declare class SchemaRegistry {
349
356
  enablePackage(id: string): InstalledPackage | undefined;
350
357
  disablePackage(id: string): InstalledPackage | undefined;
351
358
  registerApp(app: any, packageId?: string): void;
352
- getApp(name: string): any;
359
+ getApp(name: string, currentPackageId?: string): any;
353
360
  getAllApps(): any[];
354
361
  /**
355
362
  * Register a navigation contribution — a package injecting nav items into
@@ -1829,6 +1836,7 @@ declare class SysMetadataRepository implements MetadataRepository {
1829
1836
  */
1830
1837
  get(ref: MetaRef, opts?: {
1831
1838
  state?: OverlayState;
1839
+ packageId?: string | null;
1832
1840
  }): Promise<MetadataItem | null>;
1833
1841
  /**
1834
1842
  * Resolve a historical version by content hash (ADR-0009).
@@ -2975,9 +2983,15 @@ declare class MetadataFacade {
2975
2983
  */
2976
2984
  register(type: string, name: string, data: any): Promise<void>;
2977
2985
  /**
2978
- * Get a metadata item by type and name
2986
+ * Get a metadata item by type and name.
2987
+ *
2988
+ * `currentPackageId` (ADR-0048) opts into package-scoped resolution: when two
2989
+ * installed packages ship an item of the same `type`/`name`, the registry
2990
+ * prefers the one owned by `currentPackageId` (composite key
2991
+ * `${packageId}:${name}`) before falling back to first-match. Omit it for the
2992
+ * legacy context-free lookup.
2979
2993
  */
2980
- get(type: string, name: string): Promise<any>;
2994
+ get(type: string, name: string, currentPackageId?: string): Promise<any>;
2981
2995
  /**
2982
2996
  * Get the raw entry (with metadata wrapper)
2983
2997
  */
@@ -3055,6 +3069,28 @@ interface ObjectQLPluginOptions {
3055
3069
  * code change.
3056
3070
  */
3057
3071
  skipSchemaSync?: boolean;
3072
+ /**
3073
+ * Hydrate the SchemaRegistry from this kernel's local `sys_metadata`
3074
+ * even when `environmentId` is set.
3075
+ *
3076
+ * By default Phase-2 hydration in `start()` is gated on
3077
+ * `environmentId === undefined`, because the original multi-environment
3078
+ * model assumed project kernels source metadata from a remote artifact /
3079
+ * control-plane proxy and have NO local `sys_metadata` to read. That is
3080
+ * NOT true for an isolated, proxy-free project kernel that persists its
3081
+ * OWN `sys_metadata` locally (e.g. the cloud single-env tenant runtime on
3082
+ * Turso): objects CREATED AT RUNTIME there — not present in the boot
3083
+ * artifact manifest — would otherwise never re-enter the registry after a
3084
+ * restart, so `registry.getObject(name)` returns nothing for them and any
3085
+ * registry consumer (the unknown-`$select` guard, hooks, relationships)
3086
+ * silently degrades.
3087
+ *
3088
+ * Set this ONLY when the kernel's registry is per-instance isolated AND
3089
+ * `sys_metadata` lives on the kernel's own local driver (no control-plane
3090
+ * proxy) — hydrating a proxied kernel would read the wrong database.
3091
+ * Safe to leave unset: hydration tolerates a missing table.
3092
+ */
3093
+ hydrateMetadataFromDb?: boolean;
3058
3094
  }
3059
3095
  declare class ObjectQLPlugin implements Plugin {
3060
3096
  name: string;
@@ -3070,6 +3106,7 @@ declare class ObjectQLPlugin implements Plugin {
3070
3106
  private hostContext?;
3071
3107
  private environmentId?;
3072
3108
  private skipSchemaSync;
3109
+ private hydrateMetadataFromDb;
3073
3110
  /** Unsubscribe handles for metadata-event subscriptions (ADR-0008 PR-7). */
3074
3111
  private metadataUnsubscribes;
3075
3112
  constructor(qlOrOptions?: ObjectQL | ObjectQLPluginOptions, hostContext?: Record<string, any>);