@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.d.mts +109 -3
- package/dist/index.d.ts +109 -3
- package/dist/index.js +399 -56
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +363 -15
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -6
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(
|
|
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
|
-
|
|
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}`))
|
|
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
|
|
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 =
|
|
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 =
|
|
708
|
+
const singular = import_shared2.PLURAL_TO_SINGULAR[t] ?? t;
|
|
702
709
|
set.add(singular);
|
|
703
|
-
const plural =
|
|
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 (`
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
1371
|
+
var import_shared3 = require("@objectstack/spec/shared");
|
|
1364
1372
|
function computeMetadataDiagnostics(type, item) {
|
|
1365
|
-
const singular =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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:
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
3391
|
+
const singular = import_shared4.PLURAL_TO_SINGULAR[t] ?? t;
|
|
3270
3392
|
set.add(singular);
|
|
3271
|
-
const plural =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 `
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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,
|
|
5814
|
+
this._registry.registerItem((0, import_shared5.pluralToSingular)(key), toRegister, "name", id);
|
|
5472
5815
|
} else {
|
|
5473
|
-
this.logger.warn(`Skipping ${(0,
|
|
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,
|
|
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
|
-
|
|
6829
|
+
import_kernel6.ExecutionContextSchema.parse(ctx),
|
|
6487
6830
|
this
|
|
6488
6831
|
);
|
|
6489
6832
|
}
|