@objectstack/objectql 7.1.0 → 7.2.1

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
@@ -50,8 +50,10 @@ module.exports = __toCommonJS(index_exports);
50
50
 
51
51
  // src/registry.ts
52
52
  var import_data = require("@objectstack/spec/data");
53
+ var import_types = require("@objectstack/types");
53
54
  var import_kernel = require("@objectstack/spec/kernel");
54
55
  var import_ui = require("@objectstack/spec/ui");
56
+ var import_shared = require("@objectstack/spec/shared");
55
57
  var RESERVED_NAMESPACES = /* @__PURE__ */ new Set(["base", "system"]);
56
58
  var DEFAULT_OWNER_PRIORITY = 100;
57
59
  var DEFAULT_EXTENDER_PRIORITY = 200;
@@ -179,7 +181,7 @@ var SchemaRegistry = class {
179
181
  if (options.multiTenant !== void 0) {
180
182
  this.multiTenant = options.multiTenant;
181
183
  } else {
182
- this.multiTenant = String(process.env.OS_MULTI_TENANT ?? "false").toLowerCase() !== "false";
184
+ this.multiTenant = String((0, import_types.readEnvWithDeprecation)("OS_MULTI_ORG_ENABLED", "OS_MULTI_TENANT") ?? "false").toLowerCase() !== "false";
183
185
  }
184
186
  }
185
187
  get logLevel() {
@@ -281,6 +283,7 @@ var SchemaRegistry = class {
281
283
  contributors.splice(idx, 1);
282
284
  }
283
285
  }
286
+ (0, import_shared.applyProtection)(schema, { packageId });
284
287
  const contributor = {
285
288
  packageId,
286
289
  namespace: namespace || "",
@@ -428,9 +431,7 @@ var SchemaRegistry = class {
428
431
  }
429
432
  const collection = this.metadata.get(type);
430
433
  const baseName = String(item[keyField]);
431
- if (packageId) {
432
- item._packageId = packageId;
433
- }
434
+ (0, import_shared.applyProtection)(item, { packageId });
434
435
  try {
435
436
  this.validate(type, item);
436
437
  } catch (e) {
@@ -504,7 +505,9 @@ var SchemaRegistry = class {
504
505
  const direct = collection.get(name);
505
506
  if (direct) return direct;
506
507
  for (const [key, item] of collection) {
507
- if (key.endsWith(`:${name}`)) return item;
508
+ if (key.endsWith(`:${name}`)) {
509
+ return item;
510
+ }
508
511
  }
509
512
  return void 0;
510
513
  }
@@ -677,10 +680,14 @@ var SchemaRegistry = class {
677
680
  }
678
681
  };
679
682
 
683
+ // src/protocol.ts
684
+ var import_types3 = require("@objectstack/types");
685
+
680
686
  // src/sys-metadata-repository.ts
681
687
  var import_metadata_core = require("@objectstack/metadata-core");
688
+ var import_types2 = require("@objectstack/types");
682
689
  var import_kernel2 = require("@objectstack/spec/kernel");
683
- var import_shared = require("@objectstack/spec/shared");
690
+ var import_shared2 = require("@objectstack/spec/shared");
684
691
  var OVERLAY_ALLOWED_TYPES = new Set(
685
692
  import_kernel2.DEFAULT_METADATA_TYPE_REGISTRY.filter((e) => e.allowOrgOverride).map((e) => e.type)
686
693
  );
@@ -693,14 +700,14 @@ var RUNTIME_CREATE_ALLOWED_TYPES = new Set(
693
700
  var _envWritableMetadataTypes = null;
694
701
  function envWritableMetadataTypes() {
695
702
  if (_envWritableMetadataTypes !== null) return _envWritableMetadataTypes;
696
- const raw = typeof process !== "undefined" && process?.env?.OBJECTSTACK_METADATA_WRITABLE || "";
703
+ const raw = (0, import_types2.readEnvWithDeprecation)("OS_METADATA_WRITABLE", "OBJECTSTACK_METADATA_WRITABLE") || "";
697
704
  const set = /* @__PURE__ */ new Set();
698
705
  for (const tok of raw.split(",")) {
699
706
  const t = tok.trim();
700
707
  if (!t) continue;
701
- const singular = import_shared.PLURAL_TO_SINGULAR[t] ?? t;
708
+ const singular = import_shared2.PLURAL_TO_SINGULAR[t] ?? t;
702
709
  set.add(singular);
703
- const plural = import_shared.SINGULAR_TO_PLURAL[singular];
710
+ const plural = import_shared2.SINGULAR_TO_PLURAL[singular];
704
711
  if (plural) set.add(plural);
705
712
  }
706
713
  _envWritableMetadataTypes = set;
@@ -1220,12 +1227,12 @@ var SysMetadataRepository = class {
1220
1227
  * at `(type, name)`. In that case we accept types with
1221
1228
  * `allowRuntimeCreate: true`, even when `allowOrgOverride` is false.
1222
1229
  *
1223
- * The env-var escape hatch (`OBJECTSTACK_METADATA_WRITABLE`) still
1230
+ * The env-var escape hatch (`OS_METADATA_WRITABLE`) still
1224
1231
  * applies to BOTH intents, so operators can opt into artifact
1225
1232
  * overrides at runtime for emergency fixes.
1226
1233
  */
1227
1234
  assertAllowed(type, intent = "override-artifact") {
1228
- const singular = import_shared.PLURAL_TO_SINGULAR[type] ?? type;
1235
+ const singular = import_shared2.PLURAL_TO_SINGULAR[type] ?? type;
1229
1236
  const allowedByRegistry = OVERLAY_ALLOWED_TYPES.has(singular) || OVERLAY_ALLOWED_TYPES.has(type);
1230
1237
  if (allowedByRegistry) return;
1231
1238
  if (intent === "runtime-only") {
@@ -1245,7 +1252,7 @@ var SysMetadataRepository = class {
1245
1252
  const code = intent === "runtime-only" ? "not_creatable" : "not_overridable";
1246
1253
  const detail = intent === "runtime-only" ? `'${type}' has neither allowOrgOverride nor allowRuntimeCreate in the registry. ` : `'${type}' is not allowOrgOverride in the registry. `;
1247
1254
  const err = new Error(
1248
- `[${code}] ${detail}Overlay-allowed: ${Array.from(new Set(allowed)).join(", ") || "(none)"}. Set OBJECTSTACK_METADATA_WRITABLE to enable additional types at runtime.`
1255
+ `[${code}] ${detail}Overlay-allowed: ${Array.from(new Set(allowed)).join(", ") || "(none)"}. Set OS_METADATA_WRITABLE to enable additional types at runtime.`
1249
1256
  );
1250
1257
  err.code = code;
1251
1258
  err.status = 403;
@@ -1353,16 +1360,17 @@ var SysMetadataRepository = class {
1353
1360
  // src/protocol.ts
1354
1361
  var import_metadata_core2 = require("@objectstack/metadata-core");
1355
1362
  var import_data2 = require("@objectstack/spec/data");
1356
- var import_shared3 = require("@objectstack/spec/shared");
1363
+ var import_shared4 = require("@objectstack/spec/shared");
1357
1364
  var import_system = require("@objectstack/spec/system");
1358
1365
  var import_kernel4 = require("@objectstack/spec/kernel");
1366
+ var import_kernel5 = require("@objectstack/spec/kernel");
1359
1367
  var import_zod = require("zod");
1360
1368
 
1361
1369
  // src/metadata-diagnostics.ts
1362
1370
  var import_kernel3 = require("@objectstack/spec/kernel");
1363
- var import_shared2 = require("@objectstack/spec/shared");
1371
+ var import_shared3 = require("@objectstack/spec/shared");
1364
1372
  function computeMetadataDiagnostics(type, item) {
1365
- const singular = import_shared2.PLURAL_TO_SINGULAR[type] ?? type;
1373
+ const singular = import_shared3.PLURAL_TO_SINGULAR[type] ?? type;
1366
1374
  const schema = (0, import_kernel3.getMetadataTypeSchema)(singular);
1367
1375
  if (!schema) return void 0;
1368
1376
  if (item === null || item === void 0 || typeof item !== "object") {
@@ -1600,9 +1608,24 @@ var HAND_CRAFTED_SCHEMAS = {
1600
1608
  }
1601
1609
  };
1602
1610
  function resolveOverlaySchema(type, _item) {
1603
- const singular = import_shared3.PLURAL_TO_SINGULAR[type] ?? type;
1611
+ const singular = import_shared4.PLURAL_TO_SINGULAR[type] ?? type;
1604
1612
  return (0, import_kernel4.getMetadataTypeSchema)(singular) ?? null;
1605
1613
  }
1614
+ function mergeArtifactProtection(item, artifactItem) {
1615
+ if (item === void 0 || item === null) return item;
1616
+ if (artifactItem === void 0 || artifactItem === null) return item;
1617
+ const a = artifactItem;
1618
+ if (typeof a !== "object") return item;
1619
+ const out = { ...item };
1620
+ if (a._lock !== void 0) out._lock = a._lock;
1621
+ if (a._lockReason !== void 0) out._lockReason = a._lockReason;
1622
+ if (a._lockDocsUrl !== void 0) out._lockDocsUrl = a._lockDocsUrl;
1623
+ if (a._lockSource !== void 0) out._lockSource = a._lockSource;
1624
+ if (a._packageId !== void 0) out._packageId = a._packageId;
1625
+ if (a._packageVersion !== void 0) out._packageVersion = a._packageVersion;
1626
+ if (a._provenance !== void 0) out._provenance = a._provenance;
1627
+ return out;
1628
+ }
1606
1629
  function simpleHash(str) {
1607
1630
  let hash = 0;
1608
1631
  for (let i = 0; i < str.length; i++) {
@@ -2028,7 +2051,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
2028
2051
  import_kernel4.DEFAULT_METADATA_TYPE_REGISTRY.map((e) => [e.type, e])
2029
2052
  );
2030
2053
  const entries = allTypes.map((type) => {
2031
- const singular = import_shared3.PLURAL_TO_SINGULAR[type] ?? type;
2054
+ const singular = import_shared4.PLURAL_TO_SINGULAR[type] ?? type;
2032
2055
  const zodSchema = (0, import_kernel4.getMetadataTypeSchema)(singular);
2033
2056
  const schema = (zodSchema ? toJsonSchemaSafe(zodSchema) : void 0) ?? HAND_CRAFTED_SCHEMAS[singular];
2034
2057
  const form = TYPE_TO_FORM[singular];
@@ -2094,6 +2117,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
2094
2117
  const includeWarnings = request.severity === "warning";
2095
2118
  const targetTypes = request.type ? [request.type] : import_kernel4.DEFAULT_METADATA_TYPE_REGISTRY.filter((e) => (0, import_kernel4.getMetadataTypeSchema)(e.type)).map((e) => e.type);
2096
2119
  const entries = [];
2120
+ const stats = {};
2097
2121
  let scannedItems = 0;
2098
2122
  for (const t of targetTypes) {
2099
2123
  let listed;
@@ -2107,8 +2131,14 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
2107
2131
  continue;
2108
2132
  }
2109
2133
  const items = Array.isArray(listed?.items) ? listed.items : Array.isArray(listed) ? listed : [];
2134
+ const pkgSet = /* @__PURE__ */ new Set();
2135
+ let lockedCount = 0;
2110
2136
  for (const item of items) {
2111
2137
  scannedItems += 1;
2138
+ const pkg = item?._packageId ?? null;
2139
+ if (pkg) pkgSet.add(pkg);
2140
+ const lock = item?._lock;
2141
+ if (lock && lock !== "none") lockedCount += 1;
2112
2142
  const diag = item?._diagnostics ?? computeMetadataDiagnostics(t, item);
2113
2143
  if (!diag) continue;
2114
2144
  if (diag.valid && !includeWarnings) continue;
@@ -2119,12 +2149,14 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
2119
2149
  diagnostics: diag
2120
2150
  });
2121
2151
  }
2152
+ stats[t] = { count: items.length, locked: lockedCount, packages: [...pkgSet].sort() };
2122
2153
  }
2123
2154
  return {
2124
2155
  entries,
2125
2156
  total: entries.length,
2126
2157
  scannedTypes: targetTypes.length,
2127
- scannedItems
2158
+ scannedItems,
2159
+ stats
2128
2160
  };
2129
2161
  }
2130
2162
  async getMetaItems(request) {
@@ -2133,13 +2165,13 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
2133
2165
  if (this.environmentId === void 0) {
2134
2166
  items = [...this.engine.registry.listItems(request.type, packageId)];
2135
2167
  if (items.length === 0) {
2136
- const alt = import_shared3.PLURAL_TO_SINGULAR[request.type] ?? import_shared3.SINGULAR_TO_PLURAL[request.type];
2168
+ const alt = import_shared4.PLURAL_TO_SINGULAR[request.type] ?? import_shared4.SINGULAR_TO_PLURAL[request.type];
2137
2169
  if (alt) items = [...this.engine.registry.listItems(alt, packageId)];
2138
2170
  }
2139
2171
  } else {
2140
2172
  items = [...this.engine.registry.listItems(request.type, packageId)];
2141
2173
  if (items.length === 0) {
2142
- const alt = import_shared3.PLURAL_TO_SINGULAR[request.type] ?? import_shared3.SINGULAR_TO_PLURAL[request.type];
2174
+ const alt = import_shared4.PLURAL_TO_SINGULAR[request.type] ?? import_shared4.SINGULAR_TO_PLURAL[request.type];
2143
2175
  if (alt) items = [...this.engine.registry.listItems(alt, packageId)];
2144
2176
  }
2145
2177
  }
@@ -2154,7 +2186,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
2154
2186
  if (packageId) whereClause._packageId = packageId;
2155
2187
  let rs = await this.engine.find("sys_metadata", { where: whereClause });
2156
2188
  if (!rs || rs.length === 0) {
2157
- const alt = import_shared3.PLURAL_TO_SINGULAR[request.type] ?? import_shared3.SINGULAR_TO_PLURAL[request.type];
2189
+ const alt = import_shared4.PLURAL_TO_SINGULAR[request.type] ?? import_shared4.SINGULAR_TO_PLURAL[request.type];
2158
2190
  if (alt) {
2159
2191
  const altWhere = { type: alt, state: "active", organization_id: oid };
2160
2192
  if (packageId) altWhere._packageId = packageId;
@@ -2221,7 +2253,16 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
2221
2253
  }
2222
2254
  return {
2223
2255
  type: request.type,
2224
- items: decorateMetadataItems(request.type, items)
2256
+ items: decorateMetadataItems(
2257
+ request.type,
2258
+ items.map((it) => {
2259
+ const a = this.lookupArtifactItem(
2260
+ request.type,
2261
+ it?.name
2262
+ );
2263
+ return mergeArtifactProtection(it, a);
2264
+ })
2265
+ )
2225
2266
  };
2226
2267
  }
2227
2268
  async getMetaItem(request) {
@@ -2238,7 +2279,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
2238
2279
  };
2239
2280
  const rec = await this.engine.findOne("sys_metadata", { where });
2240
2281
  if (rec) return rec;
2241
- const alt = import_shared3.PLURAL_TO_SINGULAR[request.type] ?? import_shared3.SINGULAR_TO_PLURAL[request.type];
2282
+ const alt = import_shared4.PLURAL_TO_SINGULAR[request.type] ?? import_shared4.SINGULAR_TO_PLURAL[request.type];
2242
2283
  if (alt) {
2243
2284
  const altWhere = {
2244
2285
  type: alt,
@@ -2276,7 +2317,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
2276
2317
  if (fromService !== void 0 && fromService !== null) {
2277
2318
  item = fromService;
2278
2319
  } else {
2279
- const alt = import_shared3.PLURAL_TO_SINGULAR[request.type] ?? import_shared3.SINGULAR_TO_PLURAL[request.type];
2320
+ const alt = import_shared4.PLURAL_TO_SINGULAR[request.type] ?? import_shared4.SINGULAR_TO_PLURAL[request.type];
2280
2321
  if (alt) {
2281
2322
  const altFromService = await metadataService.get(alt, request.name);
2282
2323
  if (altFromService !== void 0 && altFromService !== null) {
@@ -2291,14 +2332,31 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
2291
2332
  if (item === void 0) {
2292
2333
  item = this.engine.registry.getItem(request.type, request.name);
2293
2334
  if (item === void 0) {
2294
- const alt = import_shared3.PLURAL_TO_SINGULAR[request.type] ?? import_shared3.SINGULAR_TO_PLURAL[request.type];
2335
+ const alt = import_shared4.PLURAL_TO_SINGULAR[request.type] ?? import_shared4.SINGULAR_TO_PLURAL[request.type];
2295
2336
  if (alt) item = this.engine.registry.getItem(alt, request.name);
2296
2337
  }
2297
2338
  }
2339
+ const artifactItem = this.lookupArtifactItem(request.type, request.name);
2340
+ const decorated = decorateMetadataItem(
2341
+ request.type,
2342
+ mergeArtifactProtection(item, artifactItem)
2343
+ );
2344
+ const artifactBacked = this.isArtifactBacked(request.type, request.name);
2345
+ const lockState = (0, import_kernel5.resolveLockState)(decorated, artifactBacked);
2298
2346
  return {
2299
2347
  type: request.type,
2300
2348
  name: request.name,
2301
- item: decorateMetadataItem(request.type, item)
2349
+ item: decorated,
2350
+ lock: lockState.lock,
2351
+ ...lockState.lockReason !== void 0 ? { lockReason: lockState.lockReason } : {},
2352
+ ...lockState.lockSource !== void 0 ? { lockSource: lockState.lockSource } : {},
2353
+ ...lockState.lockDocsUrl !== void 0 ? { lockDocsUrl: lockState.lockDocsUrl } : {},
2354
+ ...lockState.provenance !== void 0 ? { provenance: lockState.provenance } : {},
2355
+ ...lockState.packageId !== void 0 ? { packageId: lockState.packageId } : {},
2356
+ ...lockState.packageVersion !== void 0 ? { packageVersion: lockState.packageVersion } : {},
2357
+ editable: lockState.editable,
2358
+ deletable: lockState.deletable,
2359
+ resettable: lockState.resettable
2302
2360
  };
2303
2361
  }
2304
2362
  /**
@@ -2324,7 +2382,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
2324
2382
  if (metadataService && typeof metadataService.get === "function") {
2325
2383
  let fromService = await metadataService.get(request.type, request.name);
2326
2384
  if (fromService === void 0 || fromService === null) {
2327
- const alt = import_shared3.PLURAL_TO_SINGULAR[request.type] ?? import_shared3.SINGULAR_TO_PLURAL[request.type];
2385
+ const alt = import_shared4.PLURAL_TO_SINGULAR[request.type] ?? import_shared4.SINGULAR_TO_PLURAL[request.type];
2328
2386
  if (alt) fromService = await metadataService.get(alt, request.name);
2329
2387
  }
2330
2388
  if (fromService !== void 0 && fromService !== null) code = fromService;
@@ -2334,7 +2392,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
2334
2392
  if (code === null) {
2335
2393
  let regItem = this.engine.registry.getItem(request.type, request.name);
2336
2394
  if (regItem === void 0) {
2337
- const alt = import_shared3.PLURAL_TO_SINGULAR[request.type] ?? import_shared3.SINGULAR_TO_PLURAL[request.type];
2395
+ const alt = import_shared4.PLURAL_TO_SINGULAR[request.type] ?? import_shared4.SINGULAR_TO_PLURAL[request.type];
2338
2396
  if (alt) regItem = this.engine.registry.getItem(alt, request.name);
2339
2397
  }
2340
2398
  if (regItem !== void 0) code = regItem;
@@ -2351,7 +2409,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
2351
2409
  };
2352
2410
  let rec = await this.engine.findOne("sys_metadata", { where });
2353
2411
  if (!rec) {
2354
- const alt = import_shared3.PLURAL_TO_SINGULAR[request.type] ?? import_shared3.SINGULAR_TO_PLURAL[request.type];
2412
+ const alt = import_shared4.PLURAL_TO_SINGULAR[request.type] ?? import_shared4.SINGULAR_TO_PLURAL[request.type];
2355
2413
  if (alt) {
2356
2414
  rec = await this.engine.findOne("sys_metadata", {
2357
2415
  where: { ...where, type: alt }
@@ -2378,6 +2436,9 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
2378
2436
  }
2379
2437
  const effective = overlay ?? code;
2380
2438
  const _diagnostics = effective !== null && effective !== void 0 ? computeMetadataDiagnostics(request.type, effective) : void 0;
2439
+ const artifactBacked = this.isArtifactBacked(request.type, request.name);
2440
+ const lockSource = code ?? overlay ?? {};
2441
+ const lockState = (0, import_kernel5.resolveLockState)(lockSource, artifactBacked);
2381
2442
  return {
2382
2443
  type: request.type,
2383
2444
  name: request.name,
@@ -2385,9 +2446,70 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
2385
2446
  overlay,
2386
2447
  overlayScope,
2387
2448
  effective,
2388
- ..._diagnostics ? { _diagnostics } : {}
2449
+ ..._diagnostics ? { _diagnostics } : {},
2450
+ lock: lockState.lock,
2451
+ ...lockState.lockReason !== void 0 ? { lockReason: lockState.lockReason } : {},
2452
+ ...lockState.lockSource !== void 0 ? { lockSource: lockState.lockSource } : {},
2453
+ ...lockState.lockDocsUrl !== void 0 ? { lockDocsUrl: lockState.lockDocsUrl } : {},
2454
+ ...lockState.provenance !== void 0 ? { provenance: lockState.provenance } : {},
2455
+ ...lockState.packageId !== void 0 ? { packageId: lockState.packageId } : {},
2456
+ ...lockState.packageVersion !== void 0 ? { packageVersion: lockState.packageVersion } : {},
2457
+ editable: lockState.editable,
2458
+ deletable: lockState.deletable,
2459
+ resettable: lockState.resettable
2389
2460
  };
2390
2461
  }
2462
+ /**
2463
+ * ADR-0010 §3.6 / Phase 4.1 — read the metadata-protection audit log
2464
+ * for a single item. Returns the most-recent rows of
2465
+ * `sys_metadata_audit` for this (type, name) tuple, sorted newest
2466
+ * first. Refused (`denied`) and forced (`forced`) writes both appear
2467
+ * here — they never reach the `history` endpoint, which only tracks
2468
+ * successful body snapshots.
2469
+ *
2470
+ * The table is provisioned by `platform-objects` and is the
2471
+ * compliance surface for the lock-enforcement story. When the
2472
+ * environment has not yet provisioned the table (legacy install
2473
+ * prior to ADR-0010) the call returns `{ events: [] }` instead of
2474
+ * raising, keeping the Studio tab harmless.
2475
+ */
2476
+ async auditMetaItem(request) {
2477
+ const singular = import_shared4.PLURAL_TO_SINGULAR[request.type] ?? request.type;
2478
+ const limit = Math.min(
2479
+ Math.max(1, request.limit ?? 100),
2480
+ 500
2481
+ );
2482
+ try {
2483
+ const where = {
2484
+ type: singular,
2485
+ name: request.name
2486
+ };
2487
+ const rows = await this.engine.find("sys_metadata_audit", {
2488
+ where,
2489
+ orderBy: [{ field: "occurred_at", direction: "desc" }],
2490
+ limit
2491
+ });
2492
+ const events = (Array.isArray(rows) ? rows : []).map((r) => ({
2493
+ id: r.id,
2494
+ occurredAt: typeof r.occurred_at === "string" ? r.occurred_at : r.occurred_at instanceof Date ? r.occurred_at.toISOString() : String(r.occurred_at ?? ""),
2495
+ actor: String(r.actor ?? "system"),
2496
+ source: r.source ?? null,
2497
+ operation: r.operation,
2498
+ outcome: r.outcome,
2499
+ code: String(r.code ?? ""),
2500
+ lockState: r.lock_state ?? null,
2501
+ lockOverridden: Boolean(r.lock_overridden),
2502
+ requestId: r.request_id ?? null,
2503
+ note: r.note ?? null
2504
+ }));
2505
+ return { events };
2506
+ } catch (err) {
2507
+ console.warn(
2508
+ `[Protocol] auditMetaItem read failed for ${request.type}/${request.name}: ${err?.message ?? err}`
2509
+ );
2510
+ return { events: [] };
2511
+ }
2512
+ }
2391
2513
  async getUiView(request) {
2392
2514
  const schema = this.engine.registry.getObject(request.object);
2393
2515
  if (!schema) throw new Error(`Object ${request.object} not found`);
@@ -3261,14 +3383,14 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3261
3383
  }
3262
3384
  static envWritableTypes() {
3263
3385
  if (this._envWritableTypes !== null) return this._envWritableTypes;
3264
- const raw = typeof process !== "undefined" && process?.env?.OBJECTSTACK_METADATA_WRITABLE || "";
3386
+ const raw = (0, import_types3.readEnvWithDeprecation)("OS_METADATA_WRITABLE", "OBJECTSTACK_METADATA_WRITABLE") || "";
3265
3387
  const set = /* @__PURE__ */ new Set();
3266
3388
  for (const tok of raw.split(",")) {
3267
3389
  const t = tok.trim();
3268
3390
  if (!t) continue;
3269
- const singular = import_shared3.PLURAL_TO_SINGULAR[t] ?? t;
3391
+ const singular = import_shared4.PLURAL_TO_SINGULAR[t] ?? t;
3270
3392
  set.add(singular);
3271
- const plural = import_shared3.SINGULAR_TO_PLURAL[singular];
3393
+ const plural = import_shared4.SINGULAR_TO_PLURAL[singular];
3272
3394
  if (plural) set.add(plural);
3273
3395
  }
3274
3396
  this._envWritableTypes = set;
@@ -3280,7 +3402,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3280
3402
  }
3281
3403
  /** Normalize plural→singular before consulting the allow-list. */
3282
3404
  static isOverlayAllowed(type) {
3283
- const singular = import_shared3.PLURAL_TO_SINGULAR[type] ?? type;
3405
+ const singular = import_shared4.PLURAL_TO_SINGULAR[type] ?? type;
3284
3406
  if (this.OVERLAY_ALLOWED_TYPES.has(singular) || this.OVERLAY_ALLOWED_TYPES.has(type)) {
3285
3407
  return true;
3286
3408
  }
@@ -3289,7 +3411,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3289
3411
  }
3290
3412
  /** Does this type permit creating brand-new (artifact-free) items? */
3291
3413
  static isRuntimeCreateAllowed(type) {
3292
- const singular = import_shared3.PLURAL_TO_SINGULAR[type] ?? type;
3414
+ const singular = import_shared4.PLURAL_TO_SINGULAR[type] ?? type;
3293
3415
  if (this.RUNTIME_CREATE_ALLOWED_TYPES.has(singular) || this.RUNTIME_CREATE_ALLOWED_TYPES.has(type)) {
3294
3416
  return true;
3295
3417
  }
@@ -3318,11 +3440,163 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3318
3440
  if (!registry || typeof registry.getItem !== "function") {
3319
3441
  return false;
3320
3442
  }
3321
- const singular = import_shared3.PLURAL_TO_SINGULAR[type] ?? type;
3443
+ const singular = import_shared4.PLURAL_TO_SINGULAR[type] ?? type;
3322
3444
  const item = registry.getItem(singular, name) ?? registry.getItem(type, name);
3323
3445
  if (!item || !item._packageId) return false;
3324
3446
  return item._packageId !== "sys_metadata";
3325
3447
  }
3448
+ // ───────────────────────────────────────────────────────────────────
3449
+ // ADR-0010 — metadata protection (Phase 1: L3 item-level lock)
3450
+ // ───────────────────────────────────────────────────────────────────
3451
+ /**
3452
+ * Look up an item from the artifact registry across both the requested
3453
+ * type and its singular/plural twin. Returns `undefined` when the
3454
+ * registry is unavailable or the item is not artifact-backed.
3455
+ */
3456
+ lookupArtifactItem(type, name) {
3457
+ const registry = this.engine?.registry;
3458
+ if (!registry || typeof registry.getItem !== "function") return void 0;
3459
+ const singular = import_shared4.PLURAL_TO_SINGULAR[type] ?? type;
3460
+ return registry.getItem(singular, name) ?? registry.getItem(type, name);
3461
+ }
3462
+ /**
3463
+ * Resolve the effective `_lock` for an item by consulting the
3464
+ * artifact registry first, then the persisted overlay row. Artifact
3465
+ * always wins — by design, an overlay cannot loosen a packaged
3466
+ * lock (ADR-0010 §3.3).
3467
+ *
3468
+ * Returns `'none'` when nothing is locked, which is the common
3469
+ * case. Safe to call when `environmentId` is undefined (control-
3470
+ * plane bootstrap) — the lock check is only meaningful in tenant
3471
+ * scope and the caller is expected to also gate on `environmentId`.
3472
+ */
3473
+ async getEffectiveLock(type, name, organizationId) {
3474
+ const registry = this.engine?.registry;
3475
+ const singular = import_shared4.PLURAL_TO_SINGULAR[type] ?? type;
3476
+ let artifactItem;
3477
+ if (registry && typeof registry.getItem === "function") {
3478
+ artifactItem = registry.getItem(singular, name) ?? registry.getItem(type, name);
3479
+ }
3480
+ if (artifactItem && artifactItem._packageId && artifactItem._packageId !== "sys_metadata") {
3481
+ const p = (0, import_kernel5.extractProtection)(artifactItem);
3482
+ if (p.lock !== "none") {
3483
+ return { lock: p.lock, lockReason: p.lockReason, lockSource: "artifact" };
3484
+ }
3485
+ }
3486
+ try {
3487
+ const where = {
3488
+ type,
3489
+ name,
3490
+ state: "active",
3491
+ organization_id: organizationId ?? null
3492
+ };
3493
+ const row = await this.engine.findOne("sys_metadata", { where });
3494
+ if (row) {
3495
+ const body = typeof row.metadata === "string" ? JSON.parse(row.metadata) : row.metadata;
3496
+ const p = (0, import_kernel5.extractProtection)(body);
3497
+ if (p.lock !== "none") {
3498
+ return { lock: p.lock, lockReason: p.lockReason, lockSource: "overlay" };
3499
+ }
3500
+ }
3501
+ } catch {
3502
+ }
3503
+ return { lock: "none", lockReason: void 0, lockSource: void 0 };
3504
+ }
3505
+ /**
3506
+ * Best-effort audit-row writer (ADR-0010 §3.6). Failures here are
3507
+ * logged but never block the underlying decision: an environment
3508
+ * without the audit table provisioned (legacy installs before this
3509
+ * ADR landed) still answers normal API calls, just without the
3510
+ * compliance trail. Phase 2 will make the audit table a hard
3511
+ * dependency.
3512
+ */
3513
+ async recordMetadataAudit(entry) {
3514
+ try {
3515
+ await this.engine.insert("sys_metadata_audit", {
3516
+ occurred_at: (/* @__PURE__ */ new Date()).toISOString(),
3517
+ actor: entry.actor ?? "system",
3518
+ source: entry.source ?? "protocol",
3519
+ type: import_shared4.PLURAL_TO_SINGULAR[entry.type] ?? entry.type,
3520
+ name: entry.name,
3521
+ organization_id: entry.organizationId ?? null,
3522
+ operation: entry.operation,
3523
+ outcome: entry.outcome,
3524
+ code: entry.code,
3525
+ lock_state: entry.lockState ?? "none",
3526
+ lock_overridden: entry.lockOverridden ?? false,
3527
+ request_id: entry.requestId ?? null,
3528
+ note: entry.note ?? null
3529
+ });
3530
+ } catch (err) {
3531
+ console.warn(
3532
+ `[Protocol] sys_metadata_audit write failed for ${entry.type}/${entry.name}: ${err?.message ?? err}`
3533
+ );
3534
+ }
3535
+ }
3536
+ /**
3537
+ * Phase 1 L3 enforcement for write operations (save / publish /
3538
+ * rollback). Returns null on allow. Returns the structured `Error`
3539
+ * the caller should `throw` on deny — also records the denial in
3540
+ * the audit log so refused attempts are visible in compliance
3541
+ * reports (refused writes never reach sys_metadata_history).
3542
+ */
3543
+ async assertLockAllowsWrite(args) {
3544
+ if (this.environmentId === void 0) return null;
3545
+ const state = await this.getEffectiveLock(args.type, args.name, args.organizationId ?? null);
3546
+ const refusal = (0, import_kernel5.evaluateLockForWrite)(state.lock);
3547
+ if (!refusal) return null;
3548
+ const reason = state.lockReason ?? refusal.reason;
3549
+ const err = new Error(
3550
+ `[item_locked] ${args.type}/${args.name} is locked (_lock=${state.lock}${state.lockSource ? `, source=${state.lockSource}` : ""}). ${reason} \u2014 See ADR-0010 \xA73.3.`
3551
+ );
3552
+ err.code = "item_locked";
3553
+ err.status = 403;
3554
+ err.lock = state.lock;
3555
+ err.lockReason = reason;
3556
+ await this.recordMetadataAudit({
3557
+ type: args.type,
3558
+ name: args.name,
3559
+ organizationId: args.organizationId ?? null,
3560
+ operation: args.operation,
3561
+ outcome: "denied",
3562
+ code: "item_locked",
3563
+ lockState: state.lock,
3564
+ actor: args.actor,
3565
+ source: args.source ?? `protocol.${args.operation}MetaItem`,
3566
+ requestId: args.requestId,
3567
+ note: reason
3568
+ });
3569
+ return err;
3570
+ }
3571
+ /** Counterpart of {@link assertLockAllowsWrite} for delete. */
3572
+ async assertLockAllowsDelete(args) {
3573
+ if (this.environmentId === void 0) return null;
3574
+ const state = await this.getEffectiveLock(args.type, args.name, args.organizationId ?? null);
3575
+ const refusal = (0, import_kernel5.evaluateLockForDelete)(state.lock);
3576
+ if (!refusal) return null;
3577
+ const reason = state.lockReason ?? refusal.reason;
3578
+ const err = new Error(
3579
+ `[item_locked] ${args.type}/${args.name} is locked (_lock=${state.lock}${state.lockSource ? `, source=${state.lockSource}` : ""}). ${reason} \u2014 See ADR-0010 \xA73.3.`
3580
+ );
3581
+ err.code = "item_locked";
3582
+ err.status = 403;
3583
+ err.lock = state.lock;
3584
+ err.lockReason = reason;
3585
+ await this.recordMetadataAudit({
3586
+ type: args.type,
3587
+ name: args.name,
3588
+ organizationId: args.organizationId ?? null,
3589
+ operation: "delete",
3590
+ outcome: "denied",
3591
+ code: "item_locked",
3592
+ lockState: state.lock,
3593
+ actor: args.actor,
3594
+ source: args.source ?? "protocol.deleteMetaItem",
3595
+ requestId: args.requestId,
3596
+ note: reason
3597
+ });
3598
+ return err;
3599
+ }
3326
3600
  /**
3327
3601
  * Mirror an object-type overlay write into the in-memory engine
3328
3602
  * registry so subsequent CRUD finds the new schema. Idempotent and
@@ -3355,7 +3629,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3355
3629
  const artifactBacked = this.isArtifactBacked(request.type, request.name);
3356
3630
  if (artifactBacked && !overlayAllowed) {
3357
3631
  const err = new Error(
3358
- `[not_overridable] Metadata item '${request.type}/${request.name}' is provided by a code package and the type has not opted into per-org overlay writes (allowOrgOverride=false). Edit the source artifact and redeploy, or set OBJECTSTACK_METADATA_WRITABLE to grant a runtime escape hatch. See docs/adr/0005-metadata-customization-overlay.md.`
3632
+ `[not_overridable] Metadata item '${request.type}/${request.name}' is provided by a code package and the type has not opted into per-org overlay writes (allowOrgOverride=false). Edit the source artifact and redeploy, or set OS_METADATA_WRITABLE to grant a runtime escape hatch. See docs/adr/0005-metadata-customization-overlay.md.`
3359
3633
  );
3360
3634
  err.code = "not_overridable";
3361
3635
  err.status = 403;
@@ -3369,8 +3643,17 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3369
3643
  err.status = 403;
3370
3644
  throw err;
3371
3645
  }
3646
+ const lockErr = await this.assertLockAllowsWrite({
3647
+ type: request.type,
3648
+ name: request.name,
3649
+ ...request.organizationId ? { organizationId: request.organizationId } : {},
3650
+ operation: "save",
3651
+ ...request.actor ? { actor: request.actor } : {},
3652
+ source: "protocol.saveMetaItem"
3653
+ });
3654
+ if (lockErr) throw lockErr;
3372
3655
  }
3373
- const singularType = import_shared3.PLURAL_TO_SINGULAR[request.type] ?? request.type;
3656
+ const singularType = import_shared4.PLURAL_TO_SINGULAR[request.type] ?? request.type;
3374
3657
  if (!request.force && (singularType === "object" || singularType === "field")) {
3375
3658
  try {
3376
3659
  const existing = await this.getMetaItem({
@@ -3396,6 +3679,18 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3396
3679
  if (err?.code === "destructive_change") throw err;
3397
3680
  }
3398
3681
  }
3682
+ {
3683
+ const it = request.item;
3684
+ const looksLikeLayeredEnvelope = it && typeof it === "object" && !Array.isArray(it) && "code" in it && "overlay" in it && "overlayScope" in it && "effective" in it;
3685
+ if (looksLikeLayeredEnvelope) {
3686
+ const err = new Error(
3687
+ `[invalid_metadata] ${request.type}/${request.name}: the request body is a layered read envelope ({ code, overlay, overlayScope, effective }), not a metadata body. Unwrap and send the effective/overlay document instead \u2014 the layered shape is read-only (GET ?layers=true) and must never be persisted.`
3688
+ );
3689
+ err.code = "invalid_metadata";
3690
+ err.status = 422;
3691
+ throw err;
3692
+ }
3693
+ }
3399
3694
  {
3400
3695
  const schema = resolveOverlaySchema(request.type, request.item);
3401
3696
  if (schema) {
@@ -3418,7 +3713,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3418
3713
  }
3419
3714
  }
3420
3715
  await this.ensureOverlayIndex();
3421
- const singularTypeForRepo = import_shared3.PLURAL_TO_SINGULAR[request.type] ?? request.type;
3716
+ const singularTypeForRepo = import_shared4.PLURAL_TO_SINGULAR[request.type] ?? request.type;
3422
3717
  const overlayAllowedForRepo = _ObjectStackProtocolImplementation.isOverlayAllowed(singularTypeForRepo);
3423
3718
  const runtimeCreateAllowedForRepo = _ObjectStackProtocolImplementation.isRuntimeCreateAllowed(singularTypeForRepo);
3424
3719
  const useRepoPath = overlayAllowedForRepo || runtimeCreateAllowedForRepo;
@@ -3450,6 +3745,17 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3450
3745
  if (mode === "publish") {
3451
3746
  this.applyObjectRegistryMutation(request);
3452
3747
  }
3748
+ await this.recordMetadataAudit({
3749
+ type: request.type,
3750
+ name: request.name,
3751
+ organizationId: orgId,
3752
+ operation: "save",
3753
+ outcome: "allowed",
3754
+ code: "ok",
3755
+ ...request.actor ? { actor: request.actor } : {},
3756
+ source: "protocol.saveMetaItem",
3757
+ note: mode === "draft" ? "draft" : "active"
3758
+ });
3453
3759
  return {
3454
3760
  success: true,
3455
3761
  version: result.version,
@@ -3539,7 +3845,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3539
3845
  * "no history" uniformly.
3540
3846
  */
3541
3847
  async historyMetaItem(request) {
3542
- const singularType = import_shared3.PLURAL_TO_SINGULAR[request.type] ?? request.type;
3848
+ const singularType = import_shared4.PLURAL_TO_SINGULAR[request.type] ?? request.type;
3543
3849
  if (!_ObjectStackProtocolImplementation.isOverlayAllowed(singularType) && !_ObjectStackProtocolImplementation.isRuntimeCreateAllowed(singularType)) {
3544
3850
  return { events: [] };
3545
3851
  }
@@ -3563,7 +3869,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3563
3869
  * when there is nothing to publish.
3564
3870
  */
3565
3871
  async publishMetaItem(request) {
3566
- const singularType = import_shared3.PLURAL_TO_SINGULAR[request.type] ?? request.type;
3872
+ const singularType = import_shared4.PLURAL_TO_SINGULAR[request.type] ?? request.type;
3567
3873
  if (!_ObjectStackProtocolImplementation.isOverlayAllowed(singularType) && !_ObjectStackProtocolImplementation.isRuntimeCreateAllowed(singularType)) {
3568
3874
  const err = new Error(
3569
3875
  `[not_overridable] Metadata type '${request.type}' is not draftable \u2014 no overlay/runtime-create permission.`
@@ -3572,6 +3878,15 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3572
3878
  err.status = 403;
3573
3879
  throw err;
3574
3880
  }
3881
+ const _publishLockErr = await this.assertLockAllowsWrite({
3882
+ type: request.type,
3883
+ name: request.name,
3884
+ ...request.organizationId ? { organizationId: request.organizationId } : {},
3885
+ operation: "publish",
3886
+ ...request.actor ? { actor: request.actor } : {},
3887
+ source: "protocol.publishMetaItem"
3888
+ });
3889
+ if (_publishLockErr) throw _publishLockErr;
3575
3890
  await this.ensureOverlayIndex();
3576
3891
  const orgId = request.organizationId ?? null;
3577
3892
  const repo = this.getOverlayRepo(orgId);
@@ -3630,7 +3945,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3630
3945
  err.status = 400;
3631
3946
  throw err;
3632
3947
  }
3633
- const singularType = import_shared3.PLURAL_TO_SINGULAR[request.type] ?? request.type;
3948
+ const singularType = import_shared4.PLURAL_TO_SINGULAR[request.type] ?? request.type;
3634
3949
  if (!_ObjectStackProtocolImplementation.isOverlayAllowed(singularType) && !_ObjectStackProtocolImplementation.isRuntimeCreateAllowed(singularType)) {
3635
3950
  const err = new Error(
3636
3951
  `[not_overridable] Metadata type '${request.type}' is not revertable \u2014 no overlay/runtime-create permission.`
@@ -3639,6 +3954,15 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3639
3954
  err.status = 403;
3640
3955
  throw err;
3641
3956
  }
3957
+ const _rollbackLockErr = await this.assertLockAllowsWrite({
3958
+ type: request.type,
3959
+ name: request.name,
3960
+ ...request.organizationId ? { organizationId: request.organizationId } : {},
3961
+ operation: "rollback",
3962
+ ...request.actor ? { actor: request.actor } : {},
3963
+ source: "protocol.rollbackMetaItem"
3964
+ });
3965
+ if (_rollbackLockErr) throw _rollbackLockErr;
3642
3966
  await this.ensureOverlayIndex();
3643
3967
  const orgId = request.organizationId ?? null;
3644
3968
  const repo = this.getOverlayRepo(orgId);
@@ -3692,7 +4016,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3692
4016
  * are reported as a single change record.
3693
4017
  */
3694
4018
  async diffMetaItem(request) {
3695
- const singularType = import_shared3.PLURAL_TO_SINGULAR[request.type] ?? request.type;
4019
+ const singularType = import_shared4.PLURAL_TO_SINGULAR[request.type] ?? request.type;
3696
4020
  const orgId = request.organizationId ?? null;
3697
4021
  const events = (await this.historyMetaItem({
3698
4022
  type: singularType,
@@ -3785,8 +4109,16 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3785
4109
  err.status = 403;
3786
4110
  throw err;
3787
4111
  }
4112
+ const lockErr = await this.assertLockAllowsDelete({
4113
+ type: request.type,
4114
+ name: request.name,
4115
+ ...request.organizationId ? { organizationId: request.organizationId } : {},
4116
+ ...request.actor ? { actor: request.actor } : {},
4117
+ source: "protocol.deleteMetaItem"
4118
+ });
4119
+ if (lockErr) throw lockErr;
3788
4120
  }
3789
- const singularTypeForRepo = import_shared3.PLURAL_TO_SINGULAR[request.type] ?? request.type;
4121
+ const singularTypeForRepo = import_shared4.PLURAL_TO_SINGULAR[request.type] ?? request.type;
3790
4122
  const overlayAllowedForRepoDel = _ObjectStackProtocolImplementation.isOverlayAllowed(singularTypeForRepo);
3791
4123
  const runtimeCreateAllowedForRepoDel = _ObjectStackProtocolImplementation.isRuntimeCreateAllowed(singularTypeForRepo);
3792
4124
  const useRepoPath = overlayAllowedForRepoDel || runtimeCreateAllowedForRepoDel;
@@ -3829,6 +4161,17 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3829
4161
  } catch {
3830
4162
  }
3831
4163
  }
4164
+ await this.recordMetadataAudit({
4165
+ type: request.type,
4166
+ name: request.name,
4167
+ organizationId: orgId,
4168
+ operation: "delete",
4169
+ outcome: "allowed",
4170
+ code: "ok",
4171
+ ...request.actor ? { actor: request.actor } : {},
4172
+ source: "protocol.deleteMetaItem",
4173
+ note: targetState
4174
+ });
3832
4175
  return {
3833
4176
  success: true,
3834
4177
  reset: true,
@@ -3911,7 +4254,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3911
4254
  for (const record of records) {
3912
4255
  try {
3913
4256
  const data = typeof record.metadata === "string" ? JSON.parse(record.metadata) : record.metadata;
3914
- const normalizedType = import_shared3.PLURAL_TO_SINGULAR[record.type] ?? record.type;
4257
+ const normalizedType = import_shared4.PLURAL_TO_SINGULAR[record.type] ?? record.type;
3915
4258
  if (normalizedType === "object") {
3916
4259
  this.engine.registry.registerObject(data, record.packageId || "sys_metadata");
3917
4260
  } else {
@@ -3945,7 +4288,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3945
4288
  * — the engine never throws.
3946
4289
  */
3947
4290
  async findReferencesToMeta(request) {
3948
- const singularTarget = import_shared3.PLURAL_TO_SINGULAR[request.type] ?? request.type;
4291
+ const singularTarget = import_shared4.PLURAL_TO_SINGULAR[request.type] ?? request.type;
3949
4292
  const targetName = request.name;
3950
4293
  const matchers = REFERENCE_PATHS[singularTarget];
3951
4294
  if (!matchers || matchers.length === 0) {
@@ -4139,13 +4482,13 @@ _ObjectStackProtocolImplementation.OVERLAY_ALLOWED_TYPES = (() => {
4139
4482
  for (const entry of import_kernel4.DEFAULT_METADATA_TYPE_REGISTRY) {
4140
4483
  if (!entry.allowOrgOverride) continue;
4141
4484
  out.add(entry.type);
4142
- const plural = import_shared3.SINGULAR_TO_PLURAL[entry.type];
4485
+ const plural = import_shared4.SINGULAR_TO_PLURAL[entry.type];
4143
4486
  if (plural) out.add(plural);
4144
4487
  }
4145
4488
  return out;
4146
4489
  })();
4147
4490
  /**
4148
- * Phase 3a-env-writable: parse `OBJECTSTACK_METADATA_WRITABLE` once.
4491
+ * Phase 3a-env-writable: parse `OS_METADATA_WRITABLE` once.
4149
4492
  * Comma-separated singular type names. When the env var is set, the
4150
4493
  * listed types get treated as `allowOrgOverride: true` regardless of
4151
4494
  * their static registry entry. This is the runtime escape hatch admins
@@ -4176,7 +4519,7 @@ _ObjectStackProtocolImplementation.STATIC_REGISTRY_TYPES = (() => {
4176
4519
  const out = /* @__PURE__ */ new Set();
4177
4520
  for (const entry of import_kernel4.DEFAULT_METADATA_TYPE_REGISTRY) {
4178
4521
  out.add(entry.type);
4179
- const plural = import_shared3.SINGULAR_TO_PLURAL[entry.type];
4522
+ const plural = import_shared4.SINGULAR_TO_PLURAL[entry.type];
4180
4523
  if (plural) out.add(plural);
4181
4524
  }
4182
4525
  return out;
@@ -4186,7 +4529,7 @@ _ObjectStackProtocolImplementation.RUNTIME_CREATE_ALLOWED_TYPES = (() => {
4186
4529
  for (const entry of import_kernel4.DEFAULT_METADATA_TYPE_REGISTRY) {
4187
4530
  if (!entry.allowRuntimeCreate) continue;
4188
4531
  out.add(entry.type);
4189
- const plural = import_shared3.SINGULAR_TO_PLURAL[entry.type];
4532
+ const plural = import_shared4.SINGULAR_TO_PLURAL[entry.type];
4190
4533
  if (plural) out.add(plural);
4191
4534
  }
4192
4535
  return out;
@@ -4194,10 +4537,10 @@ _ObjectStackProtocolImplementation.RUNTIME_CREATE_ALLOWED_TYPES = (() => {
4194
4537
  var ObjectStackProtocolImplementation = _ObjectStackProtocolImplementation;
4195
4538
 
4196
4539
  // src/engine.ts
4197
- var import_kernel5 = require("@objectstack/spec/kernel");
4540
+ var import_kernel6 = require("@objectstack/spec/kernel");
4198
4541
  var import_core = require("@objectstack/core");
4199
4542
  var import_system2 = require("@objectstack/spec/system");
4200
- var import_shared4 = require("@objectstack/spec/shared");
4543
+ var import_shared5 = require("@objectstack/spec/shared");
4201
4544
  var import_formula2 = require("@objectstack/formula");
4202
4545
 
4203
4546
  // src/hook-wrappers.ts
@@ -5468,9 +5811,9 @@ var _ObjectQL = class _ObjectQL {
5468
5811
  const itemName = resolveMetadataItemName(key, item);
5469
5812
  if (itemName) {
5470
5813
  const toRegister = item.name === itemName ? item : { ...item, name: itemName };
5471
- this._registry.registerItem((0, import_shared4.pluralToSingular)(key), toRegister, "name", id);
5814
+ this._registry.registerItem((0, import_shared5.pluralToSingular)(key), toRegister, "name", id);
5472
5815
  } else {
5473
- this.logger.warn(`Skipping ${(0, import_shared4.pluralToSingular)(key)} without a derivable name`, { id });
5816
+ this.logger.warn(`Skipping ${(0, import_shared5.pluralToSingular)(key)} without a derivable name`, { id });
5474
5817
  }
5475
5818
  }
5476
5819
  }
@@ -5597,7 +5940,7 @@ var _ObjectQL = class _ObjectQL {
5597
5940
  const itemName = resolveMetadataItemName(key, item);
5598
5941
  if (itemName) {
5599
5942
  const toRegister = item.name === itemName ? item : { ...item, name: itemName };
5600
- this._registry.registerItem((0, import_shared4.pluralToSingular)(key), toRegister, "name", ownerId);
5943
+ this._registry.registerItem((0, import_shared5.pluralToSingular)(key), toRegister, "name", ownerId);
5601
5944
  }
5602
5945
  }
5603
5946
  }
@@ -6483,7 +6826,7 @@ var _ObjectQL = class _ObjectQL {
6483
6826
  */
6484
6827
  createContext(ctx) {
6485
6828
  return new ScopedContext(
6486
- import_kernel5.ExecutionContextSchema.parse(ctx),
6829
+ import_kernel6.ExecutionContextSchema.parse(ctx),
6487
6830
  this
6488
6831
  );
6489
6832
  }