@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.mjs
CHANGED
|
@@ -41,7 +41,7 @@ var FilterOperator = /* @__PURE__ */ ((FilterOperator2) => {
|
|
|
41
41
|
})(FilterOperator || {});
|
|
42
42
|
|
|
43
43
|
// src/validation/parsers.ts
|
|
44
|
-
import
|
|
44
|
+
import JsonRouter3 from "@web-ts-toolkit/express-json-router";
|
|
45
45
|
|
|
46
46
|
// src/runtime.ts
|
|
47
47
|
import mongoose from "mongoose";
|
|
@@ -87,10 +87,10 @@ function assertPublicAdmission(input, kind = "envelope") {
|
|
|
87
87
|
const add = (child, childKind, key) => {
|
|
88
88
|
pending.push({ value: child, kind: childKind, path: `${path}.${key}` });
|
|
89
89
|
};
|
|
90
|
-
const
|
|
90
|
+
const record2 = value;
|
|
91
91
|
if (slot !== "data") {
|
|
92
92
|
for (const key of privateControls) {
|
|
93
|
-
if (key in
|
|
93
|
+
if (key in record2) {
|
|
94
94
|
throw new PublicAdmissionError(
|
|
95
95
|
`Unsupported public control ${path}.${key}; use explicit per-call internal service options for backend authorization`
|
|
96
96
|
);
|
|
@@ -106,16 +106,16 @@ function assertPublicAdmission(input, kind = "envelope") {
|
|
|
106
106
|
for (const [key, child] of Object.entries(value)) add(child, key === "$$sq" ? "envelope" : "data", key);
|
|
107
107
|
continue;
|
|
108
108
|
}
|
|
109
|
-
for (const key of ["args", "options", "sqOptions"]) add(
|
|
110
|
-
add(
|
|
111
|
-
add(
|
|
112
|
-
for (const key of ["filter", "match", "data"]) add(
|
|
109
|
+
for (const key of ["args", "options", "sqOptions"]) add(record2[key], "envelope", key);
|
|
110
|
+
add(record2.include, "include", "include");
|
|
111
|
+
add(record2.populate, "populate", "populate");
|
|
112
|
+
for (const key of ["filter", "match", "data"]) add(record2[key], "data", key);
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
function assertHydrationDefaults(defaults) {
|
|
116
116
|
if (defaults && typeof defaults === "object") {
|
|
117
|
-
const
|
|
118
|
-
if ("hydration" in
|
|
117
|
+
const record2 = defaults;
|
|
118
|
+
if ("hydration" in record2)
|
|
119
119
|
throw new TypeError("defaults.hydration is unsupported; use model hydration configuration");
|
|
120
120
|
for (const bucket of [
|
|
121
121
|
"publicListOptions",
|
|
@@ -131,7 +131,7 @@ function assertHydrationDefaults(defaults) {
|
|
|
131
131
|
"publicCreateOptions",
|
|
132
132
|
"publicUpdateOptions"
|
|
133
133
|
]) {
|
|
134
|
-
const options =
|
|
134
|
+
const options = record2[bucket];
|
|
135
135
|
if (!options || typeof options !== "object" && typeof options !== "function") continue;
|
|
136
136
|
for (const key of ["lean", "hydration"]) {
|
|
137
137
|
if (!(key in options)) continue;
|
|
@@ -181,12 +181,12 @@ import mschema2Jsonschema from "mongoose-schema-jsonschema";
|
|
|
181
181
|
import {
|
|
182
182
|
addLeadingSlash,
|
|
183
183
|
forEach as forEach2,
|
|
184
|
-
get as
|
|
184
|
+
get as get3,
|
|
185
185
|
isArray as isArray4,
|
|
186
186
|
isBoolean,
|
|
187
|
-
isFunction,
|
|
187
|
+
isFunction as isFunction2,
|
|
188
188
|
isNil as isNil2,
|
|
189
|
-
isPlainObject as
|
|
189
|
+
isPlainObject as isPlainObject12,
|
|
190
190
|
isString as isString3,
|
|
191
191
|
keys,
|
|
192
192
|
normalizeUrlPath,
|
|
@@ -196,6 +196,23 @@ import {
|
|
|
196
196
|
// src/helpers/query.ts
|
|
197
197
|
import { flattenDeep, isNil, isPlainObject as isPlainObject2, isString, reduce } from "@web-ts-toolkit/utils";
|
|
198
198
|
var DEFAULT_LIST_HARD_LIMIT = 1e3;
|
|
199
|
+
function normalizeSelect(select) {
|
|
200
|
+
if (Array.isArray(select)) return flattenDeep(select.map(normalizeSelect));
|
|
201
|
+
if (isPlainObject2(select)) {
|
|
202
|
+
return reduce(
|
|
203
|
+
select,
|
|
204
|
+
(ret, val, key) => {
|
|
205
|
+
if (val === 1) ret.push(key);
|
|
206
|
+
else if (val === -1) ret.push(`-${key}`);
|
|
207
|
+
return ret;
|
|
208
|
+
},
|
|
209
|
+
[]
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
if (isString(select))
|
|
213
|
+
return String(select).split(/[\s,]+/).map((v) => v.trim()).filter(Boolean);
|
|
214
|
+
return [];
|
|
215
|
+
}
|
|
199
216
|
|
|
200
217
|
// src/helpers/sort-policy.ts
|
|
201
218
|
var fieldPathPattern = /^[A-Za-z_][A-Za-z0-9_$]*(\.[A-Za-z_][A-Za-z0-9_$]*)*$/;
|
|
@@ -1111,6 +1128,484 @@ function validateRequestComplexity(value, options, scope = "request") {
|
|
|
1111
1128
|
return errors;
|
|
1112
1129
|
}
|
|
1113
1130
|
|
|
1131
|
+
// src/acl/virtual-descriptor.ts
|
|
1132
|
+
import { isFunction, isPlainObject as isPlainObject10 } from "@web-ts-toolkit/utils";
|
|
1133
|
+
var isVirtualContainerValue = (value) => isPlainObject10(value) && "sub" in value;
|
|
1134
|
+
function getScopeVirtualsObject(root, scopePath = []) {
|
|
1135
|
+
let current = root;
|
|
1136
|
+
for (let i = 0; i < scopePath.length; i += 2) {
|
|
1137
|
+
if (!current || scopePath[i + 1] !== "sub") return null;
|
|
1138
|
+
const container = current[scopePath[i]];
|
|
1139
|
+
if (!isVirtualContainerValue(container)) return null;
|
|
1140
|
+
current = container.sub;
|
|
1141
|
+
}
|
|
1142
|
+
return isPlainObject10(current) ? current : null;
|
|
1143
|
+
}
|
|
1144
|
+
function toDescriptor(leaf, shared = {}) {
|
|
1145
|
+
const record2 = isFunction(leaf) ? { get: leaf } : isPlainObject10(leaf) ? leaf : null;
|
|
1146
|
+
if (!record2 || !isFunction(record2.get)) return void 0;
|
|
1147
|
+
const dependsOn = record2.dependsOn ?? shared.dependsOn ?? [];
|
|
1148
|
+
const populated = record2.dependsOnPopulate === void 0 ? shared.dependsOnPopulate : record2.dependsOnPopulate;
|
|
1149
|
+
return {
|
|
1150
|
+
get: record2.get,
|
|
1151
|
+
dependsOn: [...dependsOn],
|
|
1152
|
+
...populated === void 0 ? {} : {
|
|
1153
|
+
dependsOnPopulate: populated.map(({ path, select }) => ({
|
|
1154
|
+
path,
|
|
1155
|
+
...select === void 0 ? {} : { select: Array.isArray(select) ? [...select] : typeof select === "object" ? { ...select } : select }
|
|
1156
|
+
}))
|
|
1157
|
+
}
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
function resolveSnapshotDescriptor(scopeVirtuals, virtualName, access) {
|
|
1161
|
+
const entry = scopeVirtuals?.[virtualName];
|
|
1162
|
+
if (entry === void 0 || isVirtualContainerValue(entry)) return void 0;
|
|
1163
|
+
if (isFunction(entry)) return toDescriptor(entry);
|
|
1164
|
+
if (!isPlainObject10(entry)) return void 0;
|
|
1165
|
+
const record2 = entry;
|
|
1166
|
+
if ("get" in record2) return toDescriptor(record2);
|
|
1167
|
+
return toDescriptor(record2[access], record2) ?? toDescriptor(record2.default, record2);
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
// src/acl/virtual-populate-dependencies.ts
|
|
1171
|
+
import { get as get2, isPlainObject as isPlainObject11 } from "@web-ts-toolkit/utils";
|
|
1172
|
+
|
|
1173
|
+
// src/acl/authorization-snapshot.ts
|
|
1174
|
+
import { isDeepStrictEqual } from "util";
|
|
1175
|
+
import { mongo } from "mongoose";
|
|
1176
|
+
import JsonRouter2 from "@web-ts-toolkit/express-json-router";
|
|
1177
|
+
var ServiceAuthorizationError = class extends JsonRouter2.clientErrors.BadRequestError {
|
|
1178
|
+
constructor(detail) {
|
|
1179
|
+
const errors = [{ detail: `Service authorization: ${detail}` }];
|
|
1180
|
+
super("Bad Request", { errors });
|
|
1181
|
+
this.result = { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors };
|
|
1182
|
+
}
|
|
1183
|
+
};
|
|
1184
|
+
function authorizationError(detail) {
|
|
1185
|
+
throw new ServiceAuthorizationError(detail);
|
|
1186
|
+
}
|
|
1187
|
+
var unsafeKeys = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype"]);
|
|
1188
|
+
function authorizationRecord(value, label) {
|
|
1189
|
+
if (!value || typeof value !== "object" || Object.getPrototypeOf(value) !== Object.prototype && Object.getPrototypeOf(value) !== null) {
|
|
1190
|
+
authorizationError(`${label} must be a plain object`);
|
|
1191
|
+
}
|
|
1192
|
+
return value;
|
|
1193
|
+
}
|
|
1194
|
+
function copyAuthorizationValue(value, configuration = false) {
|
|
1195
|
+
const active = /* @__PURE__ */ new WeakSet();
|
|
1196
|
+
let nodes = 0;
|
|
1197
|
+
function copy(input, depth) {
|
|
1198
|
+
if (++nodes > 2e4 || depth > 100) authorizationError("snapshot exceeds structural limits");
|
|
1199
|
+
if (typeof input === "function") {
|
|
1200
|
+
if (configuration === true) return input;
|
|
1201
|
+
authorizationError("functions are not policy values");
|
|
1202
|
+
}
|
|
1203
|
+
if (typeof input === "symbol") authorizationError("symbols are not policy values");
|
|
1204
|
+
if (input === null || typeof input !== "object") return input;
|
|
1205
|
+
if (active.has(input)) authorizationError("cyclic input");
|
|
1206
|
+
active.add(input);
|
|
1207
|
+
try {
|
|
1208
|
+
if (input instanceof Date) return new Date(input.getTime());
|
|
1209
|
+
if (Buffer.isBuffer(input)) return Buffer.from(input);
|
|
1210
|
+
if (input instanceof RegExp) {
|
|
1211
|
+
const result2 = new RegExp(input.source, input.flags);
|
|
1212
|
+
result2.lastIndex = input.lastIndex;
|
|
1213
|
+
return result2;
|
|
1214
|
+
}
|
|
1215
|
+
if (input instanceof mongo.BSON.BSONValue) {
|
|
1216
|
+
try {
|
|
1217
|
+
return mongo.BSON.deserialize(mongo.BSON.serialize({ value: input }), {
|
|
1218
|
+
promoteValues: false,
|
|
1219
|
+
bsonRegExp: true
|
|
1220
|
+
}).value;
|
|
1221
|
+
} catch {
|
|
1222
|
+
authorizationError("unsupported BSON snapshot");
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
if (configuration && input instanceof Map) {
|
|
1226
|
+
return new Map([...input].map(([key, entry]) => [copy(key, depth + 1), copy(entry, depth + 1)]));
|
|
1227
|
+
}
|
|
1228
|
+
if (configuration && input instanceof Set) return new Set([...input].map((entry) => copy(entry, depth + 1)));
|
|
1229
|
+
const array = Array.isArray(input);
|
|
1230
|
+
if (array && input.length > 2e4) authorizationError("snapshot array exceeds structural limits");
|
|
1231
|
+
if (!array) authorizationRecord(input, "snapshot");
|
|
1232
|
+
const result = array ? [] : {};
|
|
1233
|
+
for (const key of Reflect.ownKeys(input)) {
|
|
1234
|
+
if (array && key === "length") continue;
|
|
1235
|
+
if (typeof key !== "string" || unsafeKeys.has(key)) authorizationError("unsafe snapshot key");
|
|
1236
|
+
if (array && !/^(0|[1-9]\d*)$/.test(key)) authorizationError("invalid array property");
|
|
1237
|
+
const descriptor = Object.getOwnPropertyDescriptor(input, key);
|
|
1238
|
+
if (!("value" in descriptor) || !descriptor.enumerable && configuration !== "document")
|
|
1239
|
+
authorizationError("accessor/non-enumerable input");
|
|
1240
|
+
Object.defineProperty(result, key, {
|
|
1241
|
+
value: copy(descriptor.value, depth + 1),
|
|
1242
|
+
enumerable: true,
|
|
1243
|
+
writable: true,
|
|
1244
|
+
configurable: true
|
|
1245
|
+
});
|
|
1246
|
+
}
|
|
1247
|
+
if (array) result.length = input.length;
|
|
1248
|
+
return Object.freeze(result);
|
|
1249
|
+
} finally {
|
|
1250
|
+
active.delete(input);
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
return copy(value, 0);
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
// src/acl/field-catalog.ts
|
|
1257
|
+
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);
|
|
1258
|
+
var isSafeOutputPath = (path) => path.split(".").every(safeSegment);
|
|
1259
|
+
var inputControlSegments = /* @__PURE__ */ new Set([
|
|
1260
|
+
"_permissions",
|
|
1261
|
+
"_view",
|
|
1262
|
+
"_edit",
|
|
1263
|
+
"documentAuthorization",
|
|
1264
|
+
"documentAuthorizationStage",
|
|
1265
|
+
"resolvedAuthorization",
|
|
1266
|
+
"execution",
|
|
1267
|
+
"executionContext",
|
|
1268
|
+
"serviceExecutionContext",
|
|
1269
|
+
"preparedQuery",
|
|
1270
|
+
"preparedFilter",
|
|
1271
|
+
"preparedModelQuery",
|
|
1272
|
+
"resolvedIdentifier",
|
|
1273
|
+
"skipParsing",
|
|
1274
|
+
"skipAuthorization",
|
|
1275
|
+
"alreadyAuthorized"
|
|
1276
|
+
]);
|
|
1277
|
+
function isSafeInputPath(path, requestIdentity = false) {
|
|
1278
|
+
return isSafeOutputPath(path) && path.split(".").every((part) => !inputControlSegments.has(part) && (requestIdentity || part !== "_id"));
|
|
1279
|
+
}
|
|
1280
|
+
function matchesCatalogPattern(pattern, path, descendants = false) {
|
|
1281
|
+
const expected = pattern.split(".");
|
|
1282
|
+
const actual = path.split(".");
|
|
1283
|
+
return (descendants ? actual.length >= expected.length : actual.length === expected.length) && expected.every((part, index) => part === "$*" || part === actual[index]);
|
|
1284
|
+
}
|
|
1285
|
+
function captureAuthorizationFieldCatalog(schema, virtuals, documentPermissionField = "_permissions") {
|
|
1286
|
+
const paths = /* @__PURE__ */ new Set();
|
|
1287
|
+
const containers = /* @__PURE__ */ new Set();
|
|
1288
|
+
const opaque = /* @__PURE__ */ new Set();
|
|
1289
|
+
const references = /* @__PURE__ */ new Set();
|
|
1290
|
+
const virtualPaths = /* @__PURE__ */ new Set();
|
|
1291
|
+
const virtualScopes = /* @__PURE__ */ new Set();
|
|
1292
|
+
const schemaVirtualPaths = /* @__PURE__ */ new Set();
|
|
1293
|
+
const arrays = /* @__PURE__ */ new Set();
|
|
1294
|
+
const protectedPaths = /* @__PURE__ */ new Set([documentPermissionField, "_doc", "$__", "__v"]);
|
|
1295
|
+
const add = (path) => {
|
|
1296
|
+
if (path.split(".").every((part) => part === "$*" || safeSegment(part))) paths.add(path);
|
|
1297
|
+
};
|
|
1298
|
+
const ancestors = (path) => {
|
|
1299
|
+
const parts = path.split(".");
|
|
1300
|
+
for (let n = 1; n < parts.length; n++) {
|
|
1301
|
+
const parent = parts.slice(0, n).join(".");
|
|
1302
|
+
add(parent);
|
|
1303
|
+
containers.add(parent);
|
|
1304
|
+
}
|
|
1305
|
+
};
|
|
1306
|
+
const visitType = (type, path, active) => {
|
|
1307
|
+
add(path);
|
|
1308
|
+
ancestors(path);
|
|
1309
|
+
const element = type.$embeddedSchemaType ?? type.embeddedSchemaType ?? type.caster;
|
|
1310
|
+
if (type.instance === "Array" || type.$isMongooseDocumentArray) arrays.add(path);
|
|
1311
|
+
if (type.options?.ref || type.options?.refPath || element?.options?.ref || element?.options?.refPath) {
|
|
1312
|
+
references.add(path);
|
|
1313
|
+
return;
|
|
1314
|
+
}
|
|
1315
|
+
if (type.$isSchemaMap || type.instance === "Map") {
|
|
1316
|
+
const value = type.$__schemaType;
|
|
1317
|
+
if (value) visitType(value, `${path}.$*`, active);
|
|
1318
|
+
else opaque.add(path);
|
|
1319
|
+
containers.delete(path);
|
|
1320
|
+
return;
|
|
1321
|
+
}
|
|
1322
|
+
const child = type.schema ?? element?.schema;
|
|
1323
|
+
if (child) {
|
|
1324
|
+
containers.add(path);
|
|
1325
|
+
if (!active.has(child)) visitSchema(child, path, /* @__PURE__ */ new Set([...active, child]));
|
|
1326
|
+
return;
|
|
1327
|
+
}
|
|
1328
|
+
if (type.instance === "Mixed" || element?.instance === "Mixed") opaque.add(path);
|
|
1329
|
+
};
|
|
1330
|
+
const visitSchema = (current, prefix, active) => {
|
|
1331
|
+
const version = current.options.versionKey;
|
|
1332
|
+
if (typeof version === "string") protectedPaths.add(prefix ? `${prefix}.${version}` : version);
|
|
1333
|
+
for (const name of Object.keys(current.virtuals)) {
|
|
1334
|
+
schemaVirtualPaths.add(prefix ? `${prefix}.${name}` : name);
|
|
1335
|
+
}
|
|
1336
|
+
for (const [name, raw] of Object.entries(current.paths)) {
|
|
1337
|
+
if (name.includes("$*")) continue;
|
|
1338
|
+
visitType(raw, prefix ? `${prefix}.${name}` : name, active);
|
|
1339
|
+
}
|
|
1340
|
+
};
|
|
1341
|
+
visitSchema(schema, "", /* @__PURE__ */ new Set([schema]));
|
|
1342
|
+
const persistedPaths = [...paths];
|
|
1343
|
+
const visitVirtuals = (record2, prefix) => {
|
|
1344
|
+
for (const [name, value] of Object.entries(record2 ?? {})) {
|
|
1345
|
+
if (value === void 0) continue;
|
|
1346
|
+
const path = prefix ? `${prefix}.${name}` : name;
|
|
1347
|
+
if (!isSafeOutputPath(path)) continue;
|
|
1348
|
+
if (value && typeof value === "object" && "sub" in value) {
|
|
1349
|
+
if (references.has(path)) continue;
|
|
1350
|
+
virtualScopes.add(path);
|
|
1351
|
+
add(path);
|
|
1352
|
+
containers.add(path);
|
|
1353
|
+
visitVirtuals(value.sub, path);
|
|
1354
|
+
} else {
|
|
1355
|
+
add(path);
|
|
1356
|
+
ancestors(path);
|
|
1357
|
+
virtualPaths.add(path);
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
};
|
|
1361
|
+
visitVirtuals(virtuals, "");
|
|
1362
|
+
const eligible = [...paths].filter((p) => ![...protectedPaths].some((v) => matchesCatalogPattern(v, p, true)));
|
|
1363
|
+
return Object.freeze({
|
|
1364
|
+
paths: Object.freeze(eligible.filter((p) => !p.includes("$*"))),
|
|
1365
|
+
persistedPaths: Object.freeze(persistedPaths),
|
|
1366
|
+
arrayPaths: Object.freeze([...arrays]),
|
|
1367
|
+
schemaVirtualPaths: Object.freeze([...schemaVirtualPaths]),
|
|
1368
|
+
metadataPaths: Object.freeze([documentPermissionField]),
|
|
1369
|
+
dynamicPaths: Object.freeze(eligible.filter((p) => p.includes("$*"))),
|
|
1370
|
+
containerPaths: Object.freeze([...containers]),
|
|
1371
|
+
opaquePaths: Object.freeze([...opaque]),
|
|
1372
|
+
referencePaths: Object.freeze([...references]),
|
|
1373
|
+
virtualPaths: Object.freeze([...virtualPaths]),
|
|
1374
|
+
virtualScopePaths: Object.freeze([...virtualScopes]),
|
|
1375
|
+
protectedPaths: Object.freeze([...protectedPaths])
|
|
1376
|
+
});
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
// src/acl/virtual-populate-dependencies.ts
|
|
1380
|
+
var elementType = (type) => type.$embeddedSchemaType ?? type.embeddedSchemaType ?? type.caster;
|
|
1381
|
+
var join = (prefix, path) => prefix ? `${prefix}.${path}` : path;
|
|
1382
|
+
var within = (path, parent) => path === parent || path.startsWith(`${parent}.`);
|
|
1383
|
+
var accesses = ["default", "list", "create", "read", "update"];
|
|
1384
|
+
var fail = (label, message) => {
|
|
1385
|
+
throw new Error(`Virtual ${label}: dependsOnPopulate ${message}`);
|
|
1386
|
+
};
|
|
1387
|
+
var varyingSchema = (schema) => Object.keys(schema.discriminators ?? {}).length > 0 || schema.discriminatorMapping?.isRoot === false;
|
|
1388
|
+
var varyingModel = (model) => !!model.baseModelName || Object.keys(model.discriminators ?? {}).length > 0 || varyingSchema(model.schema);
|
|
1389
|
+
function record(value, label) {
|
|
1390
|
+
if (!isPlainObject11(value)) return fail(label, "entries must be plain { path, select? } objects");
|
|
1391
|
+
for (const key of Reflect.ownKeys(value)) {
|
|
1392
|
+
const property = Object.getOwnPropertyDescriptor(value, key);
|
|
1393
|
+
if (typeof key !== "string" || !property.enumerable || !("value" in property))
|
|
1394
|
+
fail(label, "requires enumerable data properties");
|
|
1395
|
+
}
|
|
1396
|
+
return value;
|
|
1397
|
+
}
|
|
1398
|
+
function targetSelectionCatalog(schema, options) {
|
|
1399
|
+
const leaves = /* @__PURE__ */ new Set();
|
|
1400
|
+
const containers = /* @__PURE__ */ new Set();
|
|
1401
|
+
const forbidden = /* @__PURE__ */ new Set([options.documentPermissionField ?? "_permissions"]);
|
|
1402
|
+
const visit = (current, prefix, active) => {
|
|
1403
|
+
const version = current.options.versionKey;
|
|
1404
|
+
if (typeof version === "string") forbidden.add(join(prefix, version));
|
|
1405
|
+
for (const name of Object.keys(current.virtuals)) forbidden.add(join(prefix, name));
|
|
1406
|
+
for (const name of Object.keys(current.aliases ?? {})) forbidden.add(join(prefix, name));
|
|
1407
|
+
for (const [name, raw] of Object.entries(current.paths)) {
|
|
1408
|
+
const path = join(prefix, name);
|
|
1409
|
+
if (!isSafeInputPath(path, true)) continue;
|
|
1410
|
+
const parts = path.split(".");
|
|
1411
|
+
for (let n = 1; n < parts.length; n++) containers.add(parts.slice(0, n).join("."));
|
|
1412
|
+
let type = raw;
|
|
1413
|
+
while (type.instance === "Array" && elementType(type)) type = elementType(type);
|
|
1414
|
+
if (type.instance === "Mixed" || type.instance === "Map") {
|
|
1415
|
+
forbidden.add(path);
|
|
1416
|
+
continue;
|
|
1417
|
+
}
|
|
1418
|
+
const child = type.schema ?? raw.schema;
|
|
1419
|
+
if (raw.options?.ref !== void 0 || raw.options?.refPath !== void 0 || type.options?.ref !== void 0 || type.options?.refPath !== void 0) {
|
|
1420
|
+
if (child) forbidden.add(path);
|
|
1421
|
+
else leaves.add(path);
|
|
1422
|
+
continue;
|
|
1423
|
+
}
|
|
1424
|
+
if (child) {
|
|
1425
|
+
if (active.has(child) || varyingSchema(child)) forbidden.add(path);
|
|
1426
|
+
else {
|
|
1427
|
+
containers.add(path);
|
|
1428
|
+
visit(child, path, /* @__PURE__ */ new Set([...active, child]));
|
|
1429
|
+
}
|
|
1430
|
+
} else leaves.add(path);
|
|
1431
|
+
}
|
|
1432
|
+
};
|
|
1433
|
+
visit(schema, "", /* @__PURE__ */ new Set([schema]));
|
|
1434
|
+
const visitVirtuals = (virtuals, prefix) => {
|
|
1435
|
+
for (const [name, entry] of Object.entries(virtuals)) {
|
|
1436
|
+
if (entry === void 0) continue;
|
|
1437
|
+
const path = join(prefix, name);
|
|
1438
|
+
if (isVirtualContainerValue(entry)) visitVirtuals(entry.sub, path);
|
|
1439
|
+
else forbidden.add(path);
|
|
1440
|
+
}
|
|
1441
|
+
};
|
|
1442
|
+
visitVirtuals(options.virtuals ?? {}, "");
|
|
1443
|
+
const allowed = (path) => ![...forbidden].some((blocked) => within(path, blocked));
|
|
1444
|
+
return {
|
|
1445
|
+
leaves: [...leaves].filter(allowed).sort(),
|
|
1446
|
+
paths: new Set([...leaves, ...containers].filter(allowed))
|
|
1447
|
+
};
|
|
1448
|
+
}
|
|
1449
|
+
function normalizeSelection(select, catalog, label) {
|
|
1450
|
+
if (select !== void 0 && typeof select !== "string") {
|
|
1451
|
+
if (Array.isArray(select)) {
|
|
1452
|
+
if (Array.from(select).some((token) => typeof token !== "string")) fail(label, "select arrays require strings");
|
|
1453
|
+
} else {
|
|
1454
|
+
const projection = record(select, label);
|
|
1455
|
+
for (const [path, value] of Object.entries(projection)) {
|
|
1456
|
+
if (!isSafeInputPath(path, true) || value !== 1 && value !== -1)
|
|
1457
|
+
fail(label, "select objects require safe persisted paths with 1/-1 values");
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
const tokens = normalizeSelect(select);
|
|
1462
|
+
for (const token of tokens) {
|
|
1463
|
+
const path = token.startsWith("-") ? token.slice(1) : token;
|
|
1464
|
+
if (!isSafeInputPath(path, true) || !catalog.paths.has(path))
|
|
1465
|
+
fail(label, `select path "${path}" is not an eligible static persisted target path`);
|
|
1466
|
+
}
|
|
1467
|
+
const positive = tokens.filter((token) => !token.startsWith("-"));
|
|
1468
|
+
const negative = tokens.filter((token) => token.startsWith("-")).map((token) => token.slice(1));
|
|
1469
|
+
const selection = catalog.leaves.filter(
|
|
1470
|
+
(path) => (path === "_id" || positive.length === 0 || positive.some((included) => within(path, included))) && !negative.some((excluded) => within(path, excluded))
|
|
1471
|
+
);
|
|
1472
|
+
return {
|
|
1473
|
+
selection: Object.freeze(selection),
|
|
1474
|
+
fetchSelection: Object.freeze([.../* @__PURE__ */ new Set(["_id", ...selection])].sort()),
|
|
1475
|
+
includeId: selection.includes("_id")
|
|
1476
|
+
};
|
|
1477
|
+
}
|
|
1478
|
+
function captureVirtualPopulateDependencies(runtime, modelName, options, globalOptions = runtime.getGlobalOptions()) {
|
|
1479
|
+
const requireReadOperationAccessForPopulateDependencies = globalOptions.requireReadOperationAccessForPopulateDependencies !== false;
|
|
1480
|
+
const model = runtime.getModelInstance(modelName);
|
|
1481
|
+
const targets = /* @__PURE__ */ new Map();
|
|
1482
|
+
const scopes = [];
|
|
1483
|
+
const visit = (virtuals, schema, schemaPrefix, scopePath, unsupportedScope) => {
|
|
1484
|
+
const sourcePrefix = scopePath.filter((_, index) => index % 2 === 0).join(".");
|
|
1485
|
+
const captureList = (value, label) => {
|
|
1486
|
+
if (value === void 0) return Object.freeze([]);
|
|
1487
|
+
if (!Array.isArray(value)) return fail(label, "must be an array");
|
|
1488
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1489
|
+
return Object.freeze(
|
|
1490
|
+
Array.from(value, (entry) => {
|
|
1491
|
+
const dependency = record(entry, label);
|
|
1492
|
+
if (Object.keys(dependency).some((key) => key !== "path" && key !== "select"))
|
|
1493
|
+
fail(label, "accepts only path/select");
|
|
1494
|
+
const path = dependency.path;
|
|
1495
|
+
if (typeof path !== "string" || path.includes(".") || !isSafeInputPath(path, true))
|
|
1496
|
+
return fail(label, "path must be a safe scope-local persisted reference key");
|
|
1497
|
+
if (seen.has(path)) fail(label, `duplicate path "${path}"`);
|
|
1498
|
+
seen.add(path);
|
|
1499
|
+
if (within(join(sourcePrefix, path), options.documentPermissionField ?? "_permissions") || path === schema?.options.versionKey)
|
|
1500
|
+
fail(label, "source permission metadata/version paths are not reference inputs");
|
|
1501
|
+
if (!model || !schema || unsupportedScope)
|
|
1502
|
+
return fail(label, "requires a static embedded, non-discriminator source scope");
|
|
1503
|
+
const type = schema.path(join(schemaPrefix, path));
|
|
1504
|
+
const isArray6 = type?.instance === "Array";
|
|
1505
|
+
const scalar = type && (isArray6 ? elementType(type) : type);
|
|
1506
|
+
const ref = type?.options?.ref ?? scalar?.options?.ref;
|
|
1507
|
+
if (!scalar || scalar.instance !== "ObjectId" || typeof ref !== "string" || !ref || type?.options?.refPath !== void 0 || scalar.options?.refPath !== void 0)
|
|
1508
|
+
return fail(label, `path "${path}" requires a static string ref on an ObjectId scalar or 1D array`);
|
|
1509
|
+
let captured = targets.get(ref);
|
|
1510
|
+
if (!captured) {
|
|
1511
|
+
if (ref !== modelName && !runtime.hasModel(ref))
|
|
1512
|
+
return fail(label, `target "${ref}" must be configured first in this runtime`);
|
|
1513
|
+
const targetModel = runtime.getModelInstance(ref);
|
|
1514
|
+
if (!targetModel || targetModel.db !== model.db || model.db.models[ref] !== targetModel || varyingModel(targetModel) || targetModel.schema.path("_id")?.instance !== "ObjectId")
|
|
1515
|
+
return fail(
|
|
1516
|
+
label,
|
|
1517
|
+
`target "${ref}" requires a registered same-connection non-discriminator ObjectId model bound to the source ref`
|
|
1518
|
+
);
|
|
1519
|
+
const targetOptions = ref === modelName ? options : runtime.getModelOptions(ref);
|
|
1520
|
+
const defaults = runtime.getDefaultModelOptions();
|
|
1521
|
+
const readOption = (key) => resolveNestedOption((path2, fallback) => get2(targetOptions, path2, fallback), key, get2(defaults, key));
|
|
1522
|
+
const documentPermissionField = targetOptions.documentPermissionField ?? defaults.documentPermissionField;
|
|
1523
|
+
const catalog = captureAuthorizationFieldCatalog(targetModel.schema, void 0, documentPermissionField);
|
|
1524
|
+
const configuration = copyAuthorizationValue(
|
|
1525
|
+
{
|
|
1526
|
+
catalog,
|
|
1527
|
+
documentPermissionField,
|
|
1528
|
+
requireReadOperationAccessForPopulateDependencies,
|
|
1529
|
+
operationAccess: { read: readOption("operationAccess.read") },
|
|
1530
|
+
baseFilter: { read: readOption("baseFilter.read") },
|
|
1531
|
+
overrideFilter: { read: readOption("overrideFilter.read") },
|
|
1532
|
+
hydration: targetOptions.hydration
|
|
1533
|
+
},
|
|
1534
|
+
true
|
|
1535
|
+
);
|
|
1536
|
+
captured = {
|
|
1537
|
+
target: Object.freeze({
|
|
1538
|
+
identity: Object.freeze({}),
|
|
1539
|
+
runtime,
|
|
1540
|
+
modelName: ref,
|
|
1541
|
+
model: targetModel,
|
|
1542
|
+
schema: targetModel.schema,
|
|
1543
|
+
configuration
|
|
1544
|
+
}),
|
|
1545
|
+
catalog: targetSelectionCatalog(targetModel.schema, { ...targetOptions, documentPermissionField })
|
|
1546
|
+
};
|
|
1547
|
+
targets.set(ref, captured);
|
|
1548
|
+
}
|
|
1549
|
+
return Object.freeze({
|
|
1550
|
+
path,
|
|
1551
|
+
sourcePath: join(sourcePrefix, path),
|
|
1552
|
+
cardinality: isArray6 ? "array" : "scalar",
|
|
1553
|
+
target: captured.target,
|
|
1554
|
+
...normalizeSelection(dependency.select, captured.catalog, label)
|
|
1555
|
+
});
|
|
1556
|
+
})
|
|
1557
|
+
);
|
|
1558
|
+
};
|
|
1559
|
+
for (const [name, raw] of Object.entries(virtuals)) {
|
|
1560
|
+
if (isVirtualContainerValue(raw)) {
|
|
1561
|
+
const path = join(schemaPrefix, name);
|
|
1562
|
+
const type = schema?.path(path);
|
|
1563
|
+
const element = type && elementType(type);
|
|
1564
|
+
const child = type?.schema ?? element?.schema;
|
|
1565
|
+
const nonEmbedded = [type, element].some(
|
|
1566
|
+
(entry2) => entry2 && (entry2.options?.ref !== void 0 || entry2.options?.refPath !== void 0 || entry2.instance === "Map" || entry2.instance === "Mixed")
|
|
1567
|
+
);
|
|
1568
|
+
visit(
|
|
1569
|
+
raw.sub,
|
|
1570
|
+
child ?? schema,
|
|
1571
|
+
child ? "" : path,
|
|
1572
|
+
[...scopePath, name, "sub"],
|
|
1573
|
+
unsupportedScope || nonEmbedded || !!(child && varyingSchema(child))
|
|
1574
|
+
);
|
|
1575
|
+
continue;
|
|
1576
|
+
}
|
|
1577
|
+
if (!isPlainObject11(raw)) continue;
|
|
1578
|
+
const entry = raw;
|
|
1579
|
+
const label = join(sourcePrefix, name);
|
|
1580
|
+
captureList(entry.dependsOnPopulate, label);
|
|
1581
|
+
if (!("get" in entry))
|
|
1582
|
+
for (const access of accesses) {
|
|
1583
|
+
if (isPlainObject11(entry[access]))
|
|
1584
|
+
captureList(entry[access].dependsOnPopulate, `${label}.${access}`);
|
|
1585
|
+
}
|
|
1586
|
+
const byAccess = {};
|
|
1587
|
+
let declared = false;
|
|
1588
|
+
for (const access of accesses) {
|
|
1589
|
+
const descriptor = resolveSnapshotDescriptor(virtuals, name, access);
|
|
1590
|
+
if (descriptor) {
|
|
1591
|
+
byAccess[access] = captureList(descriptor.dependsOnPopulate, `${label}.${access}`);
|
|
1592
|
+
declared ||= descriptor.dependsOnPopulate !== void 0;
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
if (declared)
|
|
1596
|
+
scopes.push(
|
|
1597
|
+
Object.freeze({
|
|
1598
|
+
scopePath: Object.freeze([...scopePath]),
|
|
1599
|
+
virtualName: name,
|
|
1600
|
+
byAccess: Object.freeze(byAccess)
|
|
1601
|
+
})
|
|
1602
|
+
);
|
|
1603
|
+
}
|
|
1604
|
+
};
|
|
1605
|
+
visit(options.virtuals ?? {}, model?.schema, "", [], !!model && varyingModel(model));
|
|
1606
|
+
return Object.freeze({ scopes: Object.freeze(scopes) });
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1114
1609
|
// src/runtime.ts
|
|
1115
1610
|
var mongooseJsonSchemaInitialized = false;
|
|
1116
1611
|
var ensureMongooseJsonSchemaInitialized = () => {
|
|
@@ -1151,7 +1646,7 @@ var hasModelPermission = (value, modelPermissionPrefix) => {
|
|
|
1151
1646
|
return true;
|
|
1152
1647
|
});
|
|
1153
1648
|
}
|
|
1154
|
-
return
|
|
1649
|
+
return isFunction2(value);
|
|
1155
1650
|
};
|
|
1156
1651
|
var classifyPermissionSchema = (permissionSchema, modelPermissionPrefix) => {
|
|
1157
1652
|
const schemaKeys = Object.keys(permissionSchema);
|
|
@@ -1159,7 +1654,7 @@ var classifyPermissionSchema = (permissionSchema, modelPermissionPrefix) => {
|
|
|
1159
1654
|
const modelPermissionKeys = {};
|
|
1160
1655
|
forEach2(schemaKeys, (schemaKey) => {
|
|
1161
1656
|
const schemaRule = permissionSchema[schemaKey];
|
|
1162
|
-
const ruleEntries =
|
|
1657
|
+
const ruleEntries = isPlainObject12(schemaRule) ? Object.entries(schemaRule) : FIELD_ACCESS_KEYS.map((accessKey) => [accessKey, schemaRule]);
|
|
1163
1658
|
for (let x = 0; x < ruleEntries.length; x++) {
|
|
1164
1659
|
const [accessKey, value] = ruleEntries[x];
|
|
1165
1660
|
if (!isArray4(globalPermissionKeys[accessKey])) {
|
|
@@ -1200,13 +1695,13 @@ var VIRTUAL_UNSUPPORTED_ACCESSES = ["delete", "distinct", "count"];
|
|
|
1200
1695
|
var VIRTUAL_RESERVED_NAMES = /* @__PURE__ */ new Set(["_id", "__v", "id"]);
|
|
1201
1696
|
var VIRTUAL_DANGEROUS_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
|
|
1202
1697
|
var VIRTUAL_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_$]*$/;
|
|
1203
|
-
var
|
|
1698
|
+
var isVirtualContainerValue2 = (value) => isPlainObject12(value) && "sub" in value;
|
|
1204
1699
|
var virtualFullPath = (prefix, name) => prefix ? `${prefix}.${name}` : name;
|
|
1205
1700
|
var virtualPathsOverlap = (virtualPath, permissionField) => {
|
|
1206
1701
|
if (!virtualPath || !permissionField) return false;
|
|
1207
1702
|
try {
|
|
1208
|
-
if (
|
|
1209
|
-
if (
|
|
1703
|
+
if (get3(set3({}, virtualPath, true), permissionField) !== void 0) return true;
|
|
1704
|
+
if (get3(set3({}, permissionField, true), virtualPath) !== void 0) return true;
|
|
1210
1705
|
} catch {
|
|
1211
1706
|
return false;
|
|
1212
1707
|
}
|
|
@@ -1294,18 +1789,20 @@ var validateVirtualDependsOn = (dependsOn, entryLabel, storedFields, siblingVirt
|
|
|
1294
1789
|
return list;
|
|
1295
1790
|
};
|
|
1296
1791
|
var validateVirtualLeafValue = (value, entryLabel, storedFields, siblingVirtualNames, sharedDependsOn) => {
|
|
1297
|
-
if (
|
|
1298
|
-
if (!
|
|
1792
|
+
if (isFunction2(value)) return;
|
|
1793
|
+
if (!isPlainObject12(value)) {
|
|
1299
1794
|
throw new Error(`Virtual ${entryLabel}: descriptor must be a getter function or { get, dependsOn }`);
|
|
1300
1795
|
}
|
|
1301
|
-
const
|
|
1302
|
-
if (!
|
|
1796
|
+
const record2 = value;
|
|
1797
|
+
if (!isFunction2(record2.get)) {
|
|
1303
1798
|
throw new Error(`Virtual ${entryLabel}: descriptor.get must be a callable getter function`);
|
|
1304
1799
|
}
|
|
1305
|
-
validateVirtualDependsOn(
|
|
1306
|
-
for (const k of Object.keys(
|
|
1307
|
-
if (k !== "get" && k !== "dependsOn") {
|
|
1308
|
-
throw new Error(
|
|
1800
|
+
validateVirtualDependsOn(record2.dependsOn ?? sharedDependsOn, entryLabel, storedFields, siblingVirtualNames);
|
|
1801
|
+
for (const k of Object.keys(record2)) {
|
|
1802
|
+
if (k !== "get" && k !== "dependsOn" && k !== "dependsOnPopulate") {
|
|
1803
|
+
throw new Error(
|
|
1804
|
+
`Virtual ${entryLabel}: unsupported descriptor key "${k}" (expected get/dependsOn/dependsOnPopulate)`
|
|
1805
|
+
);
|
|
1309
1806
|
}
|
|
1310
1807
|
}
|
|
1311
1808
|
};
|
|
@@ -1314,7 +1811,7 @@ var validateVirtualsScope = (scope, documentPermissionField) => {
|
|
|
1314
1811
|
const leafNames = /* @__PURE__ */ new Set();
|
|
1315
1812
|
for (const [name, raw] of Object.entries(scopeVirtuals)) {
|
|
1316
1813
|
if (raw === void 0) continue;
|
|
1317
|
-
if (
|
|
1814
|
+
if (isVirtualContainerValue2(raw)) continue;
|
|
1318
1815
|
leafNames.add(name);
|
|
1319
1816
|
}
|
|
1320
1817
|
for (const [name, raw] of Object.entries(scopeVirtuals)) {
|
|
@@ -1331,15 +1828,15 @@ var validateVirtualsScope = (scope, documentPermissionField) => {
|
|
|
1331
1828
|
`Virtual ${scopeLabel}${name}: overlaps document permission field "${documentPermissionField}" (equal/ancestor/descendant rejected)`
|
|
1332
1829
|
);
|
|
1333
1830
|
}
|
|
1334
|
-
if (
|
|
1335
|
-
const
|
|
1336
|
-
const extra = Object.keys(
|
|
1831
|
+
if (isVirtualContainerValue2(raw)) {
|
|
1832
|
+
const record3 = raw;
|
|
1833
|
+
const extra = Object.keys(record3).filter((k) => k !== "sub");
|
|
1337
1834
|
if (extra.length > 0) {
|
|
1338
1835
|
throw new Error(
|
|
1339
1836
|
`Virtual ${scopeLabel}${name}: scope-container must only hold "sub" (embedded container "${name}" is not a virtual-leaf collision)`
|
|
1340
1837
|
);
|
|
1341
1838
|
}
|
|
1342
|
-
if (!
|
|
1839
|
+
if (!isPlainObject12(record3.sub)) {
|
|
1343
1840
|
throw new Error(`Virtual ${scopeLabel}${name}: scope-container "sub" must be a plain object`);
|
|
1344
1841
|
}
|
|
1345
1842
|
continue;
|
|
@@ -1349,19 +1846,19 @@ var validateVirtualsScope = (scope, documentPermissionField) => {
|
|
|
1349
1846
|
`Virtual ${scopeLabel}${name}: collides with a stored path (inspected actual Mongoose schema/child scope; getModelAtt alone is insufficient)`
|
|
1350
1847
|
);
|
|
1351
1848
|
}
|
|
1352
|
-
if (
|
|
1353
|
-
if (!
|
|
1849
|
+
if (isFunction2(raw)) continue;
|
|
1850
|
+
if (!isPlainObject12(raw)) {
|
|
1354
1851
|
throw new Error(
|
|
1355
1852
|
`Virtual ${scopeLabel}${name}: malformed descriptor (expected getter, { get, dependsOn }, or per-access record)`
|
|
1356
1853
|
);
|
|
1357
1854
|
}
|
|
1358
|
-
const
|
|
1359
|
-
const hasGet = "get" in
|
|
1360
|
-
const accessKeys = Object.keys(
|
|
1361
|
-
const unsupported2 = Object.keys(
|
|
1362
|
-
(k) => k !== "get" && k !== "dependsOn" && k !== "sub" && !VIRTUAL_SUPPORTED_ACCESSES.includes(k)
|
|
1855
|
+
const record2 = raw;
|
|
1856
|
+
const hasGet = "get" in record2;
|
|
1857
|
+
const accessKeys = Object.keys(record2).filter((k) => VIRTUAL_SUPPORTED_ACCESSES.includes(k));
|
|
1858
|
+
const unsupported2 = Object.keys(record2).filter(
|
|
1859
|
+
(k) => k !== "get" && k !== "dependsOn" && k !== "dependsOnPopulate" && k !== "sub" && !VIRTUAL_SUPPORTED_ACCESSES.includes(k)
|
|
1363
1860
|
);
|
|
1364
|
-
for (const key of Object.keys(
|
|
1861
|
+
for (const key of Object.keys(record2)) {
|
|
1365
1862
|
if (VIRTUAL_UNSUPPORTED_ACCESSES.includes(key)) {
|
|
1366
1863
|
throw new Error(
|
|
1367
1864
|
`Virtual ${scopeLabel}${name}: unsupported access "${key}" (supported: default + list/create/read/update; NOT delete/distinct/count)`
|
|
@@ -1379,21 +1876,21 @@ var validateVirtualsScope = (scope, documentPermissionField) => {
|
|
|
1379
1876
|
`Virtual ${scopeLabel}${name}: cannot mix "get" with per-access keys (use either bare { get, dependsOn } or a per-access record)`
|
|
1380
1877
|
);
|
|
1381
1878
|
}
|
|
1382
|
-
if ("sub" in
|
|
1879
|
+
if ("sub" in record2) {
|
|
1383
1880
|
throw new Error(`Virtual ${scopeLabel}${name}: cannot mix "get" with "sub" (leaf vs embedded container)`);
|
|
1384
1881
|
}
|
|
1385
1882
|
validateVirtualLeafValue(raw, `${scopeLabel}${name}`, storedFields, leafNames, void 0);
|
|
1386
1883
|
continue;
|
|
1387
1884
|
}
|
|
1388
1885
|
if (accessKeys.length > 0) {
|
|
1389
|
-
if ("sub" in
|
|
1886
|
+
if ("sub" in record2) {
|
|
1390
1887
|
throw new Error(`Virtual ${scopeLabel}${name}: cannot mix per-access keys with "sub"`);
|
|
1391
1888
|
}
|
|
1392
|
-
validateVirtualDependsOn(
|
|
1889
|
+
validateVirtualDependsOn(record2.dependsOn, `${scopeLabel}${name}`, storedFields, leafNames);
|
|
1393
1890
|
for (const access of accessKeys) {
|
|
1394
|
-
const leaf =
|
|
1891
|
+
const leaf = record2[access];
|
|
1395
1892
|
if (leaf === void 0) continue;
|
|
1396
|
-
validateVirtualLeafValue(leaf, `${scopeLabel}${name}.${access}`, storedFields, leafNames,
|
|
1893
|
+
validateVirtualLeafValue(leaf, `${scopeLabel}${name}.${access}`, storedFields, leafNames, record2.dependsOn);
|
|
1397
1894
|
}
|
|
1398
1895
|
continue;
|
|
1399
1896
|
}
|
|
@@ -1404,7 +1901,7 @@ var validateVirtualsScope = (scope, documentPermissionField) => {
|
|
|
1404
1901
|
};
|
|
1405
1902
|
var validateVirtualsTree = (model, virtuals, documentPermissionField, modelName) => {
|
|
1406
1903
|
if (virtuals === void 0 || virtuals === null) return;
|
|
1407
|
-
if (!
|
|
1904
|
+
if (!isPlainObject12(virtuals)) {
|
|
1408
1905
|
throw new Error(`Virtuals for model "${modelName}": virtuals must be a plain object mapping names to descriptors`);
|
|
1409
1906
|
}
|
|
1410
1907
|
const rootSchema = model?.schema ?? null;
|
|
@@ -1414,13 +1911,14 @@ var validateVirtualsTree = (model, virtuals, documentPermissionField, modelName)
|
|
|
1414
1911
|
{ scopeVirtuals: rootVirtuals, storedFields: rootStored, scopePrefix: "", scopeLabel: "" },
|
|
1415
1912
|
documentPermissionField
|
|
1416
1913
|
);
|
|
1417
|
-
const visitContainers = (currentVirtuals, currentSchema, currentPrefix) => {
|
|
1914
|
+
const visitContainers = (currentVirtuals, currentSchema, currentPrefix, schemaPrefix = "") => {
|
|
1418
1915
|
for (const [containerName, raw] of Object.entries(currentVirtuals)) {
|
|
1419
|
-
if (raw === void 0 || !
|
|
1420
|
-
const
|
|
1421
|
-
const
|
|
1422
|
-
const
|
|
1423
|
-
const
|
|
1916
|
+
if (raw === void 0 || !isVirtualContainerValue2(raw)) continue;
|
|
1917
|
+
const record2 = raw;
|
|
1918
|
+
const schemaPath = virtualFullPath(schemaPrefix, containerName);
|
|
1919
|
+
const childSchema = getChildSchemaForField(currentSchema, schemaPath);
|
|
1920
|
+
const nestedFields = getNestedChildFields(currentSchema, schemaPath);
|
|
1921
|
+
const currentStored = schemaPrefix ? getNestedChildFields(currentSchema, schemaPrefix) : collectStoredTopLevel(currentSchema);
|
|
1424
1922
|
if (!currentStored.has(containerName)) {
|
|
1425
1923
|
throw new Error(
|
|
1426
1924
|
`Virtual ${containerName}: scope-container "${containerName}" is not a persisted field in its scope`
|
|
@@ -1434,7 +1932,7 @@ var validateVirtualsTree = (model, virtuals, documentPermissionField, modelName)
|
|
|
1434
1932
|
}
|
|
1435
1933
|
const subStored = childSchema ? collectStoredTopLevel(childSchema) : nestedFields;
|
|
1436
1934
|
const subPrefix = currentPrefix ? `${currentPrefix}.${containerName}` : containerName;
|
|
1437
|
-
const subVirtuals =
|
|
1935
|
+
const subVirtuals = record2.sub ?? {};
|
|
1438
1936
|
validateVirtualsScope(
|
|
1439
1937
|
{
|
|
1440
1938
|
scopeVirtuals: subVirtuals,
|
|
@@ -1444,37 +1942,11 @@ var validateVirtualsTree = (model, virtuals, documentPermissionField, modelName)
|
|
|
1444
1942
|
},
|
|
1445
1943
|
documentPermissionField
|
|
1446
1944
|
);
|
|
1447
|
-
|
|
1448
|
-
visitContainers(subVirtuals, nextSchema, subPrefix);
|
|
1945
|
+
visitContainers(subVirtuals, childSchema ?? currentSchema, subPrefix, childSchema ? "" : schemaPath);
|
|
1449
1946
|
}
|
|
1450
1947
|
};
|
|
1451
1948
|
visitContainers(rootVirtuals, rootSchema, "");
|
|
1452
1949
|
};
|
|
1453
|
-
var getScopeVirtualsObject = (rootVirtuals, scopePath) => {
|
|
1454
|
-
if (!rootVirtuals || !isPlainObject10(rootVirtuals)) return null;
|
|
1455
|
-
if (!scopePath || scopePath.length === 0) return rootVirtuals;
|
|
1456
|
-
let current = rootVirtuals;
|
|
1457
|
-
for (let i = 0; i < scopePath.length; i += 2) {
|
|
1458
|
-
const field = scopePath[i];
|
|
1459
|
-
const subMarker = scopePath[i + 1];
|
|
1460
|
-
if (!field || subMarker !== "sub") return null;
|
|
1461
|
-
if (!isPlainObject10(current)) return null;
|
|
1462
|
-
const container = current[field];
|
|
1463
|
-
if (!isVirtualContainerValue(container)) return null;
|
|
1464
|
-
current = container.sub;
|
|
1465
|
-
if (!isPlainObject10(current)) return null;
|
|
1466
|
-
}
|
|
1467
|
-
return current;
|
|
1468
|
-
};
|
|
1469
|
-
var toVirtualDescriptor = (leaf, sharedDependsOn) => {
|
|
1470
|
-
if (isFunction(leaf))
|
|
1471
|
-
return { get: leaf, dependsOn: sharedDependsOn ?? [] };
|
|
1472
|
-
if (!isPlainObject10(leaf)) return null;
|
|
1473
|
-
const record = leaf;
|
|
1474
|
-
if (!isFunction(record.get)) return null;
|
|
1475
|
-
const dependsOn = record.dependsOn ?? sharedDependsOn ?? [];
|
|
1476
|
-
return { get: record.get, dependsOn };
|
|
1477
|
-
};
|
|
1478
1950
|
var AccessRuntime = class {
|
|
1479
1951
|
constructor(options = {}) {
|
|
1480
1952
|
this.globalOptions = new OptionsManager(
|
|
@@ -1482,6 +1954,7 @@ var AccessRuntime = class {
|
|
|
1482
1954
|
requestPermissionField: "_permissions",
|
|
1483
1955
|
globalPermissions: () => ({}),
|
|
1484
1956
|
requireRegisteredPopulateModels: true,
|
|
1957
|
+
requireReadOperationAccessForPopulateDependencies: true,
|
|
1485
1958
|
logger: defaultLogger,
|
|
1486
1959
|
requestComplexity: defaultRequestComplexity
|
|
1487
1960
|
},
|
|
@@ -1654,9 +2127,16 @@ var AccessRuntime = class {
|
|
|
1654
2127
|
}
|
|
1655
2128
|
/** Shallowly replace supplied global values; nested objects such as requestComplexity are not deep-merged. */
|
|
1656
2129
|
setGlobalOptions(options) {
|
|
1657
|
-
|
|
2130
|
+
const next = { ...options };
|
|
2131
|
+
if ("requireReadOperationAccessForPopulateDependencies" in next)
|
|
2132
|
+
next.requireReadOperationAccessForPopulateDependencies = next.requireReadOperationAccessForPopulateDependencies !== false;
|
|
2133
|
+
this.globalOptions.assign(next);
|
|
1658
2134
|
}
|
|
1659
2135
|
setGlobalOption(key, value) {
|
|
2136
|
+
if (key === "requireReadOperationAccessForPopulateDependencies") {
|
|
2137
|
+
this.globalOptions.set("requireReadOperationAccessForPopulateDependencies", value !== false);
|
|
2138
|
+
return;
|
|
2139
|
+
}
|
|
1660
2140
|
this.globalOptions.set(key, value);
|
|
1661
2141
|
}
|
|
1662
2142
|
getGlobalOptions() {
|
|
@@ -1736,13 +2216,14 @@ var AccessRuntime = class {
|
|
|
1736
2216
|
setModelOptions(modelName, options) {
|
|
1737
2217
|
assertPublicDefaults(options.defaults);
|
|
1738
2218
|
if ("hydration" in options) options = { ...options, hydration: normalizeHydration(options.hydration) };
|
|
1739
|
-
const manager = this.getOrCreateModelOptions(modelName);
|
|
1740
2219
|
const defaultOptions = this.getDefaultModelOptions();
|
|
1741
|
-
const currentModelOptions =
|
|
2220
|
+
const currentModelOptions = this.modelOptions[modelName]?.fetch() ?? { ...defaultModelOptions, modelName };
|
|
1742
2221
|
const merged = { ...defaultOptions, ...currentModelOptions, ...options };
|
|
1743
2222
|
const model = this.getModelInstance(modelName);
|
|
1744
2223
|
const permissionField = merged.documentPermissionField ?? currentModelOptions.documentPermissionField ?? this.getDefaultModelOption("documentPermissionField") ?? "_permissions";
|
|
1745
2224
|
validateVirtualsTree(model, merged.virtuals, permissionField, modelName);
|
|
2225
|
+
captureVirtualPopulateDependencies(this, modelName, merged);
|
|
2226
|
+
const manager = this.getOrCreateModelOptions(modelName);
|
|
1746
2227
|
manager.assign({ ...defaultOptions, ...currentModelOptions, ...options });
|
|
1747
2228
|
}
|
|
1748
2229
|
setModelOption(modelName, key, value) {
|
|
@@ -1758,11 +2239,11 @@ var AccessRuntime = class {
|
|
|
1758
2239
|
set3(proposed, keyStr.slice("defaults.".length), value);
|
|
1759
2240
|
assertPublicDefaults(proposed);
|
|
1760
2241
|
}
|
|
1761
|
-
const manager = this.
|
|
2242
|
+
const manager = this.modelOptions[modelName];
|
|
1762
2243
|
if (keyStr === "virtuals" || keyStr.startsWith("virtuals.") || keyStr === "documentPermissionField") {
|
|
1763
2244
|
const model = this.getModelInstance(modelName);
|
|
1764
|
-
const currentVirtuals = manager
|
|
1765
|
-
const currentPermissionField = manager
|
|
2245
|
+
const currentVirtuals = manager?.get("virtuals");
|
|
2246
|
+
const currentPermissionField = manager?.get("documentPermissionField") ?? this.getDefaultModelOption("documentPermissionField") ?? "_permissions";
|
|
1766
2247
|
let proposedVirtuals = currentVirtuals;
|
|
1767
2248
|
let proposedPermissionField = currentPermissionField;
|
|
1768
2249
|
if (keyStr === "documentPermissionField") {
|
|
@@ -1776,10 +2257,10 @@ var AccessRuntime = class {
|
|
|
1776
2257
|
const segments = nestedPath.split(".");
|
|
1777
2258
|
let cursor = cloned;
|
|
1778
2259
|
for (let i = 0; i < segments.length - 1; i++) {
|
|
1779
|
-
if (!
|
|
2260
|
+
if (!isPlainObject12(cursor)) break;
|
|
1780
2261
|
cursor = cursor[segments[i]];
|
|
1781
2262
|
}
|
|
1782
|
-
if (
|
|
2263
|
+
if (isPlainObject12(cursor)) {
|
|
1783
2264
|
delete cursor[segments[segments.length - 1]];
|
|
1784
2265
|
}
|
|
1785
2266
|
proposedVirtuals = cloned;
|
|
@@ -1789,8 +2270,14 @@ var AccessRuntime = class {
|
|
|
1789
2270
|
}
|
|
1790
2271
|
}
|
|
1791
2272
|
validateVirtualsTree(model, proposedVirtuals, proposedPermissionField, modelName);
|
|
2273
|
+
captureVirtualPopulateDependencies(this, modelName, {
|
|
2274
|
+
...this.getDefaultModelOptions(),
|
|
2275
|
+
...manager?.fetch(),
|
|
2276
|
+
virtuals: proposedVirtuals,
|
|
2277
|
+
documentPermissionField: proposedPermissionField
|
|
2278
|
+
});
|
|
1792
2279
|
}
|
|
1793
|
-
|
|
2280
|
+
this.getOrCreateModelOptions(modelName).set(key, value);
|
|
1794
2281
|
}
|
|
1795
2282
|
/** Frozen stored snapshot, not fully resolved per-path defaults. Read again for current configuration. */
|
|
1796
2283
|
getModelOptions(modelName) {
|
|
@@ -1829,7 +2316,7 @@ var AccessRuntime = class {
|
|
|
1829
2316
|
const rootVirtuals = manager.get("virtuals");
|
|
1830
2317
|
const scopeVirtuals = getScopeVirtualsObject(rootVirtuals, scopePath);
|
|
1831
2318
|
if (!scopeVirtuals) return [];
|
|
1832
|
-
return Object.entries(scopeVirtuals).filter(([, v]) => v !== void 0 && !
|
|
2319
|
+
return Object.entries(scopeVirtuals).filter(([, v]) => v !== void 0 && !isVirtualContainerValue2(v)).map(([k]) => k);
|
|
1833
2320
|
}
|
|
1834
2321
|
/**
|
|
1835
2322
|
* Whether a field is a registered virtual leaf in a scope (VIRT-01).
|
|
@@ -1843,7 +2330,7 @@ var AccessRuntime = class {
|
|
|
1843
2330
|
const scopeVirtuals = getScopeVirtualsObject(rootVirtuals, scopePath);
|
|
1844
2331
|
if (!scopeVirtuals || !(fieldName in scopeVirtuals)) return false;
|
|
1845
2332
|
const entry = scopeVirtuals[fieldName];
|
|
1846
|
-
if (entry === void 0 ||
|
|
2333
|
+
if (entry === void 0 || isVirtualContainerValue2(entry)) return false;
|
|
1847
2334
|
return true;
|
|
1848
2335
|
}
|
|
1849
2336
|
/**
|
|
@@ -1853,34 +2340,14 @@ var AccessRuntime = class {
|
|
|
1853
2340
|
* interpreted as a getter descriptor when the access is absent: an
|
|
1854
2341
|
* inapplicable registered name stays virtual (excluded from persisted
|
|
1855
2342
|
* projections, write admission, and DB sort/filter/distinct).
|
|
1856
|
-
* Shared
|
|
2343
|
+
* Shared `dependsOn` and `dependsOnPopulate` lists inherit independently;
|
|
2344
|
+
* each defined leaf list replaces its shared list wholesale.
|
|
1857
2345
|
*/
|
|
1858
2346
|
resolveVirtualDescriptor(modelName, virtualName, access, scopePath) {
|
|
1859
2347
|
const manager = this.getOrCreateModelOptions(modelName);
|
|
1860
2348
|
const rootVirtuals = manager.get("virtuals");
|
|
1861
2349
|
const scopeVirtuals = getScopeVirtualsObject(rootVirtuals, scopePath);
|
|
1862
|
-
|
|
1863
|
-
const entry = scopeVirtuals[virtualName];
|
|
1864
|
-
if (entry === void 0 || isVirtualContainerValue(entry)) return void 0;
|
|
1865
|
-
if (isFunction(entry)) return { get: entry, dependsOn: [] };
|
|
1866
|
-
if (!isPlainObject10(entry)) return void 0;
|
|
1867
|
-
const record = entry;
|
|
1868
|
-
if ("get" in record) {
|
|
1869
|
-
const descriptor = toVirtualDescriptor(entry, void 0);
|
|
1870
|
-
return descriptor ?? void 0;
|
|
1871
|
-
}
|
|
1872
|
-
const sharedDependsOn = record.dependsOn;
|
|
1873
|
-
const exact = record[access];
|
|
1874
|
-
if (exact !== void 0) {
|
|
1875
|
-
const descriptor = toVirtualDescriptor(exact, sharedDependsOn);
|
|
1876
|
-
if (descriptor) return descriptor;
|
|
1877
|
-
}
|
|
1878
|
-
const fallback = record.default;
|
|
1879
|
-
if (fallback !== void 0) {
|
|
1880
|
-
const descriptor = toVirtualDescriptor(fallback, sharedDependsOn);
|
|
1881
|
-
if (descriptor) return descriptor;
|
|
1882
|
-
}
|
|
1883
|
-
return void 0;
|
|
2350
|
+
return resolveSnapshotDescriptor(scopeVirtuals, virtualName, access);
|
|
1884
2351
|
}
|
|
1885
2352
|
getModelNames() {
|
|
1886
2353
|
return Object.keys(this.modelOptions);
|
|
@@ -1963,16 +2430,16 @@ var AccessRuntime = class {
|
|
|
1963
2430
|
}
|
|
1964
2431
|
getModelRef(modelName, refPath) {
|
|
1965
2432
|
this.ensureModelMeta(modelName);
|
|
1966
|
-
const value =
|
|
2433
|
+
const value = get3(this.modelRefs, `${modelName}.${refPath}`, null);
|
|
1967
2434
|
return isString3(value) ? value : null;
|
|
1968
2435
|
}
|
|
1969
2436
|
getModelSub(modelName) {
|
|
1970
2437
|
this.ensureModelMeta(modelName);
|
|
1971
|
-
return
|
|
2438
|
+
return get3(this.modelSubs, modelName, []);
|
|
1972
2439
|
}
|
|
1973
2440
|
getModelAtt(modelName) {
|
|
1974
2441
|
this.ensureModelMeta(modelName);
|
|
1975
|
-
return
|
|
2442
|
+
return get3(this.modelAtts, modelName, []);
|
|
1976
2443
|
}
|
|
1977
2444
|
};
|
|
1978
2445
|
var defaultRuntime = new AccessRuntime({ allowGlobalModelLookup: true });
|
|
@@ -1992,7 +2459,7 @@ var getGlobalOption = (key, defaultValue) => {
|
|
|
1992
2459
|
|
|
1993
2460
|
// src/validation/common.ts
|
|
1994
2461
|
import { z as z2 } from "zod";
|
|
1995
|
-
import { isPlainObject as
|
|
2462
|
+
import { isPlainObject as isPlainObject14 } from "@web-ts-toolkit/utils";
|
|
1996
2463
|
|
|
1997
2464
|
// src/validation/public-admission-schema.ts
|
|
1998
2465
|
var admissions = /* @__PURE__ */ new WeakMap();
|
|
@@ -2014,7 +2481,7 @@ function assertTransformedPublicAdmission(schema, value) {
|
|
|
2014
2481
|
}
|
|
2015
2482
|
|
|
2016
2483
|
// src/correlated-includes.ts
|
|
2017
|
-
import { cloneDeep, isArray as isArray5, isPlainObject as
|
|
2484
|
+
import { cloneDeep, isArray as isArray5, isPlainObject as isPlainObject13 } from "@web-ts-toolkit/utils";
|
|
2018
2485
|
var PARENT_MARKER_KEY = "$parent";
|
|
2019
2486
|
var ESCAPE_WRAPPER_KEY = "$escape";
|
|
2020
2487
|
var SUBQUERY_PAYLOAD_KEYS = /* @__PURE__ */ new Set(["$$sq", "$$date"]);
|
|
@@ -2025,10 +2492,10 @@ var FIELD_SEGMENT_PATTERN = /^[A-Za-z_][A-Za-z0-9_$]*$/;
|
|
|
2025
2492
|
var ARRAY_INDEX_SEGMENT_PATTERN = /^(0|[1-9]\d*)$/;
|
|
2026
2493
|
var ownKeys = (value) => Object.keys(value);
|
|
2027
2494
|
function isParentRefMarker(value) {
|
|
2028
|
-
return
|
|
2495
|
+
return isPlainObject13(value) && ownKeys(value).length === 1 && ownKeys(value)[0] === PARENT_MARKER_KEY;
|
|
2029
2496
|
}
|
|
2030
2497
|
function isEscapedParentRef(value) {
|
|
2031
|
-
return
|
|
2498
|
+
return isPlainObject13(value) && ownKeys(value).length === 1 && ownKeys(value)[0] === ESCAPE_WRAPPER_KEY;
|
|
2032
2499
|
}
|
|
2033
2500
|
function hasMarkerKey(value) {
|
|
2034
2501
|
return ownKeys(value).includes(PARENT_MARKER_KEY) || ownKeys(value).includes(ESCAPE_WRAPPER_KEY);
|
|
@@ -2052,7 +2519,7 @@ function containsParentMarkerShape(value) {
|
|
|
2052
2519
|
seen.add(current);
|
|
2053
2520
|
return current.some(visit);
|
|
2054
2521
|
}
|
|
2055
|
-
if (
|
|
2522
|
+
if (isPlainObject13(current)) {
|
|
2056
2523
|
if (seen.has(current)) return false;
|
|
2057
2524
|
seen.add(current);
|
|
2058
2525
|
if (hasMarkerKey(current)) return true;
|
|
@@ -2082,7 +2549,7 @@ function scanTemplateValueNode(value, path, issues) {
|
|
|
2082
2549
|
}
|
|
2083
2550
|
return;
|
|
2084
2551
|
}
|
|
2085
|
-
if (
|
|
2552
|
+
if (isPlainObject13(value)) {
|
|
2086
2553
|
if (hasMarkerKey(value)) {
|
|
2087
2554
|
issues.push({
|
|
2088
2555
|
message: 'Malformed $parent reference: objects carrying $parent must be exactly { "$parent": "<field>" }',
|
|
@@ -2147,7 +2614,7 @@ function scanTemplateObjectEntries(obj, basePath, issues) {
|
|
|
2147
2614
|
});
|
|
2148
2615
|
return;
|
|
2149
2616
|
}
|
|
2150
|
-
if (
|
|
2617
|
+
if (isPlainObject13(clause)) {
|
|
2151
2618
|
scanTemplateObjectEntries(clause, clausePath, issues);
|
|
2152
2619
|
}
|
|
2153
2620
|
});
|
|
@@ -2158,7 +2625,7 @@ function scanTemplateObjectEntries(obj, basePath, issues) {
|
|
|
2158
2625
|
}
|
|
2159
2626
|
function validateCorrelatedFilterTemplate(filter) {
|
|
2160
2627
|
const issues = [];
|
|
2161
|
-
if (!
|
|
2628
|
+
if (!isPlainObject13(filter)) {
|
|
2162
2629
|
issues.push({ message: "Correlated include filter must be an object", path: [] });
|
|
2163
2630
|
return issues;
|
|
2164
2631
|
}
|
|
@@ -2286,7 +2753,7 @@ var correlatedFilterSchema = z2.record(z2.string(), z2.unknown()).superRefine((f
|
|
|
2286
2753
|
}
|
|
2287
2754
|
});
|
|
2288
2755
|
var correlatedEmptyOptionsSchema = z2.unknown().refine(
|
|
2289
|
-
(value) => value === void 0 ||
|
|
2756
|
+
(value) => value === void 0 || isPlainObject14(value) && Object.keys(value).length === 0,
|
|
2290
2757
|
"Correlated include options must be absent or empty"
|
|
2291
2758
|
).optional();
|
|
2292
2759
|
var correlatedPathSchema = z2.string().min(1).superRefine((path, ctx) => {
|
|
@@ -2323,7 +2790,7 @@ var correlatedListArgsSchema = z2.object({
|
|
|
2323
2790
|
include: z2.lazy(() => correlatedNestedIncludeSchema).optional()
|
|
2324
2791
|
}).strict().superRefine((args, ctx) => checkArgsMarkers(args.select, args.sort, ctx));
|
|
2325
2792
|
var correlatedCountArgsSchema = z2.unknown().refine(
|
|
2326
|
-
(value) => value === void 0 ||
|
|
2793
|
+
(value) => value === void 0 || isPlainObject14(value) && Object.keys(value).length === 0,
|
|
2327
2794
|
"Count includes accept no args"
|
|
2328
2795
|
).optional();
|
|
2329
2796
|
var correlatedReadByIdSchema = z2.object({
|
|
@@ -2376,7 +2843,7 @@ var correlatedNestedIncludeSchema = z2.union([includeItemSchema, z2.array(includ
|
|
|
2376
2843
|
var rejectDuplicateCorrelatedPaths = (items, ctx) => {
|
|
2377
2844
|
const seen = /* @__PURE__ */ new Set();
|
|
2378
2845
|
items.forEach((item, index) => {
|
|
2379
|
-
if (
|
|
2846
|
+
if (isPlainObject14(item) && item.mode === "correlated" && typeof item.path === "string") {
|
|
2380
2847
|
if (seen.has(item.path)) {
|
|
2381
2848
|
ctx.addIssue({
|
|
2382
2849
|
code: z2.ZodIssueCode.custom,
|
|
@@ -2414,7 +2881,7 @@ function rejectKeys(body, ctx, keys2) {
|
|
|
2414
2881
|
}
|
|
2415
2882
|
|
|
2416
2883
|
// src/validation/parsers.ts
|
|
2417
|
-
var clientErrors =
|
|
2884
|
+
var clientErrors = JsonRouter3.clientErrors;
|
|
2418
2885
|
function parsePathParam(value, parameter) {
|
|
2419
2886
|
const result = stringOrStringArray.safeParse(value);
|
|
2420
2887
|
if (!result.success) {
|
|
@@ -2846,8 +3313,8 @@ function isArkTypeErrors(value) {
|
|
|
2846
3313
|
if (typeof value !== "object" || value === null) {
|
|
2847
3314
|
return false;
|
|
2848
3315
|
}
|
|
2849
|
-
const
|
|
2850
|
-
return
|
|
3316
|
+
const record2 = value;
|
|
3317
|
+
return record2.arkKind === "errors" || record2[" arkKind"] === "errors";
|
|
2851
3318
|
}
|
|
2852
3319
|
function isThenable(value) {
|
|
2853
3320
|
return (typeof value === "object" || typeof value === "function") && value !== null && "then" in value && typeof value.then === "function";
|