@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.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;
@@ -2285,13 +2305,15 @@ var SysMetadataRepository = class {
2285
2305
  err.status = 403;
2286
2306
  throw err;
2287
2307
  }
2288
- whereFor(ref, state = "active") {
2289
- return {
2308
+ whereFor(ref, state = "active", packageId) {
2309
+ const where = {
2290
2310
  type: ref.type,
2291
2311
  name: ref.name,
2292
2312
  organization_id: this.organizationId,
2293
2313
  state
2294
2314
  };
2315
+ if (packageId !== void 0) where.package_id = packageId;
2316
+ return where;
2295
2317
  }
2296
2318
  fullRef(ref) {
2297
2319
  return {
@@ -2983,8 +3005,8 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
2983
3005
  await exec("DROP INDEX IF EXISTS idx_sys_metadata_overlay_active");
2984
3006
  } catch {
2985
3007
  }
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)";
3008
+ 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'";
3009
+ const fallbackSql = "CREATE INDEX IF NOT EXISTS idx_sys_metadata_overlay_active ON sys_metadata (type, name, organization_id, package_id)";
2988
3010
  try {
2989
3011
  await exec(partialSql);
2990
3012
  } catch (err) {
@@ -2996,7 +3018,11 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
2996
3018
  }
2997
3019
  }
2998
3020
  }
2999
- const draftPartialSql = "CREATE UNIQUE INDEX IF NOT EXISTS idx_sys_metadata_overlay_draft ON sys_metadata (type, name, organization_id) WHERE state = 'draft'";
3021
+ try {
3022
+ await exec("DROP INDEX IF EXISTS idx_sys_metadata_overlay_draft");
3023
+ } catch {
3024
+ }
3025
+ 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
3026
  try {
3001
3027
  await exec(draftPartialSql);
3002
3028
  } catch (err) {
@@ -3004,7 +3030,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3004
3030
  if (/partial|where clause|syntax/i.test(msg)) {
3005
3031
  try {
3006
3032
  await exec(
3007
- "CREATE INDEX IF NOT EXISTS idx_sys_metadata_overlay_draft ON sys_metadata (type, name, organization_id)"
3033
+ "CREATE INDEX IF NOT EXISTS idx_sys_metadata_overlay_draft ON sys_metadata (type, name, organization_id, package_id)"
3008
3034
  );
3009
3035
  } catch {
3010
3036
  }
@@ -3405,7 +3431,8 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3405
3431
  items.map((it) => {
3406
3432
  const a = this.lookupArtifactItem(
3407
3433
  request.type,
3408
- it?.name
3434
+ it?.name,
3435
+ packageId ?? it?._packageId
3409
3436
  );
3410
3437
  return mergeArtifactProtection(it, a);
3411
3438
  })
@@ -3419,16 +3446,28 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3419
3446
  if (request.previewDrafts && readState !== "draft") {
3420
3447
  try {
3421
3448
  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
- });
3449
+ const lookup = async (t) => {
3450
+ const base = {
3451
+ type: t,
3452
+ name: request.name,
3453
+ state: "draft",
3454
+ organization_id: oid
3455
+ };
3456
+ if (request.packageId) {
3457
+ const scoped = await this.engine.findOne("sys_metadata", {
3458
+ where: { ...base, package_id: request.packageId }
3459
+ });
3460
+ if (scoped) return scoped;
3461
+ return await this.engine.findOne("sys_metadata", {
3462
+ where: { ...base, package_id: null }
3463
+ });
3464
+ }
3465
+ return await this.engine.findOne("sys_metadata", { where: base });
3466
+ };
3467
+ const rec = await lookup(request.type);
3425
3468
  if (rec) return rec;
3426
3469
  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
- }
3470
+ if (alt) return await lookup(alt);
3432
3471
  return void 0;
3433
3472
  };
3434
3473
  const draftRec = (orgId ? await findDraft(orgId) : void 0) ?? await findDraft(null);
@@ -3446,24 +3485,28 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3446
3485
  }
3447
3486
  try {
3448
3487
  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,
3488
+ const lookup = async (t) => {
3489
+ const base = {
3490
+ type: t,
3461
3491
  name: request.name,
3462
3492
  state: readState,
3463
3493
  organization_id: oid
3464
3494
  };
3465
- return await this.engine.findOne("sys_metadata", { where: altWhere });
3466
- }
3495
+ if (request.packageId) {
3496
+ const scoped = await this.engine.findOne("sys_metadata", {
3497
+ where: { ...base, package_id: request.packageId }
3498
+ });
3499
+ if (scoped) return scoped;
3500
+ return await this.engine.findOne("sys_metadata", {
3501
+ where: { ...base, package_id: null }
3502
+ });
3503
+ }
3504
+ return await this.engine.findOne("sys_metadata", { where: base });
3505
+ };
3506
+ const rec = await lookup(request.type);
3507
+ if (rec) return rec;
3508
+ const alt = PLURAL_TO_SINGULAR3[request.type] ?? SINGULAR_TO_PLURAL2[request.type];
3509
+ if (alt) return await lookup(alt);
3467
3510
  return void 0;
3468
3511
  };
3469
3512
  const record = (orgId ? await findOverlay(orgId) : void 0) ?? await findOverlay(null);
@@ -3492,13 +3535,13 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3492
3535
  const services = this.getServicesRegistry?.();
3493
3536
  const metadataService = services?.get("metadata");
3494
3537
  if (metadataService && typeof metadataService.get === "function") {
3495
- const fromService = await metadataService.get(request.type, request.name);
3538
+ const fromService = await metadataService.get(request.type, request.name, request.packageId);
3496
3539
  if (fromService !== void 0 && fromService !== null) {
3497
3540
  item = fromService;
3498
3541
  } else {
3499
3542
  const alt = PLURAL_TO_SINGULAR3[request.type] ?? SINGULAR_TO_PLURAL2[request.type];
3500
3543
  if (alt) {
3501
- const altFromService = await metadataService.get(alt, request.name);
3544
+ const altFromService = await metadataService.get(alt, request.name, request.packageId);
3502
3545
  if (altFromService !== void 0 && altFromService !== null) {
3503
3546
  item = altFromService;
3504
3547
  }
@@ -3509,16 +3552,16 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3509
3552
  }
3510
3553
  }
3511
3554
  if (item === void 0) {
3512
- item = this.engine.registry.getItem(request.type, request.name);
3555
+ item = this.engine.registry.getItem(request.type, request.name, request.packageId);
3513
3556
  if (item === void 0) {
3514
3557
  const alt = PLURAL_TO_SINGULAR3[request.type] ?? SINGULAR_TO_PLURAL2[request.type];
3515
- if (alt) item = this.engine.registry.getItem(alt, request.name);
3558
+ if (alt) item = this.engine.registry.getItem(alt, request.name, request.packageId);
3516
3559
  }
3517
3560
  }
3518
3561
  if ((request.type === "app" || request.type === "apps") && item) {
3519
3562
  item = this.engine.registry.applyNavContributions(item);
3520
3563
  }
3521
- const artifactItem = this.lookupArtifactItem(request.type, request.name);
3564
+ const artifactItem = this.lookupArtifactItem(request.type, request.name, request.packageId);
3522
3565
  let decorated = decorateMetadataItem(
3523
3566
  request.type,
3524
3567
  mergeArtifactProtection(item, artifactItem)
@@ -3586,20 +3629,20 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3586
3629
  const services = this.getServicesRegistry?.();
3587
3630
  const metadataService = services?.get("metadata");
3588
3631
  if (metadataService && typeof metadataService.get === "function") {
3589
- let fromService = await metadataService.get(request.type, request.name);
3632
+ let fromService = await metadataService.get(request.type, request.name, request.packageId);
3590
3633
  if (fromService === void 0 || fromService === null) {
3591
3634
  const alt = PLURAL_TO_SINGULAR3[request.type] ?? SINGULAR_TO_PLURAL2[request.type];
3592
- if (alt) fromService = await metadataService.get(alt, request.name);
3635
+ if (alt) fromService = await metadataService.get(alt, request.name, request.packageId);
3593
3636
  }
3594
3637
  if (fromService !== void 0 && fromService !== null) code = fromService;
3595
3638
  }
3596
3639
  } catch {
3597
3640
  }
3598
3641
  if (code === null) {
3599
- let regItem = this.lookupArtifactItem(request.type, request.name) ?? this.engine.registry.getItem(request.type, request.name);
3642
+ let regItem = this.lookupArtifactItem(request.type, request.name, request.packageId) ?? this.engine.registry.getItem(request.type, request.name, request.packageId);
3600
3643
  if (regItem === void 0) {
3601
3644
  const alt = PLURAL_TO_SINGULAR3[request.type] ?? SINGULAR_TO_PLURAL2[request.type];
3602
- if (alt) regItem = this.engine.registry.getItem(alt, request.name);
3645
+ if (alt) regItem = this.engine.registry.getItem(alt, request.name, request.packageId);
3603
3646
  }
3604
3647
  if (regItem !== void 0) code = regItem;
3605
3648
  }
@@ -3607,20 +3650,28 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3607
3650
  let overlayScope = null;
3608
3651
  try {
3609
3652
  const findOverlay = async (oid) => {
3610
- const where = {
3611
- type: request.type,
3612
- name: request.name,
3613
- state: "active",
3614
- organization_id: oid
3653
+ const lookup = async (t) => {
3654
+ const base = {
3655
+ type: t,
3656
+ name: request.name,
3657
+ state: "active",
3658
+ organization_id: oid
3659
+ };
3660
+ if (request.packageId) {
3661
+ const scoped = await this.engine.findOne("sys_metadata", {
3662
+ where: { ...base, package_id: request.packageId }
3663
+ });
3664
+ if (scoped) return scoped;
3665
+ return await this.engine.findOne("sys_metadata", {
3666
+ where: { ...base, package_id: null }
3667
+ });
3668
+ }
3669
+ return await this.engine.findOne("sys_metadata", { where: base });
3615
3670
  };
3616
- let rec = await this.engine.findOne("sys_metadata", { where });
3671
+ let rec = await lookup(request.type);
3617
3672
  if (!rec) {
3618
3673
  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
- }
3674
+ if (alt) rec = await lookup(alt);
3624
3675
  }
3625
3676
  return rec;
3626
3677
  };
@@ -4652,15 +4703,15 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
4652
4703
  * type and its singular/plural twin. Returns `undefined` when the
4653
4704
  * registry is unavailable or the item is not artifact-backed.
4654
4705
  */
4655
- lookupArtifactItem(type, name) {
4706
+ lookupArtifactItem(type, name, currentPackageId) {
4656
4707
  const registry = this.engine?.registry;
4657
4708
  if (!registry) return void 0;
4658
4709
  const singular = PLURAL_TO_SINGULAR3[type] ?? type;
4659
4710
  if (typeof registry.getArtifactItem === "function") {
4660
- return registry.getArtifactItem(singular, name) ?? registry.getArtifactItem(type, name);
4711
+ return registry.getArtifactItem(singular, name, currentPackageId) ?? registry.getArtifactItem(type, name, currentPackageId);
4661
4712
  }
4662
4713
  if (typeof registry.getItem !== "function") return void 0;
4663
- const item = registry.getItem(singular, name) ?? registry.getItem(type, name);
4714
+ const item = registry.getItem(singular, name, currentPackageId) ?? registry.getItem(type, name, currentPackageId);
4664
4715
  if (!item || !item._packageId || item._packageId === "sys_metadata") {
4665
4716
  return void 0;
4666
4717
  }
@@ -5026,7 +5077,10 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
5026
5077
  if (request.parentVersion !== void 0) {
5027
5078
  parentVersion = request.parentVersion;
5028
5079
  } else {
5029
- const current = await repo.get(ref, { state: mode === "draft" ? "draft" : "active" });
5080
+ const current = await repo.get(ref, {
5081
+ state: mode === "draft" ? "draft" : "active",
5082
+ packageId: request.packageId ?? null
5083
+ });
5030
5084
  parentVersion = current?.hash ?? null;
5031
5085
  }
5032
5086
  try {
@@ -9448,10 +9502,16 @@ var MetadataFacade = class {
9448
9502
  }
9449
9503
  }
9450
9504
  /**
9451
- * Get a metadata item by type and name
9452
- */
9453
- async get(type, name) {
9454
- const item = this.registry.getItem(type, name);
9505
+ * Get a metadata item by type and name.
9506
+ *
9507
+ * `currentPackageId` (ADR-0048) opts into package-scoped resolution: when two
9508
+ * installed packages ship an item of the same `type`/`name`, the registry
9509
+ * prefers the one owned by `currentPackageId` (composite key
9510
+ * `${packageId}:${name}`) before falling back to first-match. Omit it for the
9511
+ * legacy context-free lookup.
9512
+ */
9513
+ async get(type, name, currentPackageId) {
9514
+ const item = this.registry.getItem(type, name, currentPackageId);
9455
9515
  return item?.content ?? item;
9456
9516
  }
9457
9517
  /**
@@ -9530,6 +9590,7 @@ var ObjectQLPlugin = class {
9530
9590
  */
9531
9591
  this.startupTimeout = 12e4;
9532
9592
  this.skipSchemaSync = false;
9593
+ this.hydrateMetadataFromDb = false;
9533
9594
  /** Unsubscribe handles for metadata-event subscriptions (ADR-0008 PR-7). */
9534
9595
  this.metadataUnsubscribes = [];
9535
9596
  this.init = async (ctx) => {
@@ -9654,7 +9715,7 @@ var ObjectQLPlugin = class {
9654
9715
  } else {
9655
9716
  await this.syncRegisteredSchemas(ctx);
9656
9717
  }
9657
- if (this.environmentId === void 0) {
9718
+ if (this.environmentId === void 0 || this.hydrateMetadataFromDb) {
9658
9719
  await this.restoreMetadataFromDb(ctx);
9659
9720
  } else {
9660
9721
  ctx.logger.info("Project kernel \u2014 skipping sys_metadata hydration (metadata sourced from artifact)");
@@ -9696,6 +9757,7 @@ var ObjectQLPlugin = class {
9696
9757
  this.startupTimeout = opts.startupTimeout;
9697
9758
  }
9698
9759
  this.skipSchemaSync = typeof opts.skipSchemaSync === "boolean" ? opts.skipSchemaSync : process.env.OS_SKIP_SCHEMA_SYNC === "1";
9760
+ this.hydrateMetadataFromDb = opts.hydrateMetadataFromDb === true;
9699
9761
  }
9700
9762
  /**
9701
9763
  * Subscribe to `object` metadata events from the metadata service and