@web-ts-toolkit/access-router 0.54.0 → 0.56.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 +417 -9
- package/advanced.d.mts +1 -1
- package/advanced.d.ts +1 -1
- package/advanced.js +623 -156
- package/advanced.mjs +597 -130
- package/index.d.mts +5 -2
- package/index.d.ts +5 -2
- package/index.js +3033 -1831
- package/index.mjs +2780 -1578
- package/llms.txt +3 -0
- package/package.json +3 -3
- package/{parsers-ZSrF7SNu.d.mts → parsers-BXKoUKMa.d.mts} +633 -444
- package/{parsers-ZSrF7SNu.d.ts → parsers-BXKoUKMa.d.ts} +633 -444
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,492 @@ 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, globalOptions = runtime.getGlobalOptions()) {
|
|
1548
|
+
const requireReadOperationAccessForPopulateDependencies = globalOptions.requireReadOperationAccessForPopulateDependencies !== false;
|
|
1549
|
+
const model = runtime.getModelInstance(modelName);
|
|
1550
|
+
const targets = /* @__PURE__ */ new Map();
|
|
1551
|
+
const scopes = [];
|
|
1552
|
+
const visit = (virtuals, schema, schemaPrefix, scopePath, unsupportedScope) => {
|
|
1553
|
+
const sourcePrefix = scopePath.filter((_, index) => index % 2 === 0).join(".");
|
|
1554
|
+
const captureList = (value, label) => {
|
|
1555
|
+
if (value === void 0) return Object.freeze([]);
|
|
1556
|
+
if (!Array.isArray(value)) return fail(label, "must be an array");
|
|
1557
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1558
|
+
return Object.freeze(
|
|
1559
|
+
Array.from(value, (entry) => {
|
|
1560
|
+
const dependency = record(entry, label);
|
|
1561
|
+
if (Object.keys(dependency).some((key) => key !== "path" && key !== "select"))
|
|
1562
|
+
fail(label, "accepts only path/select");
|
|
1563
|
+
const path = dependency.path;
|
|
1564
|
+
if (typeof path !== "string" || path.includes(".") || !isSafeInputPath(path, true))
|
|
1565
|
+
return fail(label, "path must be a safe scope-local persisted reference key");
|
|
1566
|
+
if (seen.has(path)) fail(label, `duplicate path "${path}"`);
|
|
1567
|
+
seen.add(path);
|
|
1568
|
+
if (within(join(sourcePrefix, path), options.documentPermissionField ?? "_permissions") || path === schema?.options.versionKey)
|
|
1569
|
+
fail(label, "source permission metadata/version paths are not reference inputs");
|
|
1570
|
+
if (!model || !schema || unsupportedScope)
|
|
1571
|
+
return fail(label, "requires a static embedded, non-discriminator source scope");
|
|
1572
|
+
const type = schema.path(join(schemaPrefix, path));
|
|
1573
|
+
const isArray6 = type?.instance === "Array";
|
|
1574
|
+
const scalar = type && (isArray6 ? elementType(type) : type);
|
|
1575
|
+
const ref = type?.options?.ref ?? scalar?.options?.ref;
|
|
1576
|
+
if (!scalar || scalar.instance !== "ObjectId" || typeof ref !== "string" || !ref || type?.options?.refPath !== void 0 || scalar.options?.refPath !== void 0)
|
|
1577
|
+
return fail(label, `path "${path}" requires a static string ref on an ObjectId scalar or 1D array`);
|
|
1578
|
+
let captured = targets.get(ref);
|
|
1579
|
+
if (!captured) {
|
|
1580
|
+
if (ref !== modelName && !runtime.hasModel(ref))
|
|
1581
|
+
return fail(label, `target "${ref}" must be configured first in this runtime`);
|
|
1582
|
+
const targetModel = runtime.getModelInstance(ref);
|
|
1583
|
+
if (!targetModel || targetModel.db !== model.db || model.db.models[ref] !== targetModel || varyingModel(targetModel) || targetModel.schema.path("_id")?.instance !== "ObjectId")
|
|
1584
|
+
return fail(
|
|
1585
|
+
label,
|
|
1586
|
+
`target "${ref}" requires a registered same-connection non-discriminator ObjectId model bound to the source ref`
|
|
1587
|
+
);
|
|
1588
|
+
const targetOptions = ref === modelName ? options : runtime.getModelOptions(ref);
|
|
1589
|
+
const defaults = runtime.getDefaultModelOptions();
|
|
1590
|
+
const readOption = (key) => resolveNestedOption((path2, fallback) => (0, import_utils11.get)(targetOptions, path2, fallback), key, (0, import_utils11.get)(defaults, key));
|
|
1591
|
+
const documentPermissionField = targetOptions.documentPermissionField ?? defaults.documentPermissionField;
|
|
1592
|
+
const catalog = captureAuthorizationFieldCatalog(targetModel.schema, void 0, documentPermissionField);
|
|
1593
|
+
const configuration = copyAuthorizationValue(
|
|
1594
|
+
{
|
|
1595
|
+
catalog,
|
|
1596
|
+
documentPermissionField,
|
|
1597
|
+
requireReadOperationAccessForPopulateDependencies,
|
|
1598
|
+
operationAccess: { read: readOption("operationAccess.read") },
|
|
1599
|
+
baseFilter: { read: readOption("baseFilter.read") },
|
|
1600
|
+
overrideFilter: { read: readOption("overrideFilter.read") },
|
|
1601
|
+
hydration: targetOptions.hydration
|
|
1602
|
+
},
|
|
1603
|
+
true
|
|
1604
|
+
);
|
|
1605
|
+
captured = {
|
|
1606
|
+
target: Object.freeze({
|
|
1607
|
+
identity: Object.freeze({}),
|
|
1608
|
+
runtime,
|
|
1609
|
+
modelName: ref,
|
|
1610
|
+
model: targetModel,
|
|
1611
|
+
schema: targetModel.schema,
|
|
1612
|
+
configuration
|
|
1613
|
+
}),
|
|
1614
|
+
catalog: targetSelectionCatalog(targetModel.schema, { ...targetOptions, documentPermissionField })
|
|
1615
|
+
};
|
|
1616
|
+
targets.set(ref, captured);
|
|
1617
|
+
}
|
|
1618
|
+
return Object.freeze({
|
|
1619
|
+
path,
|
|
1620
|
+
sourcePath: join(sourcePrefix, path),
|
|
1621
|
+
cardinality: isArray6 ? "array" : "scalar",
|
|
1622
|
+
target: captured.target,
|
|
1623
|
+
...normalizeSelection(dependency.select, captured.catalog, label)
|
|
1624
|
+
});
|
|
1625
|
+
})
|
|
1626
|
+
);
|
|
1627
|
+
};
|
|
1628
|
+
for (const [name, raw] of Object.entries(virtuals)) {
|
|
1629
|
+
if (isVirtualContainerValue(raw)) {
|
|
1630
|
+
const path = join(schemaPrefix, name);
|
|
1631
|
+
const type = schema?.path(path);
|
|
1632
|
+
const element = type && elementType(type);
|
|
1633
|
+
const child = type?.schema ?? element?.schema;
|
|
1634
|
+
const nonEmbedded = [type, element].some(
|
|
1635
|
+
(entry2) => entry2 && (entry2.options?.ref !== void 0 || entry2.options?.refPath !== void 0 || entry2.instance === "Map" || entry2.instance === "Mixed")
|
|
1636
|
+
);
|
|
1637
|
+
visit(
|
|
1638
|
+
raw.sub,
|
|
1639
|
+
child ?? schema,
|
|
1640
|
+
child ? "" : path,
|
|
1641
|
+
[...scopePath, name, "sub"],
|
|
1642
|
+
unsupportedScope || nonEmbedded || !!(child && varyingSchema(child))
|
|
1643
|
+
);
|
|
1644
|
+
continue;
|
|
1645
|
+
}
|
|
1646
|
+
if (!(0, import_utils11.isPlainObject)(raw)) continue;
|
|
1647
|
+
const entry = raw;
|
|
1648
|
+
const label = join(sourcePrefix, name);
|
|
1649
|
+
captureList(entry.dependsOnPopulate, label);
|
|
1650
|
+
if (!("get" in entry))
|
|
1651
|
+
for (const access of accesses) {
|
|
1652
|
+
if ((0, import_utils11.isPlainObject)(entry[access]))
|
|
1653
|
+
captureList(entry[access].dependsOnPopulate, `${label}.${access}`);
|
|
1654
|
+
}
|
|
1655
|
+
const byAccess = {};
|
|
1656
|
+
let declared = false;
|
|
1657
|
+
for (const access of accesses) {
|
|
1658
|
+
const descriptor = resolveSnapshotDescriptor(virtuals, name, access);
|
|
1659
|
+
if (descriptor) {
|
|
1660
|
+
byAccess[access] = captureList(descriptor.dependsOnPopulate, `${label}.${access}`);
|
|
1661
|
+
declared ||= descriptor.dependsOnPopulate !== void 0;
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
if (declared)
|
|
1665
|
+
scopes.push(
|
|
1666
|
+
Object.freeze({
|
|
1667
|
+
scopePath: Object.freeze([...scopePath]),
|
|
1668
|
+
virtualName: name,
|
|
1669
|
+
byAccess: Object.freeze(byAccess)
|
|
1670
|
+
})
|
|
1671
|
+
);
|
|
1672
|
+
}
|
|
1673
|
+
};
|
|
1674
|
+
visit(options.virtuals ?? {}, model?.schema, "", [], !!model && varyingModel(model));
|
|
1675
|
+
return Object.freeze({ scopes: Object.freeze(scopes) });
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1183
1678
|
// src/runtime.ts
|
|
1184
1679
|
var mongooseJsonSchemaInitialized = false;
|
|
1185
1680
|
var ensureMongooseJsonSchemaInitialized = () => {
|
|
1186
1681
|
if (mongooseJsonSchemaInitialized) return;
|
|
1187
|
-
(0, import_mongoose_schema_jsonschema.default)(
|
|
1682
|
+
(0, import_mongoose_schema_jsonschema.default)(import_mongoose3.default);
|
|
1188
1683
|
mongooseJsonSchemaInitialized = true;
|
|
1189
1684
|
};
|
|
1190
|
-
var pluralize =
|
|
1685
|
+
var pluralize = import_mongoose3.default.pluralize();
|
|
1191
1686
|
var FIELD_ACCESS_KEYS = ["list", "create", "read", "update"];
|
|
1192
1687
|
var defaultModelOptions = {
|
|
1193
1688
|
basePath: null,
|
|
@@ -1200,44 +1695,44 @@ var defaultDataOptions = {
|
|
|
1200
1695
|
listHardLimit: DEFAULT_LIST_HARD_LIMIT
|
|
1201
1696
|
};
|
|
1202
1697
|
var normalizeBasePath = (name, value) => {
|
|
1203
|
-
if ((0,
|
|
1698
|
+
if ((0, import_utils12.isNil)(value)) {
|
|
1204
1699
|
return `/${pluralize(name)}`;
|
|
1205
1700
|
}
|
|
1206
|
-
return (0,
|
|
1701
|
+
return (0, import_utils12.isString)(value) ? (0, import_utils12.addLeadingSlash)(value) : "";
|
|
1207
1702
|
};
|
|
1208
1703
|
var hasModelPermission = (value, modelPermissionPrefix) => {
|
|
1209
1704
|
if (!modelPermissionPrefix) {
|
|
1210
1705
|
return true;
|
|
1211
1706
|
}
|
|
1212
|
-
if ((0,
|
|
1707
|
+
if ((0, import_utils12.isString)(value)) {
|
|
1213
1708
|
return value.trim().split(" ").some((item) => item.startsWith(modelPermissionPrefix));
|
|
1214
1709
|
}
|
|
1215
|
-
if ((0,
|
|
1710
|
+
if ((0, import_utils12.isArray)(value)) {
|
|
1216
1711
|
return value.some((item) => {
|
|
1217
|
-
if ((0,
|
|
1712
|
+
if ((0, import_utils12.isString)(item) || (0, import_utils12.isArray)(item)) {
|
|
1218
1713
|
return hasModelPermission(item, modelPermissionPrefix);
|
|
1219
1714
|
}
|
|
1220
1715
|
return true;
|
|
1221
1716
|
});
|
|
1222
1717
|
}
|
|
1223
|
-
return (0,
|
|
1718
|
+
return (0, import_utils12.isFunction)(value);
|
|
1224
1719
|
};
|
|
1225
1720
|
var classifyPermissionSchema = (permissionSchema, modelPermissionPrefix) => {
|
|
1226
1721
|
const schemaKeys = Object.keys(permissionSchema);
|
|
1227
1722
|
const globalPermissionKeys = {};
|
|
1228
1723
|
const modelPermissionKeys = {};
|
|
1229
|
-
(0,
|
|
1724
|
+
(0, import_utils12.forEach)(schemaKeys, (schemaKey) => {
|
|
1230
1725
|
const schemaRule = permissionSchema[schemaKey];
|
|
1231
|
-
const ruleEntries = (0,
|
|
1726
|
+
const ruleEntries = (0, import_utils12.isPlainObject)(schemaRule) ? Object.entries(schemaRule) : FIELD_ACCESS_KEYS.map((accessKey) => [accessKey, schemaRule]);
|
|
1232
1727
|
for (let x = 0; x < ruleEntries.length; x++) {
|
|
1233
1728
|
const [accessKey, value] = ruleEntries[x];
|
|
1234
|
-
if (!(0,
|
|
1729
|
+
if (!(0, import_utils12.isArray)(globalPermissionKeys[accessKey])) {
|
|
1235
1730
|
globalPermissionKeys[accessKey] = [];
|
|
1236
1731
|
}
|
|
1237
|
-
if (!(0,
|
|
1732
|
+
if (!(0, import_utils12.isArray)(modelPermissionKeys[accessKey])) {
|
|
1238
1733
|
modelPermissionKeys[accessKey] = [];
|
|
1239
1734
|
}
|
|
1240
|
-
if ((0,
|
|
1735
|
+
if ((0, import_utils12.isBoolean)(value)) {
|
|
1241
1736
|
globalPermissionKeys[accessKey].push(schemaKey);
|
|
1242
1737
|
continue;
|
|
1243
1738
|
}
|
|
@@ -1269,13 +1764,13 @@ var VIRTUAL_UNSUPPORTED_ACCESSES = ["delete", "distinct", "count"];
|
|
|
1269
1764
|
var VIRTUAL_RESERVED_NAMES = /* @__PURE__ */ new Set(["_id", "__v", "id"]);
|
|
1270
1765
|
var VIRTUAL_DANGEROUS_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
|
|
1271
1766
|
var VIRTUAL_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_$]*$/;
|
|
1272
|
-
var
|
|
1767
|
+
var isVirtualContainerValue2 = (value) => (0, import_utils12.isPlainObject)(value) && "sub" in value;
|
|
1273
1768
|
var virtualFullPath = (prefix, name) => prefix ? `${prefix}.${name}` : name;
|
|
1274
1769
|
var virtualPathsOverlap = (virtualPath, permissionField) => {
|
|
1275
1770
|
if (!virtualPath || !permissionField) return false;
|
|
1276
1771
|
try {
|
|
1277
|
-
if ((0,
|
|
1278
|
-
if ((0,
|
|
1772
|
+
if ((0, import_utils12.get)((0, import_utils12.set)({}, virtualPath, true), permissionField) !== void 0) return true;
|
|
1773
|
+
if ((0, import_utils12.get)((0, import_utils12.set)({}, permissionField, true), virtualPath) !== void 0) return true;
|
|
1279
1774
|
} catch {
|
|
1280
1775
|
return false;
|
|
1281
1776
|
}
|
|
@@ -1336,12 +1831,12 @@ var cloneVirtualsForValidation = (value) => {
|
|
|
1336
1831
|
};
|
|
1337
1832
|
var validateVirtualDependsOn = (dependsOn, entryLabel, storedFields, siblingVirtualNames) => {
|
|
1338
1833
|
if (dependsOn === void 0) return [];
|
|
1339
|
-
if (!(0,
|
|
1834
|
+
if (!(0, import_utils12.isArray)(dependsOn)) {
|
|
1340
1835
|
throw new Error(`Virtual ${entryLabel}: dependsOn must be an array of top-level persisted field names`);
|
|
1341
1836
|
}
|
|
1342
1837
|
const list = dependsOn;
|
|
1343
1838
|
for (const dep of list) {
|
|
1344
|
-
if (!(0,
|
|
1839
|
+
if (!(0, import_utils12.isString)(dep) || dep.length === 0) {
|
|
1345
1840
|
throw new Error(`Virtual ${entryLabel}: dependsOn entries must be non-empty strings`);
|
|
1346
1841
|
}
|
|
1347
1842
|
if (dep.includes(".")) {
|
|
@@ -1363,18 +1858,20 @@ var validateVirtualDependsOn = (dependsOn, entryLabel, storedFields, siblingVirt
|
|
|
1363
1858
|
return list;
|
|
1364
1859
|
};
|
|
1365
1860
|
var validateVirtualLeafValue = (value, entryLabel, storedFields, siblingVirtualNames, sharedDependsOn) => {
|
|
1366
|
-
if ((0,
|
|
1367
|
-
if (!(0,
|
|
1861
|
+
if ((0, import_utils12.isFunction)(value)) return;
|
|
1862
|
+
if (!(0, import_utils12.isPlainObject)(value)) {
|
|
1368
1863
|
throw new Error(`Virtual ${entryLabel}: descriptor must be a getter function or { get, dependsOn }`);
|
|
1369
1864
|
}
|
|
1370
|
-
const
|
|
1371
|
-
if (!(0,
|
|
1865
|
+
const record2 = value;
|
|
1866
|
+
if (!(0, import_utils12.isFunction)(record2.get)) {
|
|
1372
1867
|
throw new Error(`Virtual ${entryLabel}: descriptor.get must be a callable getter function`);
|
|
1373
1868
|
}
|
|
1374
|
-
validateVirtualDependsOn(
|
|
1375
|
-
for (const k of Object.keys(
|
|
1376
|
-
if (k !== "get" && k !== "dependsOn") {
|
|
1377
|
-
throw new Error(
|
|
1869
|
+
validateVirtualDependsOn(record2.dependsOn ?? sharedDependsOn, entryLabel, storedFields, siblingVirtualNames);
|
|
1870
|
+
for (const k of Object.keys(record2)) {
|
|
1871
|
+
if (k !== "get" && k !== "dependsOn" && k !== "dependsOnPopulate") {
|
|
1872
|
+
throw new Error(
|
|
1873
|
+
`Virtual ${entryLabel}: unsupported descriptor key "${k}" (expected get/dependsOn/dependsOnPopulate)`
|
|
1874
|
+
);
|
|
1378
1875
|
}
|
|
1379
1876
|
}
|
|
1380
1877
|
};
|
|
@@ -1383,7 +1880,7 @@ var validateVirtualsScope = (scope, documentPermissionField) => {
|
|
|
1383
1880
|
const leafNames = /* @__PURE__ */ new Set();
|
|
1384
1881
|
for (const [name, raw] of Object.entries(scopeVirtuals)) {
|
|
1385
1882
|
if (raw === void 0) continue;
|
|
1386
|
-
if (
|
|
1883
|
+
if (isVirtualContainerValue2(raw)) continue;
|
|
1387
1884
|
leafNames.add(name);
|
|
1388
1885
|
}
|
|
1389
1886
|
for (const [name, raw] of Object.entries(scopeVirtuals)) {
|
|
@@ -1400,15 +1897,15 @@ var validateVirtualsScope = (scope, documentPermissionField) => {
|
|
|
1400
1897
|
`Virtual ${scopeLabel}${name}: overlaps document permission field "${documentPermissionField}" (equal/ancestor/descendant rejected)`
|
|
1401
1898
|
);
|
|
1402
1899
|
}
|
|
1403
|
-
if (
|
|
1404
|
-
const
|
|
1405
|
-
const extra = Object.keys(
|
|
1900
|
+
if (isVirtualContainerValue2(raw)) {
|
|
1901
|
+
const record3 = raw;
|
|
1902
|
+
const extra = Object.keys(record3).filter((k) => k !== "sub");
|
|
1406
1903
|
if (extra.length > 0) {
|
|
1407
1904
|
throw new Error(
|
|
1408
1905
|
`Virtual ${scopeLabel}${name}: scope-container must only hold "sub" (embedded container "${name}" is not a virtual-leaf collision)`
|
|
1409
1906
|
);
|
|
1410
1907
|
}
|
|
1411
|
-
if (!(0,
|
|
1908
|
+
if (!(0, import_utils12.isPlainObject)(record3.sub)) {
|
|
1412
1909
|
throw new Error(`Virtual ${scopeLabel}${name}: scope-container "sub" must be a plain object`);
|
|
1413
1910
|
}
|
|
1414
1911
|
continue;
|
|
@@ -1418,19 +1915,19 @@ var validateVirtualsScope = (scope, documentPermissionField) => {
|
|
|
1418
1915
|
`Virtual ${scopeLabel}${name}: collides with a stored path (inspected actual Mongoose schema/child scope; getModelAtt alone is insufficient)`
|
|
1419
1916
|
);
|
|
1420
1917
|
}
|
|
1421
|
-
if ((0,
|
|
1422
|
-
if (!(0,
|
|
1918
|
+
if ((0, import_utils12.isFunction)(raw)) continue;
|
|
1919
|
+
if (!(0, import_utils12.isPlainObject)(raw)) {
|
|
1423
1920
|
throw new Error(
|
|
1424
1921
|
`Virtual ${scopeLabel}${name}: malformed descriptor (expected getter, { get, dependsOn }, or per-access record)`
|
|
1425
1922
|
);
|
|
1426
1923
|
}
|
|
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)
|
|
1924
|
+
const record2 = raw;
|
|
1925
|
+
const hasGet = "get" in record2;
|
|
1926
|
+
const accessKeys = Object.keys(record2).filter((k) => VIRTUAL_SUPPORTED_ACCESSES.includes(k));
|
|
1927
|
+
const unsupported2 = Object.keys(record2).filter(
|
|
1928
|
+
(k) => k !== "get" && k !== "dependsOn" && k !== "dependsOnPopulate" && k !== "sub" && !VIRTUAL_SUPPORTED_ACCESSES.includes(k)
|
|
1432
1929
|
);
|
|
1433
|
-
for (const key of Object.keys(
|
|
1930
|
+
for (const key of Object.keys(record2)) {
|
|
1434
1931
|
if (VIRTUAL_UNSUPPORTED_ACCESSES.includes(key)) {
|
|
1435
1932
|
throw new Error(
|
|
1436
1933
|
`Virtual ${scopeLabel}${name}: unsupported access "${key}" (supported: default + list/create/read/update; NOT delete/distinct/count)`
|
|
@@ -1448,21 +1945,21 @@ var validateVirtualsScope = (scope, documentPermissionField) => {
|
|
|
1448
1945
|
`Virtual ${scopeLabel}${name}: cannot mix "get" with per-access keys (use either bare { get, dependsOn } or a per-access record)`
|
|
1449
1946
|
);
|
|
1450
1947
|
}
|
|
1451
|
-
if ("sub" in
|
|
1948
|
+
if ("sub" in record2) {
|
|
1452
1949
|
throw new Error(`Virtual ${scopeLabel}${name}: cannot mix "get" with "sub" (leaf vs embedded container)`);
|
|
1453
1950
|
}
|
|
1454
1951
|
validateVirtualLeafValue(raw, `${scopeLabel}${name}`, storedFields, leafNames, void 0);
|
|
1455
1952
|
continue;
|
|
1456
1953
|
}
|
|
1457
1954
|
if (accessKeys.length > 0) {
|
|
1458
|
-
if ("sub" in
|
|
1955
|
+
if ("sub" in record2) {
|
|
1459
1956
|
throw new Error(`Virtual ${scopeLabel}${name}: cannot mix per-access keys with "sub"`);
|
|
1460
1957
|
}
|
|
1461
|
-
validateVirtualDependsOn(
|
|
1958
|
+
validateVirtualDependsOn(record2.dependsOn, `${scopeLabel}${name}`, storedFields, leafNames);
|
|
1462
1959
|
for (const access of accessKeys) {
|
|
1463
|
-
const leaf =
|
|
1960
|
+
const leaf = record2[access];
|
|
1464
1961
|
if (leaf === void 0) continue;
|
|
1465
|
-
validateVirtualLeafValue(leaf, `${scopeLabel}${name}.${access}`, storedFields, leafNames,
|
|
1962
|
+
validateVirtualLeafValue(leaf, `${scopeLabel}${name}.${access}`, storedFields, leafNames, record2.dependsOn);
|
|
1466
1963
|
}
|
|
1467
1964
|
continue;
|
|
1468
1965
|
}
|
|
@@ -1473,7 +1970,7 @@ var validateVirtualsScope = (scope, documentPermissionField) => {
|
|
|
1473
1970
|
};
|
|
1474
1971
|
var validateVirtualsTree = (model, virtuals, documentPermissionField, modelName) => {
|
|
1475
1972
|
if (virtuals === void 0 || virtuals === null) return;
|
|
1476
|
-
if (!(0,
|
|
1973
|
+
if (!(0, import_utils12.isPlainObject)(virtuals)) {
|
|
1477
1974
|
throw new Error(`Virtuals for model "${modelName}": virtuals must be a plain object mapping names to descriptors`);
|
|
1478
1975
|
}
|
|
1479
1976
|
const rootSchema = model?.schema ?? null;
|
|
@@ -1483,13 +1980,14 @@ var validateVirtualsTree = (model, virtuals, documentPermissionField, modelName)
|
|
|
1483
1980
|
{ scopeVirtuals: rootVirtuals, storedFields: rootStored, scopePrefix: "", scopeLabel: "" },
|
|
1484
1981
|
documentPermissionField
|
|
1485
1982
|
);
|
|
1486
|
-
const visitContainers = (currentVirtuals, currentSchema, currentPrefix) => {
|
|
1983
|
+
const visitContainers = (currentVirtuals, currentSchema, currentPrefix, schemaPrefix = "") => {
|
|
1487
1984
|
for (const [containerName, raw] of Object.entries(currentVirtuals)) {
|
|
1488
|
-
if (raw === void 0 || !
|
|
1489
|
-
const
|
|
1490
|
-
const
|
|
1491
|
-
const
|
|
1492
|
-
const
|
|
1985
|
+
if (raw === void 0 || !isVirtualContainerValue2(raw)) continue;
|
|
1986
|
+
const record2 = raw;
|
|
1987
|
+
const schemaPath = virtualFullPath(schemaPrefix, containerName);
|
|
1988
|
+
const childSchema = getChildSchemaForField(currentSchema, schemaPath);
|
|
1989
|
+
const nestedFields = getNestedChildFields(currentSchema, schemaPath);
|
|
1990
|
+
const currentStored = schemaPrefix ? getNestedChildFields(currentSchema, schemaPrefix) : collectStoredTopLevel(currentSchema);
|
|
1493
1991
|
if (!currentStored.has(containerName)) {
|
|
1494
1992
|
throw new Error(
|
|
1495
1993
|
`Virtual ${containerName}: scope-container "${containerName}" is not a persisted field in its scope`
|
|
@@ -1503,7 +2001,7 @@ var validateVirtualsTree = (model, virtuals, documentPermissionField, modelName)
|
|
|
1503
2001
|
}
|
|
1504
2002
|
const subStored = childSchema ? collectStoredTopLevel(childSchema) : nestedFields;
|
|
1505
2003
|
const subPrefix = currentPrefix ? `${currentPrefix}.${containerName}` : containerName;
|
|
1506
|
-
const subVirtuals =
|
|
2004
|
+
const subVirtuals = record2.sub ?? {};
|
|
1507
2005
|
validateVirtualsScope(
|
|
1508
2006
|
{
|
|
1509
2007
|
scopeVirtuals: subVirtuals,
|
|
@@ -1513,37 +2011,11 @@ var validateVirtualsTree = (model, virtuals, documentPermissionField, modelName)
|
|
|
1513
2011
|
},
|
|
1514
2012
|
documentPermissionField
|
|
1515
2013
|
);
|
|
1516
|
-
|
|
1517
|
-
visitContainers(subVirtuals, nextSchema, subPrefix);
|
|
2014
|
+
visitContainers(subVirtuals, childSchema ?? currentSchema, subPrefix, childSchema ? "" : schemaPath);
|
|
1518
2015
|
}
|
|
1519
2016
|
};
|
|
1520
2017
|
visitContainers(rootVirtuals, rootSchema, "");
|
|
1521
2018
|
};
|
|
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
2019
|
var AccessRuntime = class {
|
|
1548
2020
|
constructor(options = {}) {
|
|
1549
2021
|
this.globalOptions = new OptionsManager(
|
|
@@ -1551,6 +2023,7 @@ var AccessRuntime = class {
|
|
|
1551
2023
|
requestPermissionField: "_permissions",
|
|
1552
2024
|
globalPermissions: () => ({}),
|
|
1553
2025
|
requireRegisteredPopulateModels: true,
|
|
2026
|
+
requireReadOperationAccessForPopulateDependencies: true,
|
|
1554
2027
|
logger: defaultLogger,
|
|
1555
2028
|
requestComplexity: defaultRequestComplexity
|
|
1556
2029
|
},
|
|
@@ -1592,20 +2065,20 @@ var AccessRuntime = class {
|
|
|
1592
2065
|
this.modelInstances[modelName] = model;
|
|
1593
2066
|
}
|
|
1594
2067
|
hasModelInstance(modelName) {
|
|
1595
|
-
return modelName in this.modelInstances || this.allowGlobalModelLookup && modelName in
|
|
2068
|
+
return modelName in this.modelInstances || this.allowGlobalModelLookup && modelName in import_mongoose3.default.models;
|
|
1596
2069
|
}
|
|
1597
2070
|
getModelInstance(modelName) {
|
|
1598
2071
|
const registered = this.modelInstances[modelName];
|
|
1599
2072
|
if (registered) return registered;
|
|
1600
2073
|
if (!this.allowGlobalModelLookup) return null;
|
|
1601
|
-
const global =
|
|
2074
|
+
const global = import_mongoose3.default.models[modelName];
|
|
1602
2075
|
if (!global) return null;
|
|
1603
2076
|
this.registerModelInstance(modelName, global);
|
|
1604
2077
|
return global;
|
|
1605
2078
|
}
|
|
1606
2079
|
registerOpenApiRoute(route) {
|
|
1607
2080
|
this.openApiRegistry.register(
|
|
1608
|
-
this.openApiPathPrefix ? { ...route, path: (0,
|
|
2081
|
+
this.openApiPathPrefix ? { ...route, path: (0, import_utils12.normalizeUrlPath)(`${this.openApiPathPrefix}/${route.path}`) } : route
|
|
1609
2082
|
);
|
|
1610
2083
|
}
|
|
1611
2084
|
/**
|
|
@@ -1616,7 +2089,7 @@ var AccessRuntime = class {
|
|
|
1616
2089
|
*/
|
|
1617
2090
|
withOpenApiPathPrefix(prefix, build) {
|
|
1618
2091
|
const previous = this.openApiPathPrefix;
|
|
1619
|
-
this.openApiPathPrefix = (0,
|
|
2092
|
+
this.openApiPathPrefix = (0, import_utils12.normalizeUrlPath)(`${previous}/${prefix}`);
|
|
1620
2093
|
try {
|
|
1621
2094
|
return build();
|
|
1622
2095
|
} finally {
|
|
@@ -1723,9 +2196,16 @@ var AccessRuntime = class {
|
|
|
1723
2196
|
}
|
|
1724
2197
|
/** Shallowly replace supplied global values; nested objects such as requestComplexity are not deep-merged. */
|
|
1725
2198
|
setGlobalOptions(options) {
|
|
1726
|
-
|
|
2199
|
+
const next = { ...options };
|
|
2200
|
+
if ("requireReadOperationAccessForPopulateDependencies" in next)
|
|
2201
|
+
next.requireReadOperationAccessForPopulateDependencies = next.requireReadOperationAccessForPopulateDependencies !== false;
|
|
2202
|
+
this.globalOptions.assign(next);
|
|
1727
2203
|
}
|
|
1728
2204
|
setGlobalOption(key, value) {
|
|
2205
|
+
if (key === "requireReadOperationAccessForPopulateDependencies") {
|
|
2206
|
+
this.globalOptions.set("requireReadOperationAccessForPopulateDependencies", value !== false);
|
|
2207
|
+
return;
|
|
2208
|
+
}
|
|
1729
2209
|
this.globalOptions.set(key, value);
|
|
1730
2210
|
}
|
|
1731
2211
|
getGlobalOptions() {
|
|
@@ -1752,7 +2232,7 @@ var AccessRuntime = class {
|
|
|
1752
2232
|
if (keyStr === "defaults") assertPublicDefaults(value);
|
|
1753
2233
|
else if (keyStr.startsWith("defaults.")) {
|
|
1754
2234
|
const proposed = {};
|
|
1755
|
-
(0,
|
|
2235
|
+
(0, import_utils12.set)(proposed, keyStr.slice("defaults.".length), value);
|
|
1756
2236
|
assertPublicDefaults(proposed);
|
|
1757
2237
|
}
|
|
1758
2238
|
this.defaultModelOptions.set(key, value);
|
|
@@ -1781,7 +2261,7 @@ var AccessRuntime = class {
|
|
|
1781
2261
|
}).onchange("basePath", function(newval, key, target) {
|
|
1782
2262
|
target[key] = normalizeBasePath(
|
|
1783
2263
|
modelName,
|
|
1784
|
-
(0,
|
|
2264
|
+
(0, import_utils12.isString)(newval) || (0, import_utils12.isNil)(newval) ? newval : void 0
|
|
1785
2265
|
);
|
|
1786
2266
|
}).build();
|
|
1787
2267
|
this.modelJsonSchemas[modelName] = model.jsonSchema();
|
|
@@ -1805,13 +2285,14 @@ var AccessRuntime = class {
|
|
|
1805
2285
|
setModelOptions(modelName, options) {
|
|
1806
2286
|
assertPublicDefaults(options.defaults);
|
|
1807
2287
|
if ("hydration" in options) options = { ...options, hydration: normalizeHydration(options.hydration) };
|
|
1808
|
-
const manager = this.getOrCreateModelOptions(modelName);
|
|
1809
2288
|
const defaultOptions = this.getDefaultModelOptions();
|
|
1810
|
-
const currentModelOptions =
|
|
2289
|
+
const currentModelOptions = this.modelOptions[modelName]?.fetch() ?? { ...defaultModelOptions, modelName };
|
|
1811
2290
|
const merged = { ...defaultOptions, ...currentModelOptions, ...options };
|
|
1812
2291
|
const model = this.getModelInstance(modelName);
|
|
1813
2292
|
const permissionField = merged.documentPermissionField ?? currentModelOptions.documentPermissionField ?? this.getDefaultModelOption("documentPermissionField") ?? "_permissions";
|
|
1814
2293
|
validateVirtualsTree(model, merged.virtuals, permissionField, modelName);
|
|
2294
|
+
captureVirtualPopulateDependencies(this, modelName, merged);
|
|
2295
|
+
const manager = this.getOrCreateModelOptions(modelName);
|
|
1815
2296
|
manager.assign({ ...defaultOptions, ...currentModelOptions, ...options });
|
|
1816
2297
|
}
|
|
1817
2298
|
setModelOption(modelName, key, value) {
|
|
@@ -1824,14 +2305,14 @@ var AccessRuntime = class {
|
|
|
1824
2305
|
if (keyStr === "defaults") assertPublicDefaults(value);
|
|
1825
2306
|
else if (keyStr.startsWith("defaults.")) {
|
|
1826
2307
|
const proposed = {};
|
|
1827
|
-
(0,
|
|
2308
|
+
(0, import_utils12.set)(proposed, keyStr.slice("defaults.".length), value);
|
|
1828
2309
|
assertPublicDefaults(proposed);
|
|
1829
2310
|
}
|
|
1830
|
-
const manager = this.
|
|
2311
|
+
const manager = this.modelOptions[modelName];
|
|
1831
2312
|
if (keyStr === "virtuals" || keyStr.startsWith("virtuals.") || keyStr === "documentPermissionField") {
|
|
1832
2313
|
const model = this.getModelInstance(modelName);
|
|
1833
|
-
const currentVirtuals = manager
|
|
1834
|
-
const currentPermissionField = manager
|
|
2314
|
+
const currentVirtuals = manager?.get("virtuals");
|
|
2315
|
+
const currentPermissionField = manager?.get("documentPermissionField") ?? this.getDefaultModelOption("documentPermissionField") ?? "_permissions";
|
|
1835
2316
|
let proposedVirtuals = currentVirtuals;
|
|
1836
2317
|
let proposedPermissionField = currentPermissionField;
|
|
1837
2318
|
if (keyStr === "documentPermissionField") {
|
|
@@ -1845,21 +2326,27 @@ var AccessRuntime = class {
|
|
|
1845
2326
|
const segments = nestedPath.split(".");
|
|
1846
2327
|
let cursor = cloned;
|
|
1847
2328
|
for (let i = 0; i < segments.length - 1; i++) {
|
|
1848
|
-
if (!(0,
|
|
2329
|
+
if (!(0, import_utils12.isPlainObject)(cursor)) break;
|
|
1849
2330
|
cursor = cursor[segments[i]];
|
|
1850
2331
|
}
|
|
1851
|
-
if ((0,
|
|
2332
|
+
if ((0, import_utils12.isPlainObject)(cursor)) {
|
|
1852
2333
|
delete cursor[segments[segments.length - 1]];
|
|
1853
2334
|
}
|
|
1854
2335
|
proposedVirtuals = cloned;
|
|
1855
2336
|
} else {
|
|
1856
|
-
(0,
|
|
2337
|
+
(0, import_utils12.set)(cloned, nestedPath, value);
|
|
1857
2338
|
proposedVirtuals = cloned;
|
|
1858
2339
|
}
|
|
1859
2340
|
}
|
|
1860
2341
|
validateVirtualsTree(model, proposedVirtuals, proposedPermissionField, modelName);
|
|
2342
|
+
captureVirtualPopulateDependencies(this, modelName, {
|
|
2343
|
+
...this.getDefaultModelOptions(),
|
|
2344
|
+
...manager?.fetch(),
|
|
2345
|
+
virtuals: proposedVirtuals,
|
|
2346
|
+
documentPermissionField: proposedPermissionField
|
|
2347
|
+
});
|
|
1861
2348
|
}
|
|
1862
|
-
|
|
2349
|
+
this.getOrCreateModelOptions(modelName).set(key, value);
|
|
1863
2350
|
}
|
|
1864
2351
|
/** Frozen stored snapshot, not fully resolved per-path defaults. Read again for current configuration. */
|
|
1865
2352
|
getModelOptions(modelName) {
|
|
@@ -1898,7 +2385,7 @@ var AccessRuntime = class {
|
|
|
1898
2385
|
const rootVirtuals = manager.get("virtuals");
|
|
1899
2386
|
const scopeVirtuals = getScopeVirtualsObject(rootVirtuals, scopePath);
|
|
1900
2387
|
if (!scopeVirtuals) return [];
|
|
1901
|
-
return Object.entries(scopeVirtuals).filter(([, v]) => v !== void 0 && !
|
|
2388
|
+
return Object.entries(scopeVirtuals).filter(([, v]) => v !== void 0 && !isVirtualContainerValue2(v)).map(([k]) => k);
|
|
1902
2389
|
}
|
|
1903
2390
|
/**
|
|
1904
2391
|
* Whether a field is a registered virtual leaf in a scope (VIRT-01).
|
|
@@ -1912,7 +2399,7 @@ var AccessRuntime = class {
|
|
|
1912
2399
|
const scopeVirtuals = getScopeVirtualsObject(rootVirtuals, scopePath);
|
|
1913
2400
|
if (!scopeVirtuals || !(fieldName in scopeVirtuals)) return false;
|
|
1914
2401
|
const entry = scopeVirtuals[fieldName];
|
|
1915
|
-
if (entry === void 0 ||
|
|
2402
|
+
if (entry === void 0 || isVirtualContainerValue2(entry)) return false;
|
|
1916
2403
|
return true;
|
|
1917
2404
|
}
|
|
1918
2405
|
/**
|
|
@@ -1922,34 +2409,14 @@ var AccessRuntime = class {
|
|
|
1922
2409
|
* interpreted as a getter descriptor when the access is absent: an
|
|
1923
2410
|
* inapplicable registered name stays virtual (excluded from persisted
|
|
1924
2411
|
* projections, write admission, and DB sort/filter/distinct).
|
|
1925
|
-
* Shared
|
|
2412
|
+
* Shared `dependsOn` and `dependsOnPopulate` lists inherit independently;
|
|
2413
|
+
* each defined leaf list replaces its shared list wholesale.
|
|
1926
2414
|
*/
|
|
1927
2415
|
resolveVirtualDescriptor(modelName, virtualName, access, scopePath) {
|
|
1928
2416
|
const manager = this.getOrCreateModelOptions(modelName);
|
|
1929
2417
|
const rootVirtuals = manager.get("virtuals");
|
|
1930
2418
|
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;
|
|
2419
|
+
return resolveSnapshotDescriptor(scopeVirtuals, virtualName, access);
|
|
1953
2420
|
}
|
|
1954
2421
|
getModelNames() {
|
|
1955
2422
|
return Object.keys(this.modelOptions);
|
|
@@ -1968,7 +2435,7 @@ var AccessRuntime = class {
|
|
|
1968
2435
|
manager.onchange("basePath", function(newval, key, target) {
|
|
1969
2436
|
target[key] = normalizeBasePath(
|
|
1970
2437
|
dataName,
|
|
1971
|
-
(0,
|
|
2438
|
+
(0, import_utils12.isString)(newval) || (0, import_utils12.isNil)(newval) ? newval : void 0
|
|
1972
2439
|
);
|
|
1973
2440
|
}).build();
|
|
1974
2441
|
return manager;
|
|
@@ -2028,20 +2495,20 @@ var AccessRuntime = class {
|
|
|
2028
2495
|
const schema = model.schema;
|
|
2029
2496
|
this.modelRefs[modelName] = buildRefs(schema.tree);
|
|
2030
2497
|
this.modelSubs[modelName] = buildSubPaths(schema.tree);
|
|
2031
|
-
this.modelAtts[modelName] = (0,
|
|
2498
|
+
this.modelAtts[modelName] = (0, import_utils12.keys)(schema.obj);
|
|
2032
2499
|
}
|
|
2033
2500
|
getModelRef(modelName, refPath) {
|
|
2034
2501
|
this.ensureModelMeta(modelName);
|
|
2035
|
-
const value = (0,
|
|
2036
|
-
return (0,
|
|
2502
|
+
const value = (0, import_utils12.get)(this.modelRefs, `${modelName}.${refPath}`, null);
|
|
2503
|
+
return (0, import_utils12.isString)(value) ? value : null;
|
|
2037
2504
|
}
|
|
2038
2505
|
getModelSub(modelName) {
|
|
2039
2506
|
this.ensureModelMeta(modelName);
|
|
2040
|
-
return (0,
|
|
2507
|
+
return (0, import_utils12.get)(this.modelSubs, modelName, []);
|
|
2041
2508
|
}
|
|
2042
2509
|
getModelAtt(modelName) {
|
|
2043
2510
|
this.ensureModelMeta(modelName);
|
|
2044
|
-
return (0,
|
|
2511
|
+
return (0, import_utils12.get)(this.modelAtts, modelName, []);
|
|
2045
2512
|
}
|
|
2046
2513
|
};
|
|
2047
2514
|
var defaultRuntime = new AccessRuntime({ allowGlobalModelLookup: true });
|
|
@@ -2061,7 +2528,7 @@ var getGlobalOption = (key, defaultValue) => {
|
|
|
2061
2528
|
|
|
2062
2529
|
// src/validation/common.ts
|
|
2063
2530
|
var import_zod2 = require("zod");
|
|
2064
|
-
var
|
|
2531
|
+
var import_utils14 = require("@web-ts-toolkit/utils");
|
|
2065
2532
|
|
|
2066
2533
|
// src/validation/public-admission-schema.ts
|
|
2067
2534
|
var admissions = /* @__PURE__ */ new WeakMap();
|
|
@@ -2083,7 +2550,7 @@ function assertTransformedPublicAdmission(schema, value) {
|
|
|
2083
2550
|
}
|
|
2084
2551
|
|
|
2085
2552
|
// src/correlated-includes.ts
|
|
2086
|
-
var
|
|
2553
|
+
var import_utils13 = require("@web-ts-toolkit/utils");
|
|
2087
2554
|
var PARENT_MARKER_KEY = "$parent";
|
|
2088
2555
|
var ESCAPE_WRAPPER_KEY = "$escape";
|
|
2089
2556
|
var SUBQUERY_PAYLOAD_KEYS = /* @__PURE__ */ new Set(["$$sq", "$$date"]);
|
|
@@ -2094,10 +2561,10 @@ var FIELD_SEGMENT_PATTERN = /^[A-Za-z_][A-Za-z0-9_$]*$/;
|
|
|
2094
2561
|
var ARRAY_INDEX_SEGMENT_PATTERN = /^(0|[1-9]\d*)$/;
|
|
2095
2562
|
var ownKeys = (value) => Object.keys(value);
|
|
2096
2563
|
function isParentRefMarker(value) {
|
|
2097
|
-
return (0,
|
|
2564
|
+
return (0, import_utils13.isPlainObject)(value) && ownKeys(value).length === 1 && ownKeys(value)[0] === PARENT_MARKER_KEY;
|
|
2098
2565
|
}
|
|
2099
2566
|
function isEscapedParentRef(value) {
|
|
2100
|
-
return (0,
|
|
2567
|
+
return (0, import_utils13.isPlainObject)(value) && ownKeys(value).length === 1 && ownKeys(value)[0] === ESCAPE_WRAPPER_KEY;
|
|
2101
2568
|
}
|
|
2102
2569
|
function hasMarkerKey(value) {
|
|
2103
2570
|
return ownKeys(value).includes(PARENT_MARKER_KEY) || ownKeys(value).includes(ESCAPE_WRAPPER_KEY);
|
|
@@ -2116,12 +2583,12 @@ function containsParentMarkerShape(value) {
|
|
|
2116
2583
|
const seen = /* @__PURE__ */ new Set();
|
|
2117
2584
|
const visit = (current) => {
|
|
2118
2585
|
if (isParentRefMarker(current) || isEscapedParentRef(current)) return true;
|
|
2119
|
-
if ((0,
|
|
2586
|
+
if ((0, import_utils13.isArray)(current)) {
|
|
2120
2587
|
if (seen.has(current)) return false;
|
|
2121
2588
|
seen.add(current);
|
|
2122
2589
|
return current.some(visit);
|
|
2123
2590
|
}
|
|
2124
|
-
if ((0,
|
|
2591
|
+
if ((0, import_utils13.isPlainObject)(current)) {
|
|
2125
2592
|
if (seen.has(current)) return false;
|
|
2126
2593
|
seen.add(current);
|
|
2127
2594
|
if (hasMarkerKey(current)) return true;
|
|
@@ -2151,7 +2618,7 @@ function scanTemplateValueNode(value, path, issues) {
|
|
|
2151
2618
|
}
|
|
2152
2619
|
return;
|
|
2153
2620
|
}
|
|
2154
|
-
if ((0,
|
|
2621
|
+
if ((0, import_utils13.isPlainObject)(value)) {
|
|
2155
2622
|
if (hasMarkerKey(value)) {
|
|
2156
2623
|
issues.push({
|
|
2157
2624
|
message: 'Malformed $parent reference: objects carrying $parent must be exactly { "$parent": "<field>" }',
|
|
@@ -2162,7 +2629,7 @@ function scanTemplateValueNode(value, path, issues) {
|
|
|
2162
2629
|
scanTemplateObjectEntries(value, path, issues);
|
|
2163
2630
|
return;
|
|
2164
2631
|
}
|
|
2165
|
-
if ((0,
|
|
2632
|
+
if ((0, import_utils13.isArray)(value)) {
|
|
2166
2633
|
value.forEach((item, index) => scanTemplateValueNode(item, [...path, index], issues));
|
|
2167
2634
|
}
|
|
2168
2635
|
}
|
|
@@ -2203,7 +2670,7 @@ function scanTemplateObjectEntries(obj, basePath, issues) {
|
|
|
2203
2670
|
continue;
|
|
2204
2671
|
}
|
|
2205
2672
|
if (LOGICAL_CLAUSE_KEYS.has(key)) {
|
|
2206
|
-
if (!(0,
|
|
2673
|
+
if (!(0, import_utils13.isArray)(child)) {
|
|
2207
2674
|
scanTemplateValueNode(child, childPath, issues);
|
|
2208
2675
|
continue;
|
|
2209
2676
|
}
|
|
@@ -2216,7 +2683,7 @@ function scanTemplateObjectEntries(obj, basePath, issues) {
|
|
|
2216
2683
|
});
|
|
2217
2684
|
return;
|
|
2218
2685
|
}
|
|
2219
|
-
if ((0,
|
|
2686
|
+
if ((0, import_utils13.isPlainObject)(clause)) {
|
|
2220
2687
|
scanTemplateObjectEntries(clause, clausePath, issues);
|
|
2221
2688
|
}
|
|
2222
2689
|
});
|
|
@@ -2227,7 +2694,7 @@ function scanTemplateObjectEntries(obj, basePath, issues) {
|
|
|
2227
2694
|
}
|
|
2228
2695
|
function validateCorrelatedFilterTemplate(filter) {
|
|
2229
2696
|
const issues = [];
|
|
2230
|
-
if (!(0,
|
|
2697
|
+
if (!(0, import_utils13.isPlainObject)(filter)) {
|
|
2231
2698
|
issues.push({ message: "Correlated include filter must be an object", path: [] });
|
|
2232
2699
|
return issues;
|
|
2233
2700
|
}
|
|
@@ -2355,7 +2822,7 @@ var correlatedFilterSchema = import_zod2.z.record(import_zod2.z.string(), import
|
|
|
2355
2822
|
}
|
|
2356
2823
|
});
|
|
2357
2824
|
var correlatedEmptyOptionsSchema = import_zod2.z.unknown().refine(
|
|
2358
|
-
(value) => value === void 0 || (0,
|
|
2825
|
+
(value) => value === void 0 || (0, import_utils14.isPlainObject)(value) && Object.keys(value).length === 0,
|
|
2359
2826
|
"Correlated include options must be absent or empty"
|
|
2360
2827
|
).optional();
|
|
2361
2828
|
var correlatedPathSchema = import_zod2.z.string().min(1).superRefine((path, ctx) => {
|
|
@@ -2392,7 +2859,7 @@ var correlatedListArgsSchema = import_zod2.z.object({
|
|
|
2392
2859
|
include: import_zod2.z.lazy(() => correlatedNestedIncludeSchema).optional()
|
|
2393
2860
|
}).strict().superRefine((args, ctx) => checkArgsMarkers(args.select, args.sort, ctx));
|
|
2394
2861
|
var correlatedCountArgsSchema = import_zod2.z.unknown().refine(
|
|
2395
|
-
(value) => value === void 0 || (0,
|
|
2862
|
+
(value) => value === void 0 || (0, import_utils14.isPlainObject)(value) && Object.keys(value).length === 0,
|
|
2396
2863
|
"Count includes accept no args"
|
|
2397
2864
|
).optional();
|
|
2398
2865
|
var correlatedReadByIdSchema = import_zod2.z.object({
|
|
@@ -2445,7 +2912,7 @@ var correlatedNestedIncludeSchema = import_zod2.z.union([includeItemSchema, impo
|
|
|
2445
2912
|
var rejectDuplicateCorrelatedPaths = (items, ctx) => {
|
|
2446
2913
|
const seen = /* @__PURE__ */ new Set();
|
|
2447
2914
|
items.forEach((item, index) => {
|
|
2448
|
-
if ((0,
|
|
2915
|
+
if ((0, import_utils14.isPlainObject)(item) && item.mode === "correlated" && typeof item.path === "string") {
|
|
2449
2916
|
if (seen.has(item.path)) {
|
|
2450
2917
|
ctx.addIssue({
|
|
2451
2918
|
code: import_zod2.z.ZodIssueCode.custom,
|
|
@@ -2483,7 +2950,7 @@ function rejectKeys(body, ctx, keys2) {
|
|
|
2483
2950
|
}
|
|
2484
2951
|
|
|
2485
2952
|
// src/validation/parsers.ts
|
|
2486
|
-
var clientErrors =
|
|
2953
|
+
var clientErrors = import_express_json_router3.default.clientErrors;
|
|
2487
2954
|
function parsePathParam(value, parameter) {
|
|
2488
2955
|
const result = stringOrStringArray.safeParse(value);
|
|
2489
2956
|
if (!result.success) {
|
|
@@ -2915,8 +3382,8 @@ function isArkTypeErrors(value) {
|
|
|
2915
3382
|
if (typeof value !== "object" || value === null) {
|
|
2916
3383
|
return false;
|
|
2917
3384
|
}
|
|
2918
|
-
const
|
|
2919
|
-
return
|
|
3385
|
+
const record2 = value;
|
|
3386
|
+
return record2.arkKind === "errors" || record2[" arkKind"] === "errors";
|
|
2920
3387
|
}
|
|
2921
3388
|
function isThenable(value) {
|
|
2922
3389
|
return (typeof value === "object" || typeof value === "function") && value !== null && "then" in value && typeof value.then === "function";
|