@web-ts-toolkit/access-router 0.43.0 → 0.45.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 +97 -2
- package/advanced.d.mts +655 -19
- package/advanced.d.ts +655 -19
- package/advanced.js +478 -29
- package/advanced.mjs +478 -29
- package/index.d.mts +18 -10
- package/index.d.ts +18 -10
- package/index.js +1699 -262
- package/index.mjs +1576 -137
- package/llms.txt +19 -0
- package/package.json +18 -6
- package/{parsers-PMWujHUV.d.mts → parsers-DppKW-Fd.d.mts} +146 -26
- package/{parsers-PMWujHUV.d.ts → parsers-DppKW-Fd.d.ts} +146 -26
- package/processors.d.mts +151 -1
- package/processors.d.ts +151 -1
- package/processors.js +175 -8
- package/processors.mjs +170 -8
package/advanced.mjs
CHANGED
|
@@ -63,6 +63,12 @@ import {
|
|
|
63
63
|
import { flattenDeep, isNil, isPlainObject, isString, reduce } from "@web-ts-toolkit/utils";
|
|
64
64
|
var DEFAULT_LIST_HARD_LIMIT = 1e3;
|
|
65
65
|
|
|
66
|
+
// src/helpers/sort-policy.ts
|
|
67
|
+
var fieldPathPattern = /^[A-Za-z_][A-Za-z0-9_$]*(\.[A-Za-z_][A-Za-z0-9_$]*)*$/;
|
|
68
|
+
function isValidFieldPath(field) {
|
|
69
|
+
return typeof field === "string" && fieldPathPattern.test(field);
|
|
70
|
+
}
|
|
71
|
+
|
|
66
72
|
// src/helpers/index.ts
|
|
67
73
|
import { forEach, isArray, isEmpty, isObject, isPlainObject as isPlainObject3, isString as isString2 } from "@web-ts-toolkit/utils";
|
|
68
74
|
|
|
@@ -486,6 +492,29 @@ var cloneConfigValue = (value) => {
|
|
|
486
492
|
])
|
|
487
493
|
);
|
|
488
494
|
};
|
|
495
|
+
var cloneForRead = (value) => {
|
|
496
|
+
if (Array.isArray(value)) {
|
|
497
|
+
if (Object.isFrozen(value)) {
|
|
498
|
+
return value;
|
|
499
|
+
}
|
|
500
|
+
return value.map((item) => cloneForRead(item));
|
|
501
|
+
}
|
|
502
|
+
if (!value || typeof value !== "object") {
|
|
503
|
+
return value;
|
|
504
|
+
}
|
|
505
|
+
if (Object.getPrototypeOf(value) !== Object.prototype) {
|
|
506
|
+
return value;
|
|
507
|
+
}
|
|
508
|
+
if (Object.isFrozen(value)) {
|
|
509
|
+
return value;
|
|
510
|
+
}
|
|
511
|
+
return Object.fromEntries(
|
|
512
|
+
Object.entries(value).map(([entryKey, entryValue]) => [
|
|
513
|
+
entryKey,
|
|
514
|
+
cloneForRead(entryValue)
|
|
515
|
+
])
|
|
516
|
+
);
|
|
517
|
+
};
|
|
489
518
|
var freezeConfigValue = (value) => {
|
|
490
519
|
if (!value || typeof value !== "object") {
|
|
491
520
|
return value;
|
|
@@ -501,6 +530,7 @@ var freezeConfigValue = (value) => {
|
|
|
501
530
|
}
|
|
502
531
|
return value;
|
|
503
532
|
};
|
|
533
|
+
var cloneAndFreezeForStore = (value) => freezeConfigValue(cloneForRead(value));
|
|
504
534
|
var getNestedOption = (manager, key, defaultValue) => {
|
|
505
535
|
const keys2 = String(key).split(".");
|
|
506
536
|
if (keys2.length === 1) {
|
|
@@ -530,6 +560,14 @@ var OptionsManager = class {
|
|
|
530
560
|
target[keystr] = value;
|
|
531
561
|
_this.listeners[keystr] && _this.listeners[keystr].call(_this, value, keystr, target, oldvalue);
|
|
532
562
|
return true;
|
|
563
|
+
},
|
|
564
|
+
defineProperty(target, key, descriptor) {
|
|
565
|
+
const keystr = String(key);
|
|
566
|
+
const oldvalue = target[keystr];
|
|
567
|
+
const result = Reflect.defineProperty(target, key, descriptor);
|
|
568
|
+
const newValue = target[keystr];
|
|
569
|
+
_this.listeners[keystr] && _this.listeners[keystr].call(_this, newValue, keystr, target, oldvalue);
|
|
570
|
+
return result;
|
|
533
571
|
}
|
|
534
572
|
});
|
|
535
573
|
}
|
|
@@ -543,18 +581,18 @@ var OptionsManager = class {
|
|
|
543
581
|
set(key, value) {
|
|
544
582
|
const path = toPath(key);
|
|
545
583
|
if (path.length <= 1) {
|
|
546
|
-
set(this.currentOptions, path, this.preserveKeys.has(String(path[0])) ? value :
|
|
584
|
+
set(this.currentOptions, path, this.preserveKeys.has(String(path[0])) ? value : cloneAndFreezeForStore(value));
|
|
547
585
|
return;
|
|
548
586
|
}
|
|
549
587
|
const [rootKey, ...nestedPath] = path;
|
|
550
588
|
const currentRoot = get(this.currentOptions, [rootKey], void 0);
|
|
551
589
|
const baseRoot = isPlainObject7(currentRoot) ? currentRoot : {};
|
|
552
590
|
const nextRoot = cloneConfigValue(baseRoot);
|
|
553
|
-
set(nextRoot, nestedPath,
|
|
554
|
-
set(this.currentOptions, [rootKey], nextRoot);
|
|
591
|
+
set(nextRoot, nestedPath, cloneForRead(value));
|
|
592
|
+
set(this.currentOptions, [rootKey], freezeConfigValue(nextRoot));
|
|
555
593
|
}
|
|
556
594
|
fetch() {
|
|
557
|
-
const cloned =
|
|
595
|
+
const cloned = cloneForRead(this.currentOptions);
|
|
558
596
|
for (const key of this.preserveKeys) {
|
|
559
597
|
if (key in this.currentOptions) {
|
|
560
598
|
cloned[key] = this.currentOptions[key];
|
|
@@ -563,20 +601,20 @@ var OptionsManager = class {
|
|
|
563
601
|
return freezeConfigValue(cloned);
|
|
564
602
|
}
|
|
565
603
|
assign(options) {
|
|
566
|
-
const cloned =
|
|
604
|
+
const cloned = cloneForRead(options);
|
|
567
605
|
for (const key of this.preserveKeys) {
|
|
568
606
|
if (key in options) {
|
|
569
607
|
cloned[key] = options[key];
|
|
570
608
|
}
|
|
571
609
|
}
|
|
572
|
-
assign(this.currentOptions, cloned);
|
|
610
|
+
assign(this.currentOptions, freezeConfigValue(cloned));
|
|
573
611
|
}
|
|
574
612
|
onchange(key, func) {
|
|
575
613
|
set(this.listeners, key, func);
|
|
576
614
|
return this;
|
|
577
615
|
}
|
|
578
616
|
snapshot() {
|
|
579
|
-
const cloned =
|
|
617
|
+
const cloned = cloneForRead(this.currentOptions);
|
|
580
618
|
for (const key of this.preserveKeys) {
|
|
581
619
|
if (key in this.currentOptions) {
|
|
582
620
|
cloned[key] = this.currentOptions[key];
|
|
@@ -592,13 +630,13 @@ var OptionsManager = class {
|
|
|
592
630
|
delete current[key];
|
|
593
631
|
}
|
|
594
632
|
}
|
|
595
|
-
const cloned =
|
|
633
|
+
const cloned = cloneForRead(snap);
|
|
596
634
|
for (const key of this.preserveKeys) {
|
|
597
635
|
if (key in snap) {
|
|
598
636
|
cloned[key] = snap[key];
|
|
599
637
|
}
|
|
600
638
|
}
|
|
601
|
-
assign(current, cloned);
|
|
639
|
+
assign(current, freezeConfigValue(cloned));
|
|
602
640
|
}
|
|
603
641
|
};
|
|
604
642
|
|
|
@@ -613,7 +651,9 @@ var defaultRequestComplexity = {
|
|
|
613
651
|
maxIncludeCount: 10,
|
|
614
652
|
maxSubQueryCount: 10,
|
|
615
653
|
maxBulkConcurrency: 10,
|
|
616
|
-
maxHookConcurrency: 10
|
|
654
|
+
maxHookConcurrency: 10,
|
|
655
|
+
maxCorrelatedQueries: 100,
|
|
656
|
+
maxCorrelatedDepth: 5
|
|
617
657
|
};
|
|
618
658
|
var normalizePositiveInteger = (value, fallback) => {
|
|
619
659
|
return Number.isSafeInteger(value) && Number(value) > 0 ? Number(value) : fallback;
|
|
@@ -633,6 +673,14 @@ var resolveRequestComplexity = (options) => ({
|
|
|
633
673
|
maxHookConcurrency: normalizePositiveInteger(
|
|
634
674
|
options?.maxHookConcurrency,
|
|
635
675
|
defaultRequestComplexity.maxHookConcurrency
|
|
676
|
+
),
|
|
677
|
+
maxCorrelatedQueries: normalizePositiveInteger(
|
|
678
|
+
options?.maxCorrelatedQueries,
|
|
679
|
+
defaultRequestComplexity.maxCorrelatedQueries
|
|
680
|
+
),
|
|
681
|
+
maxCorrelatedDepth: normalizePositiveInteger(
|
|
682
|
+
options?.maxCorrelatedDepth,
|
|
683
|
+
defaultRequestComplexity.maxCorrelatedDepth
|
|
636
684
|
)
|
|
637
685
|
});
|
|
638
686
|
function validateRequestComplexity(value, options, scope = "request") {
|
|
@@ -1068,8 +1116,7 @@ var AccessRuntime = class {
|
|
|
1068
1116
|
}
|
|
1069
1117
|
setDataOptions(dataName, options) {
|
|
1070
1118
|
const manager = this.getOrCreateDataOptions(dataName);
|
|
1071
|
-
|
|
1072
|
-
manager.assign({ ...currentDataOptions, ...options });
|
|
1119
|
+
manager.assign(options);
|
|
1073
1120
|
}
|
|
1074
1121
|
setDataOption(dataName, key, value) {
|
|
1075
1122
|
const manager = this.getOrCreateDataOptions(dataName);
|
|
@@ -1079,6 +1126,11 @@ var AccessRuntime = class {
|
|
|
1079
1126
|
const manager = this.getOrCreateDataOptions(dataName);
|
|
1080
1127
|
return manager.fetch();
|
|
1081
1128
|
}
|
|
1129
|
+
getDataSnapshot(dataName) {
|
|
1130
|
+
const manager = this.getOrCreateDataOptions(dataName);
|
|
1131
|
+
const snapshot = manager.get("data");
|
|
1132
|
+
return snapshot ?? [];
|
|
1133
|
+
}
|
|
1082
1134
|
getDataOption(dataName, key, defaultValue) {
|
|
1083
1135
|
const manager = this.getOrCreateDataOptions(dataName);
|
|
1084
1136
|
return getNestedOption(manager, key, defaultValue);
|
|
@@ -1137,6 +1189,175 @@ var getGlobalOption = (key, defaultValue) => {
|
|
|
1137
1189
|
|
|
1138
1190
|
// src/validation/common.ts
|
|
1139
1191
|
import { z as z2 } from "zod";
|
|
1192
|
+
import { isPlainObject as isPlainObject11 } from "@web-ts-toolkit/utils";
|
|
1193
|
+
|
|
1194
|
+
// src/correlated-includes.ts
|
|
1195
|
+
import { cloneDeep, isArray as isArray5, isPlainObject as isPlainObject10 } from "@web-ts-toolkit/utils";
|
|
1196
|
+
var PARENT_MARKER_KEY = "$parent";
|
|
1197
|
+
var ESCAPE_WRAPPER_KEY = "$escape";
|
|
1198
|
+
var SUBQUERY_PAYLOAD_KEYS = /* @__PURE__ */ new Set(["$$sq", "$$date"]);
|
|
1199
|
+
var MARKER_FORBIDDEN_OPERATOR_KEYS = /* @__PURE__ */ new Set(["$text", "$where", "$comment"]);
|
|
1200
|
+
var LOGICAL_CLAUSE_KEYS = /* @__PURE__ */ new Set(["$and", "$or", "$nor"]);
|
|
1201
|
+
var DANGEROUS_PATH_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
|
|
1202
|
+
var FIELD_SEGMENT_PATTERN = /^[A-Za-z_][A-Za-z0-9_$]*$/;
|
|
1203
|
+
var ARRAY_INDEX_SEGMENT_PATTERN = /^(0|[1-9]\d*)$/;
|
|
1204
|
+
var ownKeys = (value) => Object.keys(value);
|
|
1205
|
+
function isParentRefMarker(value) {
|
|
1206
|
+
return isPlainObject10(value) && ownKeys(value).length === 1 && ownKeys(value)[0] === PARENT_MARKER_KEY;
|
|
1207
|
+
}
|
|
1208
|
+
function isEscapedParentRef(value) {
|
|
1209
|
+
return isPlainObject10(value) && ownKeys(value).length === 1 && ownKeys(value)[0] === ESCAPE_WRAPPER_KEY;
|
|
1210
|
+
}
|
|
1211
|
+
function hasMarkerKey(value) {
|
|
1212
|
+
return ownKeys(value).includes(PARENT_MARKER_KEY) || ownKeys(value).includes(ESCAPE_WRAPPER_KEY);
|
|
1213
|
+
}
|
|
1214
|
+
function isValidParentRefPath(path) {
|
|
1215
|
+
if (typeof path !== "string" || path.length === 0) return false;
|
|
1216
|
+
const segments = path.split(".");
|
|
1217
|
+
return segments.every(
|
|
1218
|
+
(segment) => (FIELD_SEGMENT_PATTERN.test(segment) || ARRAY_INDEX_SEGMENT_PATTERN.test(segment)) && !DANGEROUS_PATH_SEGMENTS.has(segment)
|
|
1219
|
+
);
|
|
1220
|
+
}
|
|
1221
|
+
function isValidCorrelatedOutputPath(path) {
|
|
1222
|
+
return typeof path === "string" && path.length > 0 && isValidFieldPath(path) && path !== "_id" && !path.startsWith("$");
|
|
1223
|
+
}
|
|
1224
|
+
function containsParentMarkerShape(value) {
|
|
1225
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1226
|
+
const visit = (current) => {
|
|
1227
|
+
if (isParentRefMarker(current) || isEscapedParentRef(current)) return true;
|
|
1228
|
+
if (isArray5(current)) {
|
|
1229
|
+
if (seen.has(current)) return false;
|
|
1230
|
+
seen.add(current);
|
|
1231
|
+
return current.some(visit);
|
|
1232
|
+
}
|
|
1233
|
+
if (isPlainObject10(current)) {
|
|
1234
|
+
if (seen.has(current)) return false;
|
|
1235
|
+
seen.add(current);
|
|
1236
|
+
if (hasMarkerKey(current)) return true;
|
|
1237
|
+
return Object.values(current).some(visit);
|
|
1238
|
+
}
|
|
1239
|
+
return false;
|
|
1240
|
+
};
|
|
1241
|
+
return visit(value);
|
|
1242
|
+
}
|
|
1243
|
+
function scanTemplateValueNode(value, path, issues) {
|
|
1244
|
+
if (isEscapedParentRef(value)) {
|
|
1245
|
+
const inner = value.$escape;
|
|
1246
|
+
if (!isParentRefMarker(inner) || typeof inner.$parent !== "string" || (inner.$parent?.length ?? 0) === 0) {
|
|
1247
|
+
issues.push({
|
|
1248
|
+
message: 'Invalid $escape payload: expected { "$escape": { "$parent": "<field>" } }',
|
|
1249
|
+
path
|
|
1250
|
+
});
|
|
1251
|
+
}
|
|
1252
|
+
return;
|
|
1253
|
+
}
|
|
1254
|
+
if (isParentRefMarker(value)) {
|
|
1255
|
+
if (!isValidParentRefPath(value.$parent)) {
|
|
1256
|
+
issues.push({
|
|
1257
|
+
message: `Invalid $parent reference path at ${path.length === 0 ? "#" : path.join(".")}`,
|
|
1258
|
+
path
|
|
1259
|
+
});
|
|
1260
|
+
}
|
|
1261
|
+
return;
|
|
1262
|
+
}
|
|
1263
|
+
if (isPlainObject10(value)) {
|
|
1264
|
+
if (hasMarkerKey(value)) {
|
|
1265
|
+
issues.push({
|
|
1266
|
+
message: 'Malformed $parent reference: objects carrying $parent must be exactly { "$parent": "<field>" }',
|
|
1267
|
+
path
|
|
1268
|
+
});
|
|
1269
|
+
return;
|
|
1270
|
+
}
|
|
1271
|
+
scanTemplateObjectEntries(value, path, issues);
|
|
1272
|
+
return;
|
|
1273
|
+
}
|
|
1274
|
+
if (isArray5(value)) {
|
|
1275
|
+
value.forEach((item, index) => scanTemplateValueNode(item, [...path, index], issues));
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
function scanTemplateObjectEntries(obj, basePath, issues) {
|
|
1279
|
+
for (const [key, child] of Object.entries(obj)) {
|
|
1280
|
+
const childPath = [...basePath, key];
|
|
1281
|
+
if (key === PARENT_MARKER_KEY) {
|
|
1282
|
+
issues.push({ message: "$parent is never a field name in correlated templates", path: childPath });
|
|
1283
|
+
continue;
|
|
1284
|
+
}
|
|
1285
|
+
if (SUBQUERY_PAYLOAD_KEYS.has(key)) {
|
|
1286
|
+
if (containsParentMarkerShape(child)) {
|
|
1287
|
+
issues.push({
|
|
1288
|
+
message: `Unsupported $parent reference inside ${key}: markers are not resolved in subquery payloads`,
|
|
1289
|
+
path: childPath
|
|
1290
|
+
});
|
|
1291
|
+
}
|
|
1292
|
+
continue;
|
|
1293
|
+
}
|
|
1294
|
+
if (MARKER_FORBIDDEN_OPERATOR_KEYS.has(key)) {
|
|
1295
|
+
if (containsParentMarkerShape(child)) {
|
|
1296
|
+
issues.push({
|
|
1297
|
+
message: `Unsupported $parent reference inside ${key}: markers are not resolved in ${key} values`,
|
|
1298
|
+
path: childPath
|
|
1299
|
+
});
|
|
1300
|
+
}
|
|
1301
|
+
continue;
|
|
1302
|
+
}
|
|
1303
|
+
if (key === "$elemMatch") {
|
|
1304
|
+
if (isParentRefMarker(child) || isEscapedParentRef(child)) {
|
|
1305
|
+
issues.push({
|
|
1306
|
+
message: "Unsupported $parent reference as a direct $elemMatch value",
|
|
1307
|
+
path: childPath
|
|
1308
|
+
});
|
|
1309
|
+
continue;
|
|
1310
|
+
}
|
|
1311
|
+
scanTemplateValueNode(child, childPath, issues);
|
|
1312
|
+
continue;
|
|
1313
|
+
}
|
|
1314
|
+
if (LOGICAL_CLAUSE_KEYS.has(key)) {
|
|
1315
|
+
if (!isArray5(child)) {
|
|
1316
|
+
scanTemplateValueNode(child, childPath, issues);
|
|
1317
|
+
continue;
|
|
1318
|
+
}
|
|
1319
|
+
child.forEach((clause, index) => {
|
|
1320
|
+
const clausePath = [...childPath, index];
|
|
1321
|
+
if (isParentRefMarker(clause) || isEscapedParentRef(clause)) {
|
|
1322
|
+
issues.push({
|
|
1323
|
+
message: `Unsupported $parent reference as a direct ${key} clause: clauses must be filter objects`,
|
|
1324
|
+
path: clausePath
|
|
1325
|
+
});
|
|
1326
|
+
return;
|
|
1327
|
+
}
|
|
1328
|
+
if (isPlainObject10(clause)) {
|
|
1329
|
+
scanTemplateObjectEntries(clause, clausePath, issues);
|
|
1330
|
+
}
|
|
1331
|
+
});
|
|
1332
|
+
continue;
|
|
1333
|
+
}
|
|
1334
|
+
scanTemplateValueNode(child, childPath, issues);
|
|
1335
|
+
}
|
|
1336
|
+
}
|
|
1337
|
+
function validateCorrelatedFilterTemplate(filter) {
|
|
1338
|
+
const issues = [];
|
|
1339
|
+
if (!isPlainObject10(filter)) {
|
|
1340
|
+
issues.push({ message: "Correlated include filter must be an object", path: [] });
|
|
1341
|
+
return issues;
|
|
1342
|
+
}
|
|
1343
|
+
if (isParentRefMarker(filter) || isEscapedParentRef(filter)) {
|
|
1344
|
+
issues.push({ message: "Correlated include filter must not itself be a $parent reference", path: [] });
|
|
1345
|
+
return issues;
|
|
1346
|
+
}
|
|
1347
|
+
scanTemplateObjectEntries(filter, [], issues);
|
|
1348
|
+
return issues;
|
|
1349
|
+
}
|
|
1350
|
+
function findArgsMarkerIssue(value, where) {
|
|
1351
|
+
if (value !== void 0 && containsParentMarkerShape(value)) {
|
|
1352
|
+
return {
|
|
1353
|
+
message: `Unsupported $parent reference in ${where}: references are only supported in filter and id positions`,
|
|
1354
|
+
path: []
|
|
1355
|
+
};
|
|
1356
|
+
}
|
|
1357
|
+
return null;
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
// src/validation/common.ts
|
|
1140
1361
|
var stringOrStringArray = z2.union([z2.string(), z2.array(z2.string())]);
|
|
1141
1362
|
var safeIntegerString = (min, message) => z2.string().regex(/^\d+$/, message).refine((value) => Number.isSafeInteger(Number(value)), "Expected a safe integer").refine((value) => Number(value) >= min, message);
|
|
1142
1363
|
var queryBooleanString = z2.enum(["true", "false"]);
|
|
@@ -1197,7 +1418,8 @@ var subPopulateSchema = z2.union([
|
|
|
1197
1418
|
select: projectionSchema.optional()
|
|
1198
1419
|
}).passthrough()
|
|
1199
1420
|
]);
|
|
1200
|
-
var
|
|
1421
|
+
var legacyIncludeItemSchema = z2.object({
|
|
1422
|
+
mode: z2.enum(["legacy"]).optional(),
|
|
1201
1423
|
model: z2.string().min(1),
|
|
1202
1424
|
op: z2.enum(["list", "read", "count"]),
|
|
1203
1425
|
path: z2.string().min(1),
|
|
@@ -1206,8 +1428,139 @@ var includeItemSchema = z2.object({
|
|
|
1206
1428
|
foreignField: z2.string().min(1),
|
|
1207
1429
|
args: z2.unknown().optional().describe("Forwarded to list/read includes. Count includes ignore pagination fields so counts remain exact."),
|
|
1208
1430
|
options: z2.unknown().optional()
|
|
1209
|
-
}).passthrough()
|
|
1210
|
-
|
|
1431
|
+
}).passthrough().superRefine((entry, ctx) => {
|
|
1432
|
+
if (entry.filter !== void 0 && containsParentMarkerShape(entry.filter)) {
|
|
1433
|
+
ctx.addIssue({
|
|
1434
|
+
code: z2.ZodIssueCode.custom,
|
|
1435
|
+
message: 'Legacy includes must not contain $parent markers; use a correlated include (mode: "correlated")',
|
|
1436
|
+
path: ["filter"]
|
|
1437
|
+
});
|
|
1438
|
+
}
|
|
1439
|
+
});
|
|
1440
|
+
var parentRefSchema = z2.object({ $parent: z2.string().min(1) }).strict().superRefine((value, ctx) => {
|
|
1441
|
+
if (!isValidParentRefPath(value.$parent)) {
|
|
1442
|
+
ctx.addIssue({
|
|
1443
|
+
code: z2.ZodIssueCode.custom,
|
|
1444
|
+
message: `Invalid $parent reference path: ${value.$parent}`,
|
|
1445
|
+
path: ["$parent"]
|
|
1446
|
+
});
|
|
1447
|
+
}
|
|
1448
|
+
});
|
|
1449
|
+
var correlatedIdSchema = z2.union([z2.string().min(1), parentRefSchema]);
|
|
1450
|
+
var correlatedFilterSchema = z2.record(z2.string(), z2.unknown()).superRefine((filter, ctx) => {
|
|
1451
|
+
for (const issue of validateCorrelatedFilterTemplate(filter)) {
|
|
1452
|
+
ctx.addIssue({
|
|
1453
|
+
code: z2.ZodIssueCode.custom,
|
|
1454
|
+
message: issue.message,
|
|
1455
|
+
path: issue.path.map(String)
|
|
1456
|
+
});
|
|
1457
|
+
}
|
|
1458
|
+
});
|
|
1459
|
+
var correlatedEmptyOptionsSchema = z2.unknown().refine(
|
|
1460
|
+
(value) => value === void 0 || isPlainObject11(value) && Object.keys(value).length === 0,
|
|
1461
|
+
"Correlated include options must be absent or empty"
|
|
1462
|
+
).optional();
|
|
1463
|
+
var correlatedPathSchema = z2.string().min(1).superRefine((path, ctx) => {
|
|
1464
|
+
if (!isValidCorrelatedOutputPath(path)) {
|
|
1465
|
+
ctx.addIssue({
|
|
1466
|
+
code: z2.ZodIssueCode.custom,
|
|
1467
|
+
message: `Invalid correlated include output path: ${path}`,
|
|
1468
|
+
path: []
|
|
1469
|
+
});
|
|
1470
|
+
}
|
|
1471
|
+
});
|
|
1472
|
+
var checkArgsMarkers = (select, sort, ctx) => {
|
|
1473
|
+
const selectIssue = findArgsMarkerIssue(select, "args.select");
|
|
1474
|
+
if (selectIssue) {
|
|
1475
|
+
ctx.addIssue({ code: z2.ZodIssueCode.custom, message: selectIssue.message, path: ["select"] });
|
|
1476
|
+
}
|
|
1477
|
+
const sortIssue = findArgsMarkerIssue(sort, "args.sort");
|
|
1478
|
+
if (sortIssue) {
|
|
1479
|
+
ctx.addIssue({ code: z2.ZodIssueCode.custom, message: sortIssue.message, path: ["sort"] });
|
|
1480
|
+
}
|
|
1481
|
+
};
|
|
1482
|
+
var correlatedReadArgsSchema = z2.object({
|
|
1483
|
+
select: projectionSchema.optional(),
|
|
1484
|
+
sort: sortSchema.optional(),
|
|
1485
|
+
include: z2.lazy(() => correlatedNestedIncludeSchema).optional()
|
|
1486
|
+
}).strict().superRefine((args, ctx) => checkArgsMarkers(args.select, args.sort, ctx));
|
|
1487
|
+
var correlatedListArgsSchema = z2.object({
|
|
1488
|
+
select: projectionSchema.optional(),
|
|
1489
|
+
sort: sortSchema.optional(),
|
|
1490
|
+
skip: z2.union([nonNegativeIntegerSchema, nonNegativeIntegerString]).optional(),
|
|
1491
|
+
limit: z2.union([positiveIntegerSchema, positiveIntegerString]).optional(),
|
|
1492
|
+
page: z2.union([nonNegativeIntegerSchema, nonNegativeIntegerString]).optional(),
|
|
1493
|
+
pageSize: z2.union([positiveIntegerSchema, positiveIntegerString]).optional(),
|
|
1494
|
+
include: z2.lazy(() => correlatedNestedIncludeSchema).optional()
|
|
1495
|
+
}).strict().superRefine((args, ctx) => checkArgsMarkers(args.select, args.sort, ctx));
|
|
1496
|
+
var correlatedCountArgsSchema = z2.unknown().refine(
|
|
1497
|
+
(value) => value === void 0 || isPlainObject11(value) && Object.keys(value).length === 0,
|
|
1498
|
+
"Count includes accept no args"
|
|
1499
|
+
).optional();
|
|
1500
|
+
var correlatedReadByIdSchema = z2.object({
|
|
1501
|
+
mode: z2.literal("correlated"),
|
|
1502
|
+
model: z2.string().min(1),
|
|
1503
|
+
op: z2.literal("read"),
|
|
1504
|
+
path: correlatedPathSchema,
|
|
1505
|
+
id: correlatedIdSchema,
|
|
1506
|
+
args: correlatedReadArgsSchema.optional(),
|
|
1507
|
+
options: correlatedEmptyOptionsSchema
|
|
1508
|
+
}).strict();
|
|
1509
|
+
var correlatedReadByFilterSchema = z2.object({
|
|
1510
|
+
mode: z2.literal("correlated"),
|
|
1511
|
+
model: z2.string().min(1),
|
|
1512
|
+
op: z2.literal("read"),
|
|
1513
|
+
path: correlatedPathSchema,
|
|
1514
|
+
filter: correlatedFilterSchema,
|
|
1515
|
+
args: correlatedReadArgsSchema.optional(),
|
|
1516
|
+
options: correlatedEmptyOptionsSchema
|
|
1517
|
+
}).strict();
|
|
1518
|
+
var correlatedListSchema = z2.object({
|
|
1519
|
+
mode: z2.literal("correlated"),
|
|
1520
|
+
model: z2.string().min(1),
|
|
1521
|
+
op: z2.literal("list"),
|
|
1522
|
+
path: correlatedPathSchema,
|
|
1523
|
+
filter: correlatedFilterSchema,
|
|
1524
|
+
args: correlatedListArgsSchema.optional(),
|
|
1525
|
+
options: correlatedEmptyOptionsSchema
|
|
1526
|
+
}).strict();
|
|
1527
|
+
var correlatedCountSchema = z2.object({
|
|
1528
|
+
mode: z2.literal("correlated"),
|
|
1529
|
+
model: z2.string().min(1),
|
|
1530
|
+
op: z2.literal("count"),
|
|
1531
|
+
path: correlatedPathSchema,
|
|
1532
|
+
filter: correlatedFilterSchema,
|
|
1533
|
+
args: correlatedCountArgsSchema,
|
|
1534
|
+
options: correlatedEmptyOptionsSchema
|
|
1535
|
+
}).strict();
|
|
1536
|
+
var includeItemSchema = z2.union([
|
|
1537
|
+
legacyIncludeItemSchema,
|
|
1538
|
+
correlatedReadByIdSchema,
|
|
1539
|
+
correlatedReadByFilterSchema,
|
|
1540
|
+
correlatedListSchema,
|
|
1541
|
+
correlatedCountSchema
|
|
1542
|
+
]);
|
|
1543
|
+
var correlatedNestedIncludeSchema = z2.union([includeItemSchema, z2.array(includeItemSchema)]);
|
|
1544
|
+
var rejectDuplicateCorrelatedPaths = (items, ctx) => {
|
|
1545
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1546
|
+
items.forEach((item, index) => {
|
|
1547
|
+
if (isPlainObject11(item) && item.mode === "correlated" && typeof item.path === "string") {
|
|
1548
|
+
if (seen.has(item.path)) {
|
|
1549
|
+
ctx.addIssue({
|
|
1550
|
+
code: z2.ZodIssueCode.custom,
|
|
1551
|
+
message: `Duplicate correlated include output path: ${item.path}`,
|
|
1552
|
+
path: [index, "path"]
|
|
1553
|
+
});
|
|
1554
|
+
} else {
|
|
1555
|
+
seen.add(item.path);
|
|
1556
|
+
}
|
|
1557
|
+
}
|
|
1558
|
+
});
|
|
1559
|
+
};
|
|
1560
|
+
var includeSchema = z2.union([
|
|
1561
|
+
includeItemSchema,
|
|
1562
|
+
z2.array(includeItemSchema).superRefine(rejectDuplicateCorrelatedPaths)
|
|
1563
|
+
]);
|
|
1211
1564
|
var fieldsSchema = z2.array(z2.string().min(1));
|
|
1212
1565
|
var taskSchema = z2.object({
|
|
1213
1566
|
type: z2.string().min(1),
|
|
@@ -1324,8 +1677,9 @@ function defineRequestSchema(validator, options = {}) {
|
|
|
1324
1677
|
};
|
|
1325
1678
|
}
|
|
1326
1679
|
function fromZod(schema) {
|
|
1327
|
-
return (value) => {
|
|
1328
|
-
const
|
|
1680
|
+
return async (value) => {
|
|
1681
|
+
const candidate = schema;
|
|
1682
|
+
const result = typeof candidate.safeParseAsync === "function" ? await candidate.safeParseAsync.call(schema, value) : schema.safeParse(value);
|
|
1329
1683
|
if (result.success) {
|
|
1330
1684
|
return {
|
|
1331
1685
|
success: true,
|
|
@@ -1362,6 +1716,9 @@ function fromYup(schema) {
|
|
|
1362
1716
|
data
|
|
1363
1717
|
};
|
|
1364
1718
|
} catch (error) {
|
|
1719
|
+
if (!isYupValidationError(error)) {
|
|
1720
|
+
throw error;
|
|
1721
|
+
}
|
|
1365
1722
|
return {
|
|
1366
1723
|
success: false,
|
|
1367
1724
|
issues: normalizeYupIssues(error)
|
|
@@ -1386,8 +1743,32 @@ function fromJoi(schema) {
|
|
|
1386
1743
|
}
|
|
1387
1744
|
function fromAjv(validator) {
|
|
1388
1745
|
return async (value) => {
|
|
1389
|
-
const
|
|
1390
|
-
if (
|
|
1746
|
+
const outcome = validator(value);
|
|
1747
|
+
if (isThenable(outcome)) {
|
|
1748
|
+
try {
|
|
1749
|
+
const data = await outcome;
|
|
1750
|
+
if (data === false) {
|
|
1751
|
+
return {
|
|
1752
|
+
success: false,
|
|
1753
|
+
issues: normalizeAjvIssues(validator.errors ?? [])
|
|
1754
|
+
};
|
|
1755
|
+
}
|
|
1756
|
+
return {
|
|
1757
|
+
success: true,
|
|
1758
|
+
data: data === true ? value : data
|
|
1759
|
+
};
|
|
1760
|
+
} catch (error) {
|
|
1761
|
+
if (!isAjvValidationError(error)) {
|
|
1762
|
+
throw error;
|
|
1763
|
+
}
|
|
1764
|
+
return {
|
|
1765
|
+
success: false,
|
|
1766
|
+
issues: normalizeAjvIssues(error.errors ?? [])
|
|
1767
|
+
};
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
const errorsSnapshot = validator.errors ? [...validator.errors] : [];
|
|
1771
|
+
if (outcome) {
|
|
1391
1772
|
return {
|
|
1392
1773
|
success: true,
|
|
1393
1774
|
data: value
|
|
@@ -1395,7 +1776,7 @@ function fromAjv(validator) {
|
|
|
1395
1776
|
}
|
|
1396
1777
|
return {
|
|
1397
1778
|
success: false,
|
|
1398
|
-
issues: normalizeAjvIssues(
|
|
1779
|
+
issues: normalizeAjvIssues(errorsSnapshot)
|
|
1399
1780
|
};
|
|
1400
1781
|
};
|
|
1401
1782
|
}
|
|
@@ -1416,6 +1797,25 @@ function fromValibot(schema, safeParse) {
|
|
|
1416
1797
|
}
|
|
1417
1798
|
function fromArkType(type) {
|
|
1418
1799
|
return async (value) => {
|
|
1800
|
+
if (isStandardSchema(type)) {
|
|
1801
|
+
const result2 = await type["~standard"].validate(value);
|
|
1802
|
+
if (isArkTypeErrors(result2)) {
|
|
1803
|
+
return {
|
|
1804
|
+
success: false,
|
|
1805
|
+
issues: normalizeArkTypeIssues(result2)
|
|
1806
|
+
};
|
|
1807
|
+
}
|
|
1808
|
+
if (isStandardSchemaFailure(result2)) {
|
|
1809
|
+
return {
|
|
1810
|
+
success: false,
|
|
1811
|
+
issues: normalizeArkTypeStandardIssues(result2.issues)
|
|
1812
|
+
};
|
|
1813
|
+
}
|
|
1814
|
+
return {
|
|
1815
|
+
success: true,
|
|
1816
|
+
data: result2.value
|
|
1817
|
+
};
|
|
1818
|
+
}
|
|
1419
1819
|
const result = await type(value);
|
|
1420
1820
|
if (!isArkTypeErrors(result)) {
|
|
1421
1821
|
return {
|
|
@@ -1468,6 +1868,9 @@ function fromVine(validator) {
|
|
|
1468
1868
|
data: output
|
|
1469
1869
|
};
|
|
1470
1870
|
} catch (error) {
|
|
1871
|
+
if (!isVineValidationError(error)) {
|
|
1872
|
+
throw error;
|
|
1873
|
+
}
|
|
1471
1874
|
return {
|
|
1472
1875
|
success: false,
|
|
1473
1876
|
issues: normalizeVineError(error)
|
|
@@ -1528,6 +1931,13 @@ function normalizeArkTypeIssues(issues) {
|
|
|
1528
1931
|
}));
|
|
1529
1932
|
return normalized.length ? normalized : [{ message: issues.summary ?? "Validation failed" }];
|
|
1530
1933
|
}
|
|
1934
|
+
function normalizeArkTypeStandardIssues(issues) {
|
|
1935
|
+
const normalized = issues.map((issue) => ({
|
|
1936
|
+
message: issue.message ?? issue.problem ?? "Validation failed",
|
|
1937
|
+
path: issue.path?.flatMap((segment) => normalizePathSegment(segment))
|
|
1938
|
+
}));
|
|
1939
|
+
return normalized.length ? normalized : [{ message: "Validation failed" }];
|
|
1940
|
+
}
|
|
1531
1941
|
function normalizeIoTsIssues(issues) {
|
|
1532
1942
|
return issues.map((issue) => ({
|
|
1533
1943
|
message: issue.message ?? "Validation failed",
|
|
@@ -1562,25 +1972,64 @@ function normalizeVineField(message) {
|
|
|
1562
1972
|
}
|
|
1563
1973
|
return path.length ? path : void 0;
|
|
1564
1974
|
}
|
|
1975
|
+
function decodeAjvSegment(segment) {
|
|
1976
|
+
return segment.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
1977
|
+
}
|
|
1978
|
+
function encodePointerSegment(segment) {
|
|
1979
|
+
return encodeURIComponent(segment.replace(/~/g, "~0").replace(/\//g, "~1"));
|
|
1980
|
+
}
|
|
1565
1981
|
function parseAjvPath(issue) {
|
|
1566
|
-
const
|
|
1567
|
-
|
|
1568
|
-
|
|
1982
|
+
const raw = issue.instancePath;
|
|
1983
|
+
const base = raw == null || raw === "" ? [] : (raw.startsWith("/") ? raw.slice(1) : raw).split("/").map(decodeAjvSegment);
|
|
1984
|
+
const missing = issue.params?.missingProperty;
|
|
1985
|
+
if (typeof missing === "string") {
|
|
1986
|
+
return [...base, missing];
|
|
1569
1987
|
}
|
|
1570
|
-
return
|
|
1988
|
+
return base;
|
|
1571
1989
|
}
|
|
1572
1990
|
function parsePathString(path) {
|
|
1573
1991
|
if (!path) return void 0;
|
|
1574
1992
|
return path.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean).map((segment) => /^\d+$/.test(segment) ? Number(segment) : segment);
|
|
1575
1993
|
}
|
|
1576
1994
|
function isYupValidationError(error) {
|
|
1577
|
-
|
|
1995
|
+
if (typeof error !== "object" || error === null || !("message" in error)) {
|
|
1996
|
+
return false;
|
|
1997
|
+
}
|
|
1998
|
+
const candidate = error;
|
|
1999
|
+
return candidate.name === "ValidationError" && typeof candidate.message === "string" && Array.isArray(candidate.errors) && Array.isArray(candidate.inner);
|
|
1578
2000
|
}
|
|
1579
2001
|
function isArkTypeErrors(value) {
|
|
1580
|
-
|
|
2002
|
+
if (typeof value !== "object" || value === null) {
|
|
2003
|
+
return false;
|
|
2004
|
+
}
|
|
2005
|
+
const record = value;
|
|
2006
|
+
return record.arkKind === "errors" || record[" arkKind"] === "errors";
|
|
2007
|
+
}
|
|
2008
|
+
function isThenable(value) {
|
|
2009
|
+
return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
|
|
2010
|
+
}
|
|
2011
|
+
function isAjvValidationError(error) {
|
|
2012
|
+
if (typeof error !== "object" || error === null || !("errors" in error)) {
|
|
2013
|
+
return false;
|
|
2014
|
+
}
|
|
2015
|
+
const errors = error.errors;
|
|
2016
|
+
return errors == null || Array.isArray(errors);
|
|
1581
2017
|
}
|
|
1582
2018
|
function isVineValidationError(error) {
|
|
1583
|
-
|
|
2019
|
+
if (typeof error !== "object" || error === null || !("messages" in error)) {
|
|
2020
|
+
return false;
|
|
2021
|
+
}
|
|
2022
|
+
const candidate = error;
|
|
2023
|
+
if (!Array.isArray(candidate.messages) || candidate.messages.length === 0) {
|
|
2024
|
+
return false;
|
|
2025
|
+
}
|
|
2026
|
+
const hasDiscriminator = candidate.code === "E_VALIDATION_ERROR" || candidate.name === "ValidationError";
|
|
2027
|
+
if (!hasDiscriminator) {
|
|
2028
|
+
return false;
|
|
2029
|
+
}
|
|
2030
|
+
return candidate.messages.every(
|
|
2031
|
+
(message) => typeof message === "object" && message !== null && typeof message.message === "string" && typeof message.field === "string"
|
|
2032
|
+
);
|
|
1584
2033
|
}
|
|
1585
2034
|
function formatIssue(issue, key, location = "pointer") {
|
|
1586
2035
|
const path = (issue.path ?? []).map(String);
|
|
@@ -1597,7 +2046,7 @@ function formatIssue(issue, key, location = "pointer") {
|
|
|
1597
2046
|
};
|
|
1598
2047
|
}
|
|
1599
2048
|
function buildPointer(path) {
|
|
1600
|
-
return path.length === 0 ? "#" : `#/${path.join("/")}`;
|
|
2049
|
+
return path.length === 0 ? "#" : `#/${path.map(encodePointerSegment).join("/")}`;
|
|
1601
2050
|
}
|
|
1602
2051
|
|
|
1603
2052
|
// src/validation/model-router.ts
|
|
@@ -1648,7 +2097,7 @@ var listBodySchema = z3.object({
|
|
|
1648
2097
|
}).passthrough().superRefine((body, ctx) => rejectKeys(body, ctx, ["query"]));
|
|
1649
2098
|
var countBodySchema = z3.object({
|
|
1650
2099
|
filter: objectOrArraySchema.optional()
|
|
1651
|
-
}).passthrough().superRefine((body, ctx) => rejectKeys(body, ctx, ["query", "access", "options"]));
|
|
2100
|
+
}).passthrough().superRefine((body, ctx) => rejectKeys(body, ctx, ["query", "access", "options", "include"]));
|
|
1652
2101
|
var readFilterBodySchema = z3.object({
|
|
1653
2102
|
filter: objectOrArraySchema.optional(),
|
|
1654
2103
|
select: projectionSchema.optional(),
|