@web-ts-toolkit/access-router 0.54.0 → 0.55.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/README.md +311 -8
- package/advanced.d.mts +1 -1
- package/advanced.d.ts +1 -1
- package/advanced.js +612 -155
- package/advanced.mjs +586 -129
- package/index.d.mts +5 -2
- package/index.d.ts +5 -2
- package/index.js +3046 -1904
- package/index.mjs +2954 -1812
- package/llms.txt +2 -0
- package/package.json +3 -3
- package/{parsers-ZSrF7SNu.d.mts → parsers-BrVRL2fg.d.mts} +609 -471
- package/{parsers-ZSrF7SNu.d.ts → parsers-BrVRL2fg.d.ts} +609 -471
package/advanced.js
CHANGED
|
@@ -123,10 +123,10 @@ var FilterOperator = /* @__PURE__ */ ((FilterOperator2) => {
|
|
|
123
123
|
})(FilterOperator || {});
|
|
124
124
|
|
|
125
125
|
// src/validation/parsers.ts
|
|
126
|
-
var
|
|
126
|
+
var import_express_json_router3 = __toESM(require("@web-ts-toolkit/express-json-router"));
|
|
127
127
|
|
|
128
128
|
// src/runtime.ts
|
|
129
|
-
var
|
|
129
|
+
var import_mongoose3 = __toESM(require("mongoose"));
|
|
130
130
|
|
|
131
131
|
// src/validation/public-admission.ts
|
|
132
132
|
var import_express_json_router = __toESM(require("@web-ts-toolkit/express-json-router"));
|
|
@@ -169,10 +169,10 @@ function assertPublicAdmission(input, kind = "envelope") {
|
|
|
169
169
|
const add = (child, childKind, key) => {
|
|
170
170
|
pending.push({ value: child, kind: childKind, path: `${path}.${key}` });
|
|
171
171
|
};
|
|
172
|
-
const
|
|
172
|
+
const record2 = value;
|
|
173
173
|
if (slot !== "data") {
|
|
174
174
|
for (const key of privateControls) {
|
|
175
|
-
if (key in
|
|
175
|
+
if (key in record2) {
|
|
176
176
|
throw new PublicAdmissionError(
|
|
177
177
|
`Unsupported public control ${path}.${key}; use explicit per-call internal service options for backend authorization`
|
|
178
178
|
);
|
|
@@ -188,16 +188,16 @@ function assertPublicAdmission(input, kind = "envelope") {
|
|
|
188
188
|
for (const [key, child] of Object.entries(value)) add(child, key === "$$sq" ? "envelope" : "data", key);
|
|
189
189
|
continue;
|
|
190
190
|
}
|
|
191
|
-
for (const key of ["args", "options", "sqOptions"]) add(
|
|
192
|
-
add(
|
|
193
|
-
add(
|
|
194
|
-
for (const key of ["filter", "match", "data"]) add(
|
|
191
|
+
for (const key of ["args", "options", "sqOptions"]) add(record2[key], "envelope", key);
|
|
192
|
+
add(record2.include, "include", "include");
|
|
193
|
+
add(record2.populate, "populate", "populate");
|
|
194
|
+
for (const key of ["filter", "match", "data"]) add(record2[key], "data", key);
|
|
195
195
|
}
|
|
196
196
|
}
|
|
197
197
|
function assertHydrationDefaults(defaults) {
|
|
198
198
|
if (defaults && typeof defaults === "object") {
|
|
199
|
-
const
|
|
200
|
-
if ("hydration" in
|
|
199
|
+
const record2 = defaults;
|
|
200
|
+
if ("hydration" in record2)
|
|
201
201
|
throw new TypeError("defaults.hydration is unsupported; use model hydration configuration");
|
|
202
202
|
for (const bucket of [
|
|
203
203
|
"publicListOptions",
|
|
@@ -213,7 +213,7 @@ function assertHydrationDefaults(defaults) {
|
|
|
213
213
|
"publicCreateOptions",
|
|
214
214
|
"publicUpdateOptions"
|
|
215
215
|
]) {
|
|
216
|
-
const options =
|
|
216
|
+
const options = record2[bucket];
|
|
217
217
|
if (!options || typeof options !== "object" && typeof options !== "function") continue;
|
|
218
218
|
for (const key of ["lean", "hydration"]) {
|
|
219
219
|
if (!(key in options)) continue;
|
|
@@ -260,11 +260,28 @@ function normalizeHydrationUpdate(current, key, value) {
|
|
|
260
260
|
|
|
261
261
|
// src/runtime.ts
|
|
262
262
|
var import_mongoose_schema_jsonschema = __toESM(require("mongoose-schema-jsonschema"));
|
|
263
|
-
var
|
|
263
|
+
var import_utils12 = require("@web-ts-toolkit/utils");
|
|
264
264
|
|
|
265
265
|
// src/helpers/query.ts
|
|
266
266
|
var import_utils2 = require("@web-ts-toolkit/utils");
|
|
267
267
|
var DEFAULT_LIST_HARD_LIMIT = 1e3;
|
|
268
|
+
function normalizeSelect(select) {
|
|
269
|
+
if (Array.isArray(select)) return (0, import_utils2.flattenDeep)(select.map(normalizeSelect));
|
|
270
|
+
if ((0, import_utils2.isPlainObject)(select)) {
|
|
271
|
+
return (0, import_utils2.reduce)(
|
|
272
|
+
select,
|
|
273
|
+
(ret, val, key) => {
|
|
274
|
+
if (val === 1) ret.push(key);
|
|
275
|
+
else if (val === -1) ret.push(`-${key}`);
|
|
276
|
+
return ret;
|
|
277
|
+
},
|
|
278
|
+
[]
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
if ((0, import_utils2.isString)(select))
|
|
282
|
+
return String(select).split(/[\s,]+/).map((v) => v.trim()).filter(Boolean);
|
|
283
|
+
return [];
|
|
284
|
+
}
|
|
268
285
|
|
|
269
286
|
// src/helpers/sort-policy.ts
|
|
270
287
|
var fieldPathPattern = /^[A-Za-z_][A-Za-z0-9_$]*(\.[A-Za-z_][A-Za-z0-9_$]*)*$/;
|
|
@@ -1180,14 +1197,490 @@ function validateRequestComplexity(value, options, scope = "request") {
|
|
|
1180
1197
|
return errors;
|
|
1181
1198
|
}
|
|
1182
1199
|
|
|
1200
|
+
// src/acl/virtual-descriptor.ts
|
|
1201
|
+
var import_utils10 = require("@web-ts-toolkit/utils");
|
|
1202
|
+
var isVirtualContainerValue = (value) => (0, import_utils10.isPlainObject)(value) && "sub" in value;
|
|
1203
|
+
function getScopeVirtualsObject(root, scopePath = []) {
|
|
1204
|
+
let current = root;
|
|
1205
|
+
for (let i = 0; i < scopePath.length; i += 2) {
|
|
1206
|
+
if (!current || scopePath[i + 1] !== "sub") return null;
|
|
1207
|
+
const container = current[scopePath[i]];
|
|
1208
|
+
if (!isVirtualContainerValue(container)) return null;
|
|
1209
|
+
current = container.sub;
|
|
1210
|
+
}
|
|
1211
|
+
return (0, import_utils10.isPlainObject)(current) ? current : null;
|
|
1212
|
+
}
|
|
1213
|
+
function toDescriptor(leaf, shared = {}) {
|
|
1214
|
+
const record2 = (0, import_utils10.isFunction)(leaf) ? { get: leaf } : (0, import_utils10.isPlainObject)(leaf) ? leaf : null;
|
|
1215
|
+
if (!record2 || !(0, import_utils10.isFunction)(record2.get)) return void 0;
|
|
1216
|
+
const dependsOn = record2.dependsOn ?? shared.dependsOn ?? [];
|
|
1217
|
+
const populated = record2.dependsOnPopulate === void 0 ? shared.dependsOnPopulate : record2.dependsOnPopulate;
|
|
1218
|
+
return {
|
|
1219
|
+
get: record2.get,
|
|
1220
|
+
dependsOn: [...dependsOn],
|
|
1221
|
+
...populated === void 0 ? {} : {
|
|
1222
|
+
dependsOnPopulate: populated.map(({ path, select }) => ({
|
|
1223
|
+
path,
|
|
1224
|
+
...select === void 0 ? {} : { select: Array.isArray(select) ? [...select] : typeof select === "object" ? { ...select } : select }
|
|
1225
|
+
}))
|
|
1226
|
+
}
|
|
1227
|
+
};
|
|
1228
|
+
}
|
|
1229
|
+
function resolveSnapshotDescriptor(scopeVirtuals, virtualName, access) {
|
|
1230
|
+
const entry = scopeVirtuals?.[virtualName];
|
|
1231
|
+
if (entry === void 0 || isVirtualContainerValue(entry)) return void 0;
|
|
1232
|
+
if ((0, import_utils10.isFunction)(entry)) return toDescriptor(entry);
|
|
1233
|
+
if (!(0, import_utils10.isPlainObject)(entry)) return void 0;
|
|
1234
|
+
const record2 = entry;
|
|
1235
|
+
if ("get" in record2) return toDescriptor(record2);
|
|
1236
|
+
return toDescriptor(record2[access], record2) ?? toDescriptor(record2.default, record2);
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
// src/acl/virtual-populate-dependencies.ts
|
|
1240
|
+
var import_utils11 = require("@web-ts-toolkit/utils");
|
|
1241
|
+
|
|
1242
|
+
// src/acl/authorization-snapshot.ts
|
|
1243
|
+
var import_node_util3 = require("util");
|
|
1244
|
+
var import_mongoose2 = require("mongoose");
|
|
1245
|
+
var import_express_json_router2 = __toESM(require("@web-ts-toolkit/express-json-router"));
|
|
1246
|
+
var ServiceAuthorizationError = class extends import_express_json_router2.default.clientErrors.BadRequestError {
|
|
1247
|
+
constructor(detail) {
|
|
1248
|
+
const errors = [{ detail: `Service authorization: ${detail}` }];
|
|
1249
|
+
super("Bad Request", { errors });
|
|
1250
|
+
this.result = { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors };
|
|
1251
|
+
}
|
|
1252
|
+
};
|
|
1253
|
+
function authorizationError(detail) {
|
|
1254
|
+
throw new ServiceAuthorizationError(detail);
|
|
1255
|
+
}
|
|
1256
|
+
var unsafeKeys = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
1257
|
+
function authorizationRecord(value, label) {
|
|
1258
|
+
if (!value || typeof value !== "object" || Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) {
|
|
1259
|
+
authorizationError(`${label} must be a plain object`);
|
|
1260
|
+
}
|
|
1261
|
+
return value;
|
|
1262
|
+
}
|
|
1263
|
+
function copyAuthorizationValue(value, configuration = false) {
|
|
1264
|
+
const active = /* @__PURE__ */ new WeakSet();
|
|
1265
|
+
let nodes = 0;
|
|
1266
|
+
function copy(input, depth) {
|
|
1267
|
+
if (++nodes > 2e4 || depth > 100) authorizationError("snapshot exceeds structural limits");
|
|
1268
|
+
if (typeof input === "function") {
|
|
1269
|
+
if (configuration === true) return input;
|
|
1270
|
+
authorizationError("functions are not policy values");
|
|
1271
|
+
}
|
|
1272
|
+
if (typeof input === "symbol") authorizationError("symbols are not policy values");
|
|
1273
|
+
if (input === null || typeof input !== "object") return input;
|
|
1274
|
+
if (active.has(input)) authorizationError("cyclic input");
|
|
1275
|
+
active.add(input);
|
|
1276
|
+
try {
|
|
1277
|
+
if (input instanceof Date) return new Date(input.getTime());
|
|
1278
|
+
if (Buffer.isBuffer(input)) return Buffer.from(input);
|
|
1279
|
+
if (input instanceof RegExp) {
|
|
1280
|
+
const result2 = new RegExp(input.source, input.flags);
|
|
1281
|
+
result2.lastIndex = input.lastIndex;
|
|
1282
|
+
return result2;
|
|
1283
|
+
}
|
|
1284
|
+
if (input instanceof import_mongoose2.mongo.BSON.BSONValue) {
|
|
1285
|
+
try {
|
|
1286
|
+
return import_mongoose2.mongo.BSON.deserialize(import_mongoose2.mongo.BSON.serialize({ value: input }), {
|
|
1287
|
+
promoteValues: false,
|
|
1288
|
+
bsonRegExp: true
|
|
1289
|
+
}).value;
|
|
1290
|
+
} catch {
|
|
1291
|
+
authorizationError("unsupported BSON snapshot");
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
if (configuration && input instanceof Map) {
|
|
1295
|
+
return new Map([...input].map(([key, entry]) => [copy(key, depth + 1), copy(entry, depth + 1)]));
|
|
1296
|
+
}
|
|
1297
|
+
if (configuration && input instanceof Set) return new Set([...input].map((entry) => copy(entry, depth + 1)));
|
|
1298
|
+
const array = Array.isArray(input);
|
|
1299
|
+
if (array && input.length > 2e4) authorizationError("snapshot array exceeds structural limits");
|
|
1300
|
+
if (!array) authorizationRecord(input, "snapshot");
|
|
1301
|
+
const result = array ? [] : {};
|
|
1302
|
+
for (const key of Reflect.ownKeys(input)) {
|
|
1303
|
+
if (array && key === "length") continue;
|
|
1304
|
+
if (typeof key !== "string" || unsafeKeys.has(key)) authorizationError("unsafe snapshot key");
|
|
1305
|
+
if (array && !/^(0|[1-9]\d*)$/.test(key)) authorizationError("invalid array property");
|
|
1306
|
+
const descriptor = Object.getOwnPropertyDescriptor(input, key);
|
|
1307
|
+
if (!("value" in descriptor) || !descriptor.enumerable && configuration !== "document")
|
|
1308
|
+
authorizationError("accessor/non-enumerable input");
|
|
1309
|
+
Object.defineProperty(result, key, {
|
|
1310
|
+
value: copy(descriptor.value, depth + 1),
|
|
1311
|
+
enumerable: true,
|
|
1312
|
+
writable: true,
|
|
1313
|
+
configurable: true
|
|
1314
|
+
});
|
|
1315
|
+
}
|
|
1316
|
+
if (array) result.length = input.length;
|
|
1317
|
+
return Object.freeze(result);
|
|
1318
|
+
} finally {
|
|
1319
|
+
active.delete(input);
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
return copy(value, 0);
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
// src/acl/field-catalog.ts
|
|
1326
|
+
var safeSegment = (part) => !!part && part !== "sub" && !["__proto__", "constructor", "prototype", "_doc", "__v"].includes(part) && !/^[+$-]|[\s*]/.test(part) && !part.includes("[") && !part.includes("]") && !/^\d+$/.test(part) && ![...part].some((character) => character.charCodeAt(0) < 32 || character.charCodeAt(0) === 127);
|
|
1327
|
+
var isSafeOutputPath = (path) => path.split(".").every(safeSegment);
|
|
1328
|
+
var inputControlSegments = /* @__PURE__ */ new Set([
|
|
1329
|
+
"_permissions",
|
|
1330
|
+
"_view",
|
|
1331
|
+
"_edit",
|
|
1332
|
+
"documentAuthorization",
|
|
1333
|
+
"documentAuthorizationStage",
|
|
1334
|
+
"resolvedAuthorization",
|
|
1335
|
+
"execution",
|
|
1336
|
+
"executionContext",
|
|
1337
|
+
"serviceExecutionContext",
|
|
1338
|
+
"preparedQuery",
|
|
1339
|
+
"preparedFilter",
|
|
1340
|
+
"preparedModelQuery",
|
|
1341
|
+
"resolvedIdentifier",
|
|
1342
|
+
"skipParsing",
|
|
1343
|
+
"skipAuthorization",
|
|
1344
|
+
"alreadyAuthorized"
|
|
1345
|
+
]);
|
|
1346
|
+
function isSafeInputPath(path, requestIdentity = false) {
|
|
1347
|
+
return isSafeOutputPath(path) && path.split(".").every((part) => !inputControlSegments.has(part) && (requestIdentity || part !== "_id"));
|
|
1348
|
+
}
|
|
1349
|
+
function matchesCatalogPattern(pattern, path, descendants = false) {
|
|
1350
|
+
const expected = pattern.split(".");
|
|
1351
|
+
const actual = path.split(".");
|
|
1352
|
+
return (descendants ? actual.length >= expected.length : actual.length === expected.length) && expected.every((part, index) => part === "$*" || part === actual[index]);
|
|
1353
|
+
}
|
|
1354
|
+
function captureAuthorizationFieldCatalog(schema, virtuals, documentPermissionField = "_permissions") {
|
|
1355
|
+
const paths = /* @__PURE__ */ new Set();
|
|
1356
|
+
const containers = /* @__PURE__ */ new Set();
|
|
1357
|
+
const opaque = /* @__PURE__ */ new Set();
|
|
1358
|
+
const references = /* @__PURE__ */ new Set();
|
|
1359
|
+
const virtualPaths = /* @__PURE__ */ new Set();
|
|
1360
|
+
const virtualScopes = /* @__PURE__ */ new Set();
|
|
1361
|
+
const schemaVirtualPaths = /* @__PURE__ */ new Set();
|
|
1362
|
+
const arrays = /* @__PURE__ */ new Set();
|
|
1363
|
+
const protectedPaths = /* @__PURE__ */ new Set([documentPermissionField, "_doc", "$__", "__v"]);
|
|
1364
|
+
const add = (path) => {
|
|
1365
|
+
if (path.split(".").every((part) => part === "$*" || safeSegment(part))) paths.add(path);
|
|
1366
|
+
};
|
|
1367
|
+
const ancestors = (path) => {
|
|
1368
|
+
const parts = path.split(".");
|
|
1369
|
+
for (let n = 1; n < parts.length; n++) {
|
|
1370
|
+
const parent = parts.slice(0, n).join(".");
|
|
1371
|
+
add(parent);
|
|
1372
|
+
containers.add(parent);
|
|
1373
|
+
}
|
|
1374
|
+
};
|
|
1375
|
+
const visitType = (type, path, active) => {
|
|
1376
|
+
add(path);
|
|
1377
|
+
ancestors(path);
|
|
1378
|
+
const element = type.$embeddedSchemaType ?? type.embeddedSchemaType ?? type.caster;
|
|
1379
|
+
if (type.instance === "Array" || type.$isMongooseDocumentArray) arrays.add(path);
|
|
1380
|
+
if (type.options?.ref || type.options?.refPath || element?.options?.ref || element?.options?.refPath) {
|
|
1381
|
+
references.add(path);
|
|
1382
|
+
return;
|
|
1383
|
+
}
|
|
1384
|
+
if (type.$isSchemaMap || type.instance === "Map") {
|
|
1385
|
+
const value = type.$__schemaType;
|
|
1386
|
+
if (value) visitType(value, `${path}.$*`, active);
|
|
1387
|
+
else opaque.add(path);
|
|
1388
|
+
containers.delete(path);
|
|
1389
|
+
return;
|
|
1390
|
+
}
|
|
1391
|
+
const child = type.schema ?? element?.schema;
|
|
1392
|
+
if (child) {
|
|
1393
|
+
containers.add(path);
|
|
1394
|
+
if (!active.has(child)) visitSchema(child, path, /* @__PURE__ */ new Set([...active, child]));
|
|
1395
|
+
return;
|
|
1396
|
+
}
|
|
1397
|
+
if (type.instance === "Mixed" || element?.instance === "Mixed") opaque.add(path);
|
|
1398
|
+
};
|
|
1399
|
+
const visitSchema = (current, prefix, active) => {
|
|
1400
|
+
const version = current.options.versionKey;
|
|
1401
|
+
if (typeof version === "string") protectedPaths.add(prefix ? `${prefix}.${version}` : version);
|
|
1402
|
+
for (const name of Object.keys(current.virtuals)) {
|
|
1403
|
+
schemaVirtualPaths.add(prefix ? `${prefix}.${name}` : name);
|
|
1404
|
+
}
|
|
1405
|
+
for (const [name, raw] of Object.entries(current.paths)) {
|
|
1406
|
+
if (name.includes("$*")) continue;
|
|
1407
|
+
visitType(raw, prefix ? `${prefix}.${name}` : name, active);
|
|
1408
|
+
}
|
|
1409
|
+
};
|
|
1410
|
+
visitSchema(schema, "", /* @__PURE__ */ new Set([schema]));
|
|
1411
|
+
const persistedPaths = [...paths];
|
|
1412
|
+
const visitVirtuals = (record2, prefix) => {
|
|
1413
|
+
for (const [name, value] of Object.entries(record2 ?? {})) {
|
|
1414
|
+
if (value === void 0) continue;
|
|
1415
|
+
const path = prefix ? `${prefix}.${name}` : name;
|
|
1416
|
+
if (!isSafeOutputPath(path)) continue;
|
|
1417
|
+
if (value && typeof value === "object" && "sub" in value) {
|
|
1418
|
+
if (references.has(path)) continue;
|
|
1419
|
+
virtualScopes.add(path);
|
|
1420
|
+
add(path);
|
|
1421
|
+
containers.add(path);
|
|
1422
|
+
visitVirtuals(value.sub, path);
|
|
1423
|
+
} else {
|
|
1424
|
+
add(path);
|
|
1425
|
+
ancestors(path);
|
|
1426
|
+
virtualPaths.add(path);
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
};
|
|
1430
|
+
visitVirtuals(virtuals, "");
|
|
1431
|
+
const eligible = [...paths].filter((p) => ![...protectedPaths].some((v) => matchesCatalogPattern(v, p, true)));
|
|
1432
|
+
return Object.freeze({
|
|
1433
|
+
paths: Object.freeze(eligible.filter((p) => !p.includes("$*"))),
|
|
1434
|
+
persistedPaths: Object.freeze(persistedPaths),
|
|
1435
|
+
arrayPaths: Object.freeze([...arrays]),
|
|
1436
|
+
schemaVirtualPaths: Object.freeze([...schemaVirtualPaths]),
|
|
1437
|
+
metadataPaths: Object.freeze([documentPermissionField]),
|
|
1438
|
+
dynamicPaths: Object.freeze(eligible.filter((p) => p.includes("$*"))),
|
|
1439
|
+
containerPaths: Object.freeze([...containers]),
|
|
1440
|
+
opaquePaths: Object.freeze([...opaque]),
|
|
1441
|
+
referencePaths: Object.freeze([...references]),
|
|
1442
|
+
virtualPaths: Object.freeze([...virtualPaths]),
|
|
1443
|
+
virtualScopePaths: Object.freeze([...virtualScopes]),
|
|
1444
|
+
protectedPaths: Object.freeze([...protectedPaths])
|
|
1445
|
+
});
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
// src/acl/virtual-populate-dependencies.ts
|
|
1449
|
+
var elementType = (type) => type.$embeddedSchemaType ?? type.embeddedSchemaType ?? type.caster;
|
|
1450
|
+
var join = (prefix, path) => prefix ? `${prefix}.${path}` : path;
|
|
1451
|
+
var within = (path, parent) => path === parent || path.startsWith(`${parent}.`);
|
|
1452
|
+
var accesses = ["default", "list", "create", "read", "update"];
|
|
1453
|
+
var fail = (label, message) => {
|
|
1454
|
+
throw new Error(`Virtual ${label}: dependsOnPopulate ${message}`);
|
|
1455
|
+
};
|
|
1456
|
+
var varyingSchema = (schema) => Object.keys(schema.discriminators ?? {}).length > 0 || schema.discriminatorMapping?.isRoot === false;
|
|
1457
|
+
var varyingModel = (model) => !!model.baseModelName || Object.keys(model.discriminators ?? {}).length > 0 || varyingSchema(model.schema);
|
|
1458
|
+
function record(value, label) {
|
|
1459
|
+
if (!(0, import_utils11.isPlainObject)(value)) return fail(label, "entries must be plain { path, select? } objects");
|
|
1460
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
1461
|
+
const property = Object.getOwnPropertyDescriptor(value, key);
|
|
1462
|
+
if (typeof key !== "string" || !property.enumerable || !("value" in property))
|
|
1463
|
+
fail(label, "requires enumerable data properties");
|
|
1464
|
+
}
|
|
1465
|
+
return value;
|
|
1466
|
+
}
|
|
1467
|
+
function targetSelectionCatalog(schema, options) {
|
|
1468
|
+
const leaves = /* @__PURE__ */ new Set();
|
|
1469
|
+
const containers = /* @__PURE__ */ new Set();
|
|
1470
|
+
const forbidden = /* @__PURE__ */ new Set([options.documentPermissionField ?? "_permissions"]);
|
|
1471
|
+
const visit = (current, prefix, active) => {
|
|
1472
|
+
const version = current.options.versionKey;
|
|
1473
|
+
if (typeof version === "string") forbidden.add(join(prefix, version));
|
|
1474
|
+
for (const name of Object.keys(current.virtuals)) forbidden.add(join(prefix, name));
|
|
1475
|
+
for (const name of Object.keys(current.aliases ?? {})) forbidden.add(join(prefix, name));
|
|
1476
|
+
for (const [name, raw] of Object.entries(current.paths)) {
|
|
1477
|
+
const path = join(prefix, name);
|
|
1478
|
+
if (!isSafeInputPath(path, true)) continue;
|
|
1479
|
+
const parts = path.split(".");
|
|
1480
|
+
for (let n = 1; n < parts.length; n++) containers.add(parts.slice(0, n).join("."));
|
|
1481
|
+
let type = raw;
|
|
1482
|
+
while (type.instance === "Array" && elementType(type)) type = elementType(type);
|
|
1483
|
+
if (type.instance === "Mixed" || type.instance === "Map") {
|
|
1484
|
+
forbidden.add(path);
|
|
1485
|
+
continue;
|
|
1486
|
+
}
|
|
1487
|
+
const child = type.schema ?? raw.schema;
|
|
1488
|
+
if (raw.options?.ref !== void 0 || raw.options?.refPath !== void 0 || type.options?.ref !== void 0 || type.options?.refPath !== void 0) {
|
|
1489
|
+
if (child) forbidden.add(path);
|
|
1490
|
+
else leaves.add(path);
|
|
1491
|
+
continue;
|
|
1492
|
+
}
|
|
1493
|
+
if (child) {
|
|
1494
|
+
if (active.has(child) || varyingSchema(child)) forbidden.add(path);
|
|
1495
|
+
else {
|
|
1496
|
+
containers.add(path);
|
|
1497
|
+
visit(child, path, /* @__PURE__ */ new Set([...active, child]));
|
|
1498
|
+
}
|
|
1499
|
+
} else leaves.add(path);
|
|
1500
|
+
}
|
|
1501
|
+
};
|
|
1502
|
+
visit(schema, "", /* @__PURE__ */ new Set([schema]));
|
|
1503
|
+
const visitVirtuals = (virtuals, prefix) => {
|
|
1504
|
+
for (const [name, entry] of Object.entries(virtuals)) {
|
|
1505
|
+
if (entry === void 0) continue;
|
|
1506
|
+
const path = join(prefix, name);
|
|
1507
|
+
if (isVirtualContainerValue(entry)) visitVirtuals(entry.sub, path);
|
|
1508
|
+
else forbidden.add(path);
|
|
1509
|
+
}
|
|
1510
|
+
};
|
|
1511
|
+
visitVirtuals(options.virtuals ?? {}, "");
|
|
1512
|
+
const allowed = (path) => ![...forbidden].some((blocked) => within(path, blocked));
|
|
1513
|
+
return {
|
|
1514
|
+
leaves: [...leaves].filter(allowed).sort(),
|
|
1515
|
+
paths: new Set([...leaves, ...containers].filter(allowed))
|
|
1516
|
+
};
|
|
1517
|
+
}
|
|
1518
|
+
function normalizeSelection(select, catalog, label) {
|
|
1519
|
+
if (select !== void 0 && typeof select !== "string") {
|
|
1520
|
+
if (Array.isArray(select)) {
|
|
1521
|
+
if (Array.from(select).some((token) => typeof token !== "string")) fail(label, "select arrays require strings");
|
|
1522
|
+
} else {
|
|
1523
|
+
const projection = record(select, label);
|
|
1524
|
+
for (const [path, value] of Object.entries(projection)) {
|
|
1525
|
+
if (!isSafeInputPath(path, true) || value !== 1 && value !== -1)
|
|
1526
|
+
fail(label, "select objects require safe persisted paths with 1/-1 values");
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
const tokens = normalizeSelect(select);
|
|
1531
|
+
for (const token of tokens) {
|
|
1532
|
+
const path = token.startsWith("-") ? token.slice(1) : token;
|
|
1533
|
+
if (!isSafeInputPath(path, true) || !catalog.paths.has(path))
|
|
1534
|
+
fail(label, `select path "${path}" is not an eligible static persisted target path`);
|
|
1535
|
+
}
|
|
1536
|
+
const positive = tokens.filter((token) => !token.startsWith("-"));
|
|
1537
|
+
const negative = tokens.filter((token) => token.startsWith("-")).map((token) => token.slice(1));
|
|
1538
|
+
const selection = catalog.leaves.filter(
|
|
1539
|
+
(path) => (path === "_id" || positive.length === 0 || positive.some((included) => within(path, included))) && !negative.some((excluded) => within(path, excluded))
|
|
1540
|
+
);
|
|
1541
|
+
return {
|
|
1542
|
+
selection: Object.freeze(selection),
|
|
1543
|
+
fetchSelection: Object.freeze([.../* @__PURE__ */ new Set(["_id", ...selection])].sort()),
|
|
1544
|
+
includeId: selection.includes("_id")
|
|
1545
|
+
};
|
|
1546
|
+
}
|
|
1547
|
+
function captureVirtualPopulateDependencies(runtime, modelName, options) {
|
|
1548
|
+
const model = runtime.getModelInstance(modelName);
|
|
1549
|
+
const targets = /* @__PURE__ */ new Map();
|
|
1550
|
+
const scopes = [];
|
|
1551
|
+
const visit = (virtuals, schema, schemaPrefix, scopePath, unsupportedScope) => {
|
|
1552
|
+
const sourcePrefix = scopePath.filter((_, index) => index % 2 === 0).join(".");
|
|
1553
|
+
const captureList = (value, label) => {
|
|
1554
|
+
if (value === void 0) return Object.freeze([]);
|
|
1555
|
+
if (!Array.isArray(value)) return fail(label, "must be an array");
|
|
1556
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1557
|
+
return Object.freeze(
|
|
1558
|
+
Array.from(value, (entry) => {
|
|
1559
|
+
const dependency = record(entry, label);
|
|
1560
|
+
if (Object.keys(dependency).some((key) => key !== "path" && key !== "select"))
|
|
1561
|
+
fail(label, "accepts only path/select");
|
|
1562
|
+
const path = dependency.path;
|
|
1563
|
+
if (typeof path !== "string" || path.includes(".") || !isSafeInputPath(path, true))
|
|
1564
|
+
return fail(label, "path must be a safe scope-local persisted reference key");
|
|
1565
|
+
if (seen.has(path)) fail(label, `duplicate path "${path}"`);
|
|
1566
|
+
seen.add(path);
|
|
1567
|
+
if (within(join(sourcePrefix, path), options.documentPermissionField ?? "_permissions") || path === schema?.options.versionKey)
|
|
1568
|
+
fail(label, "source permission metadata/version paths are not reference inputs");
|
|
1569
|
+
if (!model || !schema || unsupportedScope)
|
|
1570
|
+
return fail(label, "requires a static embedded, non-discriminator source scope");
|
|
1571
|
+
const type = schema.path(join(schemaPrefix, path));
|
|
1572
|
+
const isArray6 = type?.instance === "Array";
|
|
1573
|
+
const scalar = type && (isArray6 ? elementType(type) : type);
|
|
1574
|
+
const ref = type?.options?.ref ?? scalar?.options?.ref;
|
|
1575
|
+
if (!scalar || scalar.instance !== "ObjectId" || typeof ref !== "string" || !ref || type?.options?.refPath !== void 0 || scalar.options?.refPath !== void 0)
|
|
1576
|
+
return fail(label, `path "${path}" requires a static string ref on an ObjectId scalar or 1D array`);
|
|
1577
|
+
let captured = targets.get(ref);
|
|
1578
|
+
if (!captured) {
|
|
1579
|
+
if (ref !== modelName && !runtime.hasModel(ref))
|
|
1580
|
+
return fail(label, `target "${ref}" must be configured first in this runtime`);
|
|
1581
|
+
const targetModel = runtime.getModelInstance(ref);
|
|
1582
|
+
if (!targetModel || targetModel.db !== model.db || model.db.models[ref] !== targetModel || varyingModel(targetModel) || targetModel.schema.path("_id")?.instance !== "ObjectId")
|
|
1583
|
+
return fail(
|
|
1584
|
+
label,
|
|
1585
|
+
`target "${ref}" requires a registered same-connection non-discriminator ObjectId model bound to the source ref`
|
|
1586
|
+
);
|
|
1587
|
+
const targetOptions = ref === modelName ? options : runtime.getModelOptions(ref);
|
|
1588
|
+
const defaults = runtime.getDefaultModelOptions();
|
|
1589
|
+
const readOption = (key) => resolveNestedOption((path2, fallback) => (0, import_utils11.get)(targetOptions, path2, fallback), key, (0, import_utils11.get)(defaults, key));
|
|
1590
|
+
const documentPermissionField = targetOptions.documentPermissionField ?? defaults.documentPermissionField;
|
|
1591
|
+
const catalog = captureAuthorizationFieldCatalog(targetModel.schema, void 0, documentPermissionField);
|
|
1592
|
+
const configuration = copyAuthorizationValue(
|
|
1593
|
+
{
|
|
1594
|
+
catalog,
|
|
1595
|
+
documentPermissionField,
|
|
1596
|
+
operationAccess: { read: readOption("operationAccess.read") },
|
|
1597
|
+
baseFilter: { read: readOption("baseFilter.read") },
|
|
1598
|
+
overrideFilter: { read: readOption("overrideFilter.read") },
|
|
1599
|
+
hydration: targetOptions.hydration
|
|
1600
|
+
},
|
|
1601
|
+
true
|
|
1602
|
+
);
|
|
1603
|
+
captured = {
|
|
1604
|
+
target: Object.freeze({
|
|
1605
|
+
identity: Object.freeze({}),
|
|
1606
|
+
runtime,
|
|
1607
|
+
modelName: ref,
|
|
1608
|
+
model: targetModel,
|
|
1609
|
+
schema: targetModel.schema,
|
|
1610
|
+
configuration
|
|
1611
|
+
}),
|
|
1612
|
+
catalog: targetSelectionCatalog(targetModel.schema, { ...targetOptions, documentPermissionField })
|
|
1613
|
+
};
|
|
1614
|
+
targets.set(ref, captured);
|
|
1615
|
+
}
|
|
1616
|
+
return Object.freeze({
|
|
1617
|
+
path,
|
|
1618
|
+
sourcePath: join(sourcePrefix, path),
|
|
1619
|
+
cardinality: isArray6 ? "array" : "scalar",
|
|
1620
|
+
target: captured.target,
|
|
1621
|
+
...normalizeSelection(dependency.select, captured.catalog, label)
|
|
1622
|
+
});
|
|
1623
|
+
})
|
|
1624
|
+
);
|
|
1625
|
+
};
|
|
1626
|
+
for (const [name, raw] of Object.entries(virtuals)) {
|
|
1627
|
+
if (isVirtualContainerValue(raw)) {
|
|
1628
|
+
const path = join(schemaPrefix, name);
|
|
1629
|
+
const type = schema?.path(path);
|
|
1630
|
+
const element = type && elementType(type);
|
|
1631
|
+
const child = type?.schema ?? element?.schema;
|
|
1632
|
+
const nonEmbedded = [type, element].some(
|
|
1633
|
+
(entry2) => entry2 && (entry2.options?.ref !== void 0 || entry2.options?.refPath !== void 0 || entry2.instance === "Map" || entry2.instance === "Mixed")
|
|
1634
|
+
);
|
|
1635
|
+
visit(
|
|
1636
|
+
raw.sub,
|
|
1637
|
+
child ?? schema,
|
|
1638
|
+
child ? "" : path,
|
|
1639
|
+
[...scopePath, name, "sub"],
|
|
1640
|
+
unsupportedScope || nonEmbedded || !!(child && varyingSchema(child))
|
|
1641
|
+
);
|
|
1642
|
+
continue;
|
|
1643
|
+
}
|
|
1644
|
+
if (!(0, import_utils11.isPlainObject)(raw)) continue;
|
|
1645
|
+
const entry = raw;
|
|
1646
|
+
const label = join(sourcePrefix, name);
|
|
1647
|
+
captureList(entry.dependsOnPopulate, label);
|
|
1648
|
+
if (!("get" in entry))
|
|
1649
|
+
for (const access of accesses) {
|
|
1650
|
+
if ((0, import_utils11.isPlainObject)(entry[access]))
|
|
1651
|
+
captureList(entry[access].dependsOnPopulate, `${label}.${access}`);
|
|
1652
|
+
}
|
|
1653
|
+
const byAccess = {};
|
|
1654
|
+
let declared = false;
|
|
1655
|
+
for (const access of accesses) {
|
|
1656
|
+
const descriptor = resolveSnapshotDescriptor(virtuals, name, access);
|
|
1657
|
+
if (descriptor) {
|
|
1658
|
+
byAccess[access] = captureList(descriptor.dependsOnPopulate, `${label}.${access}`);
|
|
1659
|
+
declared ||= descriptor.dependsOnPopulate !== void 0;
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
if (declared)
|
|
1663
|
+
scopes.push(
|
|
1664
|
+
Object.freeze({
|
|
1665
|
+
scopePath: Object.freeze([...scopePath]),
|
|
1666
|
+
virtualName: name,
|
|
1667
|
+
byAccess: Object.freeze(byAccess)
|
|
1668
|
+
})
|
|
1669
|
+
);
|
|
1670
|
+
}
|
|
1671
|
+
};
|
|
1672
|
+
visit(options.virtuals ?? {}, model?.schema, "", [], !!model && varyingModel(model));
|
|
1673
|
+
return Object.freeze({ scopes: Object.freeze(scopes) });
|
|
1674
|
+
}
|
|
1675
|
+
|
|
1183
1676
|
// src/runtime.ts
|
|
1184
1677
|
var mongooseJsonSchemaInitialized = false;
|
|
1185
1678
|
var ensureMongooseJsonSchemaInitialized = () => {
|
|
1186
1679
|
if (mongooseJsonSchemaInitialized) return;
|
|
1187
|
-
(0, import_mongoose_schema_jsonschema.default)(
|
|
1680
|
+
(0, import_mongoose_schema_jsonschema.default)(import_mongoose3.default);
|
|
1188
1681
|
mongooseJsonSchemaInitialized = true;
|
|
1189
1682
|
};
|
|
1190
|
-
var pluralize =
|
|
1683
|
+
var pluralize = import_mongoose3.default.pluralize();
|
|
1191
1684
|
var FIELD_ACCESS_KEYS = ["list", "create", "read", "update"];
|
|
1192
1685
|
var defaultModelOptions = {
|
|
1193
1686
|
basePath: null,
|
|
@@ -1200,44 +1693,44 @@ var defaultDataOptions = {
|
|
|
1200
1693
|
listHardLimit: DEFAULT_LIST_HARD_LIMIT
|
|
1201
1694
|
};
|
|
1202
1695
|
var normalizeBasePath = (name, value) => {
|
|
1203
|
-
if ((0,
|
|
1696
|
+
if ((0, import_utils12.isNil)(value)) {
|
|
1204
1697
|
return `/${pluralize(name)}`;
|
|
1205
1698
|
}
|
|
1206
|
-
return (0,
|
|
1699
|
+
return (0, import_utils12.isString)(value) ? (0, import_utils12.addLeadingSlash)(value) : "";
|
|
1207
1700
|
};
|
|
1208
1701
|
var hasModelPermission = (value, modelPermissionPrefix) => {
|
|
1209
1702
|
if (!modelPermissionPrefix) {
|
|
1210
1703
|
return true;
|
|
1211
1704
|
}
|
|
1212
|
-
if ((0,
|
|
1705
|
+
if ((0, import_utils12.isString)(value)) {
|
|
1213
1706
|
return value.trim().split(" ").some((item) => item.startsWith(modelPermissionPrefix));
|
|
1214
1707
|
}
|
|
1215
|
-
if ((0,
|
|
1708
|
+
if ((0, import_utils12.isArray)(value)) {
|
|
1216
1709
|
return value.some((item) => {
|
|
1217
|
-
if ((0,
|
|
1710
|
+
if ((0, import_utils12.isString)(item) || (0, import_utils12.isArray)(item)) {
|
|
1218
1711
|
return hasModelPermission(item, modelPermissionPrefix);
|
|
1219
1712
|
}
|
|
1220
1713
|
return true;
|
|
1221
1714
|
});
|
|
1222
1715
|
}
|
|
1223
|
-
return (0,
|
|
1716
|
+
return (0, import_utils12.isFunction)(value);
|
|
1224
1717
|
};
|
|
1225
1718
|
var classifyPermissionSchema = (permissionSchema, modelPermissionPrefix) => {
|
|
1226
1719
|
const schemaKeys = Object.keys(permissionSchema);
|
|
1227
1720
|
const globalPermissionKeys = {};
|
|
1228
1721
|
const modelPermissionKeys = {};
|
|
1229
|
-
(0,
|
|
1722
|
+
(0, import_utils12.forEach)(schemaKeys, (schemaKey) => {
|
|
1230
1723
|
const schemaRule = permissionSchema[schemaKey];
|
|
1231
|
-
const ruleEntries = (0,
|
|
1724
|
+
const ruleEntries = (0, import_utils12.isPlainObject)(schemaRule) ? Object.entries(schemaRule) : FIELD_ACCESS_KEYS.map((accessKey) => [accessKey, schemaRule]);
|
|
1232
1725
|
for (let x = 0; x < ruleEntries.length; x++) {
|
|
1233
1726
|
const [accessKey, value] = ruleEntries[x];
|
|
1234
|
-
if (!(0,
|
|
1727
|
+
if (!(0, import_utils12.isArray)(globalPermissionKeys[accessKey])) {
|
|
1235
1728
|
globalPermissionKeys[accessKey] = [];
|
|
1236
1729
|
}
|
|
1237
|
-
if (!(0,
|
|
1730
|
+
if (!(0, import_utils12.isArray)(modelPermissionKeys[accessKey])) {
|
|
1238
1731
|
modelPermissionKeys[accessKey] = [];
|
|
1239
1732
|
}
|
|
1240
|
-
if ((0,
|
|
1733
|
+
if ((0, import_utils12.isBoolean)(value)) {
|
|
1241
1734
|
globalPermissionKeys[accessKey].push(schemaKey);
|
|
1242
1735
|
continue;
|
|
1243
1736
|
}
|
|
@@ -1269,13 +1762,13 @@ var VIRTUAL_UNSUPPORTED_ACCESSES = ["delete", "distinct", "count"];
|
|
|
1269
1762
|
var VIRTUAL_RESERVED_NAMES = /* @__PURE__ */ new Set(["_id", "__v", "id"]);
|
|
1270
1763
|
var VIRTUAL_DANGEROUS_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
|
|
1271
1764
|
var VIRTUAL_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_$]*$/;
|
|
1272
|
-
var
|
|
1765
|
+
var isVirtualContainerValue2 = (value) => (0, import_utils12.isPlainObject)(value) && "sub" in value;
|
|
1273
1766
|
var virtualFullPath = (prefix, name) => prefix ? `${prefix}.${name}` : name;
|
|
1274
1767
|
var virtualPathsOverlap = (virtualPath, permissionField) => {
|
|
1275
1768
|
if (!virtualPath || !permissionField) return false;
|
|
1276
1769
|
try {
|
|
1277
|
-
if ((0,
|
|
1278
|
-
if ((0,
|
|
1770
|
+
if ((0, import_utils12.get)((0, import_utils12.set)({}, virtualPath, true), permissionField) !== void 0) return true;
|
|
1771
|
+
if ((0, import_utils12.get)((0, import_utils12.set)({}, permissionField, true), virtualPath) !== void 0) return true;
|
|
1279
1772
|
} catch {
|
|
1280
1773
|
return false;
|
|
1281
1774
|
}
|
|
@@ -1336,12 +1829,12 @@ var cloneVirtualsForValidation = (value) => {
|
|
|
1336
1829
|
};
|
|
1337
1830
|
var validateVirtualDependsOn = (dependsOn, entryLabel, storedFields, siblingVirtualNames) => {
|
|
1338
1831
|
if (dependsOn === void 0) return [];
|
|
1339
|
-
if (!(0,
|
|
1832
|
+
if (!(0, import_utils12.isArray)(dependsOn)) {
|
|
1340
1833
|
throw new Error(`Virtual ${entryLabel}: dependsOn must be an array of top-level persisted field names`);
|
|
1341
1834
|
}
|
|
1342
1835
|
const list = dependsOn;
|
|
1343
1836
|
for (const dep of list) {
|
|
1344
|
-
if (!(0,
|
|
1837
|
+
if (!(0, import_utils12.isString)(dep) || dep.length === 0) {
|
|
1345
1838
|
throw new Error(`Virtual ${entryLabel}: dependsOn entries must be non-empty strings`);
|
|
1346
1839
|
}
|
|
1347
1840
|
if (dep.includes(".")) {
|
|
@@ -1363,18 +1856,20 @@ var validateVirtualDependsOn = (dependsOn, entryLabel, storedFields, siblingVirt
|
|
|
1363
1856
|
return list;
|
|
1364
1857
|
};
|
|
1365
1858
|
var validateVirtualLeafValue = (value, entryLabel, storedFields, siblingVirtualNames, sharedDependsOn) => {
|
|
1366
|
-
if ((0,
|
|
1367
|
-
if (!(0,
|
|
1859
|
+
if ((0, import_utils12.isFunction)(value)) return;
|
|
1860
|
+
if (!(0, import_utils12.isPlainObject)(value)) {
|
|
1368
1861
|
throw new Error(`Virtual ${entryLabel}: descriptor must be a getter function or { get, dependsOn }`);
|
|
1369
1862
|
}
|
|
1370
|
-
const
|
|
1371
|
-
if (!(0,
|
|
1863
|
+
const record2 = value;
|
|
1864
|
+
if (!(0, import_utils12.isFunction)(record2.get)) {
|
|
1372
1865
|
throw new Error(`Virtual ${entryLabel}: descriptor.get must be a callable getter function`);
|
|
1373
1866
|
}
|
|
1374
|
-
validateVirtualDependsOn(
|
|
1375
|
-
for (const k of Object.keys(
|
|
1376
|
-
if (k !== "get" && k !== "dependsOn") {
|
|
1377
|
-
throw new Error(
|
|
1867
|
+
validateVirtualDependsOn(record2.dependsOn ?? sharedDependsOn, entryLabel, storedFields, siblingVirtualNames);
|
|
1868
|
+
for (const k of Object.keys(record2)) {
|
|
1869
|
+
if (k !== "get" && k !== "dependsOn" && k !== "dependsOnPopulate") {
|
|
1870
|
+
throw new Error(
|
|
1871
|
+
`Virtual ${entryLabel}: unsupported descriptor key "${k}" (expected get/dependsOn/dependsOnPopulate)`
|
|
1872
|
+
);
|
|
1378
1873
|
}
|
|
1379
1874
|
}
|
|
1380
1875
|
};
|
|
@@ -1383,7 +1878,7 @@ var validateVirtualsScope = (scope, documentPermissionField) => {
|
|
|
1383
1878
|
const leafNames = /* @__PURE__ */ new Set();
|
|
1384
1879
|
for (const [name, raw] of Object.entries(scopeVirtuals)) {
|
|
1385
1880
|
if (raw === void 0) continue;
|
|
1386
|
-
if (
|
|
1881
|
+
if (isVirtualContainerValue2(raw)) continue;
|
|
1387
1882
|
leafNames.add(name);
|
|
1388
1883
|
}
|
|
1389
1884
|
for (const [name, raw] of Object.entries(scopeVirtuals)) {
|
|
@@ -1400,15 +1895,15 @@ var validateVirtualsScope = (scope, documentPermissionField) => {
|
|
|
1400
1895
|
`Virtual ${scopeLabel}${name}: overlaps document permission field "${documentPermissionField}" (equal/ancestor/descendant rejected)`
|
|
1401
1896
|
);
|
|
1402
1897
|
}
|
|
1403
|
-
if (
|
|
1404
|
-
const
|
|
1405
|
-
const extra = Object.keys(
|
|
1898
|
+
if (isVirtualContainerValue2(raw)) {
|
|
1899
|
+
const record3 = raw;
|
|
1900
|
+
const extra = Object.keys(record3).filter((k) => k !== "sub");
|
|
1406
1901
|
if (extra.length > 0) {
|
|
1407
1902
|
throw new Error(
|
|
1408
1903
|
`Virtual ${scopeLabel}${name}: scope-container must only hold "sub" (embedded container "${name}" is not a virtual-leaf collision)`
|
|
1409
1904
|
);
|
|
1410
1905
|
}
|
|
1411
|
-
if (!(0,
|
|
1906
|
+
if (!(0, import_utils12.isPlainObject)(record3.sub)) {
|
|
1412
1907
|
throw new Error(`Virtual ${scopeLabel}${name}: scope-container "sub" must be a plain object`);
|
|
1413
1908
|
}
|
|
1414
1909
|
continue;
|
|
@@ -1418,19 +1913,19 @@ var validateVirtualsScope = (scope, documentPermissionField) => {
|
|
|
1418
1913
|
`Virtual ${scopeLabel}${name}: collides with a stored path (inspected actual Mongoose schema/child scope; getModelAtt alone is insufficient)`
|
|
1419
1914
|
);
|
|
1420
1915
|
}
|
|
1421
|
-
if ((0,
|
|
1422
|
-
if (!(0,
|
|
1916
|
+
if ((0, import_utils12.isFunction)(raw)) continue;
|
|
1917
|
+
if (!(0, import_utils12.isPlainObject)(raw)) {
|
|
1423
1918
|
throw new Error(
|
|
1424
1919
|
`Virtual ${scopeLabel}${name}: malformed descriptor (expected getter, { get, dependsOn }, or per-access record)`
|
|
1425
1920
|
);
|
|
1426
1921
|
}
|
|
1427
|
-
const
|
|
1428
|
-
const hasGet = "get" in
|
|
1429
|
-
const accessKeys = Object.keys(
|
|
1430
|
-
const unsupported2 = Object.keys(
|
|
1431
|
-
(k) => k !== "get" && k !== "dependsOn" && k !== "sub" && !VIRTUAL_SUPPORTED_ACCESSES.includes(k)
|
|
1922
|
+
const record2 = raw;
|
|
1923
|
+
const hasGet = "get" in record2;
|
|
1924
|
+
const accessKeys = Object.keys(record2).filter((k) => VIRTUAL_SUPPORTED_ACCESSES.includes(k));
|
|
1925
|
+
const unsupported2 = Object.keys(record2).filter(
|
|
1926
|
+
(k) => k !== "get" && k !== "dependsOn" && k !== "dependsOnPopulate" && k !== "sub" && !VIRTUAL_SUPPORTED_ACCESSES.includes(k)
|
|
1432
1927
|
);
|
|
1433
|
-
for (const key of Object.keys(
|
|
1928
|
+
for (const key of Object.keys(record2)) {
|
|
1434
1929
|
if (VIRTUAL_UNSUPPORTED_ACCESSES.includes(key)) {
|
|
1435
1930
|
throw new Error(
|
|
1436
1931
|
`Virtual ${scopeLabel}${name}: unsupported access "${key}" (supported: default + list/create/read/update; NOT delete/distinct/count)`
|
|
@@ -1448,21 +1943,21 @@ var validateVirtualsScope = (scope, documentPermissionField) => {
|
|
|
1448
1943
|
`Virtual ${scopeLabel}${name}: cannot mix "get" with per-access keys (use either bare { get, dependsOn } or a per-access record)`
|
|
1449
1944
|
);
|
|
1450
1945
|
}
|
|
1451
|
-
if ("sub" in
|
|
1946
|
+
if ("sub" in record2) {
|
|
1452
1947
|
throw new Error(`Virtual ${scopeLabel}${name}: cannot mix "get" with "sub" (leaf vs embedded container)`);
|
|
1453
1948
|
}
|
|
1454
1949
|
validateVirtualLeafValue(raw, `${scopeLabel}${name}`, storedFields, leafNames, void 0);
|
|
1455
1950
|
continue;
|
|
1456
1951
|
}
|
|
1457
1952
|
if (accessKeys.length > 0) {
|
|
1458
|
-
if ("sub" in
|
|
1953
|
+
if ("sub" in record2) {
|
|
1459
1954
|
throw new Error(`Virtual ${scopeLabel}${name}: cannot mix per-access keys with "sub"`);
|
|
1460
1955
|
}
|
|
1461
|
-
validateVirtualDependsOn(
|
|
1956
|
+
validateVirtualDependsOn(record2.dependsOn, `${scopeLabel}${name}`, storedFields, leafNames);
|
|
1462
1957
|
for (const access of accessKeys) {
|
|
1463
|
-
const leaf =
|
|
1958
|
+
const leaf = record2[access];
|
|
1464
1959
|
if (leaf === void 0) continue;
|
|
1465
|
-
validateVirtualLeafValue(leaf, `${scopeLabel}${name}.${access}`, storedFields, leafNames,
|
|
1960
|
+
validateVirtualLeafValue(leaf, `${scopeLabel}${name}.${access}`, storedFields, leafNames, record2.dependsOn);
|
|
1466
1961
|
}
|
|
1467
1962
|
continue;
|
|
1468
1963
|
}
|
|
@@ -1473,7 +1968,7 @@ var validateVirtualsScope = (scope, documentPermissionField) => {
|
|
|
1473
1968
|
};
|
|
1474
1969
|
var validateVirtualsTree = (model, virtuals, documentPermissionField, modelName) => {
|
|
1475
1970
|
if (virtuals === void 0 || virtuals === null) return;
|
|
1476
|
-
if (!(0,
|
|
1971
|
+
if (!(0, import_utils12.isPlainObject)(virtuals)) {
|
|
1477
1972
|
throw new Error(`Virtuals for model "${modelName}": virtuals must be a plain object mapping names to descriptors`);
|
|
1478
1973
|
}
|
|
1479
1974
|
const rootSchema = model?.schema ?? null;
|
|
@@ -1483,13 +1978,14 @@ var validateVirtualsTree = (model, virtuals, documentPermissionField, modelName)
|
|
|
1483
1978
|
{ scopeVirtuals: rootVirtuals, storedFields: rootStored, scopePrefix: "", scopeLabel: "" },
|
|
1484
1979
|
documentPermissionField
|
|
1485
1980
|
);
|
|
1486
|
-
const visitContainers = (currentVirtuals, currentSchema, currentPrefix) => {
|
|
1981
|
+
const visitContainers = (currentVirtuals, currentSchema, currentPrefix, schemaPrefix = "") => {
|
|
1487
1982
|
for (const [containerName, raw] of Object.entries(currentVirtuals)) {
|
|
1488
|
-
if (raw === void 0 || !
|
|
1489
|
-
const
|
|
1490
|
-
const
|
|
1491
|
-
const
|
|
1492
|
-
const
|
|
1983
|
+
if (raw === void 0 || !isVirtualContainerValue2(raw)) continue;
|
|
1984
|
+
const record2 = raw;
|
|
1985
|
+
const schemaPath = virtualFullPath(schemaPrefix, containerName);
|
|
1986
|
+
const childSchema = getChildSchemaForField(currentSchema, schemaPath);
|
|
1987
|
+
const nestedFields = getNestedChildFields(currentSchema, schemaPath);
|
|
1988
|
+
const currentStored = schemaPrefix ? getNestedChildFields(currentSchema, schemaPrefix) : collectStoredTopLevel(currentSchema);
|
|
1493
1989
|
if (!currentStored.has(containerName)) {
|
|
1494
1990
|
throw new Error(
|
|
1495
1991
|
`Virtual ${containerName}: scope-container "${containerName}" is not a persisted field in its scope`
|
|
@@ -1503,7 +1999,7 @@ var validateVirtualsTree = (model, virtuals, documentPermissionField, modelName)
|
|
|
1503
1999
|
}
|
|
1504
2000
|
const subStored = childSchema ? collectStoredTopLevel(childSchema) : nestedFields;
|
|
1505
2001
|
const subPrefix = currentPrefix ? `${currentPrefix}.${containerName}` : containerName;
|
|
1506
|
-
const subVirtuals =
|
|
2002
|
+
const subVirtuals = record2.sub ?? {};
|
|
1507
2003
|
validateVirtualsScope(
|
|
1508
2004
|
{
|
|
1509
2005
|
scopeVirtuals: subVirtuals,
|
|
@@ -1513,37 +2009,11 @@ var validateVirtualsTree = (model, virtuals, documentPermissionField, modelName)
|
|
|
1513
2009
|
},
|
|
1514
2010
|
documentPermissionField
|
|
1515
2011
|
);
|
|
1516
|
-
|
|
1517
|
-
visitContainers(subVirtuals, nextSchema, subPrefix);
|
|
2012
|
+
visitContainers(subVirtuals, childSchema ?? currentSchema, subPrefix, childSchema ? "" : schemaPath);
|
|
1518
2013
|
}
|
|
1519
2014
|
};
|
|
1520
2015
|
visitContainers(rootVirtuals, rootSchema, "");
|
|
1521
2016
|
};
|
|
1522
|
-
var getScopeVirtualsObject = (rootVirtuals, scopePath) => {
|
|
1523
|
-
if (!rootVirtuals || !(0, import_utils10.isPlainObject)(rootVirtuals)) return null;
|
|
1524
|
-
if (!scopePath || scopePath.length === 0) return rootVirtuals;
|
|
1525
|
-
let current = rootVirtuals;
|
|
1526
|
-
for (let i = 0; i < scopePath.length; i += 2) {
|
|
1527
|
-
const field = scopePath[i];
|
|
1528
|
-
const subMarker = scopePath[i + 1];
|
|
1529
|
-
if (!field || subMarker !== "sub") return null;
|
|
1530
|
-
if (!(0, import_utils10.isPlainObject)(current)) return null;
|
|
1531
|
-
const container = current[field];
|
|
1532
|
-
if (!isVirtualContainerValue(container)) return null;
|
|
1533
|
-
current = container.sub;
|
|
1534
|
-
if (!(0, import_utils10.isPlainObject)(current)) return null;
|
|
1535
|
-
}
|
|
1536
|
-
return current;
|
|
1537
|
-
};
|
|
1538
|
-
var toVirtualDescriptor = (leaf, sharedDependsOn) => {
|
|
1539
|
-
if ((0, import_utils10.isFunction)(leaf))
|
|
1540
|
-
return { get: leaf, dependsOn: sharedDependsOn ?? [] };
|
|
1541
|
-
if (!(0, import_utils10.isPlainObject)(leaf)) return null;
|
|
1542
|
-
const record = leaf;
|
|
1543
|
-
if (!(0, import_utils10.isFunction)(record.get)) return null;
|
|
1544
|
-
const dependsOn = record.dependsOn ?? sharedDependsOn ?? [];
|
|
1545
|
-
return { get: record.get, dependsOn };
|
|
1546
|
-
};
|
|
1547
2017
|
var AccessRuntime = class {
|
|
1548
2018
|
constructor(options = {}) {
|
|
1549
2019
|
this.globalOptions = new OptionsManager(
|
|
@@ -1592,20 +2062,20 @@ var AccessRuntime = class {
|
|
|
1592
2062
|
this.modelInstances[modelName] = model;
|
|
1593
2063
|
}
|
|
1594
2064
|
hasModelInstance(modelName) {
|
|
1595
|
-
return modelName in this.modelInstances || this.allowGlobalModelLookup && modelName in
|
|
2065
|
+
return modelName in this.modelInstances || this.allowGlobalModelLookup && modelName in import_mongoose3.default.models;
|
|
1596
2066
|
}
|
|
1597
2067
|
getModelInstance(modelName) {
|
|
1598
2068
|
const registered = this.modelInstances[modelName];
|
|
1599
2069
|
if (registered) return registered;
|
|
1600
2070
|
if (!this.allowGlobalModelLookup) return null;
|
|
1601
|
-
const global =
|
|
2071
|
+
const global = import_mongoose3.default.models[modelName];
|
|
1602
2072
|
if (!global) return null;
|
|
1603
2073
|
this.registerModelInstance(modelName, global);
|
|
1604
2074
|
return global;
|
|
1605
2075
|
}
|
|
1606
2076
|
registerOpenApiRoute(route) {
|
|
1607
2077
|
this.openApiRegistry.register(
|
|
1608
|
-
this.openApiPathPrefix ? { ...route, path: (0,
|
|
2078
|
+
this.openApiPathPrefix ? { ...route, path: (0, import_utils12.normalizeUrlPath)(`${this.openApiPathPrefix}/${route.path}`) } : route
|
|
1609
2079
|
);
|
|
1610
2080
|
}
|
|
1611
2081
|
/**
|
|
@@ -1616,7 +2086,7 @@ var AccessRuntime = class {
|
|
|
1616
2086
|
*/
|
|
1617
2087
|
withOpenApiPathPrefix(prefix, build) {
|
|
1618
2088
|
const previous = this.openApiPathPrefix;
|
|
1619
|
-
this.openApiPathPrefix = (0,
|
|
2089
|
+
this.openApiPathPrefix = (0, import_utils12.normalizeUrlPath)(`${previous}/${prefix}`);
|
|
1620
2090
|
try {
|
|
1621
2091
|
return build();
|
|
1622
2092
|
} finally {
|
|
@@ -1752,7 +2222,7 @@ var AccessRuntime = class {
|
|
|
1752
2222
|
if (keyStr === "defaults") assertPublicDefaults(value);
|
|
1753
2223
|
else if (keyStr.startsWith("defaults.")) {
|
|
1754
2224
|
const proposed = {};
|
|
1755
|
-
(0,
|
|
2225
|
+
(0, import_utils12.set)(proposed, keyStr.slice("defaults.".length), value);
|
|
1756
2226
|
assertPublicDefaults(proposed);
|
|
1757
2227
|
}
|
|
1758
2228
|
this.defaultModelOptions.set(key, value);
|
|
@@ -1781,7 +2251,7 @@ var AccessRuntime = class {
|
|
|
1781
2251
|
}).onchange("basePath", function(newval, key, target) {
|
|
1782
2252
|
target[key] = normalizeBasePath(
|
|
1783
2253
|
modelName,
|
|
1784
|
-
(0,
|
|
2254
|
+
(0, import_utils12.isString)(newval) || (0, import_utils12.isNil)(newval) ? newval : void 0
|
|
1785
2255
|
);
|
|
1786
2256
|
}).build();
|
|
1787
2257
|
this.modelJsonSchemas[modelName] = model.jsonSchema();
|
|
@@ -1805,13 +2275,14 @@ var AccessRuntime = class {
|
|
|
1805
2275
|
setModelOptions(modelName, options) {
|
|
1806
2276
|
assertPublicDefaults(options.defaults);
|
|
1807
2277
|
if ("hydration" in options) options = { ...options, hydration: normalizeHydration(options.hydration) };
|
|
1808
|
-
const manager = this.getOrCreateModelOptions(modelName);
|
|
1809
2278
|
const defaultOptions = this.getDefaultModelOptions();
|
|
1810
|
-
const currentModelOptions =
|
|
2279
|
+
const currentModelOptions = this.modelOptions[modelName]?.fetch() ?? { ...defaultModelOptions, modelName };
|
|
1811
2280
|
const merged = { ...defaultOptions, ...currentModelOptions, ...options };
|
|
1812
2281
|
const model = this.getModelInstance(modelName);
|
|
1813
2282
|
const permissionField = merged.documentPermissionField ?? currentModelOptions.documentPermissionField ?? this.getDefaultModelOption("documentPermissionField") ?? "_permissions";
|
|
1814
2283
|
validateVirtualsTree(model, merged.virtuals, permissionField, modelName);
|
|
2284
|
+
captureVirtualPopulateDependencies(this, modelName, merged);
|
|
2285
|
+
const manager = this.getOrCreateModelOptions(modelName);
|
|
1815
2286
|
manager.assign({ ...defaultOptions, ...currentModelOptions, ...options });
|
|
1816
2287
|
}
|
|
1817
2288
|
setModelOption(modelName, key, value) {
|
|
@@ -1824,14 +2295,14 @@ var AccessRuntime = class {
|
|
|
1824
2295
|
if (keyStr === "defaults") assertPublicDefaults(value);
|
|
1825
2296
|
else if (keyStr.startsWith("defaults.")) {
|
|
1826
2297
|
const proposed = {};
|
|
1827
|
-
(0,
|
|
2298
|
+
(0, import_utils12.set)(proposed, keyStr.slice("defaults.".length), value);
|
|
1828
2299
|
assertPublicDefaults(proposed);
|
|
1829
2300
|
}
|
|
1830
|
-
const manager = this.
|
|
2301
|
+
const manager = this.modelOptions[modelName];
|
|
1831
2302
|
if (keyStr === "virtuals" || keyStr.startsWith("virtuals.") || keyStr === "documentPermissionField") {
|
|
1832
2303
|
const model = this.getModelInstance(modelName);
|
|
1833
|
-
const currentVirtuals = manager
|
|
1834
|
-
const currentPermissionField = manager
|
|
2304
|
+
const currentVirtuals = manager?.get("virtuals");
|
|
2305
|
+
const currentPermissionField = manager?.get("documentPermissionField") ?? this.getDefaultModelOption("documentPermissionField") ?? "_permissions";
|
|
1835
2306
|
let proposedVirtuals = currentVirtuals;
|
|
1836
2307
|
let proposedPermissionField = currentPermissionField;
|
|
1837
2308
|
if (keyStr === "documentPermissionField") {
|
|
@@ -1845,21 +2316,27 @@ var AccessRuntime = class {
|
|
|
1845
2316
|
const segments = nestedPath.split(".");
|
|
1846
2317
|
let cursor = cloned;
|
|
1847
2318
|
for (let i = 0; i < segments.length - 1; i++) {
|
|
1848
|
-
if (!(0,
|
|
2319
|
+
if (!(0, import_utils12.isPlainObject)(cursor)) break;
|
|
1849
2320
|
cursor = cursor[segments[i]];
|
|
1850
2321
|
}
|
|
1851
|
-
if ((0,
|
|
2322
|
+
if ((0, import_utils12.isPlainObject)(cursor)) {
|
|
1852
2323
|
delete cursor[segments[segments.length - 1]];
|
|
1853
2324
|
}
|
|
1854
2325
|
proposedVirtuals = cloned;
|
|
1855
2326
|
} else {
|
|
1856
|
-
(0,
|
|
2327
|
+
(0, import_utils12.set)(cloned, nestedPath, value);
|
|
1857
2328
|
proposedVirtuals = cloned;
|
|
1858
2329
|
}
|
|
1859
2330
|
}
|
|
1860
2331
|
validateVirtualsTree(model, proposedVirtuals, proposedPermissionField, modelName);
|
|
2332
|
+
captureVirtualPopulateDependencies(this, modelName, {
|
|
2333
|
+
...this.getDefaultModelOptions(),
|
|
2334
|
+
...manager?.fetch(),
|
|
2335
|
+
virtuals: proposedVirtuals,
|
|
2336
|
+
documentPermissionField: proposedPermissionField
|
|
2337
|
+
});
|
|
1861
2338
|
}
|
|
1862
|
-
|
|
2339
|
+
this.getOrCreateModelOptions(modelName).set(key, value);
|
|
1863
2340
|
}
|
|
1864
2341
|
/** Frozen stored snapshot, not fully resolved per-path defaults. Read again for current configuration. */
|
|
1865
2342
|
getModelOptions(modelName) {
|
|
@@ -1898,7 +2375,7 @@ var AccessRuntime = class {
|
|
|
1898
2375
|
const rootVirtuals = manager.get("virtuals");
|
|
1899
2376
|
const scopeVirtuals = getScopeVirtualsObject(rootVirtuals, scopePath);
|
|
1900
2377
|
if (!scopeVirtuals) return [];
|
|
1901
|
-
return Object.entries(scopeVirtuals).filter(([, v]) => v !== void 0 && !
|
|
2378
|
+
return Object.entries(scopeVirtuals).filter(([, v]) => v !== void 0 && !isVirtualContainerValue2(v)).map(([k]) => k);
|
|
1902
2379
|
}
|
|
1903
2380
|
/**
|
|
1904
2381
|
* Whether a field is a registered virtual leaf in a scope (VIRT-01).
|
|
@@ -1912,7 +2389,7 @@ var AccessRuntime = class {
|
|
|
1912
2389
|
const scopeVirtuals = getScopeVirtualsObject(rootVirtuals, scopePath);
|
|
1913
2390
|
if (!scopeVirtuals || !(fieldName in scopeVirtuals)) return false;
|
|
1914
2391
|
const entry = scopeVirtuals[fieldName];
|
|
1915
|
-
if (entry === void 0 ||
|
|
2392
|
+
if (entry === void 0 || isVirtualContainerValue2(entry)) return false;
|
|
1916
2393
|
return true;
|
|
1917
2394
|
}
|
|
1918
2395
|
/**
|
|
@@ -1922,34 +2399,14 @@ var AccessRuntime = class {
|
|
|
1922
2399
|
* interpreted as a getter descriptor when the access is absent: an
|
|
1923
2400
|
* inapplicable registered name stays virtual (excluded from persisted
|
|
1924
2401
|
* projections, write admission, and DB sort/filter/distinct).
|
|
1925
|
-
* Shared
|
|
2402
|
+
* Shared `dependsOn` and `dependsOnPopulate` lists inherit independently;
|
|
2403
|
+
* each defined leaf list replaces its shared list wholesale.
|
|
1926
2404
|
*/
|
|
1927
2405
|
resolveVirtualDescriptor(modelName, virtualName, access, scopePath) {
|
|
1928
2406
|
const manager = this.getOrCreateModelOptions(modelName);
|
|
1929
2407
|
const rootVirtuals = manager.get("virtuals");
|
|
1930
2408
|
const scopeVirtuals = getScopeVirtualsObject(rootVirtuals, scopePath);
|
|
1931
|
-
|
|
1932
|
-
const entry = scopeVirtuals[virtualName];
|
|
1933
|
-
if (entry === void 0 || isVirtualContainerValue(entry)) return void 0;
|
|
1934
|
-
if ((0, import_utils10.isFunction)(entry)) return { get: entry, dependsOn: [] };
|
|
1935
|
-
if (!(0, import_utils10.isPlainObject)(entry)) return void 0;
|
|
1936
|
-
const record = entry;
|
|
1937
|
-
if ("get" in record) {
|
|
1938
|
-
const descriptor = toVirtualDescriptor(entry, void 0);
|
|
1939
|
-
return descriptor ?? void 0;
|
|
1940
|
-
}
|
|
1941
|
-
const sharedDependsOn = record.dependsOn;
|
|
1942
|
-
const exact = record[access];
|
|
1943
|
-
if (exact !== void 0) {
|
|
1944
|
-
const descriptor = toVirtualDescriptor(exact, sharedDependsOn);
|
|
1945
|
-
if (descriptor) return descriptor;
|
|
1946
|
-
}
|
|
1947
|
-
const fallback = record.default;
|
|
1948
|
-
if (fallback !== void 0) {
|
|
1949
|
-
const descriptor = toVirtualDescriptor(fallback, sharedDependsOn);
|
|
1950
|
-
if (descriptor) return descriptor;
|
|
1951
|
-
}
|
|
1952
|
-
return void 0;
|
|
2409
|
+
return resolveSnapshotDescriptor(scopeVirtuals, virtualName, access);
|
|
1953
2410
|
}
|
|
1954
2411
|
getModelNames() {
|
|
1955
2412
|
return Object.keys(this.modelOptions);
|
|
@@ -1968,7 +2425,7 @@ var AccessRuntime = class {
|
|
|
1968
2425
|
manager.onchange("basePath", function(newval, key, target) {
|
|
1969
2426
|
target[key] = normalizeBasePath(
|
|
1970
2427
|
dataName,
|
|
1971
|
-
(0,
|
|
2428
|
+
(0, import_utils12.isString)(newval) || (0, import_utils12.isNil)(newval) ? newval : void 0
|
|
1972
2429
|
);
|
|
1973
2430
|
}).build();
|
|
1974
2431
|
return manager;
|
|
@@ -2028,20 +2485,20 @@ var AccessRuntime = class {
|
|
|
2028
2485
|
const schema = model.schema;
|
|
2029
2486
|
this.modelRefs[modelName] = buildRefs(schema.tree);
|
|
2030
2487
|
this.modelSubs[modelName] = buildSubPaths(schema.tree);
|
|
2031
|
-
this.modelAtts[modelName] = (0,
|
|
2488
|
+
this.modelAtts[modelName] = (0, import_utils12.keys)(schema.obj);
|
|
2032
2489
|
}
|
|
2033
2490
|
getModelRef(modelName, refPath) {
|
|
2034
2491
|
this.ensureModelMeta(modelName);
|
|
2035
|
-
const value = (0,
|
|
2036
|
-
return (0,
|
|
2492
|
+
const value = (0, import_utils12.get)(this.modelRefs, `${modelName}.${refPath}`, null);
|
|
2493
|
+
return (0, import_utils12.isString)(value) ? value : null;
|
|
2037
2494
|
}
|
|
2038
2495
|
getModelSub(modelName) {
|
|
2039
2496
|
this.ensureModelMeta(modelName);
|
|
2040
|
-
return (0,
|
|
2497
|
+
return (0, import_utils12.get)(this.modelSubs, modelName, []);
|
|
2041
2498
|
}
|
|
2042
2499
|
getModelAtt(modelName) {
|
|
2043
2500
|
this.ensureModelMeta(modelName);
|
|
2044
|
-
return (0,
|
|
2501
|
+
return (0, import_utils12.get)(this.modelAtts, modelName, []);
|
|
2045
2502
|
}
|
|
2046
2503
|
};
|
|
2047
2504
|
var defaultRuntime = new AccessRuntime({ allowGlobalModelLookup: true });
|
|
@@ -2061,7 +2518,7 @@ var getGlobalOption = (key, defaultValue) => {
|
|
|
2061
2518
|
|
|
2062
2519
|
// src/validation/common.ts
|
|
2063
2520
|
var import_zod2 = require("zod");
|
|
2064
|
-
var
|
|
2521
|
+
var import_utils14 = require("@web-ts-toolkit/utils");
|
|
2065
2522
|
|
|
2066
2523
|
// src/validation/public-admission-schema.ts
|
|
2067
2524
|
var admissions = /* @__PURE__ */ new WeakMap();
|
|
@@ -2083,7 +2540,7 @@ function assertTransformedPublicAdmission(schema, value) {
|
|
|
2083
2540
|
}
|
|
2084
2541
|
|
|
2085
2542
|
// src/correlated-includes.ts
|
|
2086
|
-
var
|
|
2543
|
+
var import_utils13 = require("@web-ts-toolkit/utils");
|
|
2087
2544
|
var PARENT_MARKER_KEY = "$parent";
|
|
2088
2545
|
var ESCAPE_WRAPPER_KEY = "$escape";
|
|
2089
2546
|
var SUBQUERY_PAYLOAD_KEYS = /* @__PURE__ */ new Set(["$$sq", "$$date"]);
|
|
@@ -2094,10 +2551,10 @@ var FIELD_SEGMENT_PATTERN = /^[A-Za-z_][A-Za-z0-9_$]*$/;
|
|
|
2094
2551
|
var ARRAY_INDEX_SEGMENT_PATTERN = /^(0|[1-9]\d*)$/;
|
|
2095
2552
|
var ownKeys = (value) => Object.keys(value);
|
|
2096
2553
|
function isParentRefMarker(value) {
|
|
2097
|
-
return (0,
|
|
2554
|
+
return (0, import_utils13.isPlainObject)(value) && ownKeys(value).length === 1 && ownKeys(value)[0] === PARENT_MARKER_KEY;
|
|
2098
2555
|
}
|
|
2099
2556
|
function isEscapedParentRef(value) {
|
|
2100
|
-
return (0,
|
|
2557
|
+
return (0, import_utils13.isPlainObject)(value) && ownKeys(value).length === 1 && ownKeys(value)[0] === ESCAPE_WRAPPER_KEY;
|
|
2101
2558
|
}
|
|
2102
2559
|
function hasMarkerKey(value) {
|
|
2103
2560
|
return ownKeys(value).includes(PARENT_MARKER_KEY) || ownKeys(value).includes(ESCAPE_WRAPPER_KEY);
|
|
@@ -2116,12 +2573,12 @@ function containsParentMarkerShape(value) {
|
|
|
2116
2573
|
const seen = /* @__PURE__ */ new Set();
|
|
2117
2574
|
const visit = (current) => {
|
|
2118
2575
|
if (isParentRefMarker(current) || isEscapedParentRef(current)) return true;
|
|
2119
|
-
if ((0,
|
|
2576
|
+
if ((0, import_utils13.isArray)(current)) {
|
|
2120
2577
|
if (seen.has(current)) return false;
|
|
2121
2578
|
seen.add(current);
|
|
2122
2579
|
return current.some(visit);
|
|
2123
2580
|
}
|
|
2124
|
-
if ((0,
|
|
2581
|
+
if ((0, import_utils13.isPlainObject)(current)) {
|
|
2125
2582
|
if (seen.has(current)) return false;
|
|
2126
2583
|
seen.add(current);
|
|
2127
2584
|
if (hasMarkerKey(current)) return true;
|
|
@@ -2151,7 +2608,7 @@ function scanTemplateValueNode(value, path, issues) {
|
|
|
2151
2608
|
}
|
|
2152
2609
|
return;
|
|
2153
2610
|
}
|
|
2154
|
-
if ((0,
|
|
2611
|
+
if ((0, import_utils13.isPlainObject)(value)) {
|
|
2155
2612
|
if (hasMarkerKey(value)) {
|
|
2156
2613
|
issues.push({
|
|
2157
2614
|
message: 'Malformed $parent reference: objects carrying $parent must be exactly { "$parent": "<field>" }',
|
|
@@ -2162,7 +2619,7 @@ function scanTemplateValueNode(value, path, issues) {
|
|
|
2162
2619
|
scanTemplateObjectEntries(value, path, issues);
|
|
2163
2620
|
return;
|
|
2164
2621
|
}
|
|
2165
|
-
if ((0,
|
|
2622
|
+
if ((0, import_utils13.isArray)(value)) {
|
|
2166
2623
|
value.forEach((item, index) => scanTemplateValueNode(item, [...path, index], issues));
|
|
2167
2624
|
}
|
|
2168
2625
|
}
|
|
@@ -2203,7 +2660,7 @@ function scanTemplateObjectEntries(obj, basePath, issues) {
|
|
|
2203
2660
|
continue;
|
|
2204
2661
|
}
|
|
2205
2662
|
if (LOGICAL_CLAUSE_KEYS.has(key)) {
|
|
2206
|
-
if (!(0,
|
|
2663
|
+
if (!(0, import_utils13.isArray)(child)) {
|
|
2207
2664
|
scanTemplateValueNode(child, childPath, issues);
|
|
2208
2665
|
continue;
|
|
2209
2666
|
}
|
|
@@ -2216,7 +2673,7 @@ function scanTemplateObjectEntries(obj, basePath, issues) {
|
|
|
2216
2673
|
});
|
|
2217
2674
|
return;
|
|
2218
2675
|
}
|
|
2219
|
-
if ((0,
|
|
2676
|
+
if ((0, import_utils13.isPlainObject)(clause)) {
|
|
2220
2677
|
scanTemplateObjectEntries(clause, clausePath, issues);
|
|
2221
2678
|
}
|
|
2222
2679
|
});
|
|
@@ -2227,7 +2684,7 @@ function scanTemplateObjectEntries(obj, basePath, issues) {
|
|
|
2227
2684
|
}
|
|
2228
2685
|
function validateCorrelatedFilterTemplate(filter) {
|
|
2229
2686
|
const issues = [];
|
|
2230
|
-
if (!(0,
|
|
2687
|
+
if (!(0, import_utils13.isPlainObject)(filter)) {
|
|
2231
2688
|
issues.push({ message: "Correlated include filter must be an object", path: [] });
|
|
2232
2689
|
return issues;
|
|
2233
2690
|
}
|
|
@@ -2355,7 +2812,7 @@ var correlatedFilterSchema = import_zod2.z.record(import_zod2.z.string(), import
|
|
|
2355
2812
|
}
|
|
2356
2813
|
});
|
|
2357
2814
|
var correlatedEmptyOptionsSchema = import_zod2.z.unknown().refine(
|
|
2358
|
-
(value) => value === void 0 || (0,
|
|
2815
|
+
(value) => value === void 0 || (0, import_utils14.isPlainObject)(value) && Object.keys(value).length === 0,
|
|
2359
2816
|
"Correlated include options must be absent or empty"
|
|
2360
2817
|
).optional();
|
|
2361
2818
|
var correlatedPathSchema = import_zod2.z.string().min(1).superRefine((path, ctx) => {
|
|
@@ -2392,7 +2849,7 @@ var correlatedListArgsSchema = import_zod2.z.object({
|
|
|
2392
2849
|
include: import_zod2.z.lazy(() => correlatedNestedIncludeSchema).optional()
|
|
2393
2850
|
}).strict().superRefine((args, ctx) => checkArgsMarkers(args.select, args.sort, ctx));
|
|
2394
2851
|
var correlatedCountArgsSchema = import_zod2.z.unknown().refine(
|
|
2395
|
-
(value) => value === void 0 || (0,
|
|
2852
|
+
(value) => value === void 0 || (0, import_utils14.isPlainObject)(value) && Object.keys(value).length === 0,
|
|
2396
2853
|
"Count includes accept no args"
|
|
2397
2854
|
).optional();
|
|
2398
2855
|
var correlatedReadByIdSchema = import_zod2.z.object({
|
|
@@ -2445,7 +2902,7 @@ var correlatedNestedIncludeSchema = import_zod2.z.union([includeItemSchema, impo
|
|
|
2445
2902
|
var rejectDuplicateCorrelatedPaths = (items, ctx) => {
|
|
2446
2903
|
const seen = /* @__PURE__ */ new Set();
|
|
2447
2904
|
items.forEach((item, index) => {
|
|
2448
|
-
if ((0,
|
|
2905
|
+
if ((0, import_utils14.isPlainObject)(item) && item.mode === "correlated" && typeof item.path === "string") {
|
|
2449
2906
|
if (seen.has(item.path)) {
|
|
2450
2907
|
ctx.addIssue({
|
|
2451
2908
|
code: import_zod2.z.ZodIssueCode.custom,
|
|
@@ -2483,7 +2940,7 @@ function rejectKeys(body, ctx, keys2) {
|
|
|
2483
2940
|
}
|
|
2484
2941
|
|
|
2485
2942
|
// src/validation/parsers.ts
|
|
2486
|
-
var clientErrors =
|
|
2943
|
+
var clientErrors = import_express_json_router3.default.clientErrors;
|
|
2487
2944
|
function parsePathParam(value, parameter) {
|
|
2488
2945
|
const result = stringOrStringArray.safeParse(value);
|
|
2489
2946
|
if (!result.success) {
|
|
@@ -2915,8 +3372,8 @@ function isArkTypeErrors(value) {
|
|
|
2915
3372
|
if (typeof value !== "object" || value === null) {
|
|
2916
3373
|
return false;
|
|
2917
3374
|
}
|
|
2918
|
-
const
|
|
2919
|
-
return
|
|
3375
|
+
const record2 = value;
|
|
3376
|
+
return record2.arkKind === "errors" || record2[" arkKind"] === "errors";
|
|
2920
3377
|
}
|
|
2921
3378
|
function isThenable(value) {
|
|
2922
3379
|
return (typeof value === "object" || typeof value === "function") && value !== null && "then" in value && typeof value.then === "function";
|