@objectstack/objectql 9.3.0 → 9.5.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.js CHANGED
@@ -332,6 +332,19 @@ var init_seed_loader = __esm({
332
332
  if (records && records.length > 0) {
333
333
  return String(records[0].id || records[0]._id);
334
334
  }
335
+ if (targetField !== "id") {
336
+ const byId = { id: value };
337
+ if (organizationId) byId.organization_id = organizationId;
338
+ const idMatch = await this.engine.find(targetObject, {
339
+ where: byId,
340
+ fields: ["id"],
341
+ limit: 1,
342
+ context: { isSystem: true }
343
+ });
344
+ if (idMatch && idMatch.length > 0) {
345
+ return String(idMatch[0].id || idMatch[0]._id);
346
+ }
347
+ }
335
348
  } catch {
336
349
  }
337
350
  return null;
@@ -950,18 +963,16 @@ function applySystemFields(schema, opts) {
950
963
  fields: { ...additions, ...schema.fields ?? {} }
951
964
  };
952
965
  }
953
- var SYS_METADATA_OWNER = "sys_metadata";
954
- function isRealPackage(pkg) {
955
- return typeof pkg === "string" && pkg.length > 0 && pkg !== SYS_METADATA_OWNER;
966
+ function isShareableNamespace(ns) {
967
+ return RESERVED_NAMESPACES.has(ns) || ns === "sys";
956
968
  }
957
- var MetadataCollisionError = class extends Error {
958
- constructor(type, name, existingPackageId, incomingPackageId) {
969
+ var NamespaceConflictError = class extends Error {
970
+ constructor(namespace, existingPackageId, incomingPackageId) {
959
971
  super(
960
- `Cross-package metadata collision: ${type}/${name} is registered by package "${existingPackageId}" and package "${incomingPackageId}". Bare-named ${type} metadata has no package coordinate in the registry, so the second registration would silently shadow the first (last-write-wins at read time). Rename one of them (a namespace prefix such as "<namespace>_${name}" is recommended), or, if this is a deliberate migration, set OS_METADATA_COLLISION=warn to downgrade to a warning. See ADR-0048.`
972
+ `Namespace conflict: namespace "${namespace}" is already owned by package "${existingPackageId}", so package "${incomingPackageId}" cannot be installed alongside it. A namespace is the mandatory prefix of every object name (e.g. "${namespace}_account") and the container that scopes a package's UI metadata, so it must be unique per installation. Choose a different namespace for "${incomingPackageId}", or uninstall "${existingPackageId}" first. If this is a deliberate migration, set OS_METADATA_COLLISION=warn to downgrade to a warning. See ADR-0048.`
961
973
  );
962
- this.name = "MetadataCollisionError";
963
- this.type = type;
964
- this.name_ = name;
974
+ this.name = "NamespaceConflictError";
975
+ this.namespace = namespace;
965
976
  this.existingPackageId = existingPackageId;
966
977
  this.incomingPackageId = incomingPackageId;
967
978
  }
@@ -1317,17 +1328,6 @@ var SchemaRegistry = class {
1317
1328
  if (collection.has(storageKey)) {
1318
1329
  this.log(`[Registry] Overwriting ${type}: ${storageKey}`);
1319
1330
  }
1320
- if (isRealPackage(packageId)) {
1321
- const conflictOwner = this.findOtherPackageOwner(collection, baseName, packageId);
1322
- if (conflictOwner) {
1323
- const err = new MetadataCollisionError(type, baseName, conflictOwner, packageId);
1324
- if (this.collisionPolicy === "warn") {
1325
- console.warn(`[Registry] ${err.message}`);
1326
- } else {
1327
- throw err;
1328
- }
1329
- }
1330
- }
1331
1331
  if (packageId && collection.has(baseName)) {
1332
1332
  const dbOnly = collection.get(baseName);
1333
1333
  if (dbOnly && !dbOnly._packageId) {
@@ -1339,22 +1339,6 @@ var SchemaRegistry = class {
1339
1339
  collection.set(storageKey, item);
1340
1340
  this.log(`[Registry] Registered ${type}: ${storageKey}`);
1341
1341
  }
1342
- /**
1343
- * Find a code package OTHER than `incoming` that already owns `baseName` in
1344
- * `collection` (ADR-0048 cross-package collision detection). Scans the live
1345
- * collection — like {@link getItem} / {@link unregisterItem} — so it always
1346
- * reflects current state with no parallel index to drift across
1347
- * reset/unregister. Returns the conflicting owner's package id, or undefined
1348
- * when the name is free or only held by the same package / a runtime overlay.
1349
- */
1350
- findOtherPackageOwner(collection, baseName, incoming) {
1351
- for (const [key, item] of collection) {
1352
- if (key !== baseName && !key.endsWith(`:${baseName}`)) continue;
1353
- const owner = item?._packageId;
1354
- if (isRealPackage(owner) && owner !== incoming) return owner;
1355
- }
1356
- return void 0;
1357
- }
1358
1342
  /**
1359
1343
  * Validate Metadata against Spec Zod Schemas
1360
1344
  */
@@ -1397,9 +1381,23 @@ var SchemaRegistry = class {
1397
1381
  console.warn(`[Registry] Attempted to unregister non-existent ${type}: ${name}`);
1398
1382
  }
1399
1383
  /**
1400
- * Universal Get Method
1384
+ * Universal Get Method.
1385
+ *
1386
+ * ADR-0048 §3.3 — *package-scoped* resolution. When `currentPackageId` is
1387
+ * given (the package the caller is resolving within — known from the route /
1388
+ * `activeApp._packageId`), a bare name resolves to *that package's* item
1389
+ * before any cross-package fallback, so two packages shipping e.g.
1390
+ * `page/home` no longer resolve by registration order (first-match-wins).
1391
+ * Because package ids are globally unique this is unambiguous. Omitting
1392
+ * `currentPackageId` preserves the legacy resolution exactly (backward
1393
+ * compatible) and is best-effort: it returns the first match.
1394
+ *
1395
+ * Precedence (highest first):
1396
+ * 1. bare-key runtime/DB overlay (ADR-0005 sanctioned override) — unchanged
1397
+ * 2. the `currentPackageId` composite entry (prefer-local)
1398
+ * 3. first composite match (legacy first-registered-wins fallback)
1401
1399
  */
1402
- getItem(type, name) {
1400
+ getItem(type, name, currentPackageId) {
1403
1401
  if (type === "object" || type === "objects") {
1404
1402
  return this.getObject(name);
1405
1403
  }
@@ -1407,6 +1405,10 @@ var SchemaRegistry = class {
1407
1405
  if (!collection) return void 0;
1408
1406
  const direct = collection.get(name);
1409
1407
  if (direct) return direct;
1408
+ if (currentPackageId) {
1409
+ const local = collection.get(`${currentPackageId}:${name}`);
1410
+ if (local) return local;
1411
+ }
1410
1412
  for (const [key, item] of collection) {
1411
1413
  if (key.endsWith(`:${name}`)) {
1412
1414
  return item;
@@ -1428,13 +1430,17 @@ var SchemaRegistry = class {
1428
1430
  * it (that masking is exactly the "registry pollution" bug where a
1429
1431
  * locked app's `_lock` read back as undefined after a PUT+GET).
1430
1432
  */
1431
- getArtifactItem(type, name) {
1433
+ getArtifactItem(type, name, currentPackageId) {
1432
1434
  if (type === "object" || type === "objects") {
1433
1435
  const obj = this.getObject(name);
1434
1436
  return obj && obj._packageId && obj._packageId !== "sys_metadata" ? obj : void 0;
1435
1437
  }
1436
1438
  const collection = this.metadata.get(type);
1437
1439
  if (!collection) return void 0;
1440
+ if (currentPackageId) {
1441
+ const local = collection.get(`${currentPackageId}:${name}`);
1442
+ if (local && local._packageId && local._packageId !== "sys_metadata") return local;
1443
+ }
1438
1444
  for (const [key, item] of collection) {
1439
1445
  if (key !== name && key.endsWith(`:${name}`)) {
1440
1446
  const it = item;
@@ -1518,6 +1524,20 @@ var SchemaRegistry = class {
1518
1524
  // Package Management
1519
1525
  // ==========================================
1520
1526
  installPackage(manifest, settings) {
1527
+ if (manifest.namespace && !isShareableNamespace(manifest.namespace)) {
1528
+ const conflictOwner = this.getNamespaceOwners(manifest.namespace).find(
1529
+ (owner) => owner !== manifest.id
1530
+ );
1531
+ if (conflictOwner) {
1532
+ if (this.collisionPolicy === "warn") {
1533
+ console.warn(
1534
+ `[Registry] Namespace conflict (downgraded to warning via OS_METADATA_COLLISION=warn): namespace "${manifest.namespace}" is already owned by "${conflictOwner}"; installing "${manifest.id}" anyway. See ADR-0048.`
1535
+ );
1536
+ } else {
1537
+ throw new NamespaceConflictError(manifest.namespace, conflictOwner, manifest.id);
1538
+ }
1539
+ }
1540
+ }
1521
1541
  const now = (/* @__PURE__ */ new Date()).toISOString();
1522
1542
  const disabled = this.initialDisabledPackageIds.has(manifest.id);
1523
1543
  const pkg = {
@@ -1595,8 +1615,8 @@ var SchemaRegistry = class {
1595
1615
  registerApp(app, packageId) {
1596
1616
  this.registerItem("app", app, "name", packageId);
1597
1617
  }
1598
- getApp(name) {
1599
- const app = this.getItem("app", name);
1618
+ getApp(name, currentPackageId) {
1619
+ const app = this.getItem("app", name, currentPackageId);
1600
1620
  if (!app) return app;
1601
1621
  return this.applyNavContributions(app);
1602
1622
  }
@@ -1816,7 +1836,7 @@ var SysMetadataRepository = class {
1816
1836
  this.assertOpen();
1817
1837
  const state = opts?.state ?? "active";
1818
1838
  const row = await this.engine.findOne("sys_metadata", {
1819
- where: this.whereFor(ref, state)
1839
+ where: this.whereFor(ref, state, opts && "packageId" in opts ? opts.packageId ?? null : void 0)
1820
1840
  });
1821
1841
  if (!row) return null;
1822
1842
  return this.rowToItem(ref, row);
@@ -1865,7 +1885,7 @@ var SysMetadataRepository = class {
1865
1885
  const hash = (0, import_metadata_core.hashSpec)(body);
1866
1886
  const result = await this.withTxn(async (ctx) => {
1867
1887
  const existing = await this.engine.findOne("sys_metadata", {
1868
- where: this.whereFor(ref, state),
1888
+ where: this.whereFor(ref, state, opts.packageId ?? null),
1869
1889
  context: ctx
1870
1890
  });
1871
1891
  const existingHash = existing?.checksum ?? null;
@@ -2069,8 +2089,10 @@ var SysMetadataRepository = class {
2069
2089
  */
2070
2090
  async promoteDraft(ref, opts) {
2071
2091
  this.assertOpen();
2072
- const draft = await this.get(ref, { state: "draft" });
2073
- if (!draft) {
2092
+ const draftRow = await this.engine.findOne("sys_metadata", {
2093
+ where: this.whereFor(ref, "draft")
2094
+ });
2095
+ if (!draftRow) {
2074
2096
  const err = new Error(
2075
2097
  `[no_draft] No pending draft exists for ${ref.type}/${ref.name} \u2014 nothing to publish.`
2076
2098
  );
@@ -2078,7 +2100,9 @@ var SysMetadataRepository = class {
2078
2100
  err.status = 404;
2079
2101
  throw err;
2080
2102
  }
2081
- const currentActive = await this.get(ref, { state: "active" });
2103
+ const draftPackageId = draftRow.package_id ?? null;
2104
+ const draft = this.rowToItem(ref, draftRow);
2105
+ const currentActive = await this.get(ref, { state: "active", packageId: draftPackageId });
2082
2106
  const result = await this.put(ref, draft.body, {
2083
2107
  parentVersion: currentActive?.hash ?? null,
2084
2108
  actor: opts.actor,
@@ -2086,7 +2110,8 @@ var SysMetadataRepository = class {
2086
2110
  message: opts.message ?? `publish draft (hash ${draft.hash})`,
2087
2111
  intent: opts.intent ?? "override-artifact",
2088
2112
  state: "active",
2089
- opType: "publish"
2113
+ opType: "publish",
2114
+ packageId: draftPackageId
2090
2115
  });
2091
2116
  try {
2092
2117
  await this.delete(ref, {
@@ -2179,10 +2204,15 @@ var SysMetadataRepository = class {
2179
2204
  */
2180
2205
  async listDrafts(filter) {
2181
2206
  this.assertOpen();
2182
- const where = {
2183
- organization_id: this.organizationId,
2184
- state: "draft"
2185
- };
2207
+ const where = { state: "draft" };
2208
+ if (this.organizationId != null) {
2209
+ where.$or = [
2210
+ { organization_id: this.organizationId },
2211
+ { organization_id: null }
2212
+ ];
2213
+ } else {
2214
+ where.organization_id = null;
2215
+ }
2186
2216
  if (filter?.type) where.type = filter.type;
2187
2217
  if (filter?.packageId) where.package_id = filter.packageId;
2188
2218
  const rows = await this.engine.find("sys_metadata", { where });
@@ -2349,13 +2379,15 @@ var SysMetadataRepository = class {
2349
2379
  err.status = 403;
2350
2380
  throw err;
2351
2381
  }
2352
- whereFor(ref, state = "active") {
2353
- return {
2382
+ whereFor(ref, state = "active", packageId) {
2383
+ const where = {
2354
2384
  type: ref.type,
2355
2385
  name: ref.name,
2356
2386
  organization_id: this.organizationId,
2357
2387
  state
2358
2388
  };
2389
+ if (packageId !== void 0) where.package_id = packageId;
2390
+ return where;
2359
2391
  }
2360
2392
  fullRef(ref) {
2361
2393
  return {
@@ -3042,8 +3074,8 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3042
3074
  await exec("DROP INDEX IF EXISTS idx_sys_metadata_overlay_active");
3043
3075
  } catch {
3044
3076
  }
3045
- const partialSql = "CREATE UNIQUE INDEX IF NOT EXISTS idx_sys_metadata_overlay_active ON sys_metadata (type, name, organization_id) WHERE state = 'active'";
3046
- const fallbackSql = "CREATE INDEX IF NOT EXISTS idx_sys_metadata_overlay_active ON sys_metadata (type, name, organization_id)";
3077
+ const partialSql = "CREATE UNIQUE INDEX IF NOT EXISTS idx_sys_metadata_overlay_active ON sys_metadata (type, name, organization_id, COALESCE(package_id, '')) WHERE state = 'active'";
3078
+ const fallbackSql = "CREATE INDEX IF NOT EXISTS idx_sys_metadata_overlay_active ON sys_metadata (type, name, organization_id, package_id)";
3047
3079
  try {
3048
3080
  await exec(partialSql);
3049
3081
  } catch (err) {
@@ -3055,7 +3087,11 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3055
3087
  }
3056
3088
  }
3057
3089
  }
3058
- const draftPartialSql = "CREATE UNIQUE INDEX IF NOT EXISTS idx_sys_metadata_overlay_draft ON sys_metadata (type, name, organization_id) WHERE state = 'draft'";
3090
+ try {
3091
+ await exec("DROP INDEX IF EXISTS idx_sys_metadata_overlay_draft");
3092
+ } catch {
3093
+ }
3094
+ const draftPartialSql = "CREATE UNIQUE INDEX IF NOT EXISTS idx_sys_metadata_overlay_draft ON sys_metadata (type, name, organization_id, COALESCE(package_id, '')) WHERE state = 'draft'";
3059
3095
  try {
3060
3096
  await exec(draftPartialSql);
3061
3097
  } catch (err) {
@@ -3063,7 +3099,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3063
3099
  if (/partial|where clause|syntax/i.test(msg)) {
3064
3100
  try {
3065
3101
  await exec(
3066
- "CREATE INDEX IF NOT EXISTS idx_sys_metadata_overlay_draft ON sys_metadata (type, name, organization_id)"
3102
+ "CREATE INDEX IF NOT EXISTS idx_sys_metadata_overlay_draft ON sys_metadata (type, name, organization_id, package_id)"
3067
3103
  );
3068
3104
  } catch {
3069
3105
  }
@@ -3464,7 +3500,8 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3464
3500
  items.map((it) => {
3465
3501
  const a = this.lookupArtifactItem(
3466
3502
  request.type,
3467
- it?.name
3503
+ it?.name,
3504
+ packageId ?? it?._packageId
3468
3505
  );
3469
3506
  return mergeArtifactProtection(it, a);
3470
3507
  })
@@ -3478,16 +3515,28 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3478
3515
  if (request.previewDrafts && readState !== "draft") {
3479
3516
  try {
3480
3517
  const findDraft = async (oid) => {
3481
- const rec = await this.engine.findOne("sys_metadata", {
3482
- where: { type: request.type, name: request.name, state: "draft", organization_id: oid }
3483
- });
3518
+ const lookup = async (t) => {
3519
+ const base = {
3520
+ type: t,
3521
+ name: request.name,
3522
+ state: "draft",
3523
+ organization_id: oid
3524
+ };
3525
+ if (request.packageId) {
3526
+ const scoped = await this.engine.findOne("sys_metadata", {
3527
+ where: { ...base, package_id: request.packageId }
3528
+ });
3529
+ if (scoped) return scoped;
3530
+ return await this.engine.findOne("sys_metadata", {
3531
+ where: { ...base, package_id: null }
3532
+ });
3533
+ }
3534
+ return await this.engine.findOne("sys_metadata", { where: base });
3535
+ };
3536
+ const rec = await lookup(request.type);
3484
3537
  if (rec) return rec;
3485
3538
  const alt = import_shared4.PLURAL_TO_SINGULAR[request.type] ?? import_shared4.SINGULAR_TO_PLURAL[request.type];
3486
- if (alt) {
3487
- return await this.engine.findOne("sys_metadata", {
3488
- where: { type: alt, name: request.name, state: "draft", organization_id: oid }
3489
- });
3490
- }
3539
+ if (alt) return await lookup(alt);
3491
3540
  return void 0;
3492
3541
  };
3493
3542
  const draftRec = (orgId ? await findDraft(orgId) : void 0) ?? await findDraft(null);
@@ -3505,24 +3554,28 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3505
3554
  }
3506
3555
  try {
3507
3556
  const findOverlay = async (oid) => {
3508
- const where = {
3509
- type: request.type,
3510
- name: request.name,
3511
- state: readState,
3512
- organization_id: oid
3513
- };
3514
- const rec = await this.engine.findOne("sys_metadata", { where });
3515
- if (rec) return rec;
3516
- const alt = import_shared4.PLURAL_TO_SINGULAR[request.type] ?? import_shared4.SINGULAR_TO_PLURAL[request.type];
3517
- if (alt) {
3518
- const altWhere = {
3519
- type: alt,
3557
+ const lookup = async (t) => {
3558
+ const base = {
3559
+ type: t,
3520
3560
  name: request.name,
3521
3561
  state: readState,
3522
3562
  organization_id: oid
3523
3563
  };
3524
- return await this.engine.findOne("sys_metadata", { where: altWhere });
3525
- }
3564
+ if (request.packageId) {
3565
+ const scoped = await this.engine.findOne("sys_metadata", {
3566
+ where: { ...base, package_id: request.packageId }
3567
+ });
3568
+ if (scoped) return scoped;
3569
+ return await this.engine.findOne("sys_metadata", {
3570
+ where: { ...base, package_id: null }
3571
+ });
3572
+ }
3573
+ return await this.engine.findOne("sys_metadata", { where: base });
3574
+ };
3575
+ const rec = await lookup(request.type);
3576
+ if (rec) return rec;
3577
+ const alt = import_shared4.PLURAL_TO_SINGULAR[request.type] ?? import_shared4.SINGULAR_TO_PLURAL[request.type];
3578
+ if (alt) return await lookup(alt);
3526
3579
  return void 0;
3527
3580
  };
3528
3581
  const record = (orgId ? await findOverlay(orgId) : void 0) ?? await findOverlay(null);
@@ -3551,13 +3604,13 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3551
3604
  const services = this.getServicesRegistry?.();
3552
3605
  const metadataService = services?.get("metadata");
3553
3606
  if (metadataService && typeof metadataService.get === "function") {
3554
- const fromService = await metadataService.get(request.type, request.name);
3607
+ const fromService = await metadataService.get(request.type, request.name, request.packageId);
3555
3608
  if (fromService !== void 0 && fromService !== null) {
3556
3609
  item = fromService;
3557
3610
  } else {
3558
3611
  const alt = import_shared4.PLURAL_TO_SINGULAR[request.type] ?? import_shared4.SINGULAR_TO_PLURAL[request.type];
3559
3612
  if (alt) {
3560
- const altFromService = await metadataService.get(alt, request.name);
3613
+ const altFromService = await metadataService.get(alt, request.name, request.packageId);
3561
3614
  if (altFromService !== void 0 && altFromService !== null) {
3562
3615
  item = altFromService;
3563
3616
  }
@@ -3568,16 +3621,16 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3568
3621
  }
3569
3622
  }
3570
3623
  if (item === void 0) {
3571
- item = this.engine.registry.getItem(request.type, request.name);
3624
+ item = this.engine.registry.getItem(request.type, request.name, request.packageId);
3572
3625
  if (item === void 0) {
3573
3626
  const alt = import_shared4.PLURAL_TO_SINGULAR[request.type] ?? import_shared4.SINGULAR_TO_PLURAL[request.type];
3574
- if (alt) item = this.engine.registry.getItem(alt, request.name);
3627
+ if (alt) item = this.engine.registry.getItem(alt, request.name, request.packageId);
3575
3628
  }
3576
3629
  }
3577
3630
  if ((request.type === "app" || request.type === "apps") && item) {
3578
3631
  item = this.engine.registry.applyNavContributions(item);
3579
3632
  }
3580
- const artifactItem = this.lookupArtifactItem(request.type, request.name);
3633
+ const artifactItem = this.lookupArtifactItem(request.type, request.name, request.packageId);
3581
3634
  let decorated = decorateMetadataItem(
3582
3635
  request.type,
3583
3636
  mergeArtifactProtection(item, artifactItem)
@@ -3645,20 +3698,20 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3645
3698
  const services = this.getServicesRegistry?.();
3646
3699
  const metadataService = services?.get("metadata");
3647
3700
  if (metadataService && typeof metadataService.get === "function") {
3648
- let fromService = await metadataService.get(request.type, request.name);
3701
+ let fromService = await metadataService.get(request.type, request.name, request.packageId);
3649
3702
  if (fromService === void 0 || fromService === null) {
3650
3703
  const alt = import_shared4.PLURAL_TO_SINGULAR[request.type] ?? import_shared4.SINGULAR_TO_PLURAL[request.type];
3651
- if (alt) fromService = await metadataService.get(alt, request.name);
3704
+ if (alt) fromService = await metadataService.get(alt, request.name, request.packageId);
3652
3705
  }
3653
3706
  if (fromService !== void 0 && fromService !== null) code = fromService;
3654
3707
  }
3655
3708
  } catch {
3656
3709
  }
3657
3710
  if (code === null) {
3658
- let regItem = this.lookupArtifactItem(request.type, request.name) ?? this.engine.registry.getItem(request.type, request.name);
3711
+ let regItem = this.lookupArtifactItem(request.type, request.name, request.packageId) ?? this.engine.registry.getItem(request.type, request.name, request.packageId);
3659
3712
  if (regItem === void 0) {
3660
3713
  const alt = import_shared4.PLURAL_TO_SINGULAR[request.type] ?? import_shared4.SINGULAR_TO_PLURAL[request.type];
3661
- if (alt) regItem = this.engine.registry.getItem(alt, request.name);
3714
+ if (alt) regItem = this.engine.registry.getItem(alt, request.name, request.packageId);
3662
3715
  }
3663
3716
  if (regItem !== void 0) code = regItem;
3664
3717
  }
@@ -3666,20 +3719,28 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3666
3719
  let overlayScope = null;
3667
3720
  try {
3668
3721
  const findOverlay = async (oid) => {
3669
- const where = {
3670
- type: request.type,
3671
- name: request.name,
3672
- state: "active",
3673
- organization_id: oid
3722
+ const lookup = async (t) => {
3723
+ const base = {
3724
+ type: t,
3725
+ name: request.name,
3726
+ state: "active",
3727
+ organization_id: oid
3728
+ };
3729
+ if (request.packageId) {
3730
+ const scoped = await this.engine.findOne("sys_metadata", {
3731
+ where: { ...base, package_id: request.packageId }
3732
+ });
3733
+ if (scoped) return scoped;
3734
+ return await this.engine.findOne("sys_metadata", {
3735
+ where: { ...base, package_id: null }
3736
+ });
3737
+ }
3738
+ return await this.engine.findOne("sys_metadata", { where: base });
3674
3739
  };
3675
- let rec = await this.engine.findOne("sys_metadata", { where });
3740
+ let rec = await lookup(request.type);
3676
3741
  if (!rec) {
3677
3742
  const alt = import_shared4.PLURAL_TO_SINGULAR[request.type] ?? import_shared4.SINGULAR_TO_PLURAL[request.type];
3678
- if (alt) {
3679
- rec = await this.engine.findOne("sys_metadata", {
3680
- where: { ...where, type: alt }
3681
- });
3682
- }
3743
+ if (alt) rec = await lookup(alt);
3683
3744
  }
3684
3745
  return rec;
3685
3746
  };
@@ -4711,15 +4772,15 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
4711
4772
  * type and its singular/plural twin. Returns `undefined` when the
4712
4773
  * registry is unavailable or the item is not artifact-backed.
4713
4774
  */
4714
- lookupArtifactItem(type, name) {
4775
+ lookupArtifactItem(type, name, currentPackageId) {
4715
4776
  const registry = this.engine?.registry;
4716
4777
  if (!registry) return void 0;
4717
4778
  const singular = import_shared4.PLURAL_TO_SINGULAR[type] ?? type;
4718
4779
  if (typeof registry.getArtifactItem === "function") {
4719
- return registry.getArtifactItem(singular, name) ?? registry.getArtifactItem(type, name);
4780
+ return registry.getArtifactItem(singular, name, currentPackageId) ?? registry.getArtifactItem(type, name, currentPackageId);
4720
4781
  }
4721
4782
  if (typeof registry.getItem !== "function") return void 0;
4722
- const item = registry.getItem(singular, name) ?? registry.getItem(type, name);
4783
+ const item = registry.getItem(singular, name, currentPackageId) ?? registry.getItem(type, name, currentPackageId);
4723
4784
  if (!item || !item._packageId || item._packageId === "sys_metadata") {
4724
4785
  return void 0;
4725
4786
  }
@@ -5085,7 +5146,10 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
5085
5146
  if (request.parentVersion !== void 0) {
5086
5147
  parentVersion = request.parentVersion;
5087
5148
  } else {
5088
- const current = await repo.get(ref, { state: mode === "draft" ? "draft" : "active" });
5149
+ const current = await repo.get(ref, {
5150
+ state: mode === "draft" ? "draft" : "active",
5151
+ packageId: request.packageId ?? null
5152
+ });
5089
5153
  parentVersion = current?.hash ?? null;
5090
5154
  }
5091
5155
  try {
@@ -9507,10 +9571,16 @@ var MetadataFacade = class {
9507
9571
  }
9508
9572
  }
9509
9573
  /**
9510
- * Get a metadata item by type and name
9511
- */
9512
- async get(type, name) {
9513
- const item = this.registry.getItem(type, name);
9574
+ * Get a metadata item by type and name.
9575
+ *
9576
+ * `currentPackageId` (ADR-0048) opts into package-scoped resolution: when two
9577
+ * installed packages ship an item of the same `type`/`name`, the registry
9578
+ * prefers the one owned by `currentPackageId` (composite key
9579
+ * `${packageId}:${name}`) before falling back to first-match. Omit it for the
9580
+ * legacy context-free lookup.
9581
+ */
9582
+ async get(type, name, currentPackageId) {
9583
+ const item = this.registry.getItem(type, name, currentPackageId);
9514
9584
  return item?.content ?? item;
9515
9585
  }
9516
9586
  /**
@@ -9584,6 +9654,7 @@ var ObjectQLPlugin = class {
9584
9654
  */
9585
9655
  this.startupTimeout = 12e4;
9586
9656
  this.skipSchemaSync = false;
9657
+ this.hydrateMetadataFromDb = false;
9587
9658
  /** Unsubscribe handles for metadata-event subscriptions (ADR-0008 PR-7). */
9588
9659
  this.metadataUnsubscribes = [];
9589
9660
  this.init = async (ctx) => {
@@ -9708,7 +9779,7 @@ var ObjectQLPlugin = class {
9708
9779
  } else {
9709
9780
  await this.syncRegisteredSchemas(ctx);
9710
9781
  }
9711
- if (this.environmentId === void 0) {
9782
+ if (this.environmentId === void 0 || this.hydrateMetadataFromDb) {
9712
9783
  await this.restoreMetadataFromDb(ctx);
9713
9784
  } else {
9714
9785
  ctx.logger.info("Project kernel \u2014 skipping sys_metadata hydration (metadata sourced from artifact)");
@@ -9750,6 +9821,7 @@ var ObjectQLPlugin = class {
9750
9821
  this.startupTimeout = opts.startupTimeout;
9751
9822
  }
9752
9823
  this.skipSchemaSync = typeof opts.skipSchemaSync === "boolean" ? opts.skipSchemaSync : process.env.OS_SKIP_SCHEMA_SYNC === "1";
9824
+ this.hydrateMetadataFromDb = opts.hydrateMetadataFromDb === true;
9753
9825
  }
9754
9826
  /**
9755
9827
  * Subscribe to `object` metadata events from the metadata service and