@objectstack/objectql 7.2.0 → 7.3.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
@@ -1,7 +1,9 @@
1
1
  // src/registry.ts
2
2
  import { ObjectSchema } from "@objectstack/spec/data";
3
+ import { readEnvWithDeprecation } from "@objectstack/types";
3
4
  import { ManifestSchema, InstalledPackageSchema } from "@objectstack/spec/kernel";
4
5
  import { AppSchema } from "@objectstack/spec/ui";
6
+ import { applyProtection } from "@objectstack/spec/shared";
5
7
  var RESERVED_NAMESPACES = /* @__PURE__ */ new Set(["base", "system"]);
6
8
  var DEFAULT_OWNER_PRIORITY = 100;
7
9
  var DEFAULT_EXTENDER_PRIORITY = 200;
@@ -126,10 +128,19 @@ var SchemaRegistry = class {
126
128
  // ==========================================
127
129
  /** Type → Name/ID → MetadataItem */
128
130
  this.metadata = /* @__PURE__ */ new Map();
131
+ /**
132
+ * Package ids that must be installed in a DISABLED state. Seeded once at
133
+ * boot (from persisted state) BEFORE any package registration so that every
134
+ * registration path — boot artifact, marketplace rehydrate, local import —
135
+ * honors persisted disable state uniformly without a fragile post-boot
136
+ * re-application hook. See {@link setInitialDisabledPackageIds} and
137
+ * {@link installPackage}.
138
+ */
139
+ this.initialDisabledPackageIds = /* @__PURE__ */ new Set();
129
140
  if (options.multiTenant !== void 0) {
130
141
  this.multiTenant = options.multiTenant;
131
142
  } else {
132
- this.multiTenant = String(process.env.OS_MULTI_TENANT ?? "false").toLowerCase() !== "false";
143
+ this.multiTenant = String(readEnvWithDeprecation("OS_MULTI_ORG_ENABLED", "OS_MULTI_TENANT") ?? "false").toLowerCase() !== "false";
133
144
  }
134
145
  }
135
146
  get logLevel() {
@@ -142,6 +153,14 @@ var SchemaRegistry = class {
142
153
  if (this._logLevel === "silent" || this._logLevel === "error" || this._logLevel === "warn") return;
143
154
  console.log(msg);
144
155
  }
156
+ /**
157
+ * Seed the set of package ids that should be installed disabled. Call this
158
+ * before package registration begins; later `installPackage` calls for these
159
+ * ids land in the `disabled` state. Replaces any previously seeded set.
160
+ */
161
+ setInitialDisabledPackageIds(ids) {
162
+ this.initialDisabledPackageIds = new Set(ids);
163
+ }
145
164
  // ==========================================
146
165
  // Namespace Management
147
166
  // ==========================================
@@ -231,6 +250,7 @@ var SchemaRegistry = class {
231
250
  contributors.splice(idx, 1);
232
251
  }
233
252
  }
253
+ applyProtection(schema, { packageId });
234
254
  const contributor = {
235
255
  packageId,
236
256
  namespace: namespace || "",
@@ -378,9 +398,7 @@ var SchemaRegistry = class {
378
398
  }
379
399
  const collection = this.metadata.get(type);
380
400
  const baseName = String(item[keyField]);
381
- if (packageId) {
382
- item._packageId = packageId;
383
- }
401
+ applyProtection(item, { packageId });
384
402
  try {
385
403
  this.validate(type, item);
386
404
  } catch (e) {
@@ -468,10 +486,23 @@ var SchemaRegistry = class {
468
486
  return this.getAllObjects(packageId);
469
487
  }
470
488
  const items = Array.from(this.metadata.get(type)?.values() || []);
489
+ let result = items;
471
490
  if (packageId) {
472
- return items.filter((item) => item._packageId === packageId);
491
+ result = result.filter((item) => item._packageId === packageId);
492
+ }
493
+ if (type !== "package") {
494
+ result = result.filter((item) => !this.isPackageDisabled(item?._packageId));
473
495
  }
474
- return items;
496
+ return result;
497
+ }
498
+ /**
499
+ * Whether a package has been explicitly disabled. Unknown packages and
500
+ * items with no owning package are treated as enabled.
501
+ */
502
+ isPackageDisabled(packageId) {
503
+ if (!packageId) return false;
504
+ const pkg = this.getPackage(packageId);
505
+ return pkg?.enabled === false || pkg?.status === "disabled";
475
506
  }
476
507
  /**
477
508
  * Get all registered metadata types (Kinds)
@@ -488,12 +519,14 @@ var SchemaRegistry = class {
488
519
  // ==========================================
489
520
  installPackage(manifest, settings) {
490
521
  const now = (/* @__PURE__ */ new Date()).toISOString();
522
+ const disabled = this.initialDisabledPackageIds.has(manifest.id);
491
523
  const pkg = {
492
524
  manifest,
493
- status: "installed",
494
- enabled: true,
525
+ status: disabled ? "disabled" : "installed",
526
+ enabled: !disabled,
495
527
  installedAt: now,
496
528
  updatedAt: now,
529
+ ...disabled ? { statusChangedAt: now } : {},
497
530
  settings
498
531
  };
499
532
  if (manifest.namespace) {
@@ -629,8 +662,12 @@ var SchemaRegistry = class {
629
662
  }
630
663
  };
631
664
 
665
+ // src/protocol.ts
666
+ import { readEnvWithDeprecation as readEnvWithDeprecation3 } from "@objectstack/types";
667
+
632
668
  // src/sys-metadata-repository.ts
633
669
  import { hashSpec, ConflictError } from "@objectstack/metadata-core";
670
+ import { readEnvWithDeprecation as readEnvWithDeprecation2 } from "@objectstack/types";
634
671
  import { DEFAULT_METADATA_TYPE_REGISTRY } from "@objectstack/spec/kernel";
635
672
  import { PLURAL_TO_SINGULAR, SINGULAR_TO_PLURAL } from "@objectstack/spec/shared";
636
673
  var OVERLAY_ALLOWED_TYPES = new Set(
@@ -645,7 +682,7 @@ var RUNTIME_CREATE_ALLOWED_TYPES = new Set(
645
682
  var _envWritableMetadataTypes = null;
646
683
  function envWritableMetadataTypes() {
647
684
  if (_envWritableMetadataTypes !== null) return _envWritableMetadataTypes;
648
- const raw = typeof process !== "undefined" && process?.env?.OBJECTSTACK_METADATA_WRITABLE || "";
685
+ const raw = readEnvWithDeprecation2("OS_METADATA_WRITABLE", "OBJECTSTACK_METADATA_WRITABLE") || "";
649
686
  const set = /* @__PURE__ */ new Set();
650
687
  for (const tok of raw.split(",")) {
651
688
  const t = tok.trim();
@@ -775,6 +812,12 @@ var SysMetadataRepository = class {
775
812
  version,
776
813
  updated_at: now
777
814
  };
815
+ if (existing) {
816
+ const existingPkg = existing.package_id ?? null;
817
+ parentRowData.package_id = existingPkg ?? opts.packageId ?? null;
818
+ } else {
819
+ parentRowData.package_id = opts.packageId ?? null;
820
+ }
778
821
  if (existing) {
779
822
  const existingId = existing.id;
780
823
  if (existingId === void 0) {
@@ -1172,7 +1215,7 @@ var SysMetadataRepository = class {
1172
1215
  * at `(type, name)`. In that case we accept types with
1173
1216
  * `allowRuntimeCreate: true`, even when `allowOrgOverride` is false.
1174
1217
  *
1175
- * The env-var escape hatch (`OBJECTSTACK_METADATA_WRITABLE`) still
1218
+ * The env-var escape hatch (`OS_METADATA_WRITABLE`) still
1176
1219
  * applies to BOTH intents, so operators can opt into artifact
1177
1220
  * overrides at runtime for emergency fixes.
1178
1221
  */
@@ -1197,7 +1240,7 @@ var SysMetadataRepository = class {
1197
1240
  const code = intent === "runtime-only" ? "not_creatable" : "not_overridable";
1198
1241
  const detail = intent === "runtime-only" ? `'${type}' has neither allowOrgOverride nor allowRuntimeCreate in the registry. ` : `'${type}' is not allowOrgOverride in the registry. `;
1199
1242
  const err = new Error(
1200
- `[${code}] ${detail}Overlay-allowed: ${Array.from(new Set(allowed)).join(", ") || "(none)"}. Set OBJECTSTACK_METADATA_WRITABLE to enable additional types at runtime.`
1243
+ `[${code}] ${detail}Overlay-allowed: ${Array.from(new Set(allowed)).join(", ") || "(none)"}. Set OS_METADATA_WRITABLE to enable additional types at runtime.`
1201
1244
  );
1202
1245
  err.code = code;
1203
1246
  err.status = 403;
@@ -1569,6 +1612,8 @@ function mergeArtifactProtection(item, artifactItem) {
1569
1612
  const out = { ...item };
1570
1613
  if (a._lock !== void 0) out._lock = a._lock;
1571
1614
  if (a._lockReason !== void 0) out._lockReason = a._lockReason;
1615
+ if (a._lockDocsUrl !== void 0) out._lockDocsUrl = a._lockDocsUrl;
1616
+ if (a._lockSource !== void 0) out._lockSource = a._lockSource;
1572
1617
  if (a._packageId !== void 0) out._packageId = a._packageId;
1573
1618
  if (a._packageVersion !== void 0) out._packageVersion = a._packageVersion;
1574
1619
  if (a._provenance !== void 0) out._provenance = a._provenance;
@@ -2080,10 +2125,13 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
2080
2125
  }
2081
2126
  const items = Array.isArray(listed?.items) ? listed.items : Array.isArray(listed) ? listed : [];
2082
2127
  const pkgSet = /* @__PURE__ */ new Set();
2128
+ let lockedCount = 0;
2083
2129
  for (const item of items) {
2084
2130
  scannedItems += 1;
2085
2131
  const pkg = item?._packageId ?? null;
2086
2132
  if (pkg) pkgSet.add(pkg);
2133
+ const lock = item?._lock;
2134
+ if (lock && lock !== "none") lockedCount += 1;
2087
2135
  const diag = item?._diagnostics ?? computeMetadataDiagnostics(t, item);
2088
2136
  if (!diag) continue;
2089
2137
  if (diag.valid && !includeWarnings) continue;
@@ -2094,7 +2142,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
2094
2142
  diagnostics: diag
2095
2143
  });
2096
2144
  }
2097
- stats[t] = { count: items.length, packages: [...pkgSet].sort() };
2145
+ stats[t] = { count: items.length, locked: lockedCount, packages: [...pkgSet].sort() };
2098
2146
  }
2099
2147
  return {
2100
2148
  entries,
@@ -2128,13 +2176,13 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
2128
2176
  state: "active",
2129
2177
  organization_id: oid
2130
2178
  };
2131
- if (packageId) whereClause._packageId = packageId;
2179
+ if (packageId) whereClause.package_id = packageId;
2132
2180
  let rs = await this.engine.find("sys_metadata", { where: whereClause });
2133
2181
  if (!rs || rs.length === 0) {
2134
2182
  const alt = PLURAL_TO_SINGULAR3[request.type] ?? SINGULAR_TO_PLURAL2[request.type];
2135
2183
  if (alt) {
2136
2184
  const altWhere = { type: alt, state: "active", organization_id: oid };
2137
- if (packageId) altWhere._packageId = packageId;
2185
+ if (packageId) altWhere.package_id = packageId;
2138
2186
  rs = await this.engine.find("sys_metadata", { where: altWhere });
2139
2187
  }
2140
2188
  }
@@ -2157,6 +2205,10 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
2157
2205
  for (const record of records) {
2158
2206
  const data = typeof record.metadata === "string" ? JSON.parse(record.metadata) : record.metadata;
2159
2207
  if (data && typeof data === "object" && "name" in data) {
2208
+ const recPkg = record.package_id ?? void 0;
2209
+ if (recPkg && data._packageId === void 0) {
2210
+ data._packageId = recPkg;
2211
+ }
2160
2212
  byName.set(data.name, data);
2161
2213
  }
2162
2214
  if (this.environmentId === void 0) {
@@ -2196,6 +2248,11 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
2196
2248
  }
2197
2249
  } catch {
2198
2250
  }
2251
+ if (request.type !== "package" && request.type !== "object" && request.type !== "objects") {
2252
+ items = items.filter(
2253
+ (it) => !this.engine.registry.isPackageDisabled(it?._packageId)
2254
+ );
2255
+ }
2199
2256
  return {
2200
2257
  type: request.type,
2201
2258
  items: decorateMetadataItems(
@@ -2239,6 +2296,10 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
2239
2296
  const record = (orgId ? await findOverlay(orgId) : void 0) ?? await findOverlay(null);
2240
2297
  if (record) {
2241
2298
  item = typeof record.metadata === "string" ? JSON.parse(record.metadata) : record.metadata;
2299
+ const recPkg = record.package_id ?? void 0;
2300
+ if (recPkg && item && typeof item === "object" && item._packageId === void 0) {
2301
+ item._packageId = recPkg;
2302
+ }
2242
2303
  }
2243
2304
  } catch {
2244
2305
  }
@@ -2295,6 +2356,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
2295
2356
  lock: lockState.lock,
2296
2357
  ...lockState.lockReason !== void 0 ? { lockReason: lockState.lockReason } : {},
2297
2358
  ...lockState.lockSource !== void 0 ? { lockSource: lockState.lockSource } : {},
2359
+ ...lockState.lockDocsUrl !== void 0 ? { lockDocsUrl: lockState.lockDocsUrl } : {},
2298
2360
  ...lockState.provenance !== void 0 ? { provenance: lockState.provenance } : {},
2299
2361
  ...lockState.packageId !== void 0 ? { packageId: lockState.packageId } : {},
2300
2362
  ...lockState.packageVersion !== void 0 ? { packageVersion: lockState.packageVersion } : {},
@@ -2394,6 +2456,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
2394
2456
  lock: lockState.lock,
2395
2457
  ...lockState.lockReason !== void 0 ? { lockReason: lockState.lockReason } : {},
2396
2458
  ...lockState.lockSource !== void 0 ? { lockSource: lockState.lockSource } : {},
2459
+ ...lockState.lockDocsUrl !== void 0 ? { lockDocsUrl: lockState.lockDocsUrl } : {},
2397
2460
  ...lockState.provenance !== void 0 ? { provenance: lockState.provenance } : {},
2398
2461
  ...lockState.packageId !== void 0 ? { packageId: lockState.packageId } : {},
2399
2462
  ...lockState.packageVersion !== void 0 ? { packageVersion: lockState.packageVersion } : {},
@@ -3326,7 +3389,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3326
3389
  }
3327
3390
  static envWritableTypes() {
3328
3391
  if (this._envWritableTypes !== null) return this._envWritableTypes;
3329
- const raw = typeof process !== "undefined" && process?.env?.OBJECTSTACK_METADATA_WRITABLE || "";
3392
+ const raw = readEnvWithDeprecation3("OS_METADATA_WRITABLE", "OBJECTSTACK_METADATA_WRITABLE") || "";
3330
3393
  const set = /* @__PURE__ */ new Set();
3331
3394
  for (const tok of raw.split(",")) {
3332
3395
  const t = tok.trim();
@@ -3572,7 +3635,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3572
3635
  const artifactBacked = this.isArtifactBacked(request.type, request.name);
3573
3636
  if (artifactBacked && !overlayAllowed) {
3574
3637
  const err = new Error(
3575
- `[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.`
3638
+ `[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.`
3576
3639
  );
3577
3640
  err.code = "not_overridable";
3578
3641
  err.status = 403;
@@ -3622,6 +3685,18 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3622
3685
  if (err?.code === "destructive_change") throw err;
3623
3686
  }
3624
3687
  }
3688
+ {
3689
+ const it = request.item;
3690
+ const looksLikeLayeredEnvelope = it && typeof it === "object" && !Array.isArray(it) && "code" in it && "overlay" in it && "overlayScope" in it && "effective" in it;
3691
+ if (looksLikeLayeredEnvelope) {
3692
+ const err = new Error(
3693
+ `[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.`
3694
+ );
3695
+ err.code = "invalid_metadata";
3696
+ err.status = 422;
3697
+ throw err;
3698
+ }
3699
+ }
3625
3700
  {
3626
3701
  const schema = resolveOverlaySchema(request.type, request.item);
3627
3702
  if (schema) {
@@ -3671,7 +3746,8 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3671
3746
  actor: request.actor ?? "system",
3672
3747
  source: "protocol.saveMetaItem",
3673
3748
  intent,
3674
- state: mode === "draft" ? "draft" : "active"
3749
+ state: mode === "draft" ? "draft" : "active",
3750
+ ...request.packageId !== void 0 ? { packageId: request.packageId } : {}
3675
3751
  });
3676
3752
  if (mode === "publish") {
3677
3753
  this.applyObjectRegistryMutation(request);
@@ -3722,12 +3798,16 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3722
3798
  where: scopedWhere
3723
3799
  });
3724
3800
  if (existing) {
3725
- await this.engine.update("sys_metadata", {
3801
+ const updateRow = {
3726
3802
  metadata: JSON.stringify(request.item),
3727
3803
  updated_at: now,
3728
3804
  version: (existing.version || 0) + 1,
3729
3805
  state: "active"
3730
- }, {
3806
+ };
3807
+ const existingPkg = existing.package_id ?? null;
3808
+ const nextPkg = existingPkg ?? request.packageId ?? null;
3809
+ if (nextPkg !== null) updateRow.package_id = nextPkg;
3810
+ await this.engine.update("sys_metadata", updateRow, {
3731
3811
  where: { id: existing.id }
3732
3812
  });
3733
3813
  } else {
@@ -3747,6 +3827,7 @@ var _ObjectStackProtocolImplementation = class _ObjectStackProtocolImplementatio
3747
3827
  updated_at: now,
3748
3828
  organization_id: orgId
3749
3829
  };
3830
+ if (request.packageId) row.package_id = request.packageId;
3750
3831
  await this.engine.insert("sys_metadata", row);
3751
3832
  }
3752
3833
  return {
@@ -4419,7 +4500,7 @@ _ObjectStackProtocolImplementation.OVERLAY_ALLOWED_TYPES = (() => {
4419
4500
  return out;
4420
4501
  })();
4421
4502
  /**
4422
- * Phase 3a-env-writable: parse `OBJECTSTACK_METADATA_WRITABLE` once.
4503
+ * Phase 3a-env-writable: parse `OS_METADATA_WRITABLE` once.
4423
4504
  * Comma-separated singular type names. When the env var is set, the
4424
4505
  * listed types get treated as `allowOrgOverride: true` regardless of
4425
4506
  * their static registry entry. This is the runtime escape hatch admins
@@ -5724,6 +5805,8 @@ var _ObjectQL = class _ObjectQL {
5724
5805
  "policies",
5725
5806
  // AI Protocol
5726
5807
  "agents",
5808
+ "tools",
5809
+ "skills",
5727
5810
  "ragPipelines",
5728
5811
  // API Protocol
5729
5812
  "apis",