@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/index.js
CHANGED
|
@@ -77,14 +77,14 @@ __export(index_exports, {
|
|
|
77
77
|
setModelOptions: () => setModelOptions
|
|
78
78
|
});
|
|
79
79
|
module.exports = __toCommonJS(index_exports);
|
|
80
|
-
var
|
|
80
|
+
var import_utils30 = require("@web-ts-toolkit/utils");
|
|
81
81
|
|
|
82
82
|
// src/middleware.ts
|
|
83
83
|
var import_express_json_router2 = __toESM(require("@web-ts-toolkit/express-json-router"));
|
|
84
|
-
var
|
|
84
|
+
var import_utils23 = require("@web-ts-toolkit/utils");
|
|
85
85
|
|
|
86
86
|
// src/core.ts
|
|
87
|
-
var
|
|
87
|
+
var import_utils22 = require("@web-ts-toolkit/utils");
|
|
88
88
|
|
|
89
89
|
// src/runtime.ts
|
|
90
90
|
var import_mongoose3 = __toESM(require("mongoose"));
|
|
@@ -841,6 +841,29 @@ var cloneConfigValue = (value) => {
|
|
|
841
841
|
])
|
|
842
842
|
);
|
|
843
843
|
};
|
|
844
|
+
var cloneForRead = (value) => {
|
|
845
|
+
if (Array.isArray(value)) {
|
|
846
|
+
if (Object.isFrozen(value)) {
|
|
847
|
+
return value;
|
|
848
|
+
}
|
|
849
|
+
return value.map((item) => cloneForRead(item));
|
|
850
|
+
}
|
|
851
|
+
if (!value || typeof value !== "object") {
|
|
852
|
+
return value;
|
|
853
|
+
}
|
|
854
|
+
if (Object.getPrototypeOf(value) !== Object.prototype) {
|
|
855
|
+
return value;
|
|
856
|
+
}
|
|
857
|
+
if (Object.isFrozen(value)) {
|
|
858
|
+
return value;
|
|
859
|
+
}
|
|
860
|
+
return Object.fromEntries(
|
|
861
|
+
Object.entries(value).map(([entryKey, entryValue]) => [
|
|
862
|
+
entryKey,
|
|
863
|
+
cloneForRead(entryValue)
|
|
864
|
+
])
|
|
865
|
+
);
|
|
866
|
+
};
|
|
844
867
|
var freezeConfigValue = (value) => {
|
|
845
868
|
if (!value || typeof value !== "object") {
|
|
846
869
|
return value;
|
|
@@ -856,6 +879,7 @@ var freezeConfigValue = (value) => {
|
|
|
856
879
|
}
|
|
857
880
|
return value;
|
|
858
881
|
};
|
|
882
|
+
var cloneAndFreezeForStore = (value) => freezeConfigValue(cloneForRead(value));
|
|
859
883
|
var getNestedOption = (manager, key, defaultValue) => {
|
|
860
884
|
const keys2 = String(key).split(".");
|
|
861
885
|
if (keys2.length === 1) {
|
|
@@ -885,6 +909,14 @@ var OptionsManager = class {
|
|
|
885
909
|
target[keystr] = value;
|
|
886
910
|
_this.listeners[keystr] && _this.listeners[keystr].call(_this, value, keystr, target, oldvalue);
|
|
887
911
|
return true;
|
|
912
|
+
},
|
|
913
|
+
defineProperty(target, key, descriptor) {
|
|
914
|
+
const keystr = String(key);
|
|
915
|
+
const oldvalue = target[keystr];
|
|
916
|
+
const result = Reflect.defineProperty(target, key, descriptor);
|
|
917
|
+
const newValue = target[keystr];
|
|
918
|
+
_this.listeners[keystr] && _this.listeners[keystr].call(_this, newValue, keystr, target, oldvalue);
|
|
919
|
+
return result;
|
|
888
920
|
}
|
|
889
921
|
});
|
|
890
922
|
}
|
|
@@ -898,18 +930,18 @@ var OptionsManager = class {
|
|
|
898
930
|
set(key, value) {
|
|
899
931
|
const path = toPath(key);
|
|
900
932
|
if (path.length <= 1) {
|
|
901
|
-
(0, import_utils9.set)(this.currentOptions, path, this.preserveKeys.has(String(path[0])) ? value :
|
|
933
|
+
(0, import_utils9.set)(this.currentOptions, path, this.preserveKeys.has(String(path[0])) ? value : cloneAndFreezeForStore(value));
|
|
902
934
|
return;
|
|
903
935
|
}
|
|
904
936
|
const [rootKey, ...nestedPath] = path;
|
|
905
937
|
const currentRoot = (0, import_utils9.get)(this.currentOptions, [rootKey], void 0);
|
|
906
938
|
const baseRoot = (0, import_utils9.isPlainObject)(currentRoot) ? currentRoot : {};
|
|
907
939
|
const nextRoot = cloneConfigValue(baseRoot);
|
|
908
|
-
(0, import_utils9.set)(nextRoot, nestedPath,
|
|
909
|
-
(0, import_utils9.set)(this.currentOptions, [rootKey], nextRoot);
|
|
940
|
+
(0, import_utils9.set)(nextRoot, nestedPath, cloneForRead(value));
|
|
941
|
+
(0, import_utils9.set)(this.currentOptions, [rootKey], freezeConfigValue(nextRoot));
|
|
910
942
|
}
|
|
911
943
|
fetch() {
|
|
912
|
-
const cloned =
|
|
944
|
+
const cloned = cloneForRead(this.currentOptions);
|
|
913
945
|
for (const key of this.preserveKeys) {
|
|
914
946
|
if (key in this.currentOptions) {
|
|
915
947
|
cloned[key] = this.currentOptions[key];
|
|
@@ -918,20 +950,20 @@ var OptionsManager = class {
|
|
|
918
950
|
return freezeConfigValue(cloned);
|
|
919
951
|
}
|
|
920
952
|
assign(options) {
|
|
921
|
-
const cloned =
|
|
953
|
+
const cloned = cloneForRead(options);
|
|
922
954
|
for (const key of this.preserveKeys) {
|
|
923
955
|
if (key in options) {
|
|
924
956
|
cloned[key] = options[key];
|
|
925
957
|
}
|
|
926
958
|
}
|
|
927
|
-
(0, import_utils9.assign)(this.currentOptions, cloned);
|
|
959
|
+
(0, import_utils9.assign)(this.currentOptions, freezeConfigValue(cloned));
|
|
928
960
|
}
|
|
929
961
|
onchange(key, func) {
|
|
930
962
|
(0, import_utils9.set)(this.listeners, key, func);
|
|
931
963
|
return this;
|
|
932
964
|
}
|
|
933
965
|
snapshot() {
|
|
934
|
-
const cloned =
|
|
966
|
+
const cloned = cloneForRead(this.currentOptions);
|
|
935
967
|
for (const key of this.preserveKeys) {
|
|
936
968
|
if (key in this.currentOptions) {
|
|
937
969
|
cloned[key] = this.currentOptions[key];
|
|
@@ -947,13 +979,13 @@ var OptionsManager = class {
|
|
|
947
979
|
delete current[key];
|
|
948
980
|
}
|
|
949
981
|
}
|
|
950
|
-
const cloned =
|
|
982
|
+
const cloned = cloneForRead(snap);
|
|
951
983
|
for (const key of this.preserveKeys) {
|
|
952
984
|
if (key in snap) {
|
|
953
985
|
cloned[key] = snap[key];
|
|
954
986
|
}
|
|
955
987
|
}
|
|
956
|
-
(0, import_utils9.assign)(current, cloned);
|
|
988
|
+
(0, import_utils9.assign)(current, freezeConfigValue(cloned));
|
|
957
989
|
}
|
|
958
990
|
};
|
|
959
991
|
|
|
@@ -968,7 +1000,9 @@ var defaultRequestComplexity = {
|
|
|
968
1000
|
maxIncludeCount: 10,
|
|
969
1001
|
maxSubQueryCount: 10,
|
|
970
1002
|
maxBulkConcurrency: 10,
|
|
971
|
-
maxHookConcurrency: 10
|
|
1003
|
+
maxHookConcurrency: 10,
|
|
1004
|
+
maxCorrelatedQueries: 100,
|
|
1005
|
+
maxCorrelatedDepth: 5
|
|
972
1006
|
};
|
|
973
1007
|
var normalizePositiveInteger = (value, fallback) => {
|
|
974
1008
|
return Number.isSafeInteger(value) && Number(value) > 0 ? Number(value) : fallback;
|
|
@@ -988,6 +1022,14 @@ var resolveRequestComplexity = (options) => ({
|
|
|
988
1022
|
maxHookConcurrency: normalizePositiveInteger(
|
|
989
1023
|
options?.maxHookConcurrency,
|
|
990
1024
|
defaultRequestComplexity.maxHookConcurrency
|
|
1025
|
+
),
|
|
1026
|
+
maxCorrelatedQueries: normalizePositiveInteger(
|
|
1027
|
+
options?.maxCorrelatedQueries,
|
|
1028
|
+
defaultRequestComplexity.maxCorrelatedQueries
|
|
1029
|
+
),
|
|
1030
|
+
maxCorrelatedDepth: normalizePositiveInteger(
|
|
1031
|
+
options?.maxCorrelatedDepth,
|
|
1032
|
+
defaultRequestComplexity.maxCorrelatedDepth
|
|
991
1033
|
)
|
|
992
1034
|
});
|
|
993
1035
|
function validateRequestComplexity(value, options, scope = "request") {
|
|
@@ -1423,8 +1465,7 @@ var AccessRuntime = class {
|
|
|
1423
1465
|
}
|
|
1424
1466
|
setDataOptions(dataName, options) {
|
|
1425
1467
|
const manager = this.getOrCreateDataOptions(dataName);
|
|
1426
|
-
|
|
1427
|
-
manager.assign({ ...currentDataOptions, ...options });
|
|
1468
|
+
manager.assign(options);
|
|
1428
1469
|
}
|
|
1429
1470
|
setDataOption(dataName, key, value) {
|
|
1430
1471
|
const manager = this.getOrCreateDataOptions(dataName);
|
|
@@ -1434,6 +1475,11 @@ var AccessRuntime = class {
|
|
|
1434
1475
|
const manager = this.getOrCreateDataOptions(dataName);
|
|
1435
1476
|
return manager.fetch();
|
|
1436
1477
|
}
|
|
1478
|
+
getDataSnapshot(dataName) {
|
|
1479
|
+
const manager = this.getOrCreateDataOptions(dataName);
|
|
1480
|
+
const snapshot = manager.get("data");
|
|
1481
|
+
return snapshot ?? [];
|
|
1482
|
+
}
|
|
1437
1483
|
getDataOption(dataName, key, defaultValue) {
|
|
1438
1484
|
const manager = this.getOrCreateDataOptions(dataName);
|
|
1439
1485
|
return getNestedOption(manager, key, defaultValue);
|
|
@@ -1555,6 +1601,9 @@ var getRuntime4 = () => getActiveRuntime() ?? defaultRuntime;
|
|
|
1555
1601
|
var getDataOptions = (dataName) => {
|
|
1556
1602
|
return getRuntime4().getDataOptions(dataName);
|
|
1557
1603
|
};
|
|
1604
|
+
var getDataSnapshot = (dataName) => {
|
|
1605
|
+
return getRuntime4().getDataSnapshot(dataName);
|
|
1606
|
+
};
|
|
1558
1607
|
var getDataOption = (dataName, key, defaultValue) => {
|
|
1559
1608
|
return getRuntime4().getDataOption(dataName, key, defaultValue);
|
|
1560
1609
|
};
|
|
@@ -1564,12 +1613,465 @@ var getRuntime5 = () => getActiveRuntime() ?? defaultRuntime;
|
|
|
1564
1613
|
var getModelRef = (modelName, refPath) => {
|
|
1565
1614
|
return getRuntime5().getModelRef(modelName, refPath);
|
|
1566
1615
|
};
|
|
1567
|
-
var getModelSub = (modelName) => {
|
|
1568
|
-
return getRuntime5().getModelSub(modelName);
|
|
1569
|
-
};
|
|
1570
1616
|
|
|
1571
1617
|
// src/services/base.ts
|
|
1618
|
+
var import_utils13 = require("@web-ts-toolkit/utils");
|
|
1619
|
+
|
|
1620
|
+
// src/correlated-includes.ts
|
|
1572
1621
|
var import_utils12 = require("@web-ts-toolkit/utils");
|
|
1622
|
+
var PARENT_MARKER_KEY = "$parent";
|
|
1623
|
+
var ESCAPE_WRAPPER_KEY = "$escape";
|
|
1624
|
+
var SUBQUERY_PAYLOAD_KEYS = /* @__PURE__ */ new Set(["$$sq", "$$date"]);
|
|
1625
|
+
var MARKER_FORBIDDEN_OPERATOR_KEYS = /* @__PURE__ */ new Set(["$text", "$where", "$comment"]);
|
|
1626
|
+
var LOGICAL_CLAUSE_KEYS = /* @__PURE__ */ new Set(["$and", "$or", "$nor"]);
|
|
1627
|
+
var DANGEROUS_PATH_SEGMENTS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
|
|
1628
|
+
var FIELD_SEGMENT_PATTERN = /^[A-Za-z_][A-Za-z0-9_$]*$/;
|
|
1629
|
+
var ARRAY_INDEX_SEGMENT_PATTERN = /^(0|[1-9]\d*)$/;
|
|
1630
|
+
var ownKeys = (value) => Object.keys(value);
|
|
1631
|
+
function isParentRefMarker(value) {
|
|
1632
|
+
return (0, import_utils12.isPlainObject)(value) && ownKeys(value).length === 1 && ownKeys(value)[0] === PARENT_MARKER_KEY;
|
|
1633
|
+
}
|
|
1634
|
+
function isEscapedParentRef(value) {
|
|
1635
|
+
return (0, import_utils12.isPlainObject)(value) && ownKeys(value).length === 1 && ownKeys(value)[0] === ESCAPE_WRAPPER_KEY;
|
|
1636
|
+
}
|
|
1637
|
+
function hasMarkerKey(value) {
|
|
1638
|
+
return ownKeys(value).includes(PARENT_MARKER_KEY) || ownKeys(value).includes(ESCAPE_WRAPPER_KEY);
|
|
1639
|
+
}
|
|
1640
|
+
function isValidParentRefPath(path) {
|
|
1641
|
+
if (typeof path !== "string" || path.length === 0) return false;
|
|
1642
|
+
const segments = path.split(".");
|
|
1643
|
+
return segments.every(
|
|
1644
|
+
(segment) => (FIELD_SEGMENT_PATTERN.test(segment) || ARRAY_INDEX_SEGMENT_PATTERN.test(segment)) && !DANGEROUS_PATH_SEGMENTS.has(segment)
|
|
1645
|
+
);
|
|
1646
|
+
}
|
|
1647
|
+
function isValidCorrelatedOutputPath(path) {
|
|
1648
|
+
return typeof path === "string" && path.length > 0 && isValidFieldPath(path) && path !== "_id" && !path.startsWith("$");
|
|
1649
|
+
}
|
|
1650
|
+
function containsParentMarkerShape(value) {
|
|
1651
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1652
|
+
const visit = (current) => {
|
|
1653
|
+
if (isParentRefMarker(current) || isEscapedParentRef(current)) return true;
|
|
1654
|
+
if ((0, import_utils12.isArray)(current)) {
|
|
1655
|
+
if (seen.has(current)) return false;
|
|
1656
|
+
seen.add(current);
|
|
1657
|
+
return current.some(visit);
|
|
1658
|
+
}
|
|
1659
|
+
if ((0, import_utils12.isPlainObject)(current)) {
|
|
1660
|
+
if (seen.has(current)) return false;
|
|
1661
|
+
seen.add(current);
|
|
1662
|
+
if (hasMarkerKey(current)) return true;
|
|
1663
|
+
return Object.values(current).some(visit);
|
|
1664
|
+
}
|
|
1665
|
+
return false;
|
|
1666
|
+
};
|
|
1667
|
+
return visit(value);
|
|
1668
|
+
}
|
|
1669
|
+
function scanTemplateValueNode(value, path, issues) {
|
|
1670
|
+
if (isEscapedParentRef(value)) {
|
|
1671
|
+
const inner = value.$escape;
|
|
1672
|
+
if (!isParentRefMarker(inner) || typeof inner.$parent !== "string" || (inner.$parent?.length ?? 0) === 0) {
|
|
1673
|
+
issues.push({
|
|
1674
|
+
message: 'Invalid $escape payload: expected { "$escape": { "$parent": "<field>" } }',
|
|
1675
|
+
path
|
|
1676
|
+
});
|
|
1677
|
+
}
|
|
1678
|
+
return;
|
|
1679
|
+
}
|
|
1680
|
+
if (isParentRefMarker(value)) {
|
|
1681
|
+
if (!isValidParentRefPath(value.$parent)) {
|
|
1682
|
+
issues.push({
|
|
1683
|
+
message: `Invalid $parent reference path at ${path.length === 0 ? "#" : path.join(".")}`,
|
|
1684
|
+
path
|
|
1685
|
+
});
|
|
1686
|
+
}
|
|
1687
|
+
return;
|
|
1688
|
+
}
|
|
1689
|
+
if ((0, import_utils12.isPlainObject)(value)) {
|
|
1690
|
+
if (hasMarkerKey(value)) {
|
|
1691
|
+
issues.push({
|
|
1692
|
+
message: 'Malformed $parent reference: objects carrying $parent must be exactly { "$parent": "<field>" }',
|
|
1693
|
+
path
|
|
1694
|
+
});
|
|
1695
|
+
return;
|
|
1696
|
+
}
|
|
1697
|
+
scanTemplateObjectEntries(value, path, issues);
|
|
1698
|
+
return;
|
|
1699
|
+
}
|
|
1700
|
+
if ((0, import_utils12.isArray)(value)) {
|
|
1701
|
+
value.forEach((item, index) => scanTemplateValueNode(item, [...path, index], issues));
|
|
1702
|
+
}
|
|
1703
|
+
}
|
|
1704
|
+
function scanTemplateObjectEntries(obj, basePath, issues) {
|
|
1705
|
+
for (const [key, child] of Object.entries(obj)) {
|
|
1706
|
+
const childPath = [...basePath, key];
|
|
1707
|
+
if (key === PARENT_MARKER_KEY) {
|
|
1708
|
+
issues.push({ message: "$parent is never a field name in correlated templates", path: childPath });
|
|
1709
|
+
continue;
|
|
1710
|
+
}
|
|
1711
|
+
if (SUBQUERY_PAYLOAD_KEYS.has(key)) {
|
|
1712
|
+
if (containsParentMarkerShape(child)) {
|
|
1713
|
+
issues.push({
|
|
1714
|
+
message: `Unsupported $parent reference inside ${key}: markers are not resolved in subquery payloads`,
|
|
1715
|
+
path: childPath
|
|
1716
|
+
});
|
|
1717
|
+
}
|
|
1718
|
+
continue;
|
|
1719
|
+
}
|
|
1720
|
+
if (MARKER_FORBIDDEN_OPERATOR_KEYS.has(key)) {
|
|
1721
|
+
if (containsParentMarkerShape(child)) {
|
|
1722
|
+
issues.push({
|
|
1723
|
+
message: `Unsupported $parent reference inside ${key}: markers are not resolved in ${key} values`,
|
|
1724
|
+
path: childPath
|
|
1725
|
+
});
|
|
1726
|
+
}
|
|
1727
|
+
continue;
|
|
1728
|
+
}
|
|
1729
|
+
if (key === "$elemMatch") {
|
|
1730
|
+
if (isParentRefMarker(child) || isEscapedParentRef(child)) {
|
|
1731
|
+
issues.push({
|
|
1732
|
+
message: "Unsupported $parent reference as a direct $elemMatch value",
|
|
1733
|
+
path: childPath
|
|
1734
|
+
});
|
|
1735
|
+
continue;
|
|
1736
|
+
}
|
|
1737
|
+
scanTemplateValueNode(child, childPath, issues);
|
|
1738
|
+
continue;
|
|
1739
|
+
}
|
|
1740
|
+
if (LOGICAL_CLAUSE_KEYS.has(key)) {
|
|
1741
|
+
if (!(0, import_utils12.isArray)(child)) {
|
|
1742
|
+
scanTemplateValueNode(child, childPath, issues);
|
|
1743
|
+
continue;
|
|
1744
|
+
}
|
|
1745
|
+
child.forEach((clause, index) => {
|
|
1746
|
+
const clausePath = [...childPath, index];
|
|
1747
|
+
if (isParentRefMarker(clause) || isEscapedParentRef(clause)) {
|
|
1748
|
+
issues.push({
|
|
1749
|
+
message: `Unsupported $parent reference as a direct ${key} clause: clauses must be filter objects`,
|
|
1750
|
+
path: clausePath
|
|
1751
|
+
});
|
|
1752
|
+
return;
|
|
1753
|
+
}
|
|
1754
|
+
if ((0, import_utils12.isPlainObject)(clause)) {
|
|
1755
|
+
scanTemplateObjectEntries(clause, clausePath, issues);
|
|
1756
|
+
}
|
|
1757
|
+
});
|
|
1758
|
+
continue;
|
|
1759
|
+
}
|
|
1760
|
+
scanTemplateValueNode(child, childPath, issues);
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
function validateCorrelatedFilterTemplate(filter2) {
|
|
1764
|
+
const issues = [];
|
|
1765
|
+
if (!(0, import_utils12.isPlainObject)(filter2)) {
|
|
1766
|
+
issues.push({ message: "Correlated include filter must be an object", path: [] });
|
|
1767
|
+
return issues;
|
|
1768
|
+
}
|
|
1769
|
+
if (isParentRefMarker(filter2) || isEscapedParentRef(filter2)) {
|
|
1770
|
+
issues.push({ message: "Correlated include filter must not itself be a $parent reference", path: [] });
|
|
1771
|
+
return issues;
|
|
1772
|
+
}
|
|
1773
|
+
scanTemplateObjectEntries(filter2, [], issues);
|
|
1774
|
+
return issues;
|
|
1775
|
+
}
|
|
1776
|
+
function validateCorrelatedIdTemplate(id) {
|
|
1777
|
+
if (typeof id === "string") {
|
|
1778
|
+
return id.length > 0 ? [] : [{ message: "Correlated include id must be a non-empty string or $parent reference", path: [] }];
|
|
1779
|
+
}
|
|
1780
|
+
if (isEscapedParentRef(id)) {
|
|
1781
|
+
return [{ message: "Unsupported $escape as a correlated include id", path: [] }];
|
|
1782
|
+
}
|
|
1783
|
+
if (isParentRefMarker(id)) {
|
|
1784
|
+
return isValidParentRefPath(id.$parent) ? [] : [{ message: "Invalid $parent reference path in correlated include id", path: [] }];
|
|
1785
|
+
}
|
|
1786
|
+
return [{ message: "Correlated include id must be a string or $parent reference", path: [] }];
|
|
1787
|
+
}
|
|
1788
|
+
function findArgsMarkerIssue(value, where) {
|
|
1789
|
+
if (value !== void 0 && containsParentMarkerShape(value)) {
|
|
1790
|
+
return {
|
|
1791
|
+
message: `Unsupported $parent reference in ${where}: references are only supported in filter and id positions`,
|
|
1792
|
+
path: []
|
|
1793
|
+
};
|
|
1794
|
+
}
|
|
1795
|
+
return null;
|
|
1796
|
+
}
|
|
1797
|
+
function collectFilterReferencePaths(filter2) {
|
|
1798
|
+
const paths = [];
|
|
1799
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1800
|
+
const visitValue = (value) => {
|
|
1801
|
+
if (isParentRefMarker(value)) {
|
|
1802
|
+
if (typeof value.$parent === "string") {
|
|
1803
|
+
paths.push(value.$parent);
|
|
1804
|
+
}
|
|
1805
|
+
return;
|
|
1806
|
+
}
|
|
1807
|
+
if (isEscapedParentRef(value)) return;
|
|
1808
|
+
if ((0, import_utils12.isArray)(value)) {
|
|
1809
|
+
if (seen.has(value)) return;
|
|
1810
|
+
seen.add(value);
|
|
1811
|
+
value.forEach(visitValue);
|
|
1812
|
+
return;
|
|
1813
|
+
}
|
|
1814
|
+
if ((0, import_utils12.isPlainObject)(value)) {
|
|
1815
|
+
if (seen.has(value)) return;
|
|
1816
|
+
seen.add(value);
|
|
1817
|
+
for (const [key, child] of Object.entries(value)) {
|
|
1818
|
+
if (SUBQUERY_PAYLOAD_KEYS.has(key) || MARKER_FORBIDDEN_OPERATOR_KEYS.has(key)) continue;
|
|
1819
|
+
visitValue(child);
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
};
|
|
1823
|
+
visitValue(filter2);
|
|
1824
|
+
return paths;
|
|
1825
|
+
}
|
|
1826
|
+
function collectCorrelatedReferencePaths(include) {
|
|
1827
|
+
const paths = [];
|
|
1828
|
+
if (isParentRefMarker(include.id) && typeof include.id !== "string") {
|
|
1829
|
+
const ref = include.id;
|
|
1830
|
+
if (typeof ref.$parent === "string") paths.push(ref.$parent);
|
|
1831
|
+
} else if (typeof include.id === "string") {
|
|
1832
|
+
}
|
|
1833
|
+
const filter2 = include.filter;
|
|
1834
|
+
if (filter2 !== void 0) {
|
|
1835
|
+
paths.push(...collectFilterReferencePaths(filter2));
|
|
1836
|
+
}
|
|
1837
|
+
return [...new Set(paths)];
|
|
1838
|
+
}
|
|
1839
|
+
var CorrelatedReferenceError = class extends Error {
|
|
1840
|
+
constructor(message) {
|
|
1841
|
+
super(message);
|
|
1842
|
+
this.name = "CorrelatedReferenceError";
|
|
1843
|
+
}
|
|
1844
|
+
};
|
|
1845
|
+
var UNRESOLVABLE = /* @__PURE__ */ Symbol("correlated-include-unresolvable");
|
|
1846
|
+
function getOwnPathValue(parent, path) {
|
|
1847
|
+
const segments = path.split(".");
|
|
1848
|
+
let current = parent;
|
|
1849
|
+
for (const segment of segments) {
|
|
1850
|
+
if (DANGEROUS_PATH_SEGMENTS.has(segment)) {
|
|
1851
|
+
throw new CorrelatedReferenceError(`Invalid $parent reference path: ${path}`);
|
|
1852
|
+
}
|
|
1853
|
+
if (typeof current !== "object" || current === null) return void 0;
|
|
1854
|
+
if (!Object.prototype.hasOwnProperty.call(current, segment)) return void 0;
|
|
1855
|
+
current = current[segment];
|
|
1856
|
+
}
|
|
1857
|
+
return current;
|
|
1858
|
+
}
|
|
1859
|
+
function cloneSubstitutedValue(value) {
|
|
1860
|
+
if ((0, import_utils12.isPlainObject)(value) || (0, import_utils12.isArray)(value)) return (0, import_utils12.cloneDeep)(value);
|
|
1861
|
+
return value;
|
|
1862
|
+
}
|
|
1863
|
+
function literalParentRefObject(path) {
|
|
1864
|
+
const literal = {};
|
|
1865
|
+
Object.defineProperty(literal, PARENT_MARKER_KEY, {
|
|
1866
|
+
value: path,
|
|
1867
|
+
writable: true,
|
|
1868
|
+
enumerable: true,
|
|
1869
|
+
configurable: true
|
|
1870
|
+
});
|
|
1871
|
+
return literal;
|
|
1872
|
+
}
|
|
1873
|
+
function resolveObjectEntries(obj, parent) {
|
|
1874
|
+
const out = {};
|
|
1875
|
+
for (const [key, child] of Object.entries(obj)) {
|
|
1876
|
+
if (key === PARENT_MARKER_KEY) {
|
|
1877
|
+
throw new CorrelatedReferenceError("$parent is never a field name in correlated templates");
|
|
1878
|
+
}
|
|
1879
|
+
if (SUBQUERY_PAYLOAD_KEYS.has(key)) {
|
|
1880
|
+
if (containsParentMarkerShape(child)) {
|
|
1881
|
+
throw new CorrelatedReferenceError(`Unsupported $parent reference inside ${key}`);
|
|
1882
|
+
}
|
|
1883
|
+
out[key] = cloneSubstitutedValue(child);
|
|
1884
|
+
continue;
|
|
1885
|
+
}
|
|
1886
|
+
if (MARKER_FORBIDDEN_OPERATOR_KEYS.has(key)) {
|
|
1887
|
+
if (containsParentMarkerShape(child)) {
|
|
1888
|
+
throw new CorrelatedReferenceError(`Unsupported $parent reference inside ${key}`);
|
|
1889
|
+
}
|
|
1890
|
+
out[key] = cloneSubstitutedValue(child);
|
|
1891
|
+
continue;
|
|
1892
|
+
}
|
|
1893
|
+
if (key === "$elemMatch") {
|
|
1894
|
+
if (isParentRefMarker(child) || isEscapedParentRef(child)) {
|
|
1895
|
+
throw new CorrelatedReferenceError("Unsupported $parent reference as a direct $elemMatch value");
|
|
1896
|
+
}
|
|
1897
|
+
out[key] = resolveValueNode(child, parent, false);
|
|
1898
|
+
continue;
|
|
1899
|
+
}
|
|
1900
|
+
if (LOGICAL_CLAUSE_KEYS.has(key)) {
|
|
1901
|
+
if (!(0, import_utils12.isArray)(child)) {
|
|
1902
|
+
out[key] = resolveValueNode(child, parent, false);
|
|
1903
|
+
continue;
|
|
1904
|
+
}
|
|
1905
|
+
out[key] = child.map((clause) => {
|
|
1906
|
+
if (isParentRefMarker(clause) || isEscapedParentRef(clause)) {
|
|
1907
|
+
throw new CorrelatedReferenceError(`Unsupported $parent reference as a direct ${key} clause`);
|
|
1908
|
+
}
|
|
1909
|
+
if (!(0, import_utils12.isPlainObject)(clause)) return cloneSubstitutedValue(clause);
|
|
1910
|
+
return resolveObjectEntries(clause, parent);
|
|
1911
|
+
});
|
|
1912
|
+
continue;
|
|
1913
|
+
}
|
|
1914
|
+
if (key.startsWith("$")) {
|
|
1915
|
+
out[key] = resolveValueNode(child, parent, false);
|
|
1916
|
+
continue;
|
|
1917
|
+
}
|
|
1918
|
+
out[key] = resolveValueNode(child, parent, true);
|
|
1919
|
+
}
|
|
1920
|
+
return out;
|
|
1921
|
+
}
|
|
1922
|
+
function resolveValueNode(template, parent, bare) {
|
|
1923
|
+
if (isEscapedParentRef(template)) {
|
|
1924
|
+
const inner = template.$escape;
|
|
1925
|
+
if (!isParentRefMarker(inner) || typeof inner.$parent !== "string" || (inner.$parent?.length ?? 0) === 0) {
|
|
1926
|
+
throw new CorrelatedReferenceError('Invalid $escape payload: expected { "$escape": { "$parent": "<field>" } }');
|
|
1927
|
+
}
|
|
1928
|
+
return literalParentRefObject(inner.$parent);
|
|
1929
|
+
}
|
|
1930
|
+
if (isParentRefMarker(template)) {
|
|
1931
|
+
const path = template.$parent;
|
|
1932
|
+
if (!isValidParentRefPath(path)) {
|
|
1933
|
+
throw new CorrelatedReferenceError(`Invalid $parent reference path: ${String(path)}`);
|
|
1934
|
+
}
|
|
1935
|
+
const value = getOwnPathValue(parent, path);
|
|
1936
|
+
if (value === void 0 || value === null) throw UNRESOLVABLE;
|
|
1937
|
+
if (bare && (0, import_utils12.isPlainObject)(value)) return { $eq: (0, import_utils12.cloneDeep)(value) };
|
|
1938
|
+
return cloneSubstitutedValue(value);
|
|
1939
|
+
}
|
|
1940
|
+
if ((0, import_utils12.isPlainObject)(template)) {
|
|
1941
|
+
if (hasMarkerKey(template)) {
|
|
1942
|
+
throw new CorrelatedReferenceError(
|
|
1943
|
+
'Malformed $parent reference: objects carrying $parent must be exactly { "$parent": "<field>" }'
|
|
1944
|
+
);
|
|
1945
|
+
}
|
|
1946
|
+
return resolveObjectEntries(template, parent);
|
|
1947
|
+
}
|
|
1948
|
+
if ((0, import_utils12.isArray)(template)) {
|
|
1949
|
+
return template.map((item) => resolveValueNode(item, parent, false));
|
|
1950
|
+
}
|
|
1951
|
+
return template;
|
|
1952
|
+
}
|
|
1953
|
+
function isUnresolvableSignal(error2) {
|
|
1954
|
+
return error2 === UNRESOLVABLE;
|
|
1955
|
+
}
|
|
1956
|
+
function resolveCorrelatedIdTemplate(id, parent) {
|
|
1957
|
+
try {
|
|
1958
|
+
if (typeof id === "string") {
|
|
1959
|
+
if (id.length === 0) throw new CorrelatedReferenceError("Correlated include id must be a non-empty string");
|
|
1960
|
+
return { status: "resolved", id };
|
|
1961
|
+
}
|
|
1962
|
+
if (isParentRefMarker(id)) {
|
|
1963
|
+
const path = id.$parent;
|
|
1964
|
+
if (!isValidParentRefPath(path)) {
|
|
1965
|
+
throw new CorrelatedReferenceError(`Invalid $parent reference path: ${String(path)}`);
|
|
1966
|
+
}
|
|
1967
|
+
const value = getOwnPathValue(parent, path);
|
|
1968
|
+
if (typeof value === "string") return { status: "resolved", id: value };
|
|
1969
|
+
if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
1970
|
+
return { status: "resolved", id: String(value) };
|
|
1971
|
+
}
|
|
1972
|
+
throw UNRESOLVABLE;
|
|
1973
|
+
}
|
|
1974
|
+
throw new CorrelatedReferenceError("Correlated include id must be a string or $parent reference");
|
|
1975
|
+
} catch (error2) {
|
|
1976
|
+
if (isUnresolvableSignal(error2)) return { status: "unresolvable" };
|
|
1977
|
+
throw error2;
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
function resolveCorrelatedFilterTemplate(filter2, parent) {
|
|
1981
|
+
try {
|
|
1982
|
+
if (!(0, import_utils12.isPlainObject)(filter2) || isParentRefMarker(filter2) || isEscapedParentRef(filter2)) {
|
|
1983
|
+
throw new CorrelatedReferenceError("Correlated include filter must be an object");
|
|
1984
|
+
}
|
|
1985
|
+
return { status: "resolved", filter: resolveObjectEntries(filter2, parent) };
|
|
1986
|
+
} catch (error2) {
|
|
1987
|
+
if (isUnresolvableSignal(error2)) return { status: "unresolvable" };
|
|
1988
|
+
throw error2;
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
function validateExpandedCorrelatedOperands(expanded, limits) {
|
|
1992
|
+
return validateRequestComplexity(expanded, limits ?? null, "filter");
|
|
1993
|
+
}
|
|
1994
|
+
var MAX_NESTED_INCLUDE_DEPTH = 10;
|
|
1995
|
+
function validateCorrelatedArgsShape(args, op, depth) {
|
|
1996
|
+
if (args === void 0) return [];
|
|
1997
|
+
if (!(0, import_utils12.isPlainObject)(args)) return ["Correlated include args must be an object"];
|
|
1998
|
+
const record = args;
|
|
1999
|
+
const allowed = op === "list" ? /* @__PURE__ */ new Set(["select", "sort", "skip", "limit", "page", "pageSize", "include"]) : /* @__PURE__ */ new Set(["select", "sort", "include"]);
|
|
2000
|
+
for (const key of Object.keys(record)) {
|
|
2001
|
+
if (!allowed.has(key)) return [`Unsupported correlated include arg: ${key}`];
|
|
2002
|
+
}
|
|
2003
|
+
if (op === "count" && Object.keys(record).length > 0) return ["Count includes accept no args"];
|
|
2004
|
+
const selectIssue = findArgsMarkerIssue(record.select, "args.select");
|
|
2005
|
+
if (selectIssue) return [selectIssue.message];
|
|
2006
|
+
const sortIssue = findArgsMarkerIssue(record.sort, "args.sort");
|
|
2007
|
+
if (sortIssue) return [sortIssue.message];
|
|
2008
|
+
if (record.include !== void 0) {
|
|
2009
|
+
if (depth >= MAX_NESTED_INCLUDE_DEPTH) return ["Correlated include nesting exceeds the supported depth"];
|
|
2010
|
+
const nested = Array.isArray(record.include) ? record.include : [record.include];
|
|
2011
|
+
for (const entry of nested) {
|
|
2012
|
+
const errors = validateCorrelatedIncludeShape(entry, depth + 1);
|
|
2013
|
+
if (errors.length > 0) return errors;
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
return [];
|
|
2017
|
+
}
|
|
2018
|
+
function validateCorrelatedIncludeShape(entry, depth = 0) {
|
|
2019
|
+
if (!(0, import_utils12.isPlainObject)(entry)) return ["Correlated include must be an object"];
|
|
2020
|
+
const record = entry;
|
|
2021
|
+
if (record.mode !== "correlated") return ['Correlated include mode must be "correlated"'];
|
|
2022
|
+
if (typeof record.model !== "string" || record.model.length === 0)
|
|
2023
|
+
return ["Correlated include model must be a non-empty string"];
|
|
2024
|
+
if (record.op !== "list" && record.op !== "read" && record.op !== "count") {
|
|
2025
|
+
return ['Correlated include op must be one of "list", "read", or "count"'];
|
|
2026
|
+
}
|
|
2027
|
+
if (!isValidCorrelatedOutputPath(record.path))
|
|
2028
|
+
return [`Invalid correlated include output path: ${String(record.path)}`];
|
|
2029
|
+
if ("localField" in record || "foreignField" in record) {
|
|
2030
|
+
return ["Correlated includes must not carry localField or foreignField"];
|
|
2031
|
+
}
|
|
2032
|
+
if (record.options !== void 0 && !((0, import_utils12.isPlainObject)(record.options) && Object.keys(record.options).length === 0)) {
|
|
2033
|
+
return ["Correlated include options must be absent or empty"];
|
|
2034
|
+
}
|
|
2035
|
+
const hasId = record.id !== void 0;
|
|
2036
|
+
const hasFilter = record.filter !== void 0;
|
|
2037
|
+
if (record.op === "read") {
|
|
2038
|
+
if (hasId === hasFilter) return ['Correlated read includes require exactly one of "id" or "filter"'];
|
|
2039
|
+
if (hasId) {
|
|
2040
|
+
const issues = validateCorrelatedIdTemplate(record.id);
|
|
2041
|
+
if (issues.length > 0) return [issues[0].message];
|
|
2042
|
+
} else {
|
|
2043
|
+
const issues = validateCorrelatedFilterTemplate(record.filter);
|
|
2044
|
+
if (issues.length > 0) return [issues[0].message];
|
|
2045
|
+
}
|
|
2046
|
+
} else {
|
|
2047
|
+
if (!hasFilter || hasId) return [`Correlated ${record.op} includes require a filter and no id`];
|
|
2048
|
+
const issues = validateCorrelatedFilterTemplate(record.filter);
|
|
2049
|
+
if (issues.length > 0) return [issues[0].message];
|
|
2050
|
+
}
|
|
2051
|
+
return validateCorrelatedArgsShape(record.args, record.op, depth);
|
|
2052
|
+
}
|
|
2053
|
+
function isCorrelatedInclude(value) {
|
|
2054
|
+
return (0, import_utils12.isPlainObject)(value) && value.mode === "correlated";
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
// src/services/base.ts
|
|
2058
|
+
var correlatedExecStates = /* @__PURE__ */ new WeakMap();
|
|
2059
|
+
function getCorrelatedTemplateDepth(includes) {
|
|
2060
|
+
let max = 0;
|
|
2061
|
+
const visit = (entries, depth) => {
|
|
2062
|
+
const list = (0, import_utils13.castArray)(entries).filter(Boolean);
|
|
2063
|
+
for (const entry of list) {
|
|
2064
|
+
if (!(0, import_utils13.isPlainObject)(entry)) continue;
|
|
2065
|
+
max = Math.max(max, depth);
|
|
2066
|
+
const nested = entry.args?.include;
|
|
2067
|
+
if (nested !== void 0) {
|
|
2068
|
+
visit(nested, depth + 1);
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
};
|
|
2072
|
+
if (includes.length > 0) visit(includes, 1);
|
|
2073
|
+
return max;
|
|
2074
|
+
}
|
|
1573
2075
|
var ClientRequestError = class extends Error {
|
|
1574
2076
|
constructor(result) {
|
|
1575
2077
|
super(String(result.code));
|
|
@@ -1584,11 +2086,11 @@ function validateClientFilter(filter2) {
|
|
|
1584
2086
|
}
|
|
1585
2087
|
const blockedOperators = /* @__PURE__ */ new Set(["$where", "$expr", "$function", "$accumulator"]);
|
|
1586
2088
|
const visit = (value, path) => {
|
|
1587
|
-
if ((0,
|
|
2089
|
+
if ((0, import_utils13.isArray)(value)) {
|
|
1588
2090
|
value.forEach((item, index) => visit(item, `${path}[${index}]`));
|
|
1589
2091
|
return;
|
|
1590
2092
|
}
|
|
1591
|
-
if (!(0,
|
|
2093
|
+
if (!(0, import_utils13.isPlainObject)(value)) return;
|
|
1592
2094
|
Object.entries(value).forEach(([key, child]) => {
|
|
1593
2095
|
const nextPath = path ? `${path}.${key}` : key;
|
|
1594
2096
|
if (blockedOperators.has(key)) {
|
|
@@ -1708,25 +2210,56 @@ var Base = class {
|
|
|
1708
2210
|
return this.req.macl.getPublicService(modelName);
|
|
1709
2211
|
}
|
|
1710
2212
|
processInclude(include) {
|
|
1711
|
-
const
|
|
1712
|
-
|
|
1713
|
-
|
|
2213
|
+
const items = (0, import_utils13.compact)((0, import_utils13.castArray)(include));
|
|
2214
|
+
const legacy = [];
|
|
2215
|
+
const correlated = [];
|
|
2216
|
+
const correlatedReferenceFields = [];
|
|
2217
|
+
for (const entry of items) {
|
|
2218
|
+
if (isCorrelatedInclude(entry)) {
|
|
2219
|
+
const shapeErrors = validateCorrelatedIncludeShape(entry);
|
|
2220
|
+
if (shapeErrors.length > 0) {
|
|
2221
|
+
this.throwClientRequestError("bad_request" /* BadRequest */, shapeErrors[0]);
|
|
2222
|
+
}
|
|
2223
|
+
correlated.push(entry);
|
|
2224
|
+
correlatedReferenceFields.push(...collectCorrelatedReferencePaths(entry));
|
|
2225
|
+
continue;
|
|
2226
|
+
}
|
|
2227
|
+
if (containsParentMarkerShape(entry)) {
|
|
2228
|
+
this.throwClientRequestError(
|
|
2229
|
+
"bad_request" /* BadRequest */,
|
|
2230
|
+
'Legacy includes must not contain $parent markers; use a correlated include (mode: "correlated")'
|
|
2231
|
+
);
|
|
2232
|
+
}
|
|
2233
|
+
const candidate = entry;
|
|
2234
|
+
if (candidate.model && candidate.op && candidate.path && candidate.localField && candidate.foreignField) {
|
|
2235
|
+
legacy.push(entry);
|
|
2236
|
+
}
|
|
2237
|
+
}
|
|
2238
|
+
const seenCorrelatedPaths = /* @__PURE__ */ new Set();
|
|
2239
|
+
for (const entry of correlated) {
|
|
2240
|
+
if (seenCorrelatedPaths.has(entry.path)) {
|
|
2241
|
+
this.throwClientRequestError("bad_request" /* BadRequest */, `Duplicate correlated include output path: ${entry.path}`);
|
|
2242
|
+
}
|
|
2243
|
+
seenCorrelatedPaths.add(entry.path);
|
|
2244
|
+
}
|
|
1714
2245
|
let includeLocalFields = [];
|
|
1715
2246
|
let includePaths = [];
|
|
1716
|
-
(0,
|
|
2247
|
+
(0, import_utils13.forEach)(legacy, (inc) => {
|
|
1717
2248
|
includeLocalFields.push(inc.localField);
|
|
1718
2249
|
includePaths.push(inc.path);
|
|
1719
2250
|
});
|
|
1720
|
-
includeLocalFields = (0,
|
|
1721
|
-
includePaths = (0,
|
|
2251
|
+
includeLocalFields = (0, import_utils13.uniq)((0, import_utils13.compact)(includeLocalFields));
|
|
2252
|
+
includePaths = (0, import_utils13.uniq)((0, import_utils13.compact)(includePaths));
|
|
1722
2253
|
return {
|
|
1723
|
-
includes,
|
|
2254
|
+
includes: legacy,
|
|
2255
|
+
correlatedIncludes: correlated,
|
|
1724
2256
|
includeLocalFields,
|
|
1725
|
-
includePaths
|
|
2257
|
+
includePaths,
|
|
2258
|
+
correlatedReferenceFields: (0, import_utils13.uniq)((0, import_utils13.compact)(correlatedReferenceFields))
|
|
1726
2259
|
};
|
|
1727
2260
|
}
|
|
1728
2261
|
getForeignKeyValues(value) {
|
|
1729
|
-
return (0,
|
|
2262
|
+
return (0, import_utils13.flatten)((0, import_utils13.castArray)(value));
|
|
1730
2263
|
}
|
|
1731
2264
|
getForeignKey(value) {
|
|
1732
2265
|
return String(value);
|
|
@@ -1734,7 +2267,7 @@ var Base = class {
|
|
|
1734
2267
|
buildForeignKeyIndex(rows, foreignField) {
|
|
1735
2268
|
const index = /* @__PURE__ */ new Map();
|
|
1736
2269
|
for (const row of rows) {
|
|
1737
|
-
for (const value of this.getForeignKeyValues((0,
|
|
2270
|
+
for (const value of this.getForeignKeyValues((0, import_utils13.get)(row, foreignField))) {
|
|
1738
2271
|
const key = this.getForeignKey(value);
|
|
1739
2272
|
const values = index.get(key);
|
|
1740
2273
|
if (values) values.push(row);
|
|
@@ -1765,7 +2298,7 @@ var Base = class {
|
|
|
1765
2298
|
return ids.size;
|
|
1766
2299
|
}
|
|
1767
2300
|
sanitizeIncludeArgs(args) {
|
|
1768
|
-
if (!(0,
|
|
2301
|
+
if (!(0, import_utils13.isPlainObject)(args)) return {};
|
|
1769
2302
|
const { overrides: _clientOverrides, ...trustedArgs } = args;
|
|
1770
2303
|
return trustedArgs;
|
|
1771
2304
|
}
|
|
@@ -1776,12 +2309,18 @@ var Base = class {
|
|
|
1776
2309
|
}
|
|
1777
2310
|
async includeDocs(docs, include) {
|
|
1778
2311
|
if (!include) return docs;
|
|
1779
|
-
const includes = (0,
|
|
2312
|
+
const includes = (0, import_utils13.compact)((0, import_utils13.castArray)(include));
|
|
1780
2313
|
if (includes.length === 0) return docs;
|
|
1781
|
-
const isSingle = !(0,
|
|
2314
|
+
const isSingle = !(0, import_utils13.isArray)(docs);
|
|
1782
2315
|
let docList = isSingle ? [docs] : docs;
|
|
1783
2316
|
for (let x = 0; x < includes.length; x++) {
|
|
1784
2317
|
const include2 = includes[x];
|
|
2318
|
+
if (isCorrelatedInclude(include2)) {
|
|
2319
|
+
this.throwClientRequestError(
|
|
2320
|
+
"bad_request" /* BadRequest */,
|
|
2321
|
+
"Correlated includes are not supported by the legacy include executor"
|
|
2322
|
+
);
|
|
2323
|
+
}
|
|
1785
2324
|
this.assertIncludeForeignField(include2);
|
|
1786
2325
|
switch (include2.op) {
|
|
1787
2326
|
case "count":
|
|
@@ -1802,7 +2341,7 @@ var Base = class {
|
|
|
1802
2341
|
const svc = await this.getAuthorizedTargetService(model, "read");
|
|
1803
2342
|
for (let y = 0; y < docs.length; y++) {
|
|
1804
2343
|
const doc = docs[y];
|
|
1805
|
-
const localValue = (0,
|
|
2344
|
+
const localValue = (0, import_utils13.get)(doc, localField);
|
|
1806
2345
|
const filter2 = { ..._filters ?? {}, [foreignField]: { $in: this.getForeignKeyValues(localValue) } };
|
|
1807
2346
|
const trustedOptions = {
|
|
1808
2347
|
...options,
|
|
@@ -1821,10 +2360,10 @@ var Base = class {
|
|
|
1821
2360
|
const { model, op, path, localField, foreignField, filter: _filters, args = {}, options = {} } = include;
|
|
1822
2361
|
const svc = await this.getAuthorizedTargetService(model, op);
|
|
1823
2362
|
const includeLocalValues = [];
|
|
1824
|
-
(0,
|
|
1825
|
-
includeLocalValues.push(...this.getForeignKeyValues((0,
|
|
2363
|
+
(0, import_utils13.forEach)(docs, (doc) => {
|
|
2364
|
+
includeLocalValues.push(...this.getForeignKeyValues((0, import_utils13.get)(doc, localField)));
|
|
1826
2365
|
});
|
|
1827
|
-
const filter2 = { ..._filters ?? {}, [foreignField]: { $in: (0,
|
|
2366
|
+
const filter2 = { ..._filters ?? {}, [foreignField]: { $in: (0, import_utils13.flatten)(includeLocalValues) } };
|
|
1828
2367
|
const authorizedFilter = await svc.genFilter(op, filter2);
|
|
1829
2368
|
const trustedArgs = {
|
|
1830
2369
|
...this.sanitizeIncludeArgs(args),
|
|
@@ -1843,7 +2382,7 @@ var Base = class {
|
|
|
1843
2382
|
const index = this.buildForeignKeyIndex(trustedResult.data, foreignField);
|
|
1844
2383
|
for (let y = 0; y < docs.length; y++) {
|
|
1845
2384
|
const doc = docs[y];
|
|
1846
|
-
const matches = this.getIndexedMatches(index, (0,
|
|
2385
|
+
const matches = this.getIndexedMatches(index, (0, import_utils13.get)(doc, localField));
|
|
1847
2386
|
setDocValue(doc, path, op === "list" ? matches : matches[0]);
|
|
1848
2387
|
}
|
|
1849
2388
|
return docs;
|
|
@@ -1851,15 +2390,365 @@ var Base = class {
|
|
|
1851
2390
|
async includeDocsCount(docs, include) {
|
|
1852
2391
|
const { model, path, localField, foreignField, filter: _filters } = include;
|
|
1853
2392
|
const svc = await this.getAuthorizedTargetService(model, "count");
|
|
1854
|
-
const localValues = docs.flatMap((doc) => this.getForeignKeyValues((0,
|
|
2393
|
+
const localValues = docs.flatMap((doc) => this.getForeignKeyValues((0, import_utils13.get)(doc, localField)));
|
|
1855
2394
|
const result = await svc.countByFieldValues(foreignField, localValues, _filters ?? {}, "count");
|
|
1856
2395
|
if (!result.success) return docs;
|
|
1857
2396
|
for (let y = 0; y < docs.length; y++) {
|
|
1858
2397
|
const doc = docs[y];
|
|
1859
|
-
setDocValue(doc, path, this.getIndexedCount(result.data, (0,
|
|
2398
|
+
setDocValue(doc, path, this.getIndexedCount(result.data, (0, import_utils13.get)(doc, localField)));
|
|
2399
|
+
}
|
|
2400
|
+
return docs;
|
|
2401
|
+
}
|
|
2402
|
+
getCorrelatedExecState() {
|
|
2403
|
+
const key = this.req;
|
|
2404
|
+
let state = correlatedExecStates.get(key);
|
|
2405
|
+
if (!state) {
|
|
2406
|
+
state = {
|
|
2407
|
+
totalQueries: 0,
|
|
2408
|
+
scheduler: new RequestConcurrencyScheduler(this.getRequestComplexity().maxBulkConcurrency)
|
|
2409
|
+
};
|
|
2410
|
+
correlatedExecStates.set(key, state);
|
|
2411
|
+
}
|
|
2412
|
+
return state;
|
|
2413
|
+
}
|
|
2414
|
+
claimCorrelatedQuerySlot() {
|
|
2415
|
+
const limits = this.getRequestComplexity();
|
|
2416
|
+
const state = this.getCorrelatedExecState();
|
|
2417
|
+
if (state.totalQueries >= limits.maxCorrelatedQueries) {
|
|
2418
|
+
this.throwClientRequestError(
|
|
2419
|
+
"bad_request" /* BadRequest */,
|
|
2420
|
+
`Correlated include query budget exceeded (max ${limits.maxCorrelatedQueries})`
|
|
2421
|
+
);
|
|
2422
|
+
}
|
|
2423
|
+
state.totalQueries += 1;
|
|
2424
|
+
}
|
|
2425
|
+
sanitizeCorrelatedArgs(include) {
|
|
2426
|
+
const args = include.args ?? {};
|
|
2427
|
+
if (include.op === "list") {
|
|
2428
|
+
const { select: select2, sort: sort2, skip, limit, page, pageSize, include: nestedInclude2 } = args;
|
|
2429
|
+
return { select: select2, sort: sort2, skip, limit, page, pageSize, nestedInclude: nestedInclude2 };
|
|
2430
|
+
}
|
|
2431
|
+
const { select, sort, include: nestedInclude } = args;
|
|
2432
|
+
return { select, sort, nestedInclude };
|
|
2433
|
+
}
|
|
2434
|
+
async includeCorrelatedDocs(docs, correlatedIncludes, snapshots, depth = 0) {
|
|
2435
|
+
if (correlatedIncludes.length === 0) return docs;
|
|
2436
|
+
const limits = this.getRequestComplexity();
|
|
2437
|
+
const templateDepth = getCorrelatedTemplateDepth(correlatedIncludes);
|
|
2438
|
+
if (depth + templateDepth > limits.maxCorrelatedDepth) {
|
|
2439
|
+
this.throwClientRequestError(
|
|
2440
|
+
"bad_request" /* BadRequest */,
|
|
2441
|
+
`Correlated include depth exceeded (max ${limits.maxCorrelatedDepth})`
|
|
2442
|
+
);
|
|
2443
|
+
}
|
|
2444
|
+
const state = this.getCorrelatedExecState();
|
|
2445
|
+
const resolvedSnapshots = snapshots ?? docs.map((doc) => (0, import_utils13.cloneDeep)(toObject(doc)));
|
|
2446
|
+
for (const include of correlatedIncludes) {
|
|
2447
|
+
switch (include.op) {
|
|
2448
|
+
case "read":
|
|
2449
|
+
await this.includeCorrelatedRead(docs, include, resolvedSnapshots, state, depth);
|
|
2450
|
+
break;
|
|
2451
|
+
case "list":
|
|
2452
|
+
await this.includeCorrelatedList(docs, include, resolvedSnapshots, state, depth);
|
|
2453
|
+
break;
|
|
2454
|
+
case "count":
|
|
2455
|
+
await this.includeCorrelatedCount(docs, include, resolvedSnapshots, state, depth);
|
|
2456
|
+
break;
|
|
2457
|
+
default:
|
|
2458
|
+
this.throwClientRequestError(
|
|
2459
|
+
"bad_request" /* BadRequest */,
|
|
2460
|
+
`Unsupported correlated include op: ${include.op}`
|
|
2461
|
+
);
|
|
2462
|
+
}
|
|
1860
2463
|
}
|
|
1861
2464
|
return docs;
|
|
1862
2465
|
}
|
|
2466
|
+
async includeCorrelatedRead(docs, include, snapshots, state, depth) {
|
|
2467
|
+
const svc = await this.getAuthorizedTargetService(include.model, "read");
|
|
2468
|
+
const { select, sort, nestedInclude } = this.sanitizeCorrelatedArgs(include);
|
|
2469
|
+
const hasId = include.id !== void 0;
|
|
2470
|
+
if (hasId) {
|
|
2471
|
+
const idTemplate = include.id;
|
|
2472
|
+
const tasks2 = docs.map((_, index) => ({ index, snapshot: snapshots[index] }));
|
|
2473
|
+
await state.scheduler.map(tasks2, async ({ index, snapshot }) => {
|
|
2474
|
+
let resolution;
|
|
2475
|
+
try {
|
|
2476
|
+
resolution = resolveCorrelatedIdTemplate(idTemplate, snapshot);
|
|
2477
|
+
} catch (error2) {
|
|
2478
|
+
if (error2 instanceof CorrelatedReferenceError) {
|
|
2479
|
+
throw new ClientRequestError({
|
|
2480
|
+
success: false,
|
|
2481
|
+
kind: "error",
|
|
2482
|
+
code: "bad_request" /* BadRequest */,
|
|
2483
|
+
errors: [{ detail: error2.message }]
|
|
2484
|
+
});
|
|
2485
|
+
}
|
|
2486
|
+
throw error2;
|
|
2487
|
+
}
|
|
2488
|
+
if (resolution.status === "unresolvable") {
|
|
2489
|
+
setDocValue(docs[index], include.path, null);
|
|
2490
|
+
return;
|
|
2491
|
+
}
|
|
2492
|
+
const resolvedId = resolution.id;
|
|
2493
|
+
let idFilter;
|
|
2494
|
+
try {
|
|
2495
|
+
idFilter = await svc.genIDFilter(resolvedId);
|
|
2496
|
+
} catch (error2) {
|
|
2497
|
+
const clientResult = this.getClientRequestErrorResult(error2);
|
|
2498
|
+
if (clientResult) throw new ClientRequestError(clientResult);
|
|
2499
|
+
throw error2;
|
|
2500
|
+
}
|
|
2501
|
+
const authorized = await svc.genFilter("read", idFilter);
|
|
2502
|
+
if (authorized === false) {
|
|
2503
|
+
throw new ClientRequestError({ success: false, kind: "error", code: "forbidden" /* Forbidden */ });
|
|
2504
|
+
}
|
|
2505
|
+
this.claimCorrelatedQuerySlot();
|
|
2506
|
+
let result;
|
|
2507
|
+
try {
|
|
2508
|
+
result = await svc.findOne(
|
|
2509
|
+
{},
|
|
2510
|
+
{
|
|
2511
|
+
select,
|
|
2512
|
+
sort,
|
|
2513
|
+
include: nestedInclude,
|
|
2514
|
+
overrides: { filter: authorized }
|
|
2515
|
+
},
|
|
2516
|
+
{ access: "read", lean: true, includePermissions: false }
|
|
2517
|
+
);
|
|
2518
|
+
} catch (error2) {
|
|
2519
|
+
const clientResult = this.getClientRequestErrorResult(error2);
|
|
2520
|
+
if (clientResult) throw new ClientRequestError(clientResult);
|
|
2521
|
+
throw error2;
|
|
2522
|
+
}
|
|
2523
|
+
if (result.success) {
|
|
2524
|
+
setDocValue(docs[index], include.path, result.data);
|
|
2525
|
+
} else if (result.code === "not_found" /* NotFound */) {
|
|
2526
|
+
setDocValue(docs[index], include.path, null);
|
|
2527
|
+
} else {
|
|
2528
|
+
throw new ClientRequestError(result);
|
|
2529
|
+
}
|
|
2530
|
+
});
|
|
2531
|
+
return;
|
|
2532
|
+
}
|
|
2533
|
+
const filterTemplate = include.filter;
|
|
2534
|
+
const templateErrors = this.validateClientFilter(filterTemplate);
|
|
2535
|
+
if (templateErrors.length > 0) {
|
|
2536
|
+
this.throwClientRequestError("bad_request" /* BadRequest */, templateErrors[0]);
|
|
2537
|
+
}
|
|
2538
|
+
let parsedTemplate;
|
|
2539
|
+
try {
|
|
2540
|
+
parsedTemplate = await this.parseClientData(
|
|
2541
|
+
filterTemplate,
|
|
2542
|
+
state.scheduler
|
|
2543
|
+
);
|
|
2544
|
+
} catch (error2) {
|
|
2545
|
+
const clientResult = this.getClientRequestErrorResult(error2);
|
|
2546
|
+
if (clientResult) throw new ClientRequestError(clientResult);
|
|
2547
|
+
throw error2;
|
|
2548
|
+
}
|
|
2549
|
+
const tasks = docs.map((_, index) => ({ index, snapshot: snapshots[index] }));
|
|
2550
|
+
await state.scheduler.map(tasks, async ({ index, snapshot }) => {
|
|
2551
|
+
let resolution;
|
|
2552
|
+
try {
|
|
2553
|
+
resolution = resolveCorrelatedFilterTemplate(parsedTemplate, snapshot);
|
|
2554
|
+
} catch (error2) {
|
|
2555
|
+
if (error2 instanceof CorrelatedReferenceError) {
|
|
2556
|
+
throw new ClientRequestError({
|
|
2557
|
+
success: false,
|
|
2558
|
+
kind: "error",
|
|
2559
|
+
code: "bad_request" /* BadRequest */,
|
|
2560
|
+
errors: [{ detail: error2.message }]
|
|
2561
|
+
});
|
|
2562
|
+
}
|
|
2563
|
+
throw error2;
|
|
2564
|
+
}
|
|
2565
|
+
if (resolution.status === "unresolvable") {
|
|
2566
|
+
setDocValue(docs[index], include.path, null);
|
|
2567
|
+
return;
|
|
2568
|
+
}
|
|
2569
|
+
const expanded = resolution.filter;
|
|
2570
|
+
const operandErrors = validateExpandedCorrelatedOperands(expanded, this.getRequestComplexity());
|
|
2571
|
+
if (operandErrors.length > 0) {
|
|
2572
|
+
throw new ClientRequestError({
|
|
2573
|
+
success: false,
|
|
2574
|
+
kind: "error",
|
|
2575
|
+
code: "bad_request" /* BadRequest */,
|
|
2576
|
+
errors: [{ detail: operandErrors[0].detail }]
|
|
2577
|
+
});
|
|
2578
|
+
}
|
|
2579
|
+
const authorized = await svc.genFilter("read", expanded);
|
|
2580
|
+
if (authorized === false) {
|
|
2581
|
+
throw new ClientRequestError({ success: false, kind: "error", code: "forbidden" /* Forbidden */ });
|
|
2582
|
+
}
|
|
2583
|
+
this.claimCorrelatedQuerySlot();
|
|
2584
|
+
let result;
|
|
2585
|
+
try {
|
|
2586
|
+
result = await svc.findOne(
|
|
2587
|
+
{},
|
|
2588
|
+
{
|
|
2589
|
+
select,
|
|
2590
|
+
sort,
|
|
2591
|
+
include: nestedInclude,
|
|
2592
|
+
overrides: { filter: authorized }
|
|
2593
|
+
},
|
|
2594
|
+
{ access: "read", lean: true, includePermissions: false }
|
|
2595
|
+
);
|
|
2596
|
+
} catch (error2) {
|
|
2597
|
+
const clientResult = this.getClientRequestErrorResult(error2);
|
|
2598
|
+
if (clientResult) throw new ClientRequestError(clientResult);
|
|
2599
|
+
throw error2;
|
|
2600
|
+
}
|
|
2601
|
+
if (result.success) {
|
|
2602
|
+
setDocValue(docs[index], include.path, result.data);
|
|
2603
|
+
} else if (result.code === "not_found" /* NotFound */) {
|
|
2604
|
+
setDocValue(docs[index], include.path, null);
|
|
2605
|
+
} else {
|
|
2606
|
+
throw new ClientRequestError(result);
|
|
2607
|
+
}
|
|
2608
|
+
});
|
|
2609
|
+
void depth;
|
|
2610
|
+
}
|
|
2611
|
+
async includeCorrelatedList(docs, include, snapshots, state, depth) {
|
|
2612
|
+
const svc = await this.getAuthorizedTargetService(include.model, "list");
|
|
2613
|
+
const { select, sort, skip, limit, page, pageSize, nestedInclude } = this.sanitizeCorrelatedArgs(include);
|
|
2614
|
+
const filterTemplate = include.filter;
|
|
2615
|
+
const templateErrors = this.validateClientFilter(filterTemplate);
|
|
2616
|
+
if (templateErrors.length > 0) {
|
|
2617
|
+
this.throwClientRequestError("bad_request" /* BadRequest */, templateErrors[0]);
|
|
2618
|
+
}
|
|
2619
|
+
let parsedTemplate;
|
|
2620
|
+
try {
|
|
2621
|
+
parsedTemplate = await this.parseClientData(
|
|
2622
|
+
filterTemplate,
|
|
2623
|
+
state.scheduler
|
|
2624
|
+
);
|
|
2625
|
+
} catch (error2) {
|
|
2626
|
+
const clientResult = this.getClientRequestErrorResult(error2);
|
|
2627
|
+
if (clientResult) throw new ClientRequestError(clientResult);
|
|
2628
|
+
throw error2;
|
|
2629
|
+
}
|
|
2630
|
+
const tasks = docs.map((_, index) => ({ index, snapshot: snapshots[index] }));
|
|
2631
|
+
await state.scheduler.map(tasks, async ({ index, snapshot }) => {
|
|
2632
|
+
let resolution;
|
|
2633
|
+
try {
|
|
2634
|
+
resolution = resolveCorrelatedFilterTemplate(parsedTemplate, snapshot);
|
|
2635
|
+
} catch (error2) {
|
|
2636
|
+
if (error2 instanceof CorrelatedReferenceError) {
|
|
2637
|
+
throw new ClientRequestError({
|
|
2638
|
+
success: false,
|
|
2639
|
+
kind: "error",
|
|
2640
|
+
code: "bad_request" /* BadRequest */,
|
|
2641
|
+
errors: [{ detail: error2.message }]
|
|
2642
|
+
});
|
|
2643
|
+
}
|
|
2644
|
+
throw error2;
|
|
2645
|
+
}
|
|
2646
|
+
if (resolution.status === "unresolvable") {
|
|
2647
|
+
setDocValue(docs[index], include.path, []);
|
|
2648
|
+
return;
|
|
2649
|
+
}
|
|
2650
|
+
const expanded = resolution.filter;
|
|
2651
|
+
const operandErrors = validateExpandedCorrelatedOperands(expanded, this.getRequestComplexity());
|
|
2652
|
+
if (operandErrors.length > 0) {
|
|
2653
|
+
throw new ClientRequestError({
|
|
2654
|
+
success: false,
|
|
2655
|
+
kind: "error",
|
|
2656
|
+
code: "bad_request" /* BadRequest */,
|
|
2657
|
+
errors: [{ detail: operandErrors[0].detail }]
|
|
2658
|
+
});
|
|
2659
|
+
}
|
|
2660
|
+
const authorized = await svc.genFilter("list", expanded);
|
|
2661
|
+
if (authorized === false) {
|
|
2662
|
+
throw new ClientRequestError({ success: false, kind: "error", code: "forbidden" /* Forbidden */ });
|
|
2663
|
+
}
|
|
2664
|
+
this.claimCorrelatedQuerySlot();
|
|
2665
|
+
let result;
|
|
2666
|
+
try {
|
|
2667
|
+
result = await svc.find(
|
|
2668
|
+
{},
|
|
2669
|
+
{
|
|
2670
|
+
select,
|
|
2671
|
+
sort,
|
|
2672
|
+
skip,
|
|
2673
|
+
limit,
|
|
2674
|
+
page,
|
|
2675
|
+
pageSize,
|
|
2676
|
+
include: nestedInclude,
|
|
2677
|
+
overrides: { filter: authorized }
|
|
2678
|
+
},
|
|
2679
|
+
{ includeCount: false, lean: true, includePermissions: false }
|
|
2680
|
+
);
|
|
2681
|
+
} catch (error2) {
|
|
2682
|
+
const clientResult = this.getClientRequestErrorResult(error2);
|
|
2683
|
+
if (clientResult) throw new ClientRequestError(clientResult);
|
|
2684
|
+
throw error2;
|
|
2685
|
+
}
|
|
2686
|
+
if (result.success) {
|
|
2687
|
+
setDocValue(docs[index], include.path, result.data);
|
|
2688
|
+
} else {
|
|
2689
|
+
throw new ClientRequestError(result);
|
|
2690
|
+
}
|
|
2691
|
+
});
|
|
2692
|
+
void depth;
|
|
2693
|
+
}
|
|
2694
|
+
async includeCorrelatedCount(docs, include, snapshots, state, depth) {
|
|
2695
|
+
const svc = await this.getAuthorizedTargetService(include.model, "count");
|
|
2696
|
+
const filterTemplate = include.filter;
|
|
2697
|
+
const templateErrors = this.validateClientFilter(filterTemplate);
|
|
2698
|
+
if (templateErrors.length > 0) {
|
|
2699
|
+
this.throwClientRequestError("bad_request" /* BadRequest */, templateErrors[0]);
|
|
2700
|
+
}
|
|
2701
|
+
const tasks = docs.map((_, index) => ({ index, snapshot: snapshots[index] }));
|
|
2702
|
+
await state.scheduler.map(tasks, async ({ index, snapshot }) => {
|
|
2703
|
+
let resolution;
|
|
2704
|
+
try {
|
|
2705
|
+
resolution = resolveCorrelatedFilterTemplate(filterTemplate, snapshot);
|
|
2706
|
+
} catch (error2) {
|
|
2707
|
+
if (error2 instanceof CorrelatedReferenceError) {
|
|
2708
|
+
throw new ClientRequestError({
|
|
2709
|
+
success: false,
|
|
2710
|
+
kind: "error",
|
|
2711
|
+
code: "bad_request" /* BadRequest */,
|
|
2712
|
+
errors: [{ detail: error2.message }]
|
|
2713
|
+
});
|
|
2714
|
+
}
|
|
2715
|
+
throw error2;
|
|
2716
|
+
}
|
|
2717
|
+
if (resolution.status === "unresolvable") {
|
|
2718
|
+
setDocValue(docs[index], include.path, 0);
|
|
2719
|
+
return;
|
|
2720
|
+
}
|
|
2721
|
+
const expanded = resolution.filter;
|
|
2722
|
+
const operandErrors = validateExpandedCorrelatedOperands(expanded, this.getRequestComplexity());
|
|
2723
|
+
if (operandErrors.length > 0) {
|
|
2724
|
+
throw new ClientRequestError({
|
|
2725
|
+
success: false,
|
|
2726
|
+
kind: "error",
|
|
2727
|
+
code: "bad_request" /* BadRequest */,
|
|
2728
|
+
errors: [{ detail: operandErrors[0].detail }]
|
|
2729
|
+
});
|
|
2730
|
+
}
|
|
2731
|
+
const authorized = await svc.genFilter("count", expanded);
|
|
2732
|
+
if (authorized === false) {
|
|
2733
|
+
throw new ClientRequestError({ success: false, kind: "error", code: "forbidden" /* Forbidden */ });
|
|
2734
|
+
}
|
|
2735
|
+
this.claimCorrelatedQuerySlot();
|
|
2736
|
+
let result;
|
|
2737
|
+
try {
|
|
2738
|
+
result = await svc.countTrusted(authorized);
|
|
2739
|
+
} catch (error2) {
|
|
2740
|
+
const clientResult = this.getClientRequestErrorResult(error2);
|
|
2741
|
+
if (clientResult) throw new ClientRequestError(clientResult);
|
|
2742
|
+
throw error2;
|
|
2743
|
+
}
|
|
2744
|
+
if (result.success) {
|
|
2745
|
+
setDocValue(docs[index], include.path, result.data);
|
|
2746
|
+
} else {
|
|
2747
|
+
throw new ClientRequestError(result);
|
|
2748
|
+
}
|
|
2749
|
+
});
|
|
2750
|
+
void depth;
|
|
2751
|
+
}
|
|
1863
2752
|
async parseClientData(filter2, scheduler = new RequestConcurrencyScheduler(this.getRequestComplexity().maxBulkConcurrency), scheduled = false) {
|
|
1864
2753
|
const result = await iterateQuery(
|
|
1865
2754
|
filter2,
|
|
@@ -1902,10 +2791,10 @@ var Base = class {
|
|
|
1902
2791
|
}
|
|
1903
2792
|
let ret = result.data;
|
|
1904
2793
|
if (sqOptions.path) {
|
|
1905
|
-
ret = (0,
|
|
2794
|
+
ret = (0, import_utils13.isArray)(ret) ? (0, import_utils13.flatten)(ret.map((v) => (0, import_utils13.get)(v, sqOptions.path))) : (0, import_utils13.get)(ret, sqOptions.path);
|
|
1906
2795
|
}
|
|
1907
2796
|
if (sqOptions.compact) {
|
|
1908
|
-
ret = (0,
|
|
2797
|
+
ret = (0, import_utils13.compact)((0, import_utils13.castArray)(ret));
|
|
1909
2798
|
}
|
|
1910
2799
|
return ret;
|
|
1911
2800
|
}
|
|
@@ -1917,7 +2806,7 @@ var Base = class {
|
|
|
1917
2806
|
};
|
|
1918
2807
|
|
|
1919
2808
|
// src/services/service.ts
|
|
1920
|
-
var
|
|
2809
|
+
var import_utils15 = require("@web-ts-toolkit/utils");
|
|
1921
2810
|
var import_just_diff = require("just-diff");
|
|
1922
2811
|
|
|
1923
2812
|
// src/model.ts
|
|
@@ -2161,28 +3050,28 @@ var error = (error2) => {
|
|
|
2161
3050
|
};
|
|
2162
3051
|
|
|
2163
3052
|
// src/services/model-subdocument-service.ts
|
|
2164
|
-
var
|
|
3053
|
+
var import_utils14 = require("@web-ts-toolkit/utils");
|
|
2165
3054
|
async function listSub(service, id, sub, options) {
|
|
2166
3055
|
const { filter: ft, select } = options ?? {};
|
|
2167
3056
|
const filterErrors = validateClientFilter(ft);
|
|
2168
3057
|
if (filterErrors.length > 0) return { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors: filterErrors };
|
|
2169
3058
|
const parentDoc = await getParentDoc(service, id, sub, null, { access: "read" });
|
|
2170
3059
|
if (!parentDoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
2171
|
-
let result = (0,
|
|
3060
|
+
let result = (0, import_utils14.get)(parentDoc, sub);
|
|
2172
3061
|
const [subFilter, subSelect] = await Promise.all([
|
|
2173
3062
|
service.genFilter(`subs.${sub}.list`, ft),
|
|
2174
3063
|
service.genQuerySelect("list", select, false, [sub, "sub"])
|
|
2175
3064
|
]);
|
|
2176
3065
|
if (subFilter === false) return { success: false, kind: "error", code: "forbidden" /* Forbidden */ };
|
|
2177
3066
|
result = filterCollection(result, subFilter);
|
|
2178
|
-
if (subSelect) result = result.map((v) => (0,
|
|
3067
|
+
if (subSelect) result = result.map((v) => (0, import_utils14.pick)(toObject(v), subSelect.concat("_id")));
|
|
2179
3068
|
return { success: true, kind: "list", code: "success" /* Success */, data: result, count: result.length };
|
|
2180
3069
|
}
|
|
2181
3070
|
async function readSub(service, id, sub, subId, options) {
|
|
2182
3071
|
const { select, populate } = options ?? {};
|
|
2183
3072
|
const parentDoc = await getParentDoc(service, id, sub, { populate }, { access: "read" });
|
|
2184
3073
|
if (!parentDoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
2185
|
-
const result = (0,
|
|
3074
|
+
const result = (0, import_utils14.get)(parentDoc, sub);
|
|
2186
3075
|
const [subFilter, subSelect] = await Promise.all([
|
|
2187
3076
|
service.genFilter(`subs.${sub}.read`, { _id: subId }),
|
|
2188
3077
|
service.genQuerySelect("read", select, false, [sub, "sub"])
|
|
@@ -2190,13 +3079,13 @@ async function readSub(service, id, sub, subId, options) {
|
|
|
2190
3079
|
if (subFilter === false) return { success: false, kind: "error", code: "forbidden" /* Forbidden */ };
|
|
2191
3080
|
let subdoc = findElement(result, subFilter);
|
|
2192
3081
|
if (!subdoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
2193
|
-
if (subSelect) subdoc = (0,
|
|
3082
|
+
if (subSelect) subdoc = (0, import_utils14.pick)(toObject(subdoc), subSelect.concat(["_id"]));
|
|
2194
3083
|
return { success: true, kind: "single", code: "success" /* Success */, data: subdoc };
|
|
2195
3084
|
}
|
|
2196
3085
|
async function updateSub(service, id, sub, subId, data) {
|
|
2197
3086
|
const parentDoc = await getParentDoc(service, id, sub, null, { access: "update" });
|
|
2198
3087
|
if (!parentDoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
2199
|
-
const result = (0,
|
|
3088
|
+
const result = (0, import_utils14.get)(parentDoc, sub);
|
|
2200
3089
|
const [subFilter, subReadSelect, subUpdateSelect] = await Promise.all([
|
|
2201
3090
|
service.genFilter(`subs.${sub}.update`, { _id: subId }),
|
|
2202
3091
|
service.genQuerySelect("read", null, false, [sub, "sub"]),
|
|
@@ -2205,10 +3094,10 @@ async function updateSub(service, id, sub, subId, data) {
|
|
|
2205
3094
|
if (subFilter === false) return { success: false, kind: "error", code: "forbidden" /* Forbidden */ };
|
|
2206
3095
|
let subdoc = findElement(result, subFilter);
|
|
2207
3096
|
if (!subdoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
2208
|
-
const allowedData = (0,
|
|
3097
|
+
const allowedData = (0, import_utils14.pick)(data, subUpdateSelect);
|
|
2209
3098
|
Object.assign(subdoc, allowedData);
|
|
2210
3099
|
await parentDoc.save();
|
|
2211
|
-
if (subReadSelect) subdoc = (0,
|
|
3100
|
+
if (subReadSelect) subdoc = (0, import_utils14.pick)(toObject(subdoc), subReadSelect.concat(["_id"]));
|
|
2212
3101
|
return { success: true, kind: "single", code: "success" /* Success */, data: subdoc };
|
|
2213
3102
|
}
|
|
2214
3103
|
async function bulkUpdateSub(service, id, sub, data) {
|
|
@@ -2223,7 +3112,7 @@ async function bulkUpdateSub(service, id, sub, data) {
|
|
|
2223
3112
|
}
|
|
2224
3113
|
const parentDoc = await getParentDoc(service, id, sub, null, { access: "update" });
|
|
2225
3114
|
if (!parentDoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
2226
|
-
let result = (0,
|
|
3115
|
+
let result = (0, import_utils14.get)(parentDoc, sub);
|
|
2227
3116
|
const [subFilter, subReadSelect, subUpdateSelect] = await Promise.all([
|
|
2228
3117
|
service.genFilter(`subs.${sub}.update`, { _id: { $in: data.map((v) => v._id) } }),
|
|
2229
3118
|
service.genQuerySelect("read", null, false, [sub, "sub"]),
|
|
@@ -2231,14 +3120,14 @@ async function bulkUpdateSub(service, id, sub, data) {
|
|
|
2231
3120
|
]);
|
|
2232
3121
|
if (subFilter === false) return { success: false, kind: "error", code: "forbidden" /* Forbidden */ };
|
|
2233
3122
|
result = filterCollection(result, subFilter);
|
|
2234
|
-
(0,
|
|
3123
|
+
(0, import_utils14.forEach)(result, (subdoc) => {
|
|
2235
3124
|
const tdata = findElementById(data, subdoc._id);
|
|
2236
3125
|
if (!tdata) return;
|
|
2237
|
-
const allowedData = (0,
|
|
3126
|
+
const allowedData = (0, import_utils14.pick)(tdata, subUpdateSelect);
|
|
2238
3127
|
Object.assign(subdoc, allowedData);
|
|
2239
3128
|
});
|
|
2240
3129
|
await parentDoc.save();
|
|
2241
|
-
if (subReadSelect) result = result.map((v) => (0,
|
|
3130
|
+
if (subReadSelect) result = result.map((v) => (0, import_utils14.pick)(toObject(v), subReadSelect.concat(["_id"])));
|
|
2242
3131
|
return { success: true, kind: "list", code: "success" /* Success */, data: result, count: result.length };
|
|
2243
3132
|
}
|
|
2244
3133
|
async function createSub(service, id, sub, data, options) {
|
|
@@ -2254,25 +3143,25 @@ async function createSub(service, id, sub, data, options) {
|
|
|
2254
3143
|
}
|
|
2255
3144
|
const parentDoc = await getParentDoc(service, id, sub, null, { access: "update" });
|
|
2256
3145
|
if (!parentDoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
2257
|
-
let result = (0,
|
|
3146
|
+
let result = (0, import_utils14.get)(parentDoc, sub);
|
|
2258
3147
|
const [subCreateSelect, subReadSelect] = await Promise.all([
|
|
2259
3148
|
service.genQuerySelect("create", null, false, [sub, "sub"]),
|
|
2260
3149
|
service.genQuerySelect("read", null, false, [sub, "sub"])
|
|
2261
3150
|
]);
|
|
2262
|
-
const allowedData = Array.isArray(data) ? data.map((row) => (0,
|
|
3151
|
+
const allowedData = Array.isArray(data) ? data.map((row) => (0, import_utils14.pick)(row, subCreateSelect)) : (0, import_utils14.pick)(data, subCreateSelect);
|
|
2263
3152
|
if (Array.isArray(allowedData)) {
|
|
2264
3153
|
addFirst === true ? result.unshift(...allowedData) : result.push(...allowedData);
|
|
2265
3154
|
} else {
|
|
2266
3155
|
addFirst === true ? result.unshift(allowedData) : result.push(allowedData);
|
|
2267
3156
|
}
|
|
2268
3157
|
await parentDoc.save();
|
|
2269
|
-
if (subReadSelect) result = result.map((v) => (0,
|
|
3158
|
+
if (subReadSelect) result = result.map((v) => (0, import_utils14.pick)(toObject(v), subReadSelect.concat(["_id"])));
|
|
2270
3159
|
return { success: true, kind: "list", code: "created" /* Created */, data: result, count: result.length };
|
|
2271
3160
|
}
|
|
2272
3161
|
async function deleteSub(service, id, sub, subId) {
|
|
2273
3162
|
const parentDoc = await getParentDoc(service, id, sub, null, { access: "update" });
|
|
2274
3163
|
if (!parentDoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
2275
|
-
const result = (0,
|
|
3164
|
+
const result = (0, import_utils14.get)(parentDoc, sub);
|
|
2276
3165
|
const subFilter = await service.genFilter(`subs.${sub}.delete`, { _id: subId });
|
|
2277
3166
|
if (subFilter === false) return { success: false, kind: "error", code: "forbidden" /* Forbidden */ };
|
|
2278
3167
|
const subdoc = findElement(result, subFilter);
|
|
@@ -2413,6 +3302,29 @@ var assertModelDocument = (value, modelName, hookName) => {
|
|
|
2413
3302
|
}
|
|
2414
3303
|
throw new Error(`${hookName} hook for model=${modelName} must return a Mongoose document instance`);
|
|
2415
3304
|
};
|
|
3305
|
+
var unsetDocPath = (doc, path) => {
|
|
3306
|
+
const segments = path.split(".");
|
|
3307
|
+
let current = doc;
|
|
3308
|
+
const unwrap = (value) => value != null && typeof value === "object" && "_doc" in value ? value._doc : value;
|
|
3309
|
+
current = unwrap(current);
|
|
3310
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
3311
|
+
if (current == null || typeof current !== "object") return;
|
|
3312
|
+
if (!Object.prototype.hasOwnProperty.call(current, segments[i])) return;
|
|
3313
|
+
current = unwrap(current[segments[i]]);
|
|
3314
|
+
}
|
|
3315
|
+
if (current != null && typeof current === "object") {
|
|
3316
|
+
delete current[segments[segments.length - 1]];
|
|
3317
|
+
}
|
|
3318
|
+
};
|
|
3319
|
+
var shouldKeepCorrelatedRef = (ref, keep) => {
|
|
3320
|
+
if (keep.has(ref)) return true;
|
|
3321
|
+
const top = ref.split(".")[0];
|
|
3322
|
+
if (keep.has(top)) return true;
|
|
3323
|
+
for (const kept of keep) {
|
|
3324
|
+
if (kept === ref || kept.startsWith(`${ref}.`) || ref.startsWith(`${kept}.`)) return true;
|
|
3325
|
+
}
|
|
3326
|
+
return false;
|
|
3327
|
+
};
|
|
2416
3328
|
var Service = class extends Base {
|
|
2417
3329
|
findRawParentDoc(args) {
|
|
2418
3330
|
return this.model.findOne({
|
|
@@ -2479,8 +3391,9 @@ var Service = class extends Base {
|
|
|
2479
3391
|
overridePopulate || this.genPopulate(populateAccess || access, populate),
|
|
2480
3392
|
this.genAllowedFields({}, access, this.baseFieldsExt)
|
|
2481
3393
|
]);
|
|
2482
|
-
const { includes, includeLocalFields, includePaths } = this.processInclude(include);
|
|
2483
|
-
const
|
|
3394
|
+
const { includes, correlatedIncludes, includeLocalFields, includePaths, correlatedReferenceFields } = this.processInclude(include);
|
|
3395
|
+
const correlatedOutputPaths = correlatedIncludes.map((entry) => entry.path);
|
|
3396
|
+
const finalSelect = normalizeSelect(_select).concat(includeLocalFields, correlatedReferenceFields);
|
|
2484
3397
|
const query = {
|
|
2485
3398
|
filter: _filter,
|
|
2486
3399
|
select: finalSelect,
|
|
@@ -2513,8 +3426,14 @@ var Service = class extends Base {
|
|
|
2513
3426
|
originalDocumentSnapshot: toObject(doc),
|
|
2514
3427
|
resolvedQuery: query
|
|
2515
3428
|
};
|
|
3429
|
+
const correlatedSnapshots = [(0, import_utils15.cloneDeep)(toObject(doc))];
|
|
2516
3430
|
try {
|
|
2517
3431
|
doc = await this.includeDocs(doc, includes);
|
|
3432
|
+
if (correlatedIncludes.length > 0) {
|
|
3433
|
+
const docList = [doc];
|
|
3434
|
+
await this.includeCorrelatedDocs(docList, correlatedIncludes, correlatedSnapshots);
|
|
3435
|
+
doc = docList[0];
|
|
3436
|
+
}
|
|
2518
3437
|
} catch (error2) {
|
|
2519
3438
|
const result = this.getClientRequestErrorResult(error2);
|
|
2520
3439
|
if (result) {
|
|
@@ -2532,8 +3451,20 @@ var Service = class extends Base {
|
|
|
2532
3451
|
doc = await this.trimOutputFields(
|
|
2533
3452
|
doc,
|
|
2534
3453
|
access,
|
|
2535
|
-
this.baseFieldsExt.concat(includePaths, normalizeSelect(overrideSelect))
|
|
3454
|
+
this.baseFieldsExt.concat(includePaths, correlatedOutputPaths, normalizeSelect(overrideSelect))
|
|
2536
3455
|
);
|
|
3456
|
+
if (correlatedReferenceFields.length > 0) {
|
|
3457
|
+
const keep = /* @__PURE__ */ new Set([
|
|
3458
|
+
...normalizeSelect(_select),
|
|
3459
|
+
...normalizeSelect(overrideSelect),
|
|
3460
|
+
...this.baseFieldsExt,
|
|
3461
|
+
...includePaths,
|
|
3462
|
+
...correlatedOutputPaths
|
|
3463
|
+
]);
|
|
3464
|
+
for (const ref of correlatedReferenceFields) {
|
|
3465
|
+
if (!shouldKeepCorrelatedRef(ref, keep)) unsetDocPath(doc, ref);
|
|
3466
|
+
}
|
|
3467
|
+
}
|
|
2537
3468
|
if (!includePermissions) doc = this.addEmptyPermissions(doc);
|
|
2538
3469
|
this.completeOp("findOne", startedAt, "success" /* Success */, _filter);
|
|
2539
3470
|
return { success: true, kind: "single", code: "success" /* Success */, data: doc, query, context };
|
|
@@ -2579,11 +3510,12 @@ var Service = class extends Base {
|
|
|
2579
3510
|
this.genAllowedFields({}, "list", this.baseFieldsExt)
|
|
2580
3511
|
]);
|
|
2581
3512
|
const finalSelect = normalizeSelect(_select);
|
|
2582
|
-
const filteredPopulate = (0,
|
|
2583
|
-
const { includes, includeLocalFields, includePaths } = this.processInclude(include);
|
|
3513
|
+
const filteredPopulate = (0, import_utils15.isArray)(finalSelect) && (0, import_utils15.isArray)(_populate) ? _populate.filter((p) => finalSelect.includes(p.path.split(".")[0])) : _populate;
|
|
3514
|
+
const { includes, correlatedIncludes, includeLocalFields, includePaths, correlatedReferenceFields } = this.processInclude(include);
|
|
3515
|
+
const correlatedOutputPaths = correlatedIncludes.map((entry) => entry.path);
|
|
2584
3516
|
const query = {
|
|
2585
3517
|
filter: _filter,
|
|
2586
|
-
select: finalSelect.concat(includeLocalFields),
|
|
3518
|
+
select: finalSelect.concat(includeLocalFields, correlatedReferenceFields),
|
|
2587
3519
|
populate: filteredPopulate,
|
|
2588
3520
|
sort,
|
|
2589
3521
|
...pagination
|
|
@@ -2592,7 +3524,7 @@ var Service = class extends Base {
|
|
|
2592
3524
|
sort,
|
|
2593
3525
|
skip: pagination.skip,
|
|
2594
3526
|
limit: pagination.limit,
|
|
2595
|
-
selectCount: finalSelect.concat(includeLocalFields).length,
|
|
3527
|
+
selectCount: finalSelect.concat(includeLocalFields, correlatedReferenceFields).length,
|
|
2596
3528
|
populateCount: Array.isArray(filteredPopulate) ? filteredPopulate.length : filteredPopulate ? 1 : 0
|
|
2597
3529
|
});
|
|
2598
3530
|
if (_filter === false) {
|
|
@@ -2616,9 +3548,13 @@ var Service = class extends Base {
|
|
|
2616
3548
|
originalDocumentSnapshot: toObject(doc),
|
|
2617
3549
|
resolvedQuery: query
|
|
2618
3550
|
}));
|
|
2619
|
-
const _decorate = (0,
|
|
3551
|
+
const _decorate = (0, import_utils15.isFunction)(decorate) ? decorate : (v) => v;
|
|
3552
|
+
const correlatedSnapshots = docs.map((doc) => (0, import_utils15.cloneDeep)(toObject(doc)));
|
|
2620
3553
|
try {
|
|
2621
3554
|
docs = await this.includeDocs(docs, includes);
|
|
3555
|
+
if (correlatedIncludes.length > 0) {
|
|
3556
|
+
docs = await this.includeCorrelatedDocs(docs, correlatedIncludes, correlatedSnapshots);
|
|
3557
|
+
}
|
|
2622
3558
|
} catch (error2) {
|
|
2623
3559
|
const result = this.getClientRequestErrorResult(error2);
|
|
2624
3560
|
if (result) {
|
|
@@ -2640,8 +3576,20 @@ var Service = class extends Base {
|
|
|
2640
3576
|
doc = await this.trimOutputFields(
|
|
2641
3577
|
doc,
|
|
2642
3578
|
"list",
|
|
2643
|
-
this.baseFieldsExt.concat(includePaths, normalizeSelect(overrideSelect))
|
|
3579
|
+
this.baseFieldsExt.concat(includePaths, correlatedOutputPaths, normalizeSelect(overrideSelect))
|
|
2644
3580
|
);
|
|
3581
|
+
if (correlatedReferenceFields.length > 0) {
|
|
3582
|
+
const keep = /* @__PURE__ */ new Set([
|
|
3583
|
+
...normalizeSelect(_select),
|
|
3584
|
+
...normalizeSelect(overrideSelect),
|
|
3585
|
+
...this.baseFieldsExt,
|
|
3586
|
+
...includePaths,
|
|
3587
|
+
...correlatedOutputPaths
|
|
3588
|
+
]);
|
|
3589
|
+
for (const ref of correlatedReferenceFields) {
|
|
3590
|
+
if (!shouldKeepCorrelatedRef(ref, keep)) unsetDocPath(doc, ref);
|
|
3591
|
+
}
|
|
3592
|
+
}
|
|
2645
3593
|
doc = await _decorate(doc, contexts[i]);
|
|
2646
3594
|
if (!includePermissions) doc = this.addEmptyPermissions(doc);
|
|
2647
3595
|
return doc;
|
|
@@ -2717,16 +3665,16 @@ var Service = class extends Base {
|
|
|
2717
3665
|
resolvedQuery: resolvedPopulate.length > 0 ? { populate: resolvedPopulate } : {}
|
|
2718
3666
|
};
|
|
2719
3667
|
const allowedFields = await this.genAllowedFields(item, "create");
|
|
2720
|
-
const allowedData = (0,
|
|
3668
|
+
const allowedData = (0, import_utils15.pick)(item, allowedFields);
|
|
2721
3669
|
context.allowedFields = allowedFields;
|
|
2722
3670
|
context.allowedData = allowedData;
|
|
2723
3671
|
const validated = await this.validate(allowedData, "create", context);
|
|
2724
|
-
if ((0,
|
|
3672
|
+
if ((0, import_utils15.isBoolean)(validated)) {
|
|
2725
3673
|
if (!validated) {
|
|
2726
3674
|
validationErrors.push({ index, errors: [] });
|
|
2727
3675
|
return void 0;
|
|
2728
3676
|
}
|
|
2729
|
-
} else if ((0,
|
|
3677
|
+
} else if ((0, import_utils15.isArray)(validated)) {
|
|
2730
3678
|
if (validated.length > 0) {
|
|
2731
3679
|
validationErrors.push({
|
|
2732
3680
|
index,
|
|
@@ -2756,7 +3704,7 @@ var Service = class extends Base {
|
|
|
2756
3704
|
contexts[index].preparedData = preparedData;
|
|
2757
3705
|
return preparedData;
|
|
2758
3706
|
});
|
|
2759
|
-
const _decorate = (0,
|
|
3707
|
+
const _decorate = (0, import_utils15.isFunction)(decorate) ? decorate : (v) => v;
|
|
2760
3708
|
const createdDocs = await this.model.create(items);
|
|
2761
3709
|
const docs = await mapWithConcurrencyLimit(createdDocs, maxBulkConcurrency, async (doc, index) => {
|
|
2762
3710
|
contexts[index].currentDocument = doc;
|
|
@@ -2874,16 +3822,16 @@ var Service = class extends Base {
|
|
|
2874
3822
|
context.docPermissions = this.getDocPermissions(doc);
|
|
2875
3823
|
context.currentDocument = doc;
|
|
2876
3824
|
const allowedFields = await this.genAllowedFields(doc, "update");
|
|
2877
|
-
const allowedData = (0,
|
|
3825
|
+
const allowedData = (0, import_utils15.pick)(data, allowedFields);
|
|
2878
3826
|
context.allowedFields = allowedFields;
|
|
2879
3827
|
context.allowedData = allowedData;
|
|
2880
3828
|
const validated = await this.validate(allowedData, "update", context);
|
|
2881
|
-
if ((0,
|
|
3829
|
+
if ((0, import_utils15.isBoolean)(validated)) {
|
|
2882
3830
|
if (!validated) {
|
|
2883
3831
|
this.completeOp("updateOne", startedAt, "bad_request" /* BadRequest */, _filter);
|
|
2884
3832
|
return { success: false, kind: "error", code: "bad_request" /* BadRequest */ };
|
|
2885
3833
|
}
|
|
2886
|
-
} else if ((0,
|
|
3834
|
+
} else if ((0, import_utils15.isArray)(validated)) {
|
|
2887
3835
|
if (validated.length > 0) {
|
|
2888
3836
|
this.completeOp("updateOne", startedAt, "bad_request" /* BadRequest */, _filter);
|
|
2889
3837
|
return { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors: validated };
|
|
@@ -2899,10 +3847,10 @@ var Service = class extends Base {
|
|
|
2899
3847
|
const diffExcludeFields = [this.options.documentPermissionField, "__v"];
|
|
2900
3848
|
this.asServiceHookContext(context).diff = (d) => {
|
|
2901
3849
|
context.changes = (0, import_just_diff.diff)(
|
|
2902
|
-
(0,
|
|
2903
|
-
(0,
|
|
3850
|
+
(0, import_utils15.omit)(context.originalDocumentSnapshot, diffExcludeFields),
|
|
3851
|
+
(0, import_utils15.omit)(d.toObject({ virtuals: false }), diffExcludeFields)
|
|
2904
3852
|
) || [];
|
|
2905
|
-
context.modifiedPaths = (0,
|
|
3853
|
+
context.modifiedPaths = (0, import_utils15.uniq)(context.changes.map((di) => di.path.length > 0 ? String(di.path[0]) : ""));
|
|
2906
3854
|
};
|
|
2907
3855
|
doc = assertModelDocument(await this.afterPersist(doc, "update", context), this.modelName, "afterPersist");
|
|
2908
3856
|
context.currentDocument = doc;
|
|
@@ -2918,7 +3866,7 @@ var Service = class extends Base {
|
|
|
2918
3866
|
if (_populate) await populateDoc(doc, _populate);
|
|
2919
3867
|
doc = await this.trimOutputFields(doc, "read", this.baseFieldsExt);
|
|
2920
3868
|
let outputDoc = doc;
|
|
2921
|
-
if ((0,
|
|
3869
|
+
if ((0, import_utils15.isFunction)(decorate)) outputDoc = await decorate(outputDoc, context);
|
|
2922
3870
|
if (!includePermissions) outputDoc = this.addEmptyPermissions(outputDoc);
|
|
2923
3871
|
this.completeOp("updateOne", startedAt, "success" /* Success */, _filter);
|
|
2924
3872
|
return { success: true, kind: "single", code: "success" /* Success */, data: outputDoc, input: prepared };
|
|
@@ -3070,6 +4018,23 @@ var Service = class extends Base {
|
|
|
3070
4018
|
if (filter2 === false) return { success: false, kind: "error", code: "forbidden" /* Forbidden */, query };
|
|
3071
4019
|
return { success: true, kind: "single", code: "success" /* Success */, data: await this.model.countDocuments(filter2), query };
|
|
3072
4020
|
}
|
|
4021
|
+
/**
|
|
4022
|
+
* Trusted count for correlated includes (ACI-03): the caller already
|
|
4023
|
+
* resolved parent references, revalidated expanded operands, and applied
|
|
4024
|
+
* explicit `count` access via `genFilter`. This skips client-filter
|
|
4025
|
+
* validation and subquery parsing so substituted parent data stays inert.
|
|
4026
|
+
*/
|
|
4027
|
+
async countTrusted(authorizedFilter) {
|
|
4028
|
+
const query = { filter: authorizedFilter };
|
|
4029
|
+
if (authorizedFilter === false) return { success: false, kind: "error", code: "forbidden" /* Forbidden */, query };
|
|
4030
|
+
return {
|
|
4031
|
+
success: true,
|
|
4032
|
+
kind: "single",
|
|
4033
|
+
code: "success" /* Success */,
|
|
4034
|
+
data: await this.model.countDocuments(authorizedFilter),
|
|
4035
|
+
query
|
|
4036
|
+
};
|
|
4037
|
+
}
|
|
3073
4038
|
async countByFieldValues(foreignField, values, filter2 = {}, access = "list") {
|
|
3074
4039
|
if (!isValidFieldPath(foreignField)) {
|
|
3075
4040
|
return {
|
|
@@ -3081,7 +4046,7 @@ var Service = class extends Base {
|
|
|
3081
4046
|
}
|
|
3082
4047
|
const filterErrors = this.validateClientFilter(filter2);
|
|
3083
4048
|
if (filterErrors.length > 0) return { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors: filterErrors };
|
|
3084
|
-
const uniqueValues = (0,
|
|
4049
|
+
const uniqueValues = (0, import_utils15.uniqBy)(values, (value) => String(value));
|
|
3085
4050
|
filter2 = await this.genFilter(access, {
|
|
3086
4051
|
...filter2,
|
|
3087
4052
|
[foreignField]: { $in: uniqueValues }
|
|
@@ -3157,7 +4122,7 @@ var Service = class extends Base {
|
|
|
3157
4122
|
return resolveExistsOptions(this, options);
|
|
3158
4123
|
}
|
|
3159
4124
|
async getFieldPermissionAccess(ids) {
|
|
3160
|
-
const uniqueIds = (0,
|
|
4125
|
+
const uniqueIds = (0, import_utils15.compact)((0, import_utils15.uniqBy)(ids, (id) => String(id)).map((id) => String(id)));
|
|
3161
4126
|
if (uniqueIds.length === 0) {
|
|
3162
4127
|
return {
|
|
3163
4128
|
readIds: /* @__PURE__ */ new Set(),
|
|
@@ -3187,7 +4152,7 @@ var Service = class extends Base {
|
|
|
3187
4152
|
return updateSub(this, id, sub, subId, data);
|
|
3188
4153
|
}
|
|
3189
4154
|
async bulkUpdateSub(id, sub, data) {
|
|
3190
|
-
return bulkUpdateSub(this, id, sub, (0,
|
|
4155
|
+
return bulkUpdateSub(this, id, sub, (0, import_utils15.castArray)(data));
|
|
3191
4156
|
}
|
|
3192
4157
|
async createSub(id, sub, data, options) {
|
|
3193
4158
|
return createSub(this, id, sub, data, options);
|
|
@@ -3201,7 +4166,7 @@ var Service = class extends Base {
|
|
|
3201
4166
|
};
|
|
3202
4167
|
|
|
3203
4168
|
// src/services/public-service.ts
|
|
3204
|
-
var
|
|
4169
|
+
var import_utils16 = require("@web-ts-toolkit/utils");
|
|
3205
4170
|
var PublicService = class extends Service {
|
|
3206
4171
|
async _list(filter2, args, options) {
|
|
3207
4172
|
const { select, populate, include, sort, skip, limit, page, pageSize, tasks } = this.resolvePublicListArgs(args);
|
|
@@ -3240,7 +4205,7 @@ var PublicService = class extends Service {
|
|
|
3240
4205
|
let d = toObject(doc);
|
|
3241
4206
|
d = await this.decorate(d, "create", context);
|
|
3242
4207
|
d = this.runTasks(d, tasks);
|
|
3243
|
-
if (select) d = (0,
|
|
4208
|
+
if (select) d = (0, import_utils16.pick)(d, [...normalizeSelect(select), ...this.baseFieldsExt]);
|
|
3244
4209
|
return d;
|
|
3245
4210
|
}
|
|
3246
4211
|
);
|
|
@@ -3345,8 +4310,8 @@ var PublicService = class extends Service {
|
|
|
3345
4310
|
let d = toObject(doc);
|
|
3346
4311
|
d = await this.decorate(d, "update", context);
|
|
3347
4312
|
d = this.runTasks(d, tasks);
|
|
3348
|
-
if (select) d = (0,
|
|
3349
|
-
else if (!returningAll) d = (0,
|
|
4313
|
+
if (select) d = (0, import_utils16.pick)(d, [...normalizeSelect(select), ...this.baseFieldsExt]);
|
|
4314
|
+
else if (!returningAll) d = (0, import_utils16.pick)(d, [...Object.keys(data), "_id"]);
|
|
3350
4315
|
return d;
|
|
3351
4316
|
}
|
|
3352
4317
|
);
|
|
@@ -3468,13 +4433,13 @@ var PublicService = class extends Service {
|
|
|
3468
4433
|
};
|
|
3469
4434
|
|
|
3470
4435
|
// src/services/data-service.ts
|
|
3471
|
-
var
|
|
4436
|
+
var import_utils17 = require("@web-ts-toolkit/utils");
|
|
3472
4437
|
var DataService = class {
|
|
3473
4438
|
constructor(req, dataName) {
|
|
3474
4439
|
this.req = req;
|
|
3475
4440
|
this.dataName = dataName;
|
|
3476
4441
|
this.options = getDataOptions(dataName);
|
|
3477
|
-
this.data =
|
|
4442
|
+
this.data = getDataSnapshot(dataName);
|
|
3478
4443
|
}
|
|
3479
4444
|
async findOne(filter2, args, options) {
|
|
3480
4445
|
const filterErrors = validateClientFilter(filter2);
|
|
@@ -3490,7 +4455,7 @@ var DataService = class {
|
|
|
3490
4455
|
let doc = await findElement(this.data, _filter);
|
|
3491
4456
|
if (!doc) return { success: false, kind: "error", code: "not_found" /* NotFound */, query };
|
|
3492
4457
|
doc = await this.trimOutputFields(doc, access);
|
|
3493
|
-
if (_select.length > 0) doc = (0,
|
|
4458
|
+
if (_select.length > 0) doc = (0, import_utils17.pick)(doc, _select);
|
|
3494
4459
|
return { success: true, kind: "single", code: "success" /* Success */, data: doc, query };
|
|
3495
4460
|
}
|
|
3496
4461
|
async findById(id, args, options) {
|
|
@@ -3530,13 +4495,13 @@ var DataService = class {
|
|
|
3530
4495
|
const totalCount = docs.length;
|
|
3531
4496
|
if (sort) {
|
|
3532
4497
|
const normalizedSort = normalizeSortForOrderBy(sort);
|
|
3533
|
-
docs = (0,
|
|
4498
|
+
docs = (0, import_utils17.orderBy)(docs, normalizedSort.fields, normalizedSort.orders);
|
|
3534
4499
|
}
|
|
3535
4500
|
const pagedDocs = docs.slice(query.skip, query.limit && query.skip + query.limit);
|
|
3536
4501
|
const { maxHookConcurrency } = this.getRequestComplexity();
|
|
3537
4502
|
const trimmed = await mapWithConcurrencyLimit(pagedDocs, maxHookConcurrency, async (doc) => {
|
|
3538
4503
|
doc = await this.trimOutputFields(doc, "list");
|
|
3539
|
-
if (_select.length > 0) doc = (0,
|
|
4504
|
+
if (_select.length > 0) doc = (0, import_utils17.pick)(doc, _select);
|
|
3540
4505
|
return doc;
|
|
3541
4506
|
});
|
|
3542
4507
|
if (!hasExplicitLimit && trimmed.length === totalCount) {
|
|
@@ -3585,30 +4550,65 @@ var DataService = class {
|
|
|
3585
4550
|
};
|
|
3586
4551
|
|
|
3587
4552
|
// src/processors.ts
|
|
3588
|
-
var
|
|
4553
|
+
var import_utils18 = require("@web-ts-toolkit/utils");
|
|
3589
4554
|
var UNSAFE_PATH_KEYS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
|
|
3590
|
-
|
|
4555
|
+
var DEFAULT_MAX_SLICE = 1e3;
|
|
4556
|
+
var DEFAULT_MASK_REPLACEMENT = "***";
|
|
4557
|
+
function failUnsafeSegment(path, fn = "copyAndDepopulate") {
|
|
3591
4558
|
throw new Error(
|
|
3592
|
-
|
|
4559
|
+
`${fn}: refusing path '${path}' because it contains an unsafe segment (__proto__, prototype, or constructor).`
|
|
3593
4560
|
);
|
|
3594
4561
|
}
|
|
3595
4562
|
function failMissingId(path) {
|
|
3596
4563
|
throw new Error(`copyAndDepopulate: path '${path}' resolved to a record missing the configured id field.`);
|
|
3597
4564
|
}
|
|
3598
|
-
function
|
|
4565
|
+
function failInvalidSlice(value, field, fn) {
|
|
4566
|
+
throw new Error(`${fn}: '${field}' must be a non-negative safe integer, received ${String(value)}.`);
|
|
4567
|
+
}
|
|
4568
|
+
function assertSafePath(path, fn = "copyAndDepopulate") {
|
|
3599
4569
|
const segs = path.split(".");
|
|
3600
4570
|
for (const seg of segs) {
|
|
3601
4571
|
if (seg.length === 0) continue;
|
|
3602
4572
|
if (UNSAFE_PATH_KEYS.has(seg)) {
|
|
3603
|
-
failUnsafeSegment(path);
|
|
4573
|
+
failUnsafeSegment(path, fn);
|
|
3604
4574
|
}
|
|
3605
4575
|
}
|
|
3606
4576
|
}
|
|
4577
|
+
function assertNonNegativeSafeInteger(value, field, fn) {
|
|
4578
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
|
|
4579
|
+
failInvalidSlice(value, field, fn);
|
|
4580
|
+
}
|
|
4581
|
+
}
|
|
4582
|
+
function resolveLeafParents(obj, src) {
|
|
4583
|
+
const segs = src.split(".");
|
|
4584
|
+
let targets = [obj];
|
|
4585
|
+
for (let ind = 0; ind < segs.length - 1; ind += 1) {
|
|
4586
|
+
const seg = segs[ind];
|
|
4587
|
+
targets = targets.reduce((ret, target) => {
|
|
4588
|
+
if (!(0, import_utils18.isPlainObject)(target)) return ret;
|
|
4589
|
+
const targetObject = target;
|
|
4590
|
+
const next = targetObject[seg];
|
|
4591
|
+
if ((0, import_utils18.isArray)(next)) {
|
|
4592
|
+
for (const item of next) {
|
|
4593
|
+
ret.push(item);
|
|
4594
|
+
}
|
|
4595
|
+
} else if (next !== null && next !== void 0) ret.push(next);
|
|
4596
|
+
return ret;
|
|
4597
|
+
}, []);
|
|
4598
|
+
}
|
|
4599
|
+
const leaf = segs[segs.length - 1];
|
|
4600
|
+
const pairs = [];
|
|
4601
|
+
(0, import_utils18.forEach)(targets, (target) => {
|
|
4602
|
+
if (!(0, import_utils18.isPlainObject)(target)) return;
|
|
4603
|
+
pairs.push({ parent: target, leaf });
|
|
4604
|
+
});
|
|
4605
|
+
return pairs;
|
|
4606
|
+
}
|
|
3607
4607
|
var copyAndDepopulate = (docObject, operations, options = { mutable: true, idField: "_id" }) => {
|
|
3608
4608
|
const mutable = options.mutable !== false;
|
|
3609
4609
|
const idField = options.idField ?? "_id";
|
|
3610
|
-
const obj = mutable ? docObject : (0,
|
|
3611
|
-
(0,
|
|
4610
|
+
const obj = mutable ? docObject : (0, import_utils18.cloneDeep)(docObject);
|
|
4611
|
+
(0, import_utils18.forEach)(Array.isArray(operations) ? operations : [], (op) => {
|
|
3612
4612
|
if (!op || typeof op.src !== "string" || typeof op.dest !== "string") return;
|
|
3613
4613
|
const src = op.src;
|
|
3614
4614
|
const dest = op.dest;
|
|
@@ -3617,19 +4617,19 @@ var copyAndDepopulate = (docObject, operations, options = { mutable: true, idFie
|
|
|
3617
4617
|
assertSafePath(dest);
|
|
3618
4618
|
let targets = [obj];
|
|
3619
4619
|
const segs = src.split(".");
|
|
3620
|
-
(0,
|
|
4620
|
+
(0, import_utils18.forEach)(segs, (seg, ind) => {
|
|
3621
4621
|
if (segs.length === ind + 1) {
|
|
3622
|
-
(0,
|
|
3623
|
-
if (!(0,
|
|
4622
|
+
(0, import_utils18.forEach)(targets, (target) => {
|
|
4623
|
+
if (!(0, import_utils18.isPlainObject)(target)) return;
|
|
3624
4624
|
const targetObject = target;
|
|
3625
4625
|
const leaf = targetObject[seg];
|
|
3626
4626
|
const sourceForDest = leaf;
|
|
3627
4627
|
let depopulated;
|
|
3628
|
-
if ((0,
|
|
4628
|
+
if ((0, import_utils18.isArray)(leaf)) {
|
|
3629
4629
|
if (leaf.length === 0) {
|
|
3630
4630
|
depopulated = [];
|
|
3631
|
-
} else if (leaf.every((item) => (0,
|
|
3632
|
-
depopulated = (0,
|
|
4631
|
+
} else if (leaf.every((item) => (0, import_utils18.isPlainObject)(item))) {
|
|
4632
|
+
depopulated = (0, import_utils18.map)(leaf, idField).map((id, i) => {
|
|
3633
4633
|
if (id === void 0) {
|
|
3634
4634
|
failMissingId(`${src}[${i}]`);
|
|
3635
4635
|
}
|
|
@@ -3638,8 +4638,8 @@ var copyAndDepopulate = (docObject, operations, options = { mutable: true, idFie
|
|
|
3638
4638
|
} else {
|
|
3639
4639
|
return;
|
|
3640
4640
|
}
|
|
3641
|
-
} else if ((0,
|
|
3642
|
-
const id = (0,
|
|
4641
|
+
} else if ((0, import_utils18.isPlainObject)(leaf)) {
|
|
4642
|
+
const id = (0, import_utils18.get)(leaf, idField);
|
|
3643
4643
|
if (id === void 0) {
|
|
3644
4644
|
failMissingId(src);
|
|
3645
4645
|
}
|
|
@@ -3647,16 +4647,19 @@ var copyAndDepopulate = (docObject, operations, options = { mutable: true, idFie
|
|
|
3647
4647
|
} else {
|
|
3648
4648
|
return;
|
|
3649
4649
|
}
|
|
3650
|
-
(0,
|
|
3651
|
-
(0,
|
|
4650
|
+
(0, import_utils18.set)(targetObject, dest, sourceForDest);
|
|
4651
|
+
(0, import_utils18.set)(targetObject, seg, depopulated);
|
|
3652
4652
|
});
|
|
3653
4653
|
} else {
|
|
3654
4654
|
targets = targets.reduce((ret, target) => {
|
|
3655
|
-
if (!(0,
|
|
4655
|
+
if (!(0, import_utils18.isPlainObject)(target)) return ret;
|
|
3656
4656
|
const targetObject = target;
|
|
3657
4657
|
const next = targetObject[seg];
|
|
3658
|
-
if ((0,
|
|
3659
|
-
|
|
4658
|
+
if ((0, import_utils18.isArray)(next)) {
|
|
4659
|
+
for (const item of next) {
|
|
4660
|
+
ret.push(item);
|
|
4661
|
+
}
|
|
4662
|
+
} else if (next !== null && next !== void 0) ret.push(next);
|
|
3660
4663
|
return ret;
|
|
3661
4664
|
}, []);
|
|
3662
4665
|
}
|
|
@@ -3664,6 +4667,125 @@ var copyAndDepopulate = (docObject, operations, options = { mutable: true, idFie
|
|
|
3664
4667
|
});
|
|
3665
4668
|
return obj;
|
|
3666
4669
|
};
|
|
4670
|
+
var copyPaths = (docObject, operations, options = { mutable: true }) => {
|
|
4671
|
+
const mutable = options.mutable !== false;
|
|
4672
|
+
const obj = mutable ? docObject : (0, import_utils18.cloneDeep)(docObject);
|
|
4673
|
+
(0, import_utils18.forEach)(Array.isArray(operations) ? operations : [], (op) => {
|
|
4674
|
+
if (!op || typeof op.src !== "string" || typeof op.dest !== "string") return;
|
|
4675
|
+
const src = op.src;
|
|
4676
|
+
const dest = op.dest;
|
|
4677
|
+
if (src.length === 0 || dest.length === 0) return;
|
|
4678
|
+
assertSafePath(src, "copyPaths");
|
|
4679
|
+
assertSafePath(dest, "copyPaths");
|
|
4680
|
+
if (src === dest) return;
|
|
4681
|
+
(0, import_utils18.forEach)(resolveLeafParents(obj, src), ({ parent, leaf }) => {
|
|
4682
|
+
if (!(0, import_utils18.hasOwn)(parent, leaf)) return;
|
|
4683
|
+
(0, import_utils18.set)(parent, dest, (0, import_utils18.cloneDeep)(parent[leaf]));
|
|
4684
|
+
});
|
|
4685
|
+
});
|
|
4686
|
+
return obj;
|
|
4687
|
+
};
|
|
4688
|
+
var movePaths = (docObject, operations, options = { mutable: true }) => {
|
|
4689
|
+
const mutable = options.mutable !== false;
|
|
4690
|
+
const obj = mutable ? docObject : (0, import_utils18.cloneDeep)(docObject);
|
|
4691
|
+
(0, import_utils18.forEach)(Array.isArray(operations) ? operations : [], (op) => {
|
|
4692
|
+
if (!op || typeof op.src !== "string" || typeof op.dest !== "string") return;
|
|
4693
|
+
const src = op.src;
|
|
4694
|
+
const dest = op.dest;
|
|
4695
|
+
if (src.length === 0 || dest.length === 0) return;
|
|
4696
|
+
assertSafePath(src, "movePaths");
|
|
4697
|
+
assertSafePath(dest, "movePaths");
|
|
4698
|
+
if (src === dest) return;
|
|
4699
|
+
(0, import_utils18.forEach)(resolveLeafParents(obj, src), ({ parent, leaf }) => {
|
|
4700
|
+
if (!(0, import_utils18.hasOwn)(parent, leaf)) return;
|
|
4701
|
+
const value = (0, import_utils18.cloneDeep)(parent[leaf]);
|
|
4702
|
+
(0, import_utils18.set)(parent, dest, value);
|
|
4703
|
+
if (dest === leaf || dest.startsWith(`${leaf}.`)) return;
|
|
4704
|
+
delete parent[leaf];
|
|
4705
|
+
});
|
|
4706
|
+
});
|
|
4707
|
+
return obj;
|
|
4708
|
+
};
|
|
4709
|
+
var sliceArrays = (docObject, operations, options = { mutable: true, maxSlice: DEFAULT_MAX_SLICE }) => {
|
|
4710
|
+
const mutable = options.mutable !== false;
|
|
4711
|
+
const maxSlice = options.maxSlice ?? DEFAULT_MAX_SLICE;
|
|
4712
|
+
assertNonNegativeSafeInteger(maxSlice, "maxSlice", "sliceArrays");
|
|
4713
|
+
const obj = mutable ? docObject : (0, import_utils18.cloneDeep)(docObject);
|
|
4714
|
+
(0, import_utils18.forEach)(Array.isArray(operations) ? operations : [], (op) => {
|
|
4715
|
+
if (!op || typeof op.src !== "string") return;
|
|
4716
|
+
const src = op.src;
|
|
4717
|
+
if (src.length === 0) return;
|
|
4718
|
+
assertSafePath(src, "sliceArrays");
|
|
4719
|
+
const rawDest = typeof op.dest === "undefined" ? void 0 : op.dest;
|
|
4720
|
+
if (typeof rawDest !== "undefined") {
|
|
4721
|
+
if (typeof rawDest !== "string" || rawDest.length === 0) return;
|
|
4722
|
+
assertSafePath(rawDest, "sliceArrays");
|
|
4723
|
+
}
|
|
4724
|
+
const skip = op.skip ?? 0;
|
|
4725
|
+
assertNonNegativeSafeInteger(skip, "skip", "sliceArrays");
|
|
4726
|
+
let limit;
|
|
4727
|
+
if (typeof op.limit !== "undefined") {
|
|
4728
|
+
assertNonNegativeSafeInteger(op.limit, "limit", "sliceArrays");
|
|
4729
|
+
limit = Math.min(op.limit, maxSlice);
|
|
4730
|
+
}
|
|
4731
|
+
(0, import_utils18.forEach)(resolveLeafParents(obj, src), ({ parent, leaf }) => {
|
|
4732
|
+
const leafValue = parent[leaf];
|
|
4733
|
+
if (!(0, import_utils18.isArray)(leafValue)) return;
|
|
4734
|
+
const end = typeof limit === "undefined" ? void 0 : skip + limit;
|
|
4735
|
+
const window = leafValue.slice(skip, end);
|
|
4736
|
+
if (typeof rawDest === "undefined") {
|
|
4737
|
+
(0, import_utils18.set)(parent, leaf, window);
|
|
4738
|
+
} else {
|
|
4739
|
+
(0, import_utils18.set)(parent, rawDest, window);
|
|
4740
|
+
}
|
|
4741
|
+
});
|
|
4742
|
+
});
|
|
4743
|
+
return obj;
|
|
4744
|
+
};
|
|
4745
|
+
var countPaths = (docObject, operations, options = { mutable: true }) => {
|
|
4746
|
+
const mutable = options.mutable !== false;
|
|
4747
|
+
const obj = mutable ? docObject : (0, import_utils18.cloneDeep)(docObject);
|
|
4748
|
+
(0, import_utils18.forEach)(Array.isArray(operations) ? operations : [], (op) => {
|
|
4749
|
+
if (!op || typeof op.src !== "string" || typeof op.dest !== "string") return;
|
|
4750
|
+
const src = op.src;
|
|
4751
|
+
const dest = op.dest;
|
|
4752
|
+
if (src.length === 0 || dest.length === 0) return;
|
|
4753
|
+
assertSafePath(src, "countPaths");
|
|
4754
|
+
assertSafePath(dest, "countPaths");
|
|
4755
|
+
(0, import_utils18.forEach)(resolveLeafParents(obj, src), ({ parent, leaf }) => {
|
|
4756
|
+
if (!(0, import_utils18.hasOwn)(parent, leaf)) return;
|
|
4757
|
+
const leafValue = parent[leaf];
|
|
4758
|
+
let count;
|
|
4759
|
+
if ((0, import_utils18.isArray)(leafValue)) {
|
|
4760
|
+
count = leafValue.length;
|
|
4761
|
+
} else if ((0, import_utils18.isPlainObject)(leafValue)) {
|
|
4762
|
+
count = Object.keys(leafValue).length;
|
|
4763
|
+
} else if (leafValue === null || typeof leafValue === "undefined") {
|
|
4764
|
+
count = 0;
|
|
4765
|
+
} else {
|
|
4766
|
+
count = 1;
|
|
4767
|
+
}
|
|
4768
|
+
(0, import_utils18.set)(parent, dest, count);
|
|
4769
|
+
});
|
|
4770
|
+
});
|
|
4771
|
+
return obj;
|
|
4772
|
+
};
|
|
4773
|
+
var maskPaths = (docObject, operations, options = { mutable: true }) => {
|
|
4774
|
+
const mutable = options.mutable !== false;
|
|
4775
|
+
const obj = mutable ? docObject : (0, import_utils18.cloneDeep)(docObject);
|
|
4776
|
+
(0, import_utils18.forEach)(Array.isArray(operations) ? operations : [], (op) => {
|
|
4777
|
+
if (!op || typeof op.src !== "string") return;
|
|
4778
|
+
const src = op.src;
|
|
4779
|
+
if (src.length === 0) return;
|
|
4780
|
+
assertSafePath(src, "maskPaths");
|
|
4781
|
+
const replacement = Object.prototype.hasOwnProperty.call(op, "replacement") ? op.replacement : DEFAULT_MASK_REPLACEMENT;
|
|
4782
|
+
(0, import_utils18.forEach)(resolveLeafParents(obj, src), ({ parent, leaf }) => {
|
|
4783
|
+
if (!(0, import_utils18.hasOwn)(parent, leaf)) return;
|
|
4784
|
+
(0, import_utils18.set)(parent, leaf, replacement);
|
|
4785
|
+
});
|
|
4786
|
+
});
|
|
4787
|
+
return obj;
|
|
4788
|
+
};
|
|
3667
4789
|
|
|
3668
4790
|
// src/symbols.ts
|
|
3669
4791
|
var MIDDLEWARE = /* @__PURE__ */ Symbol.for("@web-ts-toolkit/access-router/middleware");
|
|
@@ -3703,10 +4825,10 @@ var Cache = class {
|
|
|
3703
4825
|
};
|
|
3704
4826
|
|
|
3705
4827
|
// src/core-shared.ts
|
|
3706
|
-
var
|
|
4828
|
+
var import_utils20 = require("@web-ts-toolkit/utils");
|
|
3707
4829
|
|
|
3708
4830
|
// src/permission.ts
|
|
3709
|
-
var
|
|
4831
|
+
var import_utils19 = require("@web-ts-toolkit/utils");
|
|
3710
4832
|
var Permission = class {
|
|
3711
4833
|
constructor(permissions) {
|
|
3712
4834
|
this.permissions = { ...permissions };
|
|
@@ -3722,7 +4844,7 @@ var Permission = class {
|
|
|
3722
4844
|
return [permissionOrPermissions].concat(permissions);
|
|
3723
4845
|
}
|
|
3724
4846
|
hasKey(permission) {
|
|
3725
|
-
return (0,
|
|
4847
|
+
return (0, import_utils19.hasOwn)(this.permissions, permission);
|
|
3726
4848
|
}
|
|
3727
4849
|
has(permission) {
|
|
3728
4850
|
return this.permissions[permission] === true;
|
|
@@ -3742,10 +4864,10 @@ var permission_default = Permission;
|
|
|
3742
4864
|
|
|
3743
4865
|
// src/core-shared.ts
|
|
3744
4866
|
function isAndFilter(filter2) {
|
|
3745
|
-
return !!filter2 && (0,
|
|
4867
|
+
return !!filter2 && (0, import_utils20.isPlainObject)(filter2) && Object.keys(filter2).length === 1 && (0, import_utils20.isArray)(filter2.$and);
|
|
3746
4868
|
}
|
|
3747
4869
|
function isMergeableClause(filter2) {
|
|
3748
|
-
return !!filter2 && (0,
|
|
4870
|
+
return !!filter2 && (0, import_utils20.isPlainObject)(filter2) && Object.keys(filter2).every((key) => !key.startsWith("$"));
|
|
3749
4871
|
}
|
|
3750
4872
|
function optimizeAndFilter(clausesInput) {
|
|
3751
4873
|
const clauses = [];
|
|
@@ -3760,10 +4882,10 @@ function optimizeAndFilter(clausesInput) {
|
|
|
3760
4882
|
}
|
|
3761
4883
|
}
|
|
3762
4884
|
const dedupeKey = (value) => {
|
|
3763
|
-
if ((0,
|
|
4885
|
+
if ((0, import_utils20.isArray)(value)) {
|
|
3764
4886
|
return `[${value.map(dedupeKey).join(",")}]`;
|
|
3765
4887
|
}
|
|
3766
|
-
if ((0,
|
|
4888
|
+
if ((0, import_utils20.isPlainObject)(value)) {
|
|
3767
4889
|
return `{${Object.keys(value).sort().map((key) => `${key}:${dedupeKey(value[key])}`).join(",")}}`;
|
|
3768
4890
|
}
|
|
3769
4891
|
return JSON.stringify(value);
|
|
@@ -3787,7 +4909,7 @@ function optimizeAndFilter(clausesInput) {
|
|
|
3787
4909
|
}
|
|
3788
4910
|
let canMerge = true;
|
|
3789
4911
|
for (const [key, value] of Object.entries(clause)) {
|
|
3790
|
-
if ((0,
|
|
4912
|
+
if ((0, import_utils20.hasOwn)(mergedClause, key) && !(0, import_utils20.isEqual)(mergedClause[key], value)) {
|
|
3791
4913
|
canMerge = false;
|
|
3792
4914
|
break;
|
|
3793
4915
|
}
|
|
@@ -3798,24 +4920,24 @@ function optimizeAndFilter(clausesInput) {
|
|
|
3798
4920
|
}
|
|
3799
4921
|
Object.assign(mergedClause, clause);
|
|
3800
4922
|
}
|
|
3801
|
-
const finalClauses = (0,
|
|
4923
|
+
const finalClauses = (0, import_utils20.isEmpty)(mergedClause) ? remainingClauses : [mergedClause, ...remainingClauses];
|
|
3802
4924
|
if (finalClauses.length === 0) return null;
|
|
3803
4925
|
if (finalClauses.length === 1) return finalClauses[0];
|
|
3804
4926
|
return { $and: finalClauses };
|
|
3805
4927
|
}
|
|
3806
4928
|
function normalizeFilter(filter2) {
|
|
3807
4929
|
if (filter2 === false) return false;
|
|
3808
|
-
if (!filter2 || !(0,
|
|
4930
|
+
if (!filter2 || !(0, import_utils20.isPlainObject)(filter2) || (0, import_utils20.isEmpty)(filter2)) return null;
|
|
3809
4931
|
if (!isAndFilter(filter2)) {
|
|
3810
4932
|
return filter2;
|
|
3811
4933
|
}
|
|
3812
4934
|
return optimizeAndFilter(filter2.$and);
|
|
3813
4935
|
}
|
|
3814
4936
|
async function resolveIdentifierFilter(req, idField, resolveIdFilter, id) {
|
|
3815
|
-
if ((0,
|
|
4937
|
+
if ((0, import_utils20.isFunction)(resolveIdFilter)) {
|
|
3816
4938
|
return await resolveIdFilter.call(req, id);
|
|
3817
4939
|
}
|
|
3818
|
-
if ((0,
|
|
4940
|
+
if ((0, import_utils20.isString)(idField)) {
|
|
3819
4941
|
return { [idField]: id };
|
|
3820
4942
|
}
|
|
3821
4943
|
return { _id: id };
|
|
@@ -3831,11 +4953,12 @@ async function resolveAccessFilter({
|
|
|
3831
4953
|
}) {
|
|
3832
4954
|
let nextFilter = normalizeFilter(filter2);
|
|
3833
4955
|
const overrideFilterFn = getOption(`overrideFilter.${access}`, null);
|
|
3834
|
-
if ((0,
|
|
4956
|
+
if (nextFilter !== false && (0, import_utils20.isFunction)(overrideFilterFn)) {
|
|
3835
4957
|
nextFilter = normalizeFilter(await overrideFilterFn.call(req, nextFilter, permissions));
|
|
3836
4958
|
}
|
|
4959
|
+
if (nextFilter === false) return false;
|
|
3837
4960
|
const baseFilterFn = getOption(`baseFilter.${access}`, null);
|
|
3838
|
-
if (!(0,
|
|
4961
|
+
if (!(0, import_utils20.isFunction)(baseFilterFn)) return nextFilter ?? {};
|
|
3839
4962
|
const baseFilter = normalizeFilter(
|
|
3840
4963
|
cache.has(cacheKey) ? cache.get(cacheKey) : await baseFilterFn.call(req, permissions)
|
|
3841
4964
|
);
|
|
@@ -3843,7 +4966,7 @@ async function resolveAccessFilter({
|
|
|
3843
4966
|
cache.set(cacheKey, baseFilter);
|
|
3844
4967
|
}
|
|
3845
4968
|
if (baseFilter === false) return false;
|
|
3846
|
-
if (!baseFilter) return nextFilter
|
|
4969
|
+
if (!baseFilter) return nextFilter ?? {};
|
|
3847
4970
|
if (!nextFilter) return baseFilter;
|
|
3848
4971
|
return optimizeAndFilter([baseFilter, nextFilter]);
|
|
3849
4972
|
}
|
|
@@ -3851,37 +4974,52 @@ function getRequestPermissions(req) {
|
|
|
3851
4974
|
const requestPermissionField = getGlobalOption("requestPermissionField");
|
|
3852
4975
|
return new permission_default(req[requestPermissionField] || {});
|
|
3853
4976
|
}
|
|
4977
|
+
var permissionFieldOwners = /* @__PURE__ */ new WeakMap();
|
|
4978
|
+
function getPermissionFieldOwnerMap(req) {
|
|
4979
|
+
let owners = permissionFieldOwners.get(req);
|
|
4980
|
+
if (!owners) {
|
|
4981
|
+
owners = /* @__PURE__ */ new Map();
|
|
4982
|
+
permissionFieldOwners.set(req, owners);
|
|
4983
|
+
}
|
|
4984
|
+
return owners;
|
|
4985
|
+
}
|
|
3854
4986
|
async function setRequestPermissions(req) {
|
|
3855
4987
|
const requestPermissionField = getGlobalOption("requestPermissionField");
|
|
3856
|
-
|
|
4988
|
+
const activeRuntime = getActiveRuntime();
|
|
4989
|
+
const owners = getPermissionFieldOwnerMap(req);
|
|
4990
|
+
const owner = owners.get(requestPermissionField);
|
|
4991
|
+
const hasFieldValue = Boolean(req[requestPermissionField]);
|
|
4992
|
+
if (owner === activeRuntime) return;
|
|
4993
|
+
if (hasFieldValue && owner === void 0) return;
|
|
3857
4994
|
const globalPermissions = getGlobalOption("globalPermissions");
|
|
3858
|
-
if (!(0,
|
|
4995
|
+
if (!(0, import_utils20.isFunction)(globalPermissions)) return;
|
|
3859
4996
|
const permissions = await globalPermissions.call(req, req);
|
|
3860
|
-
if ((0,
|
|
3861
|
-
else if ((0,
|
|
3862
|
-
else if ((0,
|
|
4997
|
+
if ((0, import_utils20.isPlainObject)(permissions)) req[requestPermissionField] = permissions;
|
|
4998
|
+
else if ((0, import_utils20.isArray)(permissions)) req[requestPermissionField] = (0, import_utils20.arrayToRecord)(permissions);
|
|
4999
|
+
else if ((0, import_utils20.isString)(permissions)) req[requestPermissionField] = { [permissions]: true };
|
|
5000
|
+
owners.set(requestPermissionField, activeRuntime);
|
|
3863
5001
|
}
|
|
3864
5002
|
async function evaluateRouteGuard(req, permissions, routeGuard) {
|
|
3865
5003
|
const phas = (key) => permissions.has(key);
|
|
3866
5004
|
const { stringHandler, arrayHandler } = createValidator(phas);
|
|
3867
|
-
if ((0,
|
|
5005
|
+
if ((0, import_utils20.isBoolean)(routeGuard)) {
|
|
3868
5006
|
return routeGuard === true;
|
|
3869
5007
|
}
|
|
3870
|
-
if ((0,
|
|
5008
|
+
if ((0, import_utils20.isString)(routeGuard)) {
|
|
3871
5009
|
return stringHandler(routeGuard);
|
|
3872
5010
|
}
|
|
3873
|
-
if ((0,
|
|
5011
|
+
if ((0, import_utils20.isArray)(routeGuard)) {
|
|
3874
5012
|
return arrayHandler(routeGuard);
|
|
3875
5013
|
}
|
|
3876
|
-
if ((0,
|
|
5014
|
+
if ((0, import_utils20.isFunction)(routeGuard)) {
|
|
3877
5015
|
return routeGuard.call(req, permissions);
|
|
3878
5016
|
}
|
|
3879
5017
|
return false;
|
|
3880
5018
|
}
|
|
3881
5019
|
async function callHookChain(req, hook, doc, permissions, context) {
|
|
3882
|
-
const hooks = (0,
|
|
5020
|
+
const hooks = (0, import_utils20.castArray)(hook);
|
|
3883
5021
|
for (let x = 0; x < hooks.length; x++) {
|
|
3884
|
-
if ((0,
|
|
5022
|
+
if ((0, import_utils20.isFunction)(hooks[x])) {
|
|
3885
5023
|
doc = await hooks[x].call(req, doc, permissions, context);
|
|
3886
5024
|
}
|
|
3887
5025
|
}
|
|
@@ -3904,13 +5042,13 @@ async function collectSchemaFields({
|
|
|
3904
5042
|
if (baseFields.includes(key)) continue;
|
|
3905
5043
|
const val = permissionSchema[key];
|
|
3906
5044
|
const value = val && val[access] || val;
|
|
3907
|
-
if ((0,
|
|
5045
|
+
if ((0, import_utils20.isBoolean)(value)) {
|
|
3908
5046
|
if (value) fields.push(key);
|
|
3909
|
-
} else if ((0,
|
|
5047
|
+
} else if ((0, import_utils20.isString)(value)) {
|
|
3910
5048
|
if (stringHandler(value)) fields.push(key);
|
|
3911
|
-
} else if ((0,
|
|
5049
|
+
} else if ((0, import_utils20.isArray)(value)) {
|
|
3912
5050
|
if (arrayHandler(value)) fields.push(key);
|
|
3913
|
-
} else if ((0,
|
|
5051
|
+
} else if ((0, import_utils20.isFunction)(value)) {
|
|
3914
5052
|
if (await value.call(req, ...functionArgs)) fields.push(key);
|
|
3915
5053
|
}
|
|
3916
5054
|
}
|
|
@@ -3927,17 +5065,14 @@ function getResolvedRequestPermissions(req) {
|
|
|
3927
5065
|
async function setResolvedRequestPermissions(req) {
|
|
3928
5066
|
await setRequestPermissions(req);
|
|
3929
5067
|
}
|
|
3930
|
-
async function canActivateRequest(req, routeGuard) {
|
|
3931
|
-
const permissions = getGlobalPermissions(req);
|
|
3932
|
-
return evaluateRouteGuard(req, permissions, routeGuard);
|
|
3933
|
-
}
|
|
3934
5068
|
async function initializeAclRequest({
|
|
3935
5069
|
req,
|
|
3936
5070
|
flag,
|
|
5071
|
+
runtime,
|
|
3937
5072
|
createCore,
|
|
3938
5073
|
assignCore
|
|
3939
5074
|
}) {
|
|
3940
|
-
if (req[flag]) {
|
|
5075
|
+
if (req[flag] === runtime) {
|
|
3941
5076
|
return null;
|
|
3942
5077
|
}
|
|
3943
5078
|
const core = createCore(req);
|
|
@@ -3945,7 +5080,7 @@ async function initializeAclRequest({
|
|
|
3945
5080
|
assignCore(core);
|
|
3946
5081
|
req[PERMISSIONS] = core.getPermissions();
|
|
3947
5082
|
req[PERMISSION_KEYS] = [...req[PERMISSIONS].keys];
|
|
3948
|
-
req[flag] =
|
|
5083
|
+
req[flag] = runtime;
|
|
3949
5084
|
return core;
|
|
3950
5085
|
}
|
|
3951
5086
|
|
|
@@ -3999,7 +5134,7 @@ async function runDecorateAllHook({
|
|
|
3999
5134
|
}
|
|
4000
5135
|
|
|
4001
5136
|
// src/acl/select-resolution.ts
|
|
4002
|
-
var
|
|
5137
|
+
var import_utils21 = require("@web-ts-toolkit/utils");
|
|
4003
5138
|
async function collectAllowedFieldsForRequest({
|
|
4004
5139
|
req,
|
|
4005
5140
|
permissionSchema,
|
|
@@ -4067,20 +5202,21 @@ async function resolveSelectForRequest({
|
|
|
4067
5202
|
const excludeall = normalizedSelect.every((v) => v.startsWith("-"));
|
|
4068
5203
|
if (excludeall) {
|
|
4069
5204
|
normalizedSelect = normalizedSelect.map((v) => v.substring(1));
|
|
4070
|
-
fields = (0,
|
|
5205
|
+
fields = (0, import_utils21.difference)(fields, normalizedSelect);
|
|
4071
5206
|
if (excludeid) fields.push("-_id");
|
|
4072
5207
|
} else {
|
|
4073
|
-
fields = (0,
|
|
5208
|
+
fields = (0, import_utils21.intersection)(normalizedSelect, fields.concat(excludeid ? "-_id" : "_id"));
|
|
4074
5209
|
}
|
|
4075
5210
|
}
|
|
4076
5211
|
return fields.concat(alwaysSelectFields);
|
|
4077
5212
|
}
|
|
4078
|
-
return (0,
|
|
5213
|
+
return (0, import_utils21.intersection)(normalizedSelect, fields);
|
|
4079
5214
|
}
|
|
4080
5215
|
|
|
4081
5216
|
// src/core.ts
|
|
4082
5217
|
var Core = class {
|
|
4083
5218
|
constructor(req) {
|
|
5219
|
+
this.cachedPermissions = null;
|
|
4084
5220
|
this.req = req;
|
|
4085
5221
|
this.caches = {
|
|
4086
5222
|
baseFilter: new Cache()
|
|
@@ -4089,10 +5225,10 @@ var Core = class {
|
|
|
4089
5225
|
getIdentifier(modelName) {
|
|
4090
5226
|
const resolveIdFilter = getModelOption(modelName, "resolveIdFilter");
|
|
4091
5227
|
const idField = getModelOption(modelName, "idField");
|
|
4092
|
-
if ((0,
|
|
5228
|
+
if ((0, import_utils22.isFunction)(resolveIdFilter)) {
|
|
4093
5229
|
return null;
|
|
4094
5230
|
}
|
|
4095
|
-
if ((0,
|
|
5231
|
+
if ((0, import_utils22.isString)(idField)) {
|
|
4096
5232
|
return idField;
|
|
4097
5233
|
}
|
|
4098
5234
|
return "_id";
|
|
@@ -4170,11 +5306,11 @@ var Core = class {
|
|
|
4170
5306
|
async genPopulate(modelName, access = "read", _populate = null, subPaths = []) {
|
|
4171
5307
|
if (!_populate) return [];
|
|
4172
5308
|
let populate = Array.isArray(_populate) ? _populate : [_populate];
|
|
4173
|
-
populate = (0,
|
|
5309
|
+
populate = (0, import_utils22.compact)(
|
|
4174
5310
|
await Promise.all(
|
|
4175
5311
|
populate.map(async (p) => {
|
|
4176
|
-
const populateAccess = !(0,
|
|
4177
|
-
const ret = (0,
|
|
5312
|
+
const populateAccess = !(0, import_utils22.isString)(p) && p.access ? p.access : access;
|
|
5313
|
+
const ret = (0, import_utils22.isString)(p) ? { path: p } : {
|
|
4178
5314
|
path: p.path,
|
|
4179
5315
|
select: normalizeSelect(p.select)
|
|
4180
5316
|
};
|
|
@@ -4212,10 +5348,10 @@ var Core = class {
|
|
|
4212
5348
|
}
|
|
4213
5349
|
async validate(modelName, allowedData, access, context) {
|
|
4214
5350
|
const validate = getModelOption(modelName, `validate.${access}`, null);
|
|
4215
|
-
if ((0,
|
|
5351
|
+
if ((0, import_utils22.isFunction)(validate)) {
|
|
4216
5352
|
const permissions = this.getGlobalPermissions();
|
|
4217
5353
|
return validate.call(this.req, allowedData, permissions, context);
|
|
4218
|
-
} else if ((0,
|
|
5354
|
+
} else if ((0, import_utils22.isBoolean)(validate) || (0, import_utils22.isArray)(validate)) {
|
|
4219
5355
|
return validate;
|
|
4220
5356
|
} else {
|
|
4221
5357
|
return true;
|
|
@@ -4240,7 +5376,7 @@ var Core = class {
|
|
|
4240
5376
|
const changeOptions = getModelOption(modelName, `onChange`, {});
|
|
4241
5377
|
for (let x = 0; x < context.modifiedPaths.length; x++) {
|
|
4242
5378
|
const mpath = context.modifiedPaths[x];
|
|
4243
|
-
if ((0,
|
|
5379
|
+
if ((0, import_utils22.isFunction)(changeOptions[mpath])) {
|
|
4244
5380
|
await changeOptions[mpath].call(
|
|
4245
5381
|
this.req,
|
|
4246
5382
|
context.originalDocumentSnapshot[mpath],
|
|
@@ -4264,7 +5400,7 @@ var Core = class {
|
|
|
4264
5400
|
async genDocPermissions(modelName, doc, access, context) {
|
|
4265
5401
|
const docPermissionsFn = getModelOption(modelName, `docPermissions.${access}`, null);
|
|
4266
5402
|
let docPermissions = {};
|
|
4267
|
-
if ((0,
|
|
5403
|
+
if ((0, import_utils22.isFunction)(docPermissionsFn)) {
|
|
4268
5404
|
const permissions = this.getGlobalPermissions();
|
|
4269
5405
|
try {
|
|
4270
5406
|
docPermissions = await docPermissionsFn.call(this.req, doc, permissions, context);
|
|
@@ -4315,7 +5451,7 @@ var Core = class {
|
|
|
4315
5451
|
readExists ? this.genAllowedFields(modelName, doc, "read") : [],
|
|
4316
5452
|
updateExists ? this.genAllowedFields(modelName, doc, "update") : []
|
|
4317
5453
|
]);
|
|
4318
|
-
const viewObj = (0,
|
|
5454
|
+
const viewObj = (0, import_utils22.reduce)(
|
|
4319
5455
|
views,
|
|
4320
5456
|
(ret, view) => {
|
|
4321
5457
|
ret[view] = true;
|
|
@@ -4323,7 +5459,7 @@ var Core = class {
|
|
|
4323
5459
|
},
|
|
4324
5460
|
{}
|
|
4325
5461
|
);
|
|
4326
|
-
const editObj = (0,
|
|
5462
|
+
const editObj = (0, import_utils22.reduce)(
|
|
4327
5463
|
edits,
|
|
4328
5464
|
(ret, view) => {
|
|
4329
5465
|
ret[view] = true;
|
|
@@ -4347,9 +5483,9 @@ var Core = class {
|
|
|
4347
5483
|
return runDecorateAllHook({ req: this.req, hook: decorateAll, docs, permissions, context });
|
|
4348
5484
|
}
|
|
4349
5485
|
runTasks(modelName, docObject, task) {
|
|
4350
|
-
const tasks = (0,
|
|
5486
|
+
const tasks = (0, import_utils22.compact)((0, import_utils22.castArray)(task));
|
|
4351
5487
|
if (tasks.length === 0) return docObject;
|
|
4352
|
-
(0,
|
|
5488
|
+
(0, import_utils22.forEach)(tasks, (task2) => {
|
|
4353
5489
|
const { type, args, options } = task2;
|
|
4354
5490
|
switch (type) {
|
|
4355
5491
|
case "COPY_AND_DEPOPULATE":
|
|
@@ -4359,18 +5495,55 @@ var Core = class {
|
|
|
4359
5495
|
options
|
|
4360
5496
|
);
|
|
4361
5497
|
break;
|
|
5498
|
+
case "COPY":
|
|
5499
|
+
docObject = copyPaths(
|
|
5500
|
+
docObject,
|
|
5501
|
+
args,
|
|
5502
|
+
options
|
|
5503
|
+
);
|
|
5504
|
+
break;
|
|
5505
|
+
case "MOVE":
|
|
5506
|
+
docObject = movePaths(
|
|
5507
|
+
docObject,
|
|
5508
|
+
args,
|
|
5509
|
+
options
|
|
5510
|
+
);
|
|
5511
|
+
break;
|
|
5512
|
+
case "SLICE":
|
|
5513
|
+
docObject = sliceArrays(
|
|
5514
|
+
docObject,
|
|
5515
|
+
args,
|
|
5516
|
+
options
|
|
5517
|
+
);
|
|
5518
|
+
break;
|
|
5519
|
+
case "COUNT":
|
|
5520
|
+
docObject = countPaths(
|
|
5521
|
+
docObject,
|
|
5522
|
+
args,
|
|
5523
|
+
options
|
|
5524
|
+
);
|
|
5525
|
+
break;
|
|
5526
|
+
case "MASK":
|
|
5527
|
+
docObject = maskPaths(
|
|
5528
|
+
docObject,
|
|
5529
|
+
args,
|
|
5530
|
+
options
|
|
5531
|
+
);
|
|
5532
|
+
break;
|
|
4362
5533
|
}
|
|
4363
5534
|
});
|
|
4364
5535
|
return docObject;
|
|
4365
5536
|
}
|
|
4366
5537
|
getPermissions() {
|
|
5538
|
+
if (this.cachedPermissions) return this.cachedPermissions;
|
|
4367
5539
|
return getResolvedRequestPermissions(this.req);
|
|
4368
5540
|
}
|
|
4369
5541
|
async setPermissions() {
|
|
4370
5542
|
await setResolvedRequestPermissions(this.req);
|
|
5543
|
+
this.cachedPermissions = getResolvedRequestPermissions(this.req);
|
|
4371
5544
|
}
|
|
4372
5545
|
async canActivate(routeGuard) {
|
|
4373
|
-
return
|
|
5546
|
+
return evaluateRouteGuard(this.req, this.getGlobalPermissions(), routeGuard);
|
|
4374
5547
|
}
|
|
4375
5548
|
async isAllowed(modelName, access) {
|
|
4376
5549
|
if (access.startsWith("subs")) {
|
|
@@ -4380,9 +5553,9 @@ var Core = class {
|
|
|
4380
5553
|
}
|
|
4381
5554
|
const [, field, op] = keys2;
|
|
4382
5555
|
const subOption = getExactModelOption(modelName, `operationAccess.${access}`);
|
|
4383
|
-
if ((0,
|
|
5556
|
+
if ((0, import_utils22.isUndefined)(subOption)) {
|
|
4384
5557
|
const subFieldOption = getExactModelOption(modelName, `operationAccess.subs.${field}`);
|
|
4385
|
-
if ((0,
|
|
5558
|
+
if ((0, import_utils22.isUndefined)(subFieldOption)) {
|
|
4386
5559
|
const opOption = getModelOption(modelName, `operationAccess.${op}`);
|
|
4387
5560
|
return this.canActivate(opOption);
|
|
4388
5561
|
}
|
|
@@ -4406,6 +5579,7 @@ var Core = class {
|
|
|
4406
5579
|
return this.getPublicService(modelName);
|
|
4407
5580
|
}
|
|
4408
5581
|
getGlobalPermissions() {
|
|
5582
|
+
if (this.cachedPermissions) return this.cachedPermissions;
|
|
4409
5583
|
return getGlobalPermissions(this.req);
|
|
4410
5584
|
}
|
|
4411
5585
|
};
|
|
@@ -4415,6 +5589,7 @@ var createSetCore = (runtime = defaultRuntime) => {
|
|
|
4415
5589
|
await initializeAclRequest({
|
|
4416
5590
|
req,
|
|
4417
5591
|
flag: MIDDLEWARE,
|
|
5592
|
+
runtime,
|
|
4418
5593
|
createCore: (request) => new Core(request),
|
|
4419
5594
|
assignCore: (core) => {
|
|
4420
5595
|
req.macl = core;
|
|
@@ -4432,25 +5607,25 @@ function guard(condition) {
|
|
|
4432
5607
|
const permissions = req[PERMISSIONS];
|
|
4433
5608
|
let cond = condition;
|
|
4434
5609
|
let phas = (key) => permissions.has(key);
|
|
4435
|
-
if ((0,
|
|
5610
|
+
if ((0, import_utils23.isPlainObject)(condition)) {
|
|
4436
5611
|
const { modelName, id, condition: _cond } = condition;
|
|
4437
5612
|
const svc = req.macl.getPublicService(modelName);
|
|
4438
5613
|
const select = getModelOption(modelName, `alwaysSelectFields.read`, void 0);
|
|
4439
|
-
let _id = (0,
|
|
4440
|
-
if ((0,
|
|
5614
|
+
let _id = (0, import_utils23.isString)(id) ? id : null;
|
|
5615
|
+
if ((0, import_utils23.isPlainObject)(id)) {
|
|
4441
5616
|
const { type = "param", key } = id;
|
|
4442
5617
|
if (type === "param") {
|
|
4443
5618
|
const paramValue = req.params[key];
|
|
4444
|
-
_id = (0,
|
|
5619
|
+
_id = (0, import_utils23.isArray)(paramValue) ? paramValue[0] ?? null : paramValue ?? null;
|
|
4445
5620
|
} else if (type === "query") {
|
|
4446
5621
|
const _qval = req.query[key];
|
|
4447
|
-
if ((0,
|
|
5622
|
+
if ((0, import_utils23.isArray)(_qval)) _id = _qval.length > 0 ? _qval[0] : null;
|
|
4448
5623
|
else _id = _qval;
|
|
4449
5624
|
} else {
|
|
4450
5625
|
_id = null;
|
|
4451
5626
|
}
|
|
4452
5627
|
}
|
|
4453
|
-
if (!(0,
|
|
5628
|
+
if (!(0, import_utils23.isString)(_id) || !_id) {
|
|
4454
5629
|
return next(new import_express_json_router2.default.clientErrors.BadRequestError());
|
|
4455
5630
|
}
|
|
4456
5631
|
const result = await svc._read(_id, { select });
|
|
@@ -4462,11 +5637,11 @@ function guard(condition) {
|
|
|
4462
5637
|
cond = _cond;
|
|
4463
5638
|
}
|
|
4464
5639
|
const { stringHandler, arrayHandler } = createValidator(phas);
|
|
4465
|
-
if ((0,
|
|
5640
|
+
if ((0, import_utils23.isString)(cond)) {
|
|
4466
5641
|
if (stringHandler(cond)) return next();
|
|
4467
|
-
} else if ((0,
|
|
5642
|
+
} else if ((0, import_utils23.isArray)(cond)) {
|
|
4468
5643
|
if (arrayHandler(cond)) return next();
|
|
4469
|
-
} else if ((0,
|
|
5644
|
+
} else if ((0, import_utils23.isFunction)(cond)) {
|
|
4470
5645
|
const result = await cond.call(req, permissions);
|
|
4471
5646
|
if (!!result) return next();
|
|
4472
5647
|
}
|
|
@@ -4479,7 +5654,7 @@ var import_express2 = __toESM(require("express"));
|
|
|
4479
5654
|
|
|
4480
5655
|
// src/routers/model-router.ts
|
|
4481
5656
|
var import_express_json_router5 = __toESM(require("@web-ts-toolkit/express-json-router"));
|
|
4482
|
-
var
|
|
5657
|
+
var import_utils27 = require("@web-ts-toolkit/utils");
|
|
4483
5658
|
|
|
4484
5659
|
// src/routers/router-mutation.ts
|
|
4485
5660
|
var MODEL_BUILD_TIME_OPTION_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -4608,6 +5783,7 @@ var import_express_json_router4 = __toESM(require("@web-ts-toolkit/express-json-
|
|
|
4608
5783
|
|
|
4609
5784
|
// src/validation/common.ts
|
|
4610
5785
|
var import_zod2 = require("zod");
|
|
5786
|
+
var import_utils24 = require("@web-ts-toolkit/utils");
|
|
4611
5787
|
var stringOrStringArray = import_zod2.z.union([import_zod2.z.string(), import_zod2.z.array(import_zod2.z.string())]);
|
|
4612
5788
|
var safeIntegerString = (min, message) => import_zod2.z.string().regex(/^\d+$/, message).refine((value) => Number.isSafeInteger(Number(value)), "Expected a safe integer").refine((value) => Number(value) >= min, message);
|
|
4613
5789
|
var queryBooleanString = import_zod2.z.enum(["true", "false"]);
|
|
@@ -4668,7 +5844,8 @@ var subPopulateSchema = import_zod2.z.union([
|
|
|
4668
5844
|
select: projectionSchema.optional()
|
|
4669
5845
|
}).passthrough()
|
|
4670
5846
|
]);
|
|
4671
|
-
var
|
|
5847
|
+
var legacyIncludeItemSchema = import_zod2.z.object({
|
|
5848
|
+
mode: import_zod2.z.enum(["legacy"]).optional(),
|
|
4672
5849
|
model: import_zod2.z.string().min(1),
|
|
4673
5850
|
op: import_zod2.z.enum(["list", "read", "count"]),
|
|
4674
5851
|
path: import_zod2.z.string().min(1),
|
|
@@ -4677,8 +5854,139 @@ var includeItemSchema = import_zod2.z.object({
|
|
|
4677
5854
|
foreignField: import_zod2.z.string().min(1),
|
|
4678
5855
|
args: import_zod2.z.unknown().optional().describe("Forwarded to list/read includes. Count includes ignore pagination fields so counts remain exact."),
|
|
4679
5856
|
options: import_zod2.z.unknown().optional()
|
|
4680
|
-
}).passthrough()
|
|
4681
|
-
|
|
5857
|
+
}).passthrough().superRefine((entry, ctx) => {
|
|
5858
|
+
if (entry.filter !== void 0 && containsParentMarkerShape(entry.filter)) {
|
|
5859
|
+
ctx.addIssue({
|
|
5860
|
+
code: import_zod2.z.ZodIssueCode.custom,
|
|
5861
|
+
message: 'Legacy includes must not contain $parent markers; use a correlated include (mode: "correlated")',
|
|
5862
|
+
path: ["filter"]
|
|
5863
|
+
});
|
|
5864
|
+
}
|
|
5865
|
+
});
|
|
5866
|
+
var parentRefSchema = import_zod2.z.object({ $parent: import_zod2.z.string().min(1) }).strict().superRefine((value, ctx) => {
|
|
5867
|
+
if (!isValidParentRefPath(value.$parent)) {
|
|
5868
|
+
ctx.addIssue({
|
|
5869
|
+
code: import_zod2.z.ZodIssueCode.custom,
|
|
5870
|
+
message: `Invalid $parent reference path: ${value.$parent}`,
|
|
5871
|
+
path: ["$parent"]
|
|
5872
|
+
});
|
|
5873
|
+
}
|
|
5874
|
+
});
|
|
5875
|
+
var correlatedIdSchema = import_zod2.z.union([import_zod2.z.string().min(1), parentRefSchema]);
|
|
5876
|
+
var correlatedFilterSchema = import_zod2.z.record(import_zod2.z.string(), import_zod2.z.unknown()).superRefine((filter2, ctx) => {
|
|
5877
|
+
for (const issue of validateCorrelatedFilterTemplate(filter2)) {
|
|
5878
|
+
ctx.addIssue({
|
|
5879
|
+
code: import_zod2.z.ZodIssueCode.custom,
|
|
5880
|
+
message: issue.message,
|
|
5881
|
+
path: issue.path.map(String)
|
|
5882
|
+
});
|
|
5883
|
+
}
|
|
5884
|
+
});
|
|
5885
|
+
var correlatedEmptyOptionsSchema = import_zod2.z.unknown().refine(
|
|
5886
|
+
(value) => value === void 0 || (0, import_utils24.isPlainObject)(value) && Object.keys(value).length === 0,
|
|
5887
|
+
"Correlated include options must be absent or empty"
|
|
5888
|
+
).optional();
|
|
5889
|
+
var correlatedPathSchema = import_zod2.z.string().min(1).superRefine((path, ctx) => {
|
|
5890
|
+
if (!isValidCorrelatedOutputPath(path)) {
|
|
5891
|
+
ctx.addIssue({
|
|
5892
|
+
code: import_zod2.z.ZodIssueCode.custom,
|
|
5893
|
+
message: `Invalid correlated include output path: ${path}`,
|
|
5894
|
+
path: []
|
|
5895
|
+
});
|
|
5896
|
+
}
|
|
5897
|
+
});
|
|
5898
|
+
var checkArgsMarkers = (select, sort, ctx) => {
|
|
5899
|
+
const selectIssue = findArgsMarkerIssue(select, "args.select");
|
|
5900
|
+
if (selectIssue) {
|
|
5901
|
+
ctx.addIssue({ code: import_zod2.z.ZodIssueCode.custom, message: selectIssue.message, path: ["select"] });
|
|
5902
|
+
}
|
|
5903
|
+
const sortIssue = findArgsMarkerIssue(sort, "args.sort");
|
|
5904
|
+
if (sortIssue) {
|
|
5905
|
+
ctx.addIssue({ code: import_zod2.z.ZodIssueCode.custom, message: sortIssue.message, path: ["sort"] });
|
|
5906
|
+
}
|
|
5907
|
+
};
|
|
5908
|
+
var correlatedReadArgsSchema = import_zod2.z.object({
|
|
5909
|
+
select: projectionSchema.optional(),
|
|
5910
|
+
sort: sortSchema.optional(),
|
|
5911
|
+
include: import_zod2.z.lazy(() => correlatedNestedIncludeSchema).optional()
|
|
5912
|
+
}).strict().superRefine((args, ctx) => checkArgsMarkers(args.select, args.sort, ctx));
|
|
5913
|
+
var correlatedListArgsSchema = import_zod2.z.object({
|
|
5914
|
+
select: projectionSchema.optional(),
|
|
5915
|
+
sort: sortSchema.optional(),
|
|
5916
|
+
skip: import_zod2.z.union([nonNegativeIntegerSchema, nonNegativeIntegerString]).optional(),
|
|
5917
|
+
limit: import_zod2.z.union([positiveIntegerSchema, positiveIntegerString]).optional(),
|
|
5918
|
+
page: import_zod2.z.union([nonNegativeIntegerSchema, nonNegativeIntegerString]).optional(),
|
|
5919
|
+
pageSize: import_zod2.z.union([positiveIntegerSchema, positiveIntegerString]).optional(),
|
|
5920
|
+
include: import_zod2.z.lazy(() => correlatedNestedIncludeSchema).optional()
|
|
5921
|
+
}).strict().superRefine((args, ctx) => checkArgsMarkers(args.select, args.sort, ctx));
|
|
5922
|
+
var correlatedCountArgsSchema = import_zod2.z.unknown().refine(
|
|
5923
|
+
(value) => value === void 0 || (0, import_utils24.isPlainObject)(value) && Object.keys(value).length === 0,
|
|
5924
|
+
"Count includes accept no args"
|
|
5925
|
+
).optional();
|
|
5926
|
+
var correlatedReadByIdSchema = import_zod2.z.object({
|
|
5927
|
+
mode: import_zod2.z.literal("correlated"),
|
|
5928
|
+
model: import_zod2.z.string().min(1),
|
|
5929
|
+
op: import_zod2.z.literal("read"),
|
|
5930
|
+
path: correlatedPathSchema,
|
|
5931
|
+
id: correlatedIdSchema,
|
|
5932
|
+
args: correlatedReadArgsSchema.optional(),
|
|
5933
|
+
options: correlatedEmptyOptionsSchema
|
|
5934
|
+
}).strict();
|
|
5935
|
+
var correlatedReadByFilterSchema = import_zod2.z.object({
|
|
5936
|
+
mode: import_zod2.z.literal("correlated"),
|
|
5937
|
+
model: import_zod2.z.string().min(1),
|
|
5938
|
+
op: import_zod2.z.literal("read"),
|
|
5939
|
+
path: correlatedPathSchema,
|
|
5940
|
+
filter: correlatedFilterSchema,
|
|
5941
|
+
args: correlatedReadArgsSchema.optional(),
|
|
5942
|
+
options: correlatedEmptyOptionsSchema
|
|
5943
|
+
}).strict();
|
|
5944
|
+
var correlatedListSchema = import_zod2.z.object({
|
|
5945
|
+
mode: import_zod2.z.literal("correlated"),
|
|
5946
|
+
model: import_zod2.z.string().min(1),
|
|
5947
|
+
op: import_zod2.z.literal("list"),
|
|
5948
|
+
path: correlatedPathSchema,
|
|
5949
|
+
filter: correlatedFilterSchema,
|
|
5950
|
+
args: correlatedListArgsSchema.optional(),
|
|
5951
|
+
options: correlatedEmptyOptionsSchema
|
|
5952
|
+
}).strict();
|
|
5953
|
+
var correlatedCountSchema = import_zod2.z.object({
|
|
5954
|
+
mode: import_zod2.z.literal("correlated"),
|
|
5955
|
+
model: import_zod2.z.string().min(1),
|
|
5956
|
+
op: import_zod2.z.literal("count"),
|
|
5957
|
+
path: correlatedPathSchema,
|
|
5958
|
+
filter: correlatedFilterSchema,
|
|
5959
|
+
args: correlatedCountArgsSchema,
|
|
5960
|
+
options: correlatedEmptyOptionsSchema
|
|
5961
|
+
}).strict();
|
|
5962
|
+
var includeItemSchema = import_zod2.z.union([
|
|
5963
|
+
legacyIncludeItemSchema,
|
|
5964
|
+
correlatedReadByIdSchema,
|
|
5965
|
+
correlatedReadByFilterSchema,
|
|
5966
|
+
correlatedListSchema,
|
|
5967
|
+
correlatedCountSchema
|
|
5968
|
+
]);
|
|
5969
|
+
var correlatedNestedIncludeSchema = import_zod2.z.union([includeItemSchema, import_zod2.z.array(includeItemSchema)]);
|
|
5970
|
+
var rejectDuplicateCorrelatedPaths = (items, ctx) => {
|
|
5971
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5972
|
+
items.forEach((item, index) => {
|
|
5973
|
+
if ((0, import_utils24.isPlainObject)(item) && item.mode === "correlated" && typeof item.path === "string") {
|
|
5974
|
+
if (seen.has(item.path)) {
|
|
5975
|
+
ctx.addIssue({
|
|
5976
|
+
code: import_zod2.z.ZodIssueCode.custom,
|
|
5977
|
+
message: `Duplicate correlated include output path: ${item.path}`,
|
|
5978
|
+
path: [index, "path"]
|
|
5979
|
+
});
|
|
5980
|
+
} else {
|
|
5981
|
+
seen.add(item.path);
|
|
5982
|
+
}
|
|
5983
|
+
}
|
|
5984
|
+
});
|
|
5985
|
+
};
|
|
5986
|
+
var includeSchema = import_zod2.z.union([
|
|
5987
|
+
includeItemSchema,
|
|
5988
|
+
import_zod2.z.array(includeItemSchema).superRefine(rejectDuplicateCorrelatedPaths)
|
|
5989
|
+
]);
|
|
4682
5990
|
var fieldsSchema = import_zod2.z.array(import_zod2.z.string().min(1));
|
|
4683
5991
|
var taskSchema = import_zod2.z.object({
|
|
4684
5992
|
type: import_zod2.z.string().min(1),
|
|
@@ -4795,8 +6103,9 @@ function defineRequestSchema(validator, options = {}) {
|
|
|
4795
6103
|
};
|
|
4796
6104
|
}
|
|
4797
6105
|
function fromZod(schema) {
|
|
4798
|
-
return (value) => {
|
|
4799
|
-
const
|
|
6106
|
+
return async (value) => {
|
|
6107
|
+
const candidate = schema;
|
|
6108
|
+
const result = typeof candidate.safeParseAsync === "function" ? await candidate.safeParseAsync.call(schema, value) : schema.safeParse(value);
|
|
4800
6109
|
if (result.success) {
|
|
4801
6110
|
return {
|
|
4802
6111
|
success: true,
|
|
@@ -4833,6 +6142,9 @@ function fromYup(schema) {
|
|
|
4833
6142
|
data
|
|
4834
6143
|
};
|
|
4835
6144
|
} catch (error2) {
|
|
6145
|
+
if (!isYupValidationError(error2)) {
|
|
6146
|
+
throw error2;
|
|
6147
|
+
}
|
|
4836
6148
|
return {
|
|
4837
6149
|
success: false,
|
|
4838
6150
|
issues: normalizeYupIssues(error2)
|
|
@@ -4857,8 +6169,32 @@ function fromJoi(schema) {
|
|
|
4857
6169
|
}
|
|
4858
6170
|
function fromAjv(validator) {
|
|
4859
6171
|
return async (value) => {
|
|
4860
|
-
const
|
|
4861
|
-
if (
|
|
6172
|
+
const outcome = validator(value);
|
|
6173
|
+
if (isThenable(outcome)) {
|
|
6174
|
+
try {
|
|
6175
|
+
const data = await outcome;
|
|
6176
|
+
if (data === false) {
|
|
6177
|
+
return {
|
|
6178
|
+
success: false,
|
|
6179
|
+
issues: normalizeAjvIssues(validator.errors ?? [])
|
|
6180
|
+
};
|
|
6181
|
+
}
|
|
6182
|
+
return {
|
|
6183
|
+
success: true,
|
|
6184
|
+
data: data === true ? value : data
|
|
6185
|
+
};
|
|
6186
|
+
} catch (error2) {
|
|
6187
|
+
if (!isAjvValidationError(error2)) {
|
|
6188
|
+
throw error2;
|
|
6189
|
+
}
|
|
6190
|
+
return {
|
|
6191
|
+
success: false,
|
|
6192
|
+
issues: normalizeAjvIssues(error2.errors ?? [])
|
|
6193
|
+
};
|
|
6194
|
+
}
|
|
6195
|
+
}
|
|
6196
|
+
const errorsSnapshot = validator.errors ? [...validator.errors] : [];
|
|
6197
|
+
if (outcome) {
|
|
4862
6198
|
return {
|
|
4863
6199
|
success: true,
|
|
4864
6200
|
data: value
|
|
@@ -4866,7 +6202,7 @@ function fromAjv(validator) {
|
|
|
4866
6202
|
}
|
|
4867
6203
|
return {
|
|
4868
6204
|
success: false,
|
|
4869
|
-
issues: normalizeAjvIssues(
|
|
6205
|
+
issues: normalizeAjvIssues(errorsSnapshot)
|
|
4870
6206
|
};
|
|
4871
6207
|
};
|
|
4872
6208
|
}
|
|
@@ -4887,6 +6223,25 @@ function fromValibot(schema, safeParse) {
|
|
|
4887
6223
|
}
|
|
4888
6224
|
function fromArkType(type) {
|
|
4889
6225
|
return async (value) => {
|
|
6226
|
+
if (isStandardSchema(type)) {
|
|
6227
|
+
const result2 = await type["~standard"].validate(value);
|
|
6228
|
+
if (isArkTypeErrors(result2)) {
|
|
6229
|
+
return {
|
|
6230
|
+
success: false,
|
|
6231
|
+
issues: normalizeArkTypeIssues(result2)
|
|
6232
|
+
};
|
|
6233
|
+
}
|
|
6234
|
+
if (isStandardSchemaFailure(result2)) {
|
|
6235
|
+
return {
|
|
6236
|
+
success: false,
|
|
6237
|
+
issues: normalizeArkTypeStandardIssues(result2.issues)
|
|
6238
|
+
};
|
|
6239
|
+
}
|
|
6240
|
+
return {
|
|
6241
|
+
success: true,
|
|
6242
|
+
data: result2.value
|
|
6243
|
+
};
|
|
6244
|
+
}
|
|
4890
6245
|
const result = await type(value);
|
|
4891
6246
|
if (!isArkTypeErrors(result)) {
|
|
4892
6247
|
return {
|
|
@@ -4939,6 +6294,9 @@ function fromVine(validator) {
|
|
|
4939
6294
|
data: output
|
|
4940
6295
|
};
|
|
4941
6296
|
} catch (error2) {
|
|
6297
|
+
if (!isVineValidationError(error2)) {
|
|
6298
|
+
throw error2;
|
|
6299
|
+
}
|
|
4942
6300
|
return {
|
|
4943
6301
|
success: false,
|
|
4944
6302
|
issues: normalizeVineError(error2)
|
|
@@ -4999,6 +6357,13 @@ function normalizeArkTypeIssues(issues) {
|
|
|
4999
6357
|
}));
|
|
5000
6358
|
return normalized.length ? normalized : [{ message: issues.summary ?? "Validation failed" }];
|
|
5001
6359
|
}
|
|
6360
|
+
function normalizeArkTypeStandardIssues(issues) {
|
|
6361
|
+
const normalized = issues.map((issue) => ({
|
|
6362
|
+
message: issue.message ?? issue.problem ?? "Validation failed",
|
|
6363
|
+
path: issue.path?.flatMap((segment) => normalizePathSegment(segment))
|
|
6364
|
+
}));
|
|
6365
|
+
return normalized.length ? normalized : [{ message: "Validation failed" }];
|
|
6366
|
+
}
|
|
5002
6367
|
function normalizeIoTsIssues(issues) {
|
|
5003
6368
|
return issues.map((issue) => ({
|
|
5004
6369
|
message: issue.message ?? "Validation failed",
|
|
@@ -5033,25 +6398,64 @@ function normalizeVineField(message) {
|
|
|
5033
6398
|
}
|
|
5034
6399
|
return path.length ? path : void 0;
|
|
5035
6400
|
}
|
|
6401
|
+
function decodeAjvSegment(segment) {
|
|
6402
|
+
return segment.replace(/~1/g, "/").replace(/~0/g, "~");
|
|
6403
|
+
}
|
|
6404
|
+
function encodePointerSegment(segment) {
|
|
6405
|
+
return encodeURIComponent(segment.replace(/~/g, "~0").replace(/\//g, "~1"));
|
|
6406
|
+
}
|
|
5036
6407
|
function parseAjvPath(issue) {
|
|
5037
|
-
const
|
|
5038
|
-
|
|
5039
|
-
|
|
6408
|
+
const raw = issue.instancePath;
|
|
6409
|
+
const base = raw == null || raw === "" ? [] : (raw.startsWith("/") ? raw.slice(1) : raw).split("/").map(decodeAjvSegment);
|
|
6410
|
+
const missing = issue.params?.missingProperty;
|
|
6411
|
+
if (typeof missing === "string") {
|
|
6412
|
+
return [...base, missing];
|
|
5040
6413
|
}
|
|
5041
|
-
return
|
|
6414
|
+
return base;
|
|
5042
6415
|
}
|
|
5043
6416
|
function parsePathString(path) {
|
|
5044
6417
|
if (!path) return void 0;
|
|
5045
6418
|
return path.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean).map((segment) => /^\d+$/.test(segment) ? Number(segment) : segment);
|
|
5046
6419
|
}
|
|
5047
6420
|
function isYupValidationError(error2) {
|
|
5048
|
-
|
|
6421
|
+
if (typeof error2 !== "object" || error2 === null || !("message" in error2)) {
|
|
6422
|
+
return false;
|
|
6423
|
+
}
|
|
6424
|
+
const candidate = error2;
|
|
6425
|
+
return candidate.name === "ValidationError" && typeof candidate.message === "string" && Array.isArray(candidate.errors) && Array.isArray(candidate.inner);
|
|
5049
6426
|
}
|
|
5050
6427
|
function isArkTypeErrors(value) {
|
|
5051
|
-
|
|
6428
|
+
if (typeof value !== "object" || value === null) {
|
|
6429
|
+
return false;
|
|
6430
|
+
}
|
|
6431
|
+
const record = value;
|
|
6432
|
+
return record.arkKind === "errors" || record[" arkKind"] === "errors";
|
|
6433
|
+
}
|
|
6434
|
+
function isThenable(value) {
|
|
6435
|
+
return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
|
|
6436
|
+
}
|
|
6437
|
+
function isAjvValidationError(error2) {
|
|
6438
|
+
if (typeof error2 !== "object" || error2 === null || !("errors" in error2)) {
|
|
6439
|
+
return false;
|
|
6440
|
+
}
|
|
6441
|
+
const errors = error2.errors;
|
|
6442
|
+
return errors == null || Array.isArray(errors);
|
|
5052
6443
|
}
|
|
5053
6444
|
function isVineValidationError(error2) {
|
|
5054
|
-
|
|
6445
|
+
if (typeof error2 !== "object" || error2 === null || !("messages" in error2)) {
|
|
6446
|
+
return false;
|
|
6447
|
+
}
|
|
6448
|
+
const candidate = error2;
|
|
6449
|
+
if (!Array.isArray(candidate.messages) || candidate.messages.length === 0) {
|
|
6450
|
+
return false;
|
|
6451
|
+
}
|
|
6452
|
+
const hasDiscriminator = candidate.code === "E_VALIDATION_ERROR" || candidate.name === "ValidationError";
|
|
6453
|
+
if (!hasDiscriminator) {
|
|
6454
|
+
return false;
|
|
6455
|
+
}
|
|
6456
|
+
return candidate.messages.every(
|
|
6457
|
+
(message) => typeof message === "object" && message !== null && typeof message.message === "string" && typeof message.field === "string"
|
|
6458
|
+
);
|
|
5055
6459
|
}
|
|
5056
6460
|
function formatIssue(issue, key, location = "pointer") {
|
|
5057
6461
|
const path = (issue.path ?? []).map(String);
|
|
@@ -5068,7 +6472,7 @@ function formatIssue(issue, key, location = "pointer") {
|
|
|
5068
6472
|
};
|
|
5069
6473
|
}
|
|
5070
6474
|
function buildPointer(path) {
|
|
5071
|
-
return path.length === 0 ? "#" : `#/${path.join("/")}`;
|
|
6475
|
+
return path.length === 0 ? "#" : `#/${path.map(encodePointerSegment).join("/")}`;
|
|
5072
6476
|
}
|
|
5073
6477
|
|
|
5074
6478
|
// src/validation/model-router.ts
|
|
@@ -5119,7 +6523,7 @@ var listBodySchema = import_zod3.z.object({
|
|
|
5119
6523
|
}).passthrough().superRefine((body, ctx) => rejectKeys(body, ctx, ["query"]));
|
|
5120
6524
|
var countBodySchema = import_zod3.z.object({
|
|
5121
6525
|
filter: objectOrArraySchema.optional()
|
|
5122
|
-
}).passthrough().superRefine((body, ctx) => rejectKeys(body, ctx, ["query", "access", "options"]));
|
|
6526
|
+
}).passthrough().superRefine((body, ctx) => rejectKeys(body, ctx, ["query", "access", "options", "include"]));
|
|
5123
6527
|
var readFilterBodySchema = import_zod3.z.object({
|
|
5124
6528
|
filter: objectOrArraySchema.optional(),
|
|
5125
6529
|
select: projectionSchema.optional(),
|
|
@@ -5474,7 +6878,7 @@ var rootDataQueryEntrySchema = import_zod5.z.union([
|
|
|
5474
6878
|
var rootQuerySchema = import_zod5.z.array(import_zod5.z.union([rootModelQueryEntrySchema, rootDataQueryEntrySchema]));
|
|
5475
6879
|
|
|
5476
6880
|
// src/routers/shared.ts
|
|
5477
|
-
var
|
|
6881
|
+
var import_utils25 = require("@web-ts-toolkit/utils");
|
|
5478
6882
|
|
|
5479
6883
|
// src/routers/model-router-collection-routes.ts
|
|
5480
6884
|
function setModelCollectionRoutes(context) {
|
|
@@ -5492,14 +6896,14 @@ function setModelCollectionRoutes(context) {
|
|
|
5492
6896
|
await context.assertAllowed(req, "list");
|
|
5493
6897
|
const { skip, limit, page, page_size, skim, include_permissions, include_count, include_extra_headers } = parseQuery(requestSchemas.listQuery, req.query);
|
|
5494
6898
|
const svc = context.getPublicService(req);
|
|
5495
|
-
const includeCount = (0,
|
|
5496
|
-
const includeExtraHeaders = (0,
|
|
6899
|
+
const includeCount = (0, import_utils25.parseBooleanString)(include_count);
|
|
6900
|
+
const includeExtraHeaders = (0, import_utils25.parseBooleanString)(include_extra_headers);
|
|
5497
6901
|
const result = await svc._list(
|
|
5498
6902
|
{},
|
|
5499
6903
|
{ skip, limit, page, pageSize: page_size },
|
|
5500
6904
|
{
|
|
5501
|
-
skim: (0,
|
|
5502
|
-
includePermissions: (0,
|
|
6905
|
+
skim: (0, import_utils25.parseBooleanString)(skim),
|
|
6906
|
+
includePermissions: (0, import_utils25.parseBooleanString)(include_permissions),
|
|
5503
6907
|
includeCount
|
|
5504
6908
|
}
|
|
5505
6909
|
);
|
|
@@ -5554,7 +6958,7 @@ function setModelCollectionRoutes(context) {
|
|
|
5554
6958
|
context.getRequestSchema("requestSchemas.create")
|
|
5555
6959
|
);
|
|
5556
6960
|
const svc = context.getPublicService(req);
|
|
5557
|
-
const result = await svc._create(data, {}, { includePermissions: (0,
|
|
6961
|
+
const result = await svc._create(data, {}, { includePermissions: (0, import_utils25.parseBooleanString)(include_permissions) });
|
|
5558
6962
|
handleResultError(result);
|
|
5559
6963
|
return formatModelCreatedResponse(result);
|
|
5560
6964
|
});
|
|
@@ -5569,26 +6973,28 @@ function setModelCollectionRoutes(context) {
|
|
|
5569
6973
|
router.post(`/${options.mutationRouteSegment}`, async (req) => {
|
|
5570
6974
|
await context.assertAllowed(req, "create");
|
|
5571
6975
|
const { include_permissions } = parseQuery(requestSchemas.createQuery, req.query);
|
|
5572
|
-
const
|
|
6976
|
+
const intermediateBody = await parseNestedBodyWithSchema(
|
|
5573
6977
|
advancedCreateBodySchema,
|
|
5574
6978
|
req.body,
|
|
5575
6979
|
"data",
|
|
5576
6980
|
context.getRequestSchema("requestSchemas.advancedCreate.data")
|
|
5577
6981
|
);
|
|
5578
|
-
const { data, select, populate, tasks } =
|
|
5579
|
-
const
|
|
5580
|
-
await parseBodyWithSchema(
|
|
6982
|
+
const { data: nestedData, select: nestedSelect, populate: nestedPopulate, tasks: nestedTasks } = intermediateBody;
|
|
6983
|
+
const nestedOptions = intermediateBody.options ?? {};
|
|
6984
|
+
const finalBody = await parseBodyWithSchema(
|
|
5581
6985
|
advancedCreateBodySchema,
|
|
5582
|
-
{ data, select, populate, tasks, options:
|
|
6986
|
+
{ data: nestedData, select: nestedSelect, populate: nestedPopulate, tasks: nestedTasks, options: nestedOptions },
|
|
5583
6987
|
context.getRequestSchema("requestSchemas.advancedCreate.default") ?? context.getRequestSchema("requestSchemas.advancedCreate")
|
|
5584
6988
|
);
|
|
5585
|
-
const {
|
|
6989
|
+
const { data: finalData, select: finalSelect, populate: finalPopulate, tasks: finalTasks } = finalBody;
|
|
6990
|
+
const finalOptions = finalBody.options ?? {};
|
|
6991
|
+
const { includePermissions, populateAccess } = finalOptions;
|
|
5586
6992
|
const svc = context.getPublicService(req);
|
|
5587
6993
|
const result = await svc._create(
|
|
5588
|
-
|
|
5589
|
-
{ select, populate, tasks },
|
|
6994
|
+
finalData,
|
|
6995
|
+
{ select: finalSelect, populate: finalPopulate, tasks: finalTasks },
|
|
5590
6996
|
{
|
|
5591
|
-
includePermissions: includePermissions ?? (0,
|
|
6997
|
+
includePermissions: includePermissions ?? (0, import_utils25.parseBooleanString)(include_permissions),
|
|
5592
6998
|
populateAccess
|
|
5593
6999
|
}
|
|
5594
7000
|
);
|
|
@@ -5680,8 +7086,8 @@ function setModelDocumentRoutes(context) {
|
|
|
5680
7086
|
id,
|
|
5681
7087
|
{},
|
|
5682
7088
|
{
|
|
5683
|
-
includePermissions: (0,
|
|
5684
|
-
tryList: (0,
|
|
7089
|
+
includePermissions: (0, import_utils25.parseBooleanString)(include_permissions),
|
|
7090
|
+
tryList: (0, import_utils25.parseBooleanString)(try_list)
|
|
5685
7091
|
}
|
|
5686
7092
|
);
|
|
5687
7093
|
handleResultError(result);
|
|
@@ -5766,8 +7172,8 @@ function setModelDocumentRoutes(context) {
|
|
|
5766
7172
|
data,
|
|
5767
7173
|
{},
|
|
5768
7174
|
{
|
|
5769
|
-
returningAll: (0,
|
|
5770
|
-
includePermissions: (0,
|
|
7175
|
+
returningAll: (0, import_utils25.parseBooleanString)(returning_all),
|
|
7176
|
+
includePermissions: (0, import_utils25.parseBooleanString)(include_permissions)
|
|
5771
7177
|
}
|
|
5772
7178
|
);
|
|
5773
7179
|
handleResultError(result);
|
|
@@ -5785,28 +7191,36 @@ function setModelDocumentRoutes(context) {
|
|
|
5785
7191
|
await context.assertAllowed(req, "update");
|
|
5786
7192
|
const id = parsePathParam(req.params[options.idParam], options.idParam);
|
|
5787
7193
|
const { returning_all, include_permissions } = parseQuery(requestSchemas.updateQuery, req.query);
|
|
5788
|
-
const
|
|
7194
|
+
const intermediateBody = await parseNestedBodyWithSchema(
|
|
5789
7195
|
advancedUpdateBodySchema,
|
|
5790
7196
|
req.body,
|
|
5791
7197
|
"data",
|
|
5792
7198
|
context.getRequestSchema("requestSchemas.advancedUpdate.data")
|
|
5793
7199
|
);
|
|
5794
|
-
const { data, select, populate, tasks } =
|
|
5795
|
-
const
|
|
5796
|
-
await parseBodyWithSchema(
|
|
7200
|
+
const { data: nestedData, select: nestedSelect, populate: nestedPopulate, tasks: nestedTasks } = intermediateBody;
|
|
7201
|
+
const nestedOptions = intermediateBody.options ?? {};
|
|
7202
|
+
const finalBody = await parseBodyWithSchema(
|
|
5797
7203
|
advancedUpdateBodySchema,
|
|
5798
|
-
{
|
|
7204
|
+
{
|
|
7205
|
+
data: nestedData,
|
|
7206
|
+
select: nestedSelect,
|
|
7207
|
+
populate: nestedPopulate,
|
|
7208
|
+
tasks: nestedTasks,
|
|
7209
|
+
options: nestedOptions
|
|
7210
|
+
},
|
|
5799
7211
|
context.getRequestSchema("requestSchemas.advancedUpdate.default") ?? context.getRequestSchema("requestSchemas.advancedUpdate")
|
|
5800
7212
|
);
|
|
5801
|
-
const {
|
|
7213
|
+
const { data: finalData, select: finalSelect, populate: finalPopulate, tasks: finalTasks } = finalBody;
|
|
7214
|
+
const finalOptions = finalBody.options ?? {};
|
|
7215
|
+
const { returningAll, includePermissions, populateAccess } = finalOptions;
|
|
5802
7216
|
const svc = context.getPublicService(req);
|
|
5803
7217
|
const result = await svc._update(
|
|
5804
7218
|
id,
|
|
5805
|
-
|
|
5806
|
-
{ select, populate, tasks },
|
|
7219
|
+
finalData,
|
|
7220
|
+
{ select: finalSelect, populate: finalPopulate, tasks: finalTasks },
|
|
5807
7221
|
{
|
|
5808
|
-
returningAll: returningAll ?? (0,
|
|
5809
|
-
includePermissions: includePermissions ?? (0,
|
|
7222
|
+
returningAll: returningAll ?? (0, import_utils25.parseBooleanString)(returning_all),
|
|
7223
|
+
includePermissions: includePermissions ?? (0, import_utils25.parseBooleanString)(include_permissions),
|
|
5810
7224
|
populateAccess
|
|
5811
7225
|
}
|
|
5812
7226
|
);
|
|
@@ -5834,8 +7248,8 @@ function setModelDocumentRoutes(context) {
|
|
|
5834
7248
|
body,
|
|
5835
7249
|
{},
|
|
5836
7250
|
{
|
|
5837
|
-
returningAll: (0,
|
|
5838
|
-
includePermissions: (0,
|
|
7251
|
+
returningAll: (0, import_utils25.parseBooleanString)(returning_all),
|
|
7252
|
+
includePermissions: (0, import_utils25.parseBooleanString)(include_permissions)
|
|
5839
7253
|
}
|
|
5840
7254
|
);
|
|
5841
7255
|
handleResultError(result);
|
|
@@ -5853,26 +7267,34 @@ function setModelDocumentRoutes(context) {
|
|
|
5853
7267
|
await context.assertAllowed(req, "upsert");
|
|
5854
7268
|
const svc = context.getPublicService(req);
|
|
5855
7269
|
const { returning_all, include_permissions } = parseQuery(requestSchemas.upsertQuery, req.query);
|
|
5856
|
-
const
|
|
7270
|
+
const intermediateBody = await parseNestedBodyWithSchema(
|
|
5857
7271
|
advancedUpsertBodySchema,
|
|
5858
7272
|
req.body,
|
|
5859
7273
|
"data",
|
|
5860
7274
|
context.getRequestSchema("requestSchemas.advancedUpsert.data")
|
|
5861
7275
|
);
|
|
5862
|
-
const { data, select, populate, tasks } =
|
|
5863
|
-
const
|
|
5864
|
-
await parseBodyWithSchema(
|
|
7276
|
+
const { data: nestedData, select: nestedSelect, populate: nestedPopulate, tasks: nestedTasks } = intermediateBody;
|
|
7277
|
+
const nestedOptions = intermediateBody.options ?? {};
|
|
7278
|
+
const finalBody = await parseBodyWithSchema(
|
|
5865
7279
|
advancedUpsertBodySchema,
|
|
5866
|
-
{
|
|
7280
|
+
{
|
|
7281
|
+
data: nestedData,
|
|
7282
|
+
select: nestedSelect,
|
|
7283
|
+
populate: nestedPopulate,
|
|
7284
|
+
tasks: nestedTasks,
|
|
7285
|
+
options: nestedOptions
|
|
7286
|
+
},
|
|
5867
7287
|
context.getRequestSchema("requestSchemas.advancedUpsert.default") ?? context.getRequestSchema("requestSchemas.advancedUpsert")
|
|
5868
7288
|
);
|
|
5869
|
-
const {
|
|
7289
|
+
const { data: finalData, select: finalSelect, populate: finalPopulate, tasks: finalTasks } = finalBody;
|
|
7290
|
+
const finalOptions = finalBody.options ?? {};
|
|
7291
|
+
const { returningAll, includePermissions, populateAccess } = finalOptions;
|
|
5870
7292
|
const result = await svc._upsert(
|
|
5871
|
-
|
|
5872
|
-
{ select, populate, tasks },
|
|
7293
|
+
finalData,
|
|
7294
|
+
{ select: finalSelect, populate: finalPopulate, tasks: finalTasks },
|
|
5873
7295
|
{
|
|
5874
|
-
returningAll: returningAll ?? (0,
|
|
5875
|
-
includePermissions: includePermissions ?? (0,
|
|
7296
|
+
returningAll: returningAll ?? (0, import_utils25.parseBooleanString)(returning_all),
|
|
7297
|
+
includePermissions: includePermissions ?? (0, import_utils25.parseBooleanString)(include_permissions),
|
|
5876
7298
|
populateAccess
|
|
5877
7299
|
}
|
|
5878
7300
|
);
|
|
@@ -5939,7 +7361,7 @@ function setModelDocumentRoutes(context) {
|
|
|
5939
7361
|
|
|
5940
7362
|
// src/routers/model-router-subdocument-routes.ts
|
|
5941
7363
|
function setModelSubDocumentRoutes(context) {
|
|
5942
|
-
const subs = getModelSub(context.modelName);
|
|
7364
|
+
const subs = context.runtime.getModelSub(context.modelName);
|
|
5943
7365
|
for (let x = 0; x < subs.length; x++) {
|
|
5944
7366
|
const sub = subs[x];
|
|
5945
7367
|
context.router.get(`/:${context.options.idParam}/${sub}`, async (req) => {
|
|
@@ -6110,11 +7532,11 @@ function setModelSubDocumentRoutes(context) {
|
|
|
6110
7532
|
}
|
|
6111
7533
|
|
|
6112
7534
|
// src/openapi/route-registration.ts
|
|
6113
|
-
var
|
|
7535
|
+
var import_utils26 = require("@web-ts-toolkit/utils");
|
|
6114
7536
|
function registerOpenApiRoute(runtime, basePath, defaultTag, route) {
|
|
6115
7537
|
runtime.registerOpenApiRoute({
|
|
6116
7538
|
...route,
|
|
6117
|
-
path: (0,
|
|
7539
|
+
path: (0, import_utils26.normalizeUrlPath)(basePath + route.path),
|
|
6118
7540
|
tags: route.tags ?? [defaultTag]
|
|
6119
7541
|
});
|
|
6120
7542
|
}
|
|
@@ -6122,8 +7544,8 @@ function registerOpenApiRoute(runtime, basePath, defaultTag, route) {
|
|
|
6122
7544
|
// src/routers/model-router.ts
|
|
6123
7545
|
var clientErrors2 = import_express_json_router5.default.clientErrors;
|
|
6124
7546
|
function setOption(parentKey, optionKey, option) {
|
|
6125
|
-
const key = (0,
|
|
6126
|
-
const value = (0,
|
|
7547
|
+
const key = (0, import_utils27.isUndefined)(option) ? parentKey : `${parentKey}.${optionKey}`;
|
|
7548
|
+
const value = (0, import_utils27.isUndefined)(option) ? optionKey : option;
|
|
6127
7549
|
assertMutableRouterOption("model", key);
|
|
6128
7550
|
this.runtime.setModelOption(this.modelName, key, value);
|
|
6129
7551
|
return this;
|
|
@@ -6164,7 +7586,9 @@ var ModelRouter = class _ModelRouter {
|
|
|
6164
7586
|
"baseFilter"
|
|
6165
7587
|
);
|
|
6166
7588
|
/**
|
|
6167
|
-
*
|
|
7589
|
+
* Trusted filter replacements applied before base-filter composition.
|
|
7590
|
+
* They may replace ordinary filters, but cannot replace an existing `false` denial.
|
|
7591
|
+
* Returning `false` denies the transaction.
|
|
6168
7592
|
* @operation `list`, `read`, `update`, `delete`
|
|
6169
7593
|
*/
|
|
6170
7594
|
this.overrideFilter = setOption.bind(this, "overrideFilter");
|
|
@@ -6283,7 +7707,7 @@ var ModelRouter = class _ModelRouter {
|
|
|
6283
7707
|
this.runtime = runtime;
|
|
6284
7708
|
this.runtime.setModelOptions(modelName, initialOptions);
|
|
6285
7709
|
this.options = this.runtime.getModelOptions(modelName);
|
|
6286
|
-
this.fullBasePath = (0,
|
|
7710
|
+
this.fullBasePath = (0, import_utils27.normalizeUrlPath)(this.options.parentPath + this.options.basePath);
|
|
6287
7711
|
this.modelName = modelName;
|
|
6288
7712
|
this.router = new import_express_json_router5.default(this.options.basePath, createSetCore(this.runtime), accessRouterResponseHandler);
|
|
6289
7713
|
this.model = new model_default(modelName, this.runtime);
|
|
@@ -6324,6 +7748,7 @@ var ModelRouter = class _ModelRouter {
|
|
|
6324
7748
|
modelName: this.modelName,
|
|
6325
7749
|
router: this.router,
|
|
6326
7750
|
options: this.options,
|
|
7751
|
+
runtime: this.runtime,
|
|
6327
7752
|
getRequestSchema: this.getRequestSchema.bind(this),
|
|
6328
7753
|
getPublicService: this.getPublicService.bind(this),
|
|
6329
7754
|
assertAllowed: this.assertAllowed.bind(this),
|
|
@@ -6338,6 +7763,7 @@ var ModelRouter = class _ModelRouter {
|
|
|
6338
7763
|
modelName: this.modelName,
|
|
6339
7764
|
router: this.router,
|
|
6340
7765
|
options: this.options,
|
|
7766
|
+
runtime: this.runtime,
|
|
6341
7767
|
getRequestSchema: this.getRequestSchema.bind(this),
|
|
6342
7768
|
getPublicService: this.getPublicService.bind(this),
|
|
6343
7769
|
assertAllowed: this.assertAllowed.bind(this),
|
|
@@ -6352,6 +7778,7 @@ var ModelRouter = class _ModelRouter {
|
|
|
6352
7778
|
modelName: this.modelName,
|
|
6353
7779
|
router: this.router,
|
|
6354
7780
|
options: this.options,
|
|
7781
|
+
runtime: this.runtime,
|
|
6355
7782
|
getRequestSchema: this.getRequestSchema.bind(this),
|
|
6356
7783
|
getPublicService: this.getPublicService.bind(this),
|
|
6357
7784
|
assertAllowed: this.assertAllowed.bind(this),
|
|
@@ -6360,13 +7787,13 @@ var ModelRouter = class _ModelRouter {
|
|
|
6360
7787
|
}
|
|
6361
7788
|
logEndpoints() {
|
|
6362
7789
|
runWithRuntime(this.runtime, () => {
|
|
6363
|
-
(0,
|
|
6364
|
-
logInfoMessage(`${(0,
|
|
7790
|
+
(0, import_utils27.forEach)(this.router.getEndpoints(), ({ method, path }) => {
|
|
7791
|
+
logInfoMessage(`${(0, import_utils27.padEnd)(method, 6)} ${(0, import_utils27.normalizeUrlPath)(this.options.parentPath + path)}`);
|
|
6365
7792
|
});
|
|
6366
7793
|
});
|
|
6367
7794
|
}
|
|
6368
7795
|
set(keyOrOptions, value) {
|
|
6369
|
-
if (arguments.length === 2 && (0,
|
|
7796
|
+
if (arguments.length === 2 && (0, import_utils27.isString)(keyOrOptions)) {
|
|
6370
7797
|
assertMutableRouterOption("model", keyOrOptions);
|
|
6371
7798
|
this.runtime.setModelOption(
|
|
6372
7799
|
this.modelName,
|
|
@@ -6374,7 +7801,7 @@ var ModelRouter = class _ModelRouter {
|
|
|
6374
7801
|
value
|
|
6375
7802
|
);
|
|
6376
7803
|
}
|
|
6377
|
-
if (arguments.length === 1 && (0,
|
|
7804
|
+
if (arguments.length === 1 && (0, import_utils27.isPlainObject)(keyOrOptions)) {
|
|
6378
7805
|
assertMutableRouterOptions("model", keyOrOptions);
|
|
6379
7806
|
this.runtime.setModelOptions(this.modelName, keyOrOptions);
|
|
6380
7807
|
}
|
|
@@ -6397,11 +7824,12 @@ var ModelRouter = class _ModelRouter {
|
|
|
6397
7824
|
|
|
6398
7825
|
// src/routers/root-router.ts
|
|
6399
7826
|
var import_express_json_router6 = __toESM(require("@web-ts-toolkit/express-json-router"));
|
|
6400
|
-
var
|
|
7827
|
+
var import_utils28 = require("@web-ts-toolkit/utils");
|
|
6401
7828
|
|
|
6402
7829
|
// src/core-data.ts
|
|
6403
7830
|
var DataCore = class {
|
|
6404
7831
|
constructor(req) {
|
|
7832
|
+
this.cachedPermissions = null;
|
|
6405
7833
|
this.req = req;
|
|
6406
7834
|
this.caches = {
|
|
6407
7835
|
baseFilter: new Cache()
|
|
@@ -6495,13 +7923,15 @@ var DataCore = class {
|
|
|
6495
7923
|
});
|
|
6496
7924
|
}
|
|
6497
7925
|
getPermissions() {
|
|
7926
|
+
if (this.cachedPermissions) return this.cachedPermissions;
|
|
6498
7927
|
return getResolvedRequestPermissions(this.req);
|
|
6499
7928
|
}
|
|
6500
7929
|
async setPermissions() {
|
|
6501
7930
|
await setResolvedRequestPermissions(this.req);
|
|
7931
|
+
this.cachedPermissions = getResolvedRequestPermissions(this.req);
|
|
6502
7932
|
}
|
|
6503
7933
|
async canActivate(routeGuard) {
|
|
6504
|
-
return
|
|
7934
|
+
return evaluateRouteGuard(this.req, this.getGlobalPermissions(), routeGuard);
|
|
6505
7935
|
}
|
|
6506
7936
|
async isAllowed(dataName, access) {
|
|
6507
7937
|
const operationAccess = getDataOption(dataName, `operationAccess.${access}`);
|
|
@@ -6517,6 +7947,7 @@ var DataCore = class {
|
|
|
6517
7947
|
return this.getService(dataName);
|
|
6518
7948
|
}
|
|
6519
7949
|
getGlobalPermissions() {
|
|
7950
|
+
if (this.cachedPermissions) return this.cachedPermissions;
|
|
6520
7951
|
return getGlobalPermissions(this.req);
|
|
6521
7952
|
}
|
|
6522
7953
|
};
|
|
@@ -6526,6 +7957,7 @@ var createSetDataCore = (runtime = defaultRuntime) => {
|
|
|
6526
7957
|
await initializeAclRequest({
|
|
6527
7958
|
req,
|
|
6528
7959
|
flag: DATA_MIDDLEWARE,
|
|
7960
|
+
runtime,
|
|
6529
7961
|
createCore: (request) => new DataCore(request),
|
|
6530
7962
|
assignCore: (core) => {
|
|
6531
7963
|
req.dacl = core;
|
|
@@ -6658,7 +8090,7 @@ var RootRouter = class {
|
|
|
6658
8090
|
this.setRoutes();
|
|
6659
8091
|
}
|
|
6660
8092
|
registerOpenApiRoute(route) {
|
|
6661
|
-
registerOpenApiRoute(this.runtime, (0,
|
|
8093
|
+
registerOpenApiRoute(this.runtime, (0, import_utils28.normalizeUrlPath)(this.basename), "root", route);
|
|
6662
8094
|
}
|
|
6663
8095
|
wrapResult(index, item, result) {
|
|
6664
8096
|
return {
|
|
@@ -6719,7 +8151,7 @@ var RootRouter = class {
|
|
|
6719
8151
|
groupItemsByOrder(items) {
|
|
6720
8152
|
const groups = /* @__PURE__ */ new Map();
|
|
6721
8153
|
items.forEach((item, index) => {
|
|
6722
|
-
const order = (0,
|
|
8154
|
+
const order = (0, import_utils28.isNumber)(item.order) ? item.order : 0;
|
|
6723
8155
|
const group = groups.get(order);
|
|
6724
8156
|
if (group) {
|
|
6725
8157
|
group.push({ index, item });
|
|
@@ -6790,7 +8222,7 @@ var RootRouter = class {
|
|
|
6790
8222
|
const arrResult = await this.runGroup(req, groupedItems[x]);
|
|
6791
8223
|
results.push(...arrResult);
|
|
6792
8224
|
}
|
|
6793
|
-
return (0,
|
|
8225
|
+
return (0, import_utils28.orderBy)(results, ["index"], ["asc"]);
|
|
6794
8226
|
});
|
|
6795
8227
|
this.registerOpenApiRoute({
|
|
6796
8228
|
method: "post",
|
|
@@ -6808,11 +8240,11 @@ var RootRouter = class {
|
|
|
6808
8240
|
|
|
6809
8241
|
// src/routers/data-router.ts
|
|
6810
8242
|
var import_express_json_router7 = __toESM(require("@web-ts-toolkit/express-json-router"));
|
|
6811
|
-
var
|
|
8243
|
+
var import_utils29 = require("@web-ts-toolkit/utils");
|
|
6812
8244
|
var clientErrors4 = import_express_json_router7.default.clientErrors;
|
|
6813
8245
|
function setOption2(parentKey, optionKey, option) {
|
|
6814
|
-
const key = (0,
|
|
6815
|
-
const value = (0,
|
|
8246
|
+
const key = (0, import_utils29.isUndefined)(option) ? parentKey : `${parentKey}.${optionKey}`;
|
|
8247
|
+
const value = (0, import_utils29.isUndefined)(option) ? optionKey : option;
|
|
6816
8248
|
assertMutableRouterOption("data", key);
|
|
6817
8249
|
this.runtime.setDataOption(this.dataName, key, value);
|
|
6818
8250
|
return this;
|
|
@@ -6830,6 +8262,11 @@ var DataRouter = class {
|
|
|
6830
8262
|
this,
|
|
6831
8263
|
"baseFilter"
|
|
6832
8264
|
);
|
|
8265
|
+
/**
|
|
8266
|
+
* Trusted filter replacements applied before base-filter composition.
|
|
8267
|
+
* They may replace ordinary filters, but cannot replace an existing `false` denial.
|
|
8268
|
+
* Returning `false` denies the transaction.
|
|
8269
|
+
*/
|
|
6833
8270
|
this.overrideFilter = setOption2.bind(this, "overrideFilter");
|
|
6834
8271
|
this.decorate = setOption2.bind(
|
|
6835
8272
|
this,
|
|
@@ -6848,7 +8285,7 @@ var DataRouter = class {
|
|
|
6848
8285
|
this.runtime = runtime;
|
|
6849
8286
|
this.runtime.setDataOptions(dataName, initialOptions);
|
|
6850
8287
|
this.options = this.runtime.getDataOptions(dataName);
|
|
6851
|
-
this.fullBasePath = (0,
|
|
8288
|
+
this.fullBasePath = (0, import_utils29.normalizeUrlPath)(this.options.parentPath + this.options.basePath);
|
|
6852
8289
|
this.dataName = dataName;
|
|
6853
8290
|
this.router = new import_express_json_router7.default(this.options.basePath, createSetDataCore(this.runtime), accessRouterResponseHandler);
|
|
6854
8291
|
this.setCollectionRoutes();
|
|
@@ -6881,8 +8318,8 @@ var DataRouter = class {
|
|
|
6881
8318
|
req.query
|
|
6882
8319
|
);
|
|
6883
8320
|
const svc = this.getService(req);
|
|
6884
|
-
const includeCount = (0,
|
|
6885
|
-
const includeExtraHeaders = (0,
|
|
8321
|
+
const includeCount = (0, import_utils25.parseBooleanString)(include_count);
|
|
8322
|
+
const includeExtraHeaders = (0, import_utils25.parseBooleanString)(include_extra_headers);
|
|
6886
8323
|
const result = await svc.find(
|
|
6887
8324
|
{},
|
|
6888
8325
|
{ skip, limit, page, pageSize: page_size },
|
|
@@ -7000,11 +8437,11 @@ var DataRouter = class {
|
|
|
7000
8437
|
});
|
|
7001
8438
|
}
|
|
7002
8439
|
set(keyOrOptions, value) {
|
|
7003
|
-
if (arguments.length === 2 && (0,
|
|
8440
|
+
if (arguments.length === 2 && (0, import_utils29.isString)(keyOrOptions)) {
|
|
7004
8441
|
assertMutableRouterOption("data", keyOrOptions);
|
|
7005
8442
|
this.runtime.setDataOption(this.dataName, keyOrOptions, value);
|
|
7006
8443
|
}
|
|
7007
|
-
if (arguments.length === 1 && (0,
|
|
8444
|
+
if (arguments.length === 1 && (0, import_utils29.isPlainObject)(keyOrOptions)) {
|
|
7008
8445
|
assertMutableRouterOptions("data", keyOrOptions);
|
|
7009
8446
|
this.runtime.setDataOptions(this.dataName, keyOrOptions);
|
|
7010
8447
|
}
|
|
@@ -7127,7 +8564,7 @@ function createRuntimeApi(runtime) {
|
|
|
7127
8564
|
const wtt2 = runtimeMiddleware;
|
|
7128
8565
|
wtt2.runtime = runtime;
|
|
7129
8566
|
wtt2.createRouter = function(modelName, options) {
|
|
7130
|
-
if ((0,
|
|
8567
|
+
if ((0, import_utils30.isUndefined)(options)) {
|
|
7131
8568
|
return new RootRouter(modelName, runtime);
|
|
7132
8569
|
}
|
|
7133
8570
|
if (typeof modelName === "string") {
|
|
@@ -7148,10 +8585,10 @@ function createRuntimeApi(runtime) {
|
|
|
7148
8585
|
};
|
|
7149
8586
|
wtt2.combineRoutes = combineRoutes;
|
|
7150
8587
|
wtt2.set = function(keyOrOptions, value) {
|
|
7151
|
-
if (arguments.length === 2 && (0,
|
|
8588
|
+
if (arguments.length === 2 && (0, import_utils30.isString)(keyOrOptions)) {
|
|
7152
8589
|
return runtime.setGlobalOption(keyOrOptions, value);
|
|
7153
8590
|
}
|
|
7154
|
-
if (arguments.length === 1 && (0,
|
|
8591
|
+
if (arguments.length === 1 && (0, import_utils30.isPlainObject)(keyOrOptions)) {
|
|
7155
8592
|
return runtime.setGlobalOptions(keyOrOptions);
|
|
7156
8593
|
}
|
|
7157
8594
|
};
|