@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.mjs CHANGED
@@ -310,6 +310,19 @@ var init_seed_loader = __esm({
310
310
  if (records && records.length > 0) {
311
311
  return String(records[0].id || records[0]._id);
312
312
  }
313
+ if (targetField !== "id") {
314
+ const byId = { id: value };
315
+ if (organizationId) byId.organization_id = organizationId;
316
+ const idMatch = await this.engine.find(targetObject, {
317
+ where: byId,
318
+ fields: ["id"],
319
+ limit: 1,
320
+ context: { isSystem: true }
321
+ });
322
+ if (idMatch && idMatch.length > 0) {
323
+ return String(idMatch[0].id || idMatch[0]._id);
324
+ }
325
+ }
313
326
  } catch {
314
327
  }
315
328
  return null;
@@ -886,18 +899,16 @@ function applySystemFields(schema, opts) {
886
899
  fields: { ...additions, ...schema.fields ?? {} }
887
900
  };
888
901
  }
889
- var SYS_METADATA_OWNER = "sys_metadata";
890
- function isRealPackage(pkg) {
891
- return typeof pkg === "string" && pkg.length > 0 && pkg !== SYS_METADATA_OWNER;
902
+ function isShareableNamespace(ns) {
903
+ return RESERVED_NAMESPACES.has(ns) || ns === "sys";
892
904
  }
893
- var MetadataCollisionError = class extends Error {
894
- constructor(type, name, existingPackageId, incomingPackageId) {
905
+ var NamespaceConflictError = class extends Error {
906
+ constructor(namespace, existingPackageId, incomingPackageId) {
895
907
  super(
896
- `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.`
908
+ `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.`
897
909
  );
898
- this.name = "MetadataCollisionError";
899
- this.type = type;
900
- this.name_ = name;
910
+ this.name = "NamespaceConflictError";
911
+ this.namespace = namespace;
901
912
  this.existingPackageId = existingPackageId;
902
913
  this.incomingPackageId = incomingPackageId;
903
914
  }
@@ -1253,17 +1264,6 @@ var SchemaRegistry = class {
1253
1264
  if (collection.has(storageKey)) {
1254
1265
  this.log(`[Registry] Overwriting ${type}: ${storageKey}`);
1255
1266
  }
1256
- if (isRealPackage(packageId)) {
1257
- const conflictOwner = this.findOtherPackageOwner(collection, baseName, packageId);
1258
- if (conflictOwner) {
1259
- const err = new MetadataCollisionError(type, baseName, conflictOwner, packageId);
1260
- if (this.collisionPolicy === "warn") {
1261
- console.warn(`[Registry] ${err.message}`);
1262
- } else {
1263
- throw err;
1264
- }
1265
- }
1266
- }
1267
1267
  if (packageId && collection.has(baseName)) {
1268
1268
  const dbOnly = collection.get(baseName);
1269
1269
  if (dbOnly && !dbOnly._packageId) {
@@ -1275,22 +1275,6 @@ var SchemaRegistry = class {
1275
1275
  collection.set(storageKey, item);
1276
1276
  this.log(`[Registry] Registered ${type}: ${storageKey}`);
1277
1277
  }
1278
- /**
1279
- * Find a code package OTHER than `incoming` that already owns `baseName` in
1280
- * `collection` (ADR-0048 cross-package collision detection). Scans the live
1281
- * collection — like {@link getItem} / {@link unregisterItem} — so it always
1282
- * reflects current state with no parallel index to drift across
1283
- * reset/unregister. Returns the conflicting owner's package id, or undefined
1284
- * when the name is free or only held by the same package / a runtime overlay.
1285
- */
1286
- findOtherPackageOwner(collection, baseName, incoming) {
1287
- for (const [key, item] of collection) {
1288
- if (key !== baseName && !key.endsWith(`:${baseName}`)) continue;
1289
- const owner = item?._packageId;
1290
- if (isRealPackage(owner) && owner !== incoming) return owner;
1291
- }
1292
- return void 0;
1293
- }
1294
1278
  /**
1295
1279
  * Validate Metadata against Spec Zod Schemas
1296
1280
  */
@@ -1333,9 +1317,23 @@ var SchemaRegistry = class {
1333
1317
  console.warn(`[Registry] Attempted to unregister non-existent ${type}: ${name}`);
1334
1318
  }
1335
1319
  /**
1336
- * Universal Get Method
1320
+ * Universal Get Method.
1321
+ *
1322
+ * ADR-0048 §3.3 — *package-scoped* resolution. When `currentPackageId` is
1323
+ * given (the package the caller is resolving within — known from the route /
1324
+ * `activeApp._packageId`), a bare name resolves to *that package's* item
1325
+ * before any cross-package fallback, so two packages shipping e.g.
1326
+ * `page/home` no longer resolve by registration order (first-match-wins).
1327
+ * Because package ids are globally unique this is unambiguous. Omitting
1328
+ * `currentPackageId` preserves the legacy resolution exactly (backward
1329
+ * compatible) and is best-effort: it returns the first match.
1330
+ *
1331
+ * Precedence (highest first):
1332
+ * 1. bare-key runtime/DB overlay (ADR-0005 sanctioned override) — unchanged
1333
+ * 2. the `currentPackageId` composite entry (prefer-local)
1334
+ * 3. first composite match (legacy first-registered-wins fallback)
1337
1335
  */
1338
- getItem(type, name) {
1336
+ getItem(type, name, currentPackageId) {
1339
1337
  if (type === "object" || type === "objects") {
1340
1338
  return this.getObject(name);
1341
1339
  }
@@ -1343,6 +1341,10 @@ var SchemaRegistry = class {
1343
1341
  if (!collection) return void 0;
1344
1342
  const direct = collection.get(name);
1345
1343
  if (direct) return direct;
1344
+ if (currentPackageId) {
1345
+ const local = collection.get(`${currentPackageId}:${name}`);
1346
+ if (local) return local;
1347
+ }
1346
1348
  for (const [key, item] of collection) {
1347
1349
  if (key.endsWith(`:${name}`)) {
1348
1350
  return item;
@@ -1364,13 +1366,17 @@ var SchemaRegistry = class {
1364
1366
  * it (that masking is exactly the "registry pollution" bug where a
1365
1367
  * locked app's `_lock` read back as undefined after a PUT+GET).
1366
1368
  */
1367
- getArtifactItem(type, name) {
1369
+ getArtifactItem(type, name, currentPackageId) {
1368
1370
  if (type === "object" || type === "objects") {
1369
1371
  const obj = this.getObject(name);
1370
1372
  return obj && obj._packageId && obj._packageId !== "sys_metadata" ? obj : void 0;
1371
1373
  }
1372
1374
  const collection = this.metadata.get(type);
1373
1375
  if (!collection) return void 0;
1376
+ if (currentPackageId) {
1377
+ const local = collection.get(`${currentPackageId}:${name}`);
1378
+ if (local && local._packageId && local._packageId !== "sys_metadata") return local;
1379
+ }
1374
1380
  for (const [key, item] of collection) {
1375
1381
  if (key !== name && key.endsWith(`:${name}`)) {
1376
1382
  const it = item;
@@ -1454,6 +1460,20 @@ var SchemaRegistry = class {
1454
1460
  // Package Management
1455
1461
  // ==========================================
1456
1462
  installPackage(manifest, settings) {
1463
+ if (manifest.namespace && !isShareableNamespace(manifest.namespace)) {
1464
+ const conflictOwner = this.getNamespaceOwners(manifest.namespace).find(
1465
+ (owner) => owner !== manifest.id
1466
+ );
1467
+ if (conflictOwner) {
1468
+ if (this.collisionPolicy === "warn") {
1469
+ console.warn(
1470
+ `[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.`
1471
+ );
1472
+ } else {
1473
+ throw new NamespaceConflictError(manifest.namespace, conflictOwner, manifest.id);
1474
+ }
1475
+ }
1476
+ }
1457
1477
  const now = (/* @__PURE__ */ new Date()).toISOString();
1458
1478
  const disabled = this.initialDisabledPackageIds.has(manifest.id);
1459
1479
  const pkg = {
@@ -1531,8 +1551,8 @@ var SchemaRegistry = class {
1531
1551
  registerApp(app, packageId) {
1532
1552
  this.registerItem("app", app, "name", packageId);
1533
1553
  }
1534
- getApp(name) {
1535
- const app = this.getItem("app", name);
1554
+ getApp(name, currentPackageId) {
1555
+ const app = this.getItem("app", name, currentPackageId);
1536
1556
  if (!app) return app;
1537
1557
  return this.applyNavContributions(app);
1538
1558
  }
@@ -1752,7 +1772,7 @@ var SysMetadataRepository = class {
1752
1772
  this.assertOpen();
1753
1773
  const state = opts?.state ?? "active";
1754
1774
  const row = await this.engine.findOne("sys_metadata", {
1755
- where: this.whereFor(ref, state)
1775
+ where: this.whereFor(ref, state, opts && "packageId" in opts ? opts.packageId ?? null : void 0)
1756
1776
  });
1757
1777
  if (!row) return null;
1758
1778
  return this.rowToItem(ref, row);
@@ -1801,7 +1821,7 @@ var SysMetadataRepository = class {
1801
1821
  const hash = hashSpec(body);
1802
1822
  const result = await this.withTxn(async (ctx) => {
1803
1823
  const existing = await this.engine.findOne("sys_metadata", {
1804
- where: this.whereFor(ref, state),
1824
+ where: this.whereFor(ref, state, opts.packageId ?? null),
1805
1825
  context: ctx
1806
1826
  });
1807
1827
  const existingHash = existing?.checksum ?? null;
@@ -2005,8 +2025,10 @@ var SysMetadataRepository = class {
2005
2025
  */
2006
2026
  async promoteDraft(ref, opts) {
2007
2027
  this.assertOpen();
2008
- const draft = await this.get(ref, { state: "draft" });
2009
- if (!draft) {
2028
+ const draftRow = await this.engine.findOne("sys_metadata", {
2029
+ where: this.whereFor(ref, "draft")
2030
+ });
2031
+ if (!draftRow) {
2010
2032
  const err = new Error(
2011
2033
  `[no_draft] No pending draft exists for ${ref.type}/${ref.name} \u2014 nothing to publish.`
2012
2034
  );
@@ -2014,7 +2036,9 @@ var SysMetadataRepository = class {
2014
2036
  err.status = 404;
2015
2037
  throw err;
2016
2038
  }
2017
- const currentActive = await this.get(ref, { state: "active" });
2039
+ const draftPackageId = draftRow.package_id ?? null;
2040
+ const draft = this.rowToItem(ref, draftRow);
2041
+ const currentActive = await this.get(ref, { state: "active", packageId: draftPackageId });
2018
2042
  const result = await this.put(ref, draft.body, {
2019
2043
  parentVersion: currentActive?.hash ?? null,
2020
2044
  actor: opts.actor,
@@ -2022,7 +2046,8 @@ var SysMetadataRepository = class {
2022
2046
  message: opts.message ?? `publish draft (hash ${draft.hash})`,
2023
2047
  intent: opts.intent ?? "override-artifact",
2024
2048
  state: "active",
2025
- opType: "publish"
2049
+ opType: "publish",
2050
+ packageId: draftPackageId
2026
2051
  });
2027
2052
  try {
2028
2053
  await this.delete(ref, {
@@ -2115,10 +2140,15 @@ var SysMetadataRepository = class {
2115
2140
  */
2116
2141
  async listDrafts(filter) {
2117
2142
  this.assertOpen();
2118
- const where = {
2119
- organization_id: this.organizationId,
2120
- state: "draft"
2121
- };
2143
+ const where = { state: "draft" };
2144
+ if (this.organizationId != null) {
2145
+ where.$or = [
2146
+ { organization_id: this.organizationId },
2147
+ { organization_id: null }
2148
+ ];
2149
+ } else {
2150
+ where.organization_id = null;
2151
+ }
2122
2152
  if (filter?.type) where.type = filter.type;
2123
2153
  if (filter?.packageId) where.package_id = filter.packageId;
2124
2154
  const rows = await this.engine.find("sys_metadata", { where });
@@ -2285,13 +2315,15 @@ var SysMetadataRepository = class {
2285
2315
  err.status = 403;
2286
2316
  throw err;
2287
2317
  }
2288
- whereFor(ref, state = "active") {
2289
- return {
2318
+ whereFor(ref, state = "active", packageId) {
2319
+ const where = {
2290
2320
  type: ref.type,
2291
2321
  name: ref.name,
2292
2322
  organization_id: this.organizationId,
2293
2323
  state
2294
2324
  };
2325
+ if (packageId !== void 0) where.package_id = packageId;
2326
+ return where;
2295
2327
  }
2296
2328
  fullRef(ref) {
2297
2329
  return {
@@ -2983,8 +3015,8 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
2983
3015
  await exec("DROP INDEX IF EXISTS idx_sys_metadata_overlay_active");
2984
3016
  } catch {
2985
3017
  }
2986
- const partialSql = "CREATE UNIQUE INDEX IF NOT EXISTS idx_sys_metadata_overlay_active ON sys_metadata (type, name, organization_id) WHERE state = 'active'";
2987
- const fallbackSql = "CREATE INDEX IF NOT EXISTS idx_sys_metadata_overlay_active ON sys_metadata (type, name, organization_id)";
3018
+ 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'";
3019
+ const fallbackSql = "CREATE INDEX IF NOT EXISTS idx_sys_metadata_overlay_active ON sys_metadata (type, name, organization_id, package_id)";
2988
3020
  try {
2989
3021
  await exec(partialSql);
2990
3022
  } catch (err) {
@@ -2996,7 +3028,11 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
2996
3028
  }
2997
3029
  }
2998
3030
  }
2999
- const draftPartialSql = "CREATE UNIQUE INDEX IF NOT EXISTS idx_sys_metadata_overlay_draft ON sys_metadata (type, name, organization_id) WHERE state = 'draft'";
3031
+ try {
3032
+ await exec("DROP INDEX IF EXISTS idx_sys_metadata_overlay_draft");
3033
+ } catch {
3034
+ }
3035
+ 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'";
3000
3036
  try {
3001
3037
  await exec(draftPartialSql);
3002
3038
  } catch (err) {
@@ -3004,7 +3040,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3004
3040
  if (/partial|where clause|syntax/i.test(msg)) {
3005
3041
  try {
3006
3042
  await exec(
3007
- "CREATE INDEX IF NOT EXISTS idx_sys_metadata_overlay_draft ON sys_metadata (type, name, organization_id)"
3043
+ "CREATE INDEX IF NOT EXISTS idx_sys_metadata_overlay_draft ON sys_metadata (type, name, organization_id, package_id)"
3008
3044
  );
3009
3045
  } catch {
3010
3046
  }
@@ -3405,7 +3441,8 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3405
3441
  items.map((it) => {
3406
3442
  const a = this.lookupArtifactItem(
3407
3443
  request.type,
3408
- it?.name
3444
+ it?.name,
3445
+ packageId ?? it?._packageId
3409
3446
  );
3410
3447
  return mergeArtifactProtection(it, a);
3411
3448
  })
@@ -3419,16 +3456,28 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3419
3456
  if (request.previewDrafts && readState !== "draft") {
3420
3457
  try {
3421
3458
  const findDraft = async (oid) => {
3422
- const rec = await this.engine.findOne("sys_metadata", {
3423
- where: { type: request.type, name: request.name, state: "draft", organization_id: oid }
3424
- });
3459
+ const lookup = async (t) => {
3460
+ const base = {
3461
+ type: t,
3462
+ name: request.name,
3463
+ state: "draft",
3464
+ organization_id: oid
3465
+ };
3466
+ if (request.packageId) {
3467
+ const scoped = await this.engine.findOne("sys_metadata", {
3468
+ where: { ...base, package_id: request.packageId }
3469
+ });
3470
+ if (scoped) return scoped;
3471
+ return await this.engine.findOne("sys_metadata", {
3472
+ where: { ...base, package_id: null }
3473
+ });
3474
+ }
3475
+ return await this.engine.findOne("sys_metadata", { where: base });
3476
+ };
3477
+ const rec = await lookup(request.type);
3425
3478
  if (rec) return rec;
3426
3479
  const alt = PLURAL_TO_SINGULAR3[request.type] ?? SINGULAR_TO_PLURAL2[request.type];
3427
- if (alt) {
3428
- return await this.engine.findOne("sys_metadata", {
3429
- where: { type: alt, name: request.name, state: "draft", organization_id: oid }
3430
- });
3431
- }
3480
+ if (alt) return await lookup(alt);
3432
3481
  return void 0;
3433
3482
  };
3434
3483
  const draftRec = (orgId ? await findDraft(orgId) : void 0) ?? await findDraft(null);
@@ -3446,24 +3495,28 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3446
3495
  }
3447
3496
  try {
3448
3497
  const findOverlay = async (oid) => {
3449
- const where = {
3450
- type: request.type,
3451
- name: request.name,
3452
- state: readState,
3453
- organization_id: oid
3454
- };
3455
- const rec = await this.engine.findOne("sys_metadata", { where });
3456
- if (rec) return rec;
3457
- const alt = PLURAL_TO_SINGULAR3[request.type] ?? SINGULAR_TO_PLURAL2[request.type];
3458
- if (alt) {
3459
- const altWhere = {
3460
- type: alt,
3498
+ const lookup = async (t) => {
3499
+ const base = {
3500
+ type: t,
3461
3501
  name: request.name,
3462
3502
  state: readState,
3463
3503
  organization_id: oid
3464
3504
  };
3465
- return await this.engine.findOne("sys_metadata", { where: altWhere });
3466
- }
3505
+ if (request.packageId) {
3506
+ const scoped = await this.engine.findOne("sys_metadata", {
3507
+ where: { ...base, package_id: request.packageId }
3508
+ });
3509
+ if (scoped) return scoped;
3510
+ return await this.engine.findOne("sys_metadata", {
3511
+ where: { ...base, package_id: null }
3512
+ });
3513
+ }
3514
+ return await this.engine.findOne("sys_metadata", { where: base });
3515
+ };
3516
+ const rec = await lookup(request.type);
3517
+ if (rec) return rec;
3518
+ const alt = PLURAL_TO_SINGULAR3[request.type] ?? SINGULAR_TO_PLURAL2[request.type];
3519
+ if (alt) return await lookup(alt);
3467
3520
  return void 0;
3468
3521
  };
3469
3522
  const record = (orgId ? await findOverlay(orgId) : void 0) ?? await findOverlay(null);
@@ -3492,13 +3545,13 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3492
3545
  const services = this.getServicesRegistry?.();
3493
3546
  const metadataService = services?.get("metadata");
3494
3547
  if (metadataService && typeof metadataService.get === "function") {
3495
- const fromService = await metadataService.get(request.type, request.name);
3548
+ const fromService = await metadataService.get(request.type, request.name, request.packageId);
3496
3549
  if (fromService !== void 0 && fromService !== null) {
3497
3550
  item = fromService;
3498
3551
  } else {
3499
3552
  const alt = PLURAL_TO_SINGULAR3[request.type] ?? SINGULAR_TO_PLURAL2[request.type];
3500
3553
  if (alt) {
3501
- const altFromService = await metadataService.get(alt, request.name);
3554
+ const altFromService = await metadataService.get(alt, request.name, request.packageId);
3502
3555
  if (altFromService !== void 0 && altFromService !== null) {
3503
3556
  item = altFromService;
3504
3557
  }
@@ -3509,16 +3562,16 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3509
3562
  }
3510
3563
  }
3511
3564
  if (item === void 0) {
3512
- item = this.engine.registry.getItem(request.type, request.name);
3565
+ item = this.engine.registry.getItem(request.type, request.name, request.packageId);
3513
3566
  if (item === void 0) {
3514
3567
  const alt = PLURAL_TO_SINGULAR3[request.type] ?? SINGULAR_TO_PLURAL2[request.type];
3515
- if (alt) item = this.engine.registry.getItem(alt, request.name);
3568
+ if (alt) item = this.engine.registry.getItem(alt, request.name, request.packageId);
3516
3569
  }
3517
3570
  }
3518
3571
  if ((request.type === "app" || request.type === "apps") && item) {
3519
3572
  item = this.engine.registry.applyNavContributions(item);
3520
3573
  }
3521
- const artifactItem = this.lookupArtifactItem(request.type, request.name);
3574
+ const artifactItem = this.lookupArtifactItem(request.type, request.name, request.packageId);
3522
3575
  let decorated = decorateMetadataItem(
3523
3576
  request.type,
3524
3577
  mergeArtifactProtection(item, artifactItem)
@@ -3586,20 +3639,20 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3586
3639
  const services = this.getServicesRegistry?.();
3587
3640
  const metadataService = services?.get("metadata");
3588
3641
  if (metadataService && typeof metadataService.get === "function") {
3589
- let fromService = await metadataService.get(request.type, request.name);
3642
+ let fromService = await metadataService.get(request.type, request.name, request.packageId);
3590
3643
  if (fromService === void 0 || fromService === null) {
3591
3644
  const alt = PLURAL_TO_SINGULAR3[request.type] ?? SINGULAR_TO_PLURAL2[request.type];
3592
- if (alt) fromService = await metadataService.get(alt, request.name);
3645
+ if (alt) fromService = await metadataService.get(alt, request.name, request.packageId);
3593
3646
  }
3594
3647
  if (fromService !== void 0 && fromService !== null) code = fromService;
3595
3648
  }
3596
3649
  } catch {
3597
3650
  }
3598
3651
  if (code === null) {
3599
- let regItem = this.lookupArtifactItem(request.type, request.name) ?? this.engine.registry.getItem(request.type, request.name);
3652
+ let regItem = this.lookupArtifactItem(request.type, request.name, request.packageId) ?? this.engine.registry.getItem(request.type, request.name, request.packageId);
3600
3653
  if (regItem === void 0) {
3601
3654
  const alt = PLURAL_TO_SINGULAR3[request.type] ?? SINGULAR_TO_PLURAL2[request.type];
3602
- if (alt) regItem = this.engine.registry.getItem(alt, request.name);
3655
+ if (alt) regItem = this.engine.registry.getItem(alt, request.name, request.packageId);
3603
3656
  }
3604
3657
  if (regItem !== void 0) code = regItem;
3605
3658
  }
@@ -3607,20 +3660,28 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3607
3660
  let overlayScope = null;
3608
3661
  try {
3609
3662
  const findOverlay = async (oid) => {
3610
- const where = {
3611
- type: request.type,
3612
- name: request.name,
3613
- state: "active",
3614
- organization_id: oid
3663
+ const lookup = async (t) => {
3664
+ const base = {
3665
+ type: t,
3666
+ name: request.name,
3667
+ state: "active",
3668
+ organization_id: oid
3669
+ };
3670
+ if (request.packageId) {
3671
+ const scoped = await this.engine.findOne("sys_metadata", {
3672
+ where: { ...base, package_id: request.packageId }
3673
+ });
3674
+ if (scoped) return scoped;
3675
+ return await this.engine.findOne("sys_metadata", {
3676
+ where: { ...base, package_id: null }
3677
+ });
3678
+ }
3679
+ return await this.engine.findOne("sys_metadata", { where: base });
3615
3680
  };
3616
- let rec = await this.engine.findOne("sys_metadata", { where });
3681
+ let rec = await lookup(request.type);
3617
3682
  if (!rec) {
3618
3683
  const alt = PLURAL_TO_SINGULAR3[request.type] ?? SINGULAR_TO_PLURAL2[request.type];
3619
- if (alt) {
3620
- rec = await this.engine.findOne("sys_metadata", {
3621
- where: { ...where, type: alt }
3622
- });
3623
- }
3684
+ if (alt) rec = await lookup(alt);
3624
3685
  }
3625
3686
  return rec;
3626
3687
  };
@@ -4652,15 +4713,15 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
4652
4713
  * type and its singular/plural twin. Returns `undefined` when the
4653
4714
  * registry is unavailable or the item is not artifact-backed.
4654
4715
  */
4655
- lookupArtifactItem(type, name) {
4716
+ lookupArtifactItem(type, name, currentPackageId) {
4656
4717
  const registry = this.engine?.registry;
4657
4718
  if (!registry) return void 0;
4658
4719
  const singular = PLURAL_TO_SINGULAR3[type] ?? type;
4659
4720
  if (typeof registry.getArtifactItem === "function") {
4660
- return registry.getArtifactItem(singular, name) ?? registry.getArtifactItem(type, name);
4721
+ return registry.getArtifactItem(singular, name, currentPackageId) ?? registry.getArtifactItem(type, name, currentPackageId);
4661
4722
  }
4662
4723
  if (typeof registry.getItem !== "function") return void 0;
4663
- const item = registry.getItem(singular, name) ?? registry.getItem(type, name);
4724
+ const item = registry.getItem(singular, name, currentPackageId) ?? registry.getItem(type, name, currentPackageId);
4664
4725
  if (!item || !item._packageId || item._packageId === "sys_metadata") {
4665
4726
  return void 0;
4666
4727
  }
@@ -5026,7 +5087,10 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
5026
5087
  if (request.parentVersion !== void 0) {
5027
5088
  parentVersion = request.parentVersion;
5028
5089
  } else {
5029
- const current = await repo.get(ref, { state: mode === "draft" ? "draft" : "active" });
5090
+ const current = await repo.get(ref, {
5091
+ state: mode === "draft" ? "draft" : "active",
5092
+ packageId: request.packageId ?? null
5093
+ });
5030
5094
  parentVersion = current?.hash ?? null;
5031
5095
  }
5032
5096
  try {
@@ -9448,10 +9512,16 @@ var MetadataFacade = class {
9448
9512
  }
9449
9513
  }
9450
9514
  /**
9451
- * Get a metadata item by type and name
9452
- */
9453
- async get(type, name) {
9454
- const item = this.registry.getItem(type, name);
9515
+ * Get a metadata item by type and name.
9516
+ *
9517
+ * `currentPackageId` (ADR-0048) opts into package-scoped resolution: when two
9518
+ * installed packages ship an item of the same `type`/`name`, the registry
9519
+ * prefers the one owned by `currentPackageId` (composite key
9520
+ * `${packageId}:${name}`) before falling back to first-match. Omit it for the
9521
+ * legacy context-free lookup.
9522
+ */
9523
+ async get(type, name, currentPackageId) {
9524
+ const item = this.registry.getItem(type, name, currentPackageId);
9455
9525
  return item?.content ?? item;
9456
9526
  }
9457
9527
  /**
@@ -9530,6 +9600,7 @@ var ObjectQLPlugin = class {
9530
9600
  */
9531
9601
  this.startupTimeout = 12e4;
9532
9602
  this.skipSchemaSync = false;
9603
+ this.hydrateMetadataFromDb = false;
9533
9604
  /** Unsubscribe handles for metadata-event subscriptions (ADR-0008 PR-7). */
9534
9605
  this.metadataUnsubscribes = [];
9535
9606
  this.init = async (ctx) => {
@@ -9654,7 +9725,7 @@ var ObjectQLPlugin = class {
9654
9725
  } else {
9655
9726
  await this.syncRegisteredSchemas(ctx);
9656
9727
  }
9657
- if (this.environmentId === void 0) {
9728
+ if (this.environmentId === void 0 || this.hydrateMetadataFromDb) {
9658
9729
  await this.restoreMetadataFromDb(ctx);
9659
9730
  } else {
9660
9731
  ctx.logger.info("Project kernel \u2014 skipping sys_metadata hydration (metadata sourced from artifact)");
@@ -9696,6 +9767,7 @@ var ObjectQLPlugin = class {
9696
9767
  this.startupTimeout = opts.startupTimeout;
9697
9768
  }
9698
9769
  this.skipSchemaSync = typeof opts.skipSchemaSync === "boolean" ? opts.skipSchemaSync : process.env.OS_SKIP_SCHEMA_SYNC === "1";
9770
+ this.hydrateMetadataFromDb = opts.hydrateMetadataFromDb === true;
9699
9771
  }
9700
9772
  /**
9701
9773
  * Subscribe to `object` metadata events from the metadata service and