@web-ts-toolkit/access-router 0.44.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 +77 -2
- package/advanced.d.mts +655 -19
- package/advanced.d.ts +655 -19
- package/advanced.js +322 -5
- package/advanced.mjs +322 -5
- package/index.d.mts +2 -2
- package/index.d.ts +2 -2
- package/index.js +1448 -197
- package/index.mjs +1323 -70
- package/llms.txt +16 -0
- package/package.json +3 -3
- package/{parsers-B5dbVmjm.d.mts → parsers-DppKW-Fd.d.mts} +79 -5
- package/{parsers-B5dbVmjm.d.ts → parsers-DppKW-Fd.d.ts} +79 -5
- package/processors.d.mts +151 -1
- package/processors.d.ts +151 -1
- package/processors.js +170 -6
- package/processors.mjs +165 -6
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"));
|
|
@@ -1000,7 +1000,9 @@ var defaultRequestComplexity = {
|
|
|
1000
1000
|
maxIncludeCount: 10,
|
|
1001
1001
|
maxSubQueryCount: 10,
|
|
1002
1002
|
maxBulkConcurrency: 10,
|
|
1003
|
-
maxHookConcurrency: 10
|
|
1003
|
+
maxHookConcurrency: 10,
|
|
1004
|
+
maxCorrelatedQueries: 100,
|
|
1005
|
+
maxCorrelatedDepth: 5
|
|
1004
1006
|
};
|
|
1005
1007
|
var normalizePositiveInteger = (value, fallback) => {
|
|
1006
1008
|
return Number.isSafeInteger(value) && Number(value) > 0 ? Number(value) : fallback;
|
|
@@ -1020,6 +1022,14 @@ var resolveRequestComplexity = (options) => ({
|
|
|
1020
1022
|
maxHookConcurrency: normalizePositiveInteger(
|
|
1021
1023
|
options?.maxHookConcurrency,
|
|
1022
1024
|
defaultRequestComplexity.maxHookConcurrency
|
|
1025
|
+
),
|
|
1026
|
+
maxCorrelatedQueries: normalizePositiveInteger(
|
|
1027
|
+
options?.maxCorrelatedQueries,
|
|
1028
|
+
defaultRequestComplexity.maxCorrelatedQueries
|
|
1029
|
+
),
|
|
1030
|
+
maxCorrelatedDepth: normalizePositiveInteger(
|
|
1031
|
+
options?.maxCorrelatedDepth,
|
|
1032
|
+
defaultRequestComplexity.maxCorrelatedDepth
|
|
1023
1033
|
)
|
|
1024
1034
|
});
|
|
1025
1035
|
function validateRequestComplexity(value, options, scope = "request") {
|
|
@@ -1605,7 +1615,463 @@ var getModelRef = (modelName, refPath) => {
|
|
|
1605
1615
|
};
|
|
1606
1616
|
|
|
1607
1617
|
// src/services/base.ts
|
|
1618
|
+
var import_utils13 = require("@web-ts-toolkit/utils");
|
|
1619
|
+
|
|
1620
|
+
// src/correlated-includes.ts
|
|
1608
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
|
+
}
|
|
1609
2075
|
var ClientRequestError = class extends Error {
|
|
1610
2076
|
constructor(result) {
|
|
1611
2077
|
super(String(result.code));
|
|
@@ -1620,11 +2086,11 @@ function validateClientFilter(filter2) {
|
|
|
1620
2086
|
}
|
|
1621
2087
|
const blockedOperators = /* @__PURE__ */ new Set(["$where", "$expr", "$function", "$accumulator"]);
|
|
1622
2088
|
const visit = (value, path) => {
|
|
1623
|
-
if ((0,
|
|
2089
|
+
if ((0, import_utils13.isArray)(value)) {
|
|
1624
2090
|
value.forEach((item, index) => visit(item, `${path}[${index}]`));
|
|
1625
2091
|
return;
|
|
1626
2092
|
}
|
|
1627
|
-
if (!(0,
|
|
2093
|
+
if (!(0, import_utils13.isPlainObject)(value)) return;
|
|
1628
2094
|
Object.entries(value).forEach(([key, child]) => {
|
|
1629
2095
|
const nextPath = path ? `${path}.${key}` : key;
|
|
1630
2096
|
if (blockedOperators.has(key)) {
|
|
@@ -1744,25 +2210,56 @@ var Base = class {
|
|
|
1744
2210
|
return this.req.macl.getPublicService(modelName);
|
|
1745
2211
|
}
|
|
1746
2212
|
processInclude(include) {
|
|
1747
|
-
const
|
|
1748
|
-
|
|
1749
|
-
|
|
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
|
+
}
|
|
1750
2245
|
let includeLocalFields = [];
|
|
1751
2246
|
let includePaths = [];
|
|
1752
|
-
(0,
|
|
2247
|
+
(0, import_utils13.forEach)(legacy, (inc) => {
|
|
1753
2248
|
includeLocalFields.push(inc.localField);
|
|
1754
2249
|
includePaths.push(inc.path);
|
|
1755
2250
|
});
|
|
1756
|
-
includeLocalFields = (0,
|
|
1757
|
-
includePaths = (0,
|
|
2251
|
+
includeLocalFields = (0, import_utils13.uniq)((0, import_utils13.compact)(includeLocalFields));
|
|
2252
|
+
includePaths = (0, import_utils13.uniq)((0, import_utils13.compact)(includePaths));
|
|
1758
2253
|
return {
|
|
1759
|
-
includes,
|
|
2254
|
+
includes: legacy,
|
|
2255
|
+
correlatedIncludes: correlated,
|
|
1760
2256
|
includeLocalFields,
|
|
1761
|
-
includePaths
|
|
2257
|
+
includePaths,
|
|
2258
|
+
correlatedReferenceFields: (0, import_utils13.uniq)((0, import_utils13.compact)(correlatedReferenceFields))
|
|
1762
2259
|
};
|
|
1763
2260
|
}
|
|
1764
2261
|
getForeignKeyValues(value) {
|
|
1765
|
-
return (0,
|
|
2262
|
+
return (0, import_utils13.flatten)((0, import_utils13.castArray)(value));
|
|
1766
2263
|
}
|
|
1767
2264
|
getForeignKey(value) {
|
|
1768
2265
|
return String(value);
|
|
@@ -1770,7 +2267,7 @@ var Base = class {
|
|
|
1770
2267
|
buildForeignKeyIndex(rows, foreignField) {
|
|
1771
2268
|
const index = /* @__PURE__ */ new Map();
|
|
1772
2269
|
for (const row of rows) {
|
|
1773
|
-
for (const value of this.getForeignKeyValues((0,
|
|
2270
|
+
for (const value of this.getForeignKeyValues((0, import_utils13.get)(row, foreignField))) {
|
|
1774
2271
|
const key = this.getForeignKey(value);
|
|
1775
2272
|
const values = index.get(key);
|
|
1776
2273
|
if (values) values.push(row);
|
|
@@ -1801,7 +2298,7 @@ var Base = class {
|
|
|
1801
2298
|
return ids.size;
|
|
1802
2299
|
}
|
|
1803
2300
|
sanitizeIncludeArgs(args) {
|
|
1804
|
-
if (!(0,
|
|
2301
|
+
if (!(0, import_utils13.isPlainObject)(args)) return {};
|
|
1805
2302
|
const { overrides: _clientOverrides, ...trustedArgs } = args;
|
|
1806
2303
|
return trustedArgs;
|
|
1807
2304
|
}
|
|
@@ -1812,12 +2309,18 @@ var Base = class {
|
|
|
1812
2309
|
}
|
|
1813
2310
|
async includeDocs(docs, include) {
|
|
1814
2311
|
if (!include) return docs;
|
|
1815
|
-
const includes = (0,
|
|
2312
|
+
const includes = (0, import_utils13.compact)((0, import_utils13.castArray)(include));
|
|
1816
2313
|
if (includes.length === 0) return docs;
|
|
1817
|
-
const isSingle = !(0,
|
|
2314
|
+
const isSingle = !(0, import_utils13.isArray)(docs);
|
|
1818
2315
|
let docList = isSingle ? [docs] : docs;
|
|
1819
2316
|
for (let x = 0; x < includes.length; x++) {
|
|
1820
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
|
+
}
|
|
1821
2324
|
this.assertIncludeForeignField(include2);
|
|
1822
2325
|
switch (include2.op) {
|
|
1823
2326
|
case "count":
|
|
@@ -1838,7 +2341,7 @@ var Base = class {
|
|
|
1838
2341
|
const svc = await this.getAuthorizedTargetService(model, "read");
|
|
1839
2342
|
for (let y = 0; y < docs.length; y++) {
|
|
1840
2343
|
const doc = docs[y];
|
|
1841
|
-
const localValue = (0,
|
|
2344
|
+
const localValue = (0, import_utils13.get)(doc, localField);
|
|
1842
2345
|
const filter2 = { ..._filters ?? {}, [foreignField]: { $in: this.getForeignKeyValues(localValue) } };
|
|
1843
2346
|
const trustedOptions = {
|
|
1844
2347
|
...options,
|
|
@@ -1857,10 +2360,10 @@ var Base = class {
|
|
|
1857
2360
|
const { model, op, path, localField, foreignField, filter: _filters, args = {}, options = {} } = include;
|
|
1858
2361
|
const svc = await this.getAuthorizedTargetService(model, op);
|
|
1859
2362
|
const includeLocalValues = [];
|
|
1860
|
-
(0,
|
|
1861
|
-
includeLocalValues.push(...this.getForeignKeyValues((0,
|
|
2363
|
+
(0, import_utils13.forEach)(docs, (doc) => {
|
|
2364
|
+
includeLocalValues.push(...this.getForeignKeyValues((0, import_utils13.get)(doc, localField)));
|
|
1862
2365
|
});
|
|
1863
|
-
const filter2 = { ..._filters ?? {}, [foreignField]: { $in: (0,
|
|
2366
|
+
const filter2 = { ..._filters ?? {}, [foreignField]: { $in: (0, import_utils13.flatten)(includeLocalValues) } };
|
|
1864
2367
|
const authorizedFilter = await svc.genFilter(op, filter2);
|
|
1865
2368
|
const trustedArgs = {
|
|
1866
2369
|
...this.sanitizeIncludeArgs(args),
|
|
@@ -1879,7 +2382,7 @@ var Base = class {
|
|
|
1879
2382
|
const index = this.buildForeignKeyIndex(trustedResult.data, foreignField);
|
|
1880
2383
|
for (let y = 0; y < docs.length; y++) {
|
|
1881
2384
|
const doc = docs[y];
|
|
1882
|
-
const matches = this.getIndexedMatches(index, (0,
|
|
2385
|
+
const matches = this.getIndexedMatches(index, (0, import_utils13.get)(doc, localField));
|
|
1883
2386
|
setDocValue(doc, path, op === "list" ? matches : matches[0]);
|
|
1884
2387
|
}
|
|
1885
2388
|
return docs;
|
|
@@ -1887,15 +2390,365 @@ var Base = class {
|
|
|
1887
2390
|
async includeDocsCount(docs, include) {
|
|
1888
2391
|
const { model, path, localField, foreignField, filter: _filters } = include;
|
|
1889
2392
|
const svc = await this.getAuthorizedTargetService(model, "count");
|
|
1890
|
-
const localValues = docs.flatMap((doc) => this.getForeignKeyValues((0,
|
|
2393
|
+
const localValues = docs.flatMap((doc) => this.getForeignKeyValues((0, import_utils13.get)(doc, localField)));
|
|
1891
2394
|
const result = await svc.countByFieldValues(foreignField, localValues, _filters ?? {}, "count");
|
|
1892
2395
|
if (!result.success) return docs;
|
|
1893
2396
|
for (let y = 0; y < docs.length; y++) {
|
|
1894
2397
|
const doc = docs[y];
|
|
1895
|
-
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
|
+
}
|
|
1896
2463
|
}
|
|
1897
2464
|
return docs;
|
|
1898
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
|
+
}
|
|
1899
2752
|
async parseClientData(filter2, scheduler = new RequestConcurrencyScheduler(this.getRequestComplexity().maxBulkConcurrency), scheduled = false) {
|
|
1900
2753
|
const result = await iterateQuery(
|
|
1901
2754
|
filter2,
|
|
@@ -1938,10 +2791,10 @@ var Base = class {
|
|
|
1938
2791
|
}
|
|
1939
2792
|
let ret = result.data;
|
|
1940
2793
|
if (sqOptions.path) {
|
|
1941
|
-
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);
|
|
1942
2795
|
}
|
|
1943
2796
|
if (sqOptions.compact) {
|
|
1944
|
-
ret = (0,
|
|
2797
|
+
ret = (0, import_utils13.compact)((0, import_utils13.castArray)(ret));
|
|
1945
2798
|
}
|
|
1946
2799
|
return ret;
|
|
1947
2800
|
}
|
|
@@ -1953,7 +2806,7 @@ var Base = class {
|
|
|
1953
2806
|
};
|
|
1954
2807
|
|
|
1955
2808
|
// src/services/service.ts
|
|
1956
|
-
var
|
|
2809
|
+
var import_utils15 = require("@web-ts-toolkit/utils");
|
|
1957
2810
|
var import_just_diff = require("just-diff");
|
|
1958
2811
|
|
|
1959
2812
|
// src/model.ts
|
|
@@ -2197,28 +3050,28 @@ var error = (error2) => {
|
|
|
2197
3050
|
};
|
|
2198
3051
|
|
|
2199
3052
|
// src/services/model-subdocument-service.ts
|
|
2200
|
-
var
|
|
3053
|
+
var import_utils14 = require("@web-ts-toolkit/utils");
|
|
2201
3054
|
async function listSub(service, id, sub, options) {
|
|
2202
3055
|
const { filter: ft, select } = options ?? {};
|
|
2203
3056
|
const filterErrors = validateClientFilter(ft);
|
|
2204
3057
|
if (filterErrors.length > 0) return { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors: filterErrors };
|
|
2205
3058
|
const parentDoc = await getParentDoc(service, id, sub, null, { access: "read" });
|
|
2206
3059
|
if (!parentDoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
2207
|
-
let result = (0,
|
|
3060
|
+
let result = (0, import_utils14.get)(parentDoc, sub);
|
|
2208
3061
|
const [subFilter, subSelect] = await Promise.all([
|
|
2209
3062
|
service.genFilter(`subs.${sub}.list`, ft),
|
|
2210
3063
|
service.genQuerySelect("list", select, false, [sub, "sub"])
|
|
2211
3064
|
]);
|
|
2212
3065
|
if (subFilter === false) return { success: false, kind: "error", code: "forbidden" /* Forbidden */ };
|
|
2213
3066
|
result = filterCollection(result, subFilter);
|
|
2214
|
-
if (subSelect) result = result.map((v) => (0,
|
|
3067
|
+
if (subSelect) result = result.map((v) => (0, import_utils14.pick)(toObject(v), subSelect.concat("_id")));
|
|
2215
3068
|
return { success: true, kind: "list", code: "success" /* Success */, data: result, count: result.length };
|
|
2216
3069
|
}
|
|
2217
3070
|
async function readSub(service, id, sub, subId, options) {
|
|
2218
3071
|
const { select, populate } = options ?? {};
|
|
2219
3072
|
const parentDoc = await getParentDoc(service, id, sub, { populate }, { access: "read" });
|
|
2220
3073
|
if (!parentDoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
2221
|
-
const result = (0,
|
|
3074
|
+
const result = (0, import_utils14.get)(parentDoc, sub);
|
|
2222
3075
|
const [subFilter, subSelect] = await Promise.all([
|
|
2223
3076
|
service.genFilter(`subs.${sub}.read`, { _id: subId }),
|
|
2224
3077
|
service.genQuerySelect("read", select, false, [sub, "sub"])
|
|
@@ -2226,13 +3079,13 @@ async function readSub(service, id, sub, subId, options) {
|
|
|
2226
3079
|
if (subFilter === false) return { success: false, kind: "error", code: "forbidden" /* Forbidden */ };
|
|
2227
3080
|
let subdoc = findElement(result, subFilter);
|
|
2228
3081
|
if (!subdoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
2229
|
-
if (subSelect) subdoc = (0,
|
|
3082
|
+
if (subSelect) subdoc = (0, import_utils14.pick)(toObject(subdoc), subSelect.concat(["_id"]));
|
|
2230
3083
|
return { success: true, kind: "single", code: "success" /* Success */, data: subdoc };
|
|
2231
3084
|
}
|
|
2232
3085
|
async function updateSub(service, id, sub, subId, data) {
|
|
2233
3086
|
const parentDoc = await getParentDoc(service, id, sub, null, { access: "update" });
|
|
2234
3087
|
if (!parentDoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
2235
|
-
const result = (0,
|
|
3088
|
+
const result = (0, import_utils14.get)(parentDoc, sub);
|
|
2236
3089
|
const [subFilter, subReadSelect, subUpdateSelect] = await Promise.all([
|
|
2237
3090
|
service.genFilter(`subs.${sub}.update`, { _id: subId }),
|
|
2238
3091
|
service.genQuerySelect("read", null, false, [sub, "sub"]),
|
|
@@ -2241,10 +3094,10 @@ async function updateSub(service, id, sub, subId, data) {
|
|
|
2241
3094
|
if (subFilter === false) return { success: false, kind: "error", code: "forbidden" /* Forbidden */ };
|
|
2242
3095
|
let subdoc = findElement(result, subFilter);
|
|
2243
3096
|
if (!subdoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
2244
|
-
const allowedData = (0,
|
|
3097
|
+
const allowedData = (0, import_utils14.pick)(data, subUpdateSelect);
|
|
2245
3098
|
Object.assign(subdoc, allowedData);
|
|
2246
3099
|
await parentDoc.save();
|
|
2247
|
-
if (subReadSelect) subdoc = (0,
|
|
3100
|
+
if (subReadSelect) subdoc = (0, import_utils14.pick)(toObject(subdoc), subReadSelect.concat(["_id"]));
|
|
2248
3101
|
return { success: true, kind: "single", code: "success" /* Success */, data: subdoc };
|
|
2249
3102
|
}
|
|
2250
3103
|
async function bulkUpdateSub(service, id, sub, data) {
|
|
@@ -2259,7 +3112,7 @@ async function bulkUpdateSub(service, id, sub, data) {
|
|
|
2259
3112
|
}
|
|
2260
3113
|
const parentDoc = await getParentDoc(service, id, sub, null, { access: "update" });
|
|
2261
3114
|
if (!parentDoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
2262
|
-
let result = (0,
|
|
3115
|
+
let result = (0, import_utils14.get)(parentDoc, sub);
|
|
2263
3116
|
const [subFilter, subReadSelect, subUpdateSelect] = await Promise.all([
|
|
2264
3117
|
service.genFilter(`subs.${sub}.update`, { _id: { $in: data.map((v) => v._id) } }),
|
|
2265
3118
|
service.genQuerySelect("read", null, false, [sub, "sub"]),
|
|
@@ -2267,14 +3120,14 @@ async function bulkUpdateSub(service, id, sub, data) {
|
|
|
2267
3120
|
]);
|
|
2268
3121
|
if (subFilter === false) return { success: false, kind: "error", code: "forbidden" /* Forbidden */ };
|
|
2269
3122
|
result = filterCollection(result, subFilter);
|
|
2270
|
-
(0,
|
|
3123
|
+
(0, import_utils14.forEach)(result, (subdoc) => {
|
|
2271
3124
|
const tdata = findElementById(data, subdoc._id);
|
|
2272
3125
|
if (!tdata) return;
|
|
2273
|
-
const allowedData = (0,
|
|
3126
|
+
const allowedData = (0, import_utils14.pick)(tdata, subUpdateSelect);
|
|
2274
3127
|
Object.assign(subdoc, allowedData);
|
|
2275
3128
|
});
|
|
2276
3129
|
await parentDoc.save();
|
|
2277
|
-
if (subReadSelect) result = result.map((v) => (0,
|
|
3130
|
+
if (subReadSelect) result = result.map((v) => (0, import_utils14.pick)(toObject(v), subReadSelect.concat(["_id"])));
|
|
2278
3131
|
return { success: true, kind: "list", code: "success" /* Success */, data: result, count: result.length };
|
|
2279
3132
|
}
|
|
2280
3133
|
async function createSub(service, id, sub, data, options) {
|
|
@@ -2290,25 +3143,25 @@ async function createSub(service, id, sub, data, options) {
|
|
|
2290
3143
|
}
|
|
2291
3144
|
const parentDoc = await getParentDoc(service, id, sub, null, { access: "update" });
|
|
2292
3145
|
if (!parentDoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
2293
|
-
let result = (0,
|
|
3146
|
+
let result = (0, import_utils14.get)(parentDoc, sub);
|
|
2294
3147
|
const [subCreateSelect, subReadSelect] = await Promise.all([
|
|
2295
3148
|
service.genQuerySelect("create", null, false, [sub, "sub"]),
|
|
2296
3149
|
service.genQuerySelect("read", null, false, [sub, "sub"])
|
|
2297
3150
|
]);
|
|
2298
|
-
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);
|
|
2299
3152
|
if (Array.isArray(allowedData)) {
|
|
2300
3153
|
addFirst === true ? result.unshift(...allowedData) : result.push(...allowedData);
|
|
2301
3154
|
} else {
|
|
2302
3155
|
addFirst === true ? result.unshift(allowedData) : result.push(allowedData);
|
|
2303
3156
|
}
|
|
2304
3157
|
await parentDoc.save();
|
|
2305
|
-
if (subReadSelect) result = result.map((v) => (0,
|
|
3158
|
+
if (subReadSelect) result = result.map((v) => (0, import_utils14.pick)(toObject(v), subReadSelect.concat(["_id"])));
|
|
2306
3159
|
return { success: true, kind: "list", code: "created" /* Created */, data: result, count: result.length };
|
|
2307
3160
|
}
|
|
2308
3161
|
async function deleteSub(service, id, sub, subId) {
|
|
2309
3162
|
const parentDoc = await getParentDoc(service, id, sub, null, { access: "update" });
|
|
2310
3163
|
if (!parentDoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
2311
|
-
const result = (0,
|
|
3164
|
+
const result = (0, import_utils14.get)(parentDoc, sub);
|
|
2312
3165
|
const subFilter = await service.genFilter(`subs.${sub}.delete`, { _id: subId });
|
|
2313
3166
|
if (subFilter === false) return { success: false, kind: "error", code: "forbidden" /* Forbidden */ };
|
|
2314
3167
|
const subdoc = findElement(result, subFilter);
|
|
@@ -2449,6 +3302,29 @@ var assertModelDocument = (value, modelName, hookName) => {
|
|
|
2449
3302
|
}
|
|
2450
3303
|
throw new Error(`${hookName} hook for model=${modelName} must return a Mongoose document instance`);
|
|
2451
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
|
+
};
|
|
2452
3328
|
var Service = class extends Base {
|
|
2453
3329
|
findRawParentDoc(args) {
|
|
2454
3330
|
return this.model.findOne({
|
|
@@ -2515,8 +3391,9 @@ var Service = class extends Base {
|
|
|
2515
3391
|
overridePopulate || this.genPopulate(populateAccess || access, populate),
|
|
2516
3392
|
this.genAllowedFields({}, access, this.baseFieldsExt)
|
|
2517
3393
|
]);
|
|
2518
|
-
const { includes, includeLocalFields, includePaths } = this.processInclude(include);
|
|
2519
|
-
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);
|
|
2520
3397
|
const query = {
|
|
2521
3398
|
filter: _filter,
|
|
2522
3399
|
select: finalSelect,
|
|
@@ -2549,8 +3426,14 @@ var Service = class extends Base {
|
|
|
2549
3426
|
originalDocumentSnapshot: toObject(doc),
|
|
2550
3427
|
resolvedQuery: query
|
|
2551
3428
|
};
|
|
3429
|
+
const correlatedSnapshots = [(0, import_utils15.cloneDeep)(toObject(doc))];
|
|
2552
3430
|
try {
|
|
2553
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
|
+
}
|
|
2554
3437
|
} catch (error2) {
|
|
2555
3438
|
const result = this.getClientRequestErrorResult(error2);
|
|
2556
3439
|
if (result) {
|
|
@@ -2568,8 +3451,20 @@ var Service = class extends Base {
|
|
|
2568
3451
|
doc = await this.trimOutputFields(
|
|
2569
3452
|
doc,
|
|
2570
3453
|
access,
|
|
2571
|
-
this.baseFieldsExt.concat(includePaths, normalizeSelect(overrideSelect))
|
|
3454
|
+
this.baseFieldsExt.concat(includePaths, correlatedOutputPaths, normalizeSelect(overrideSelect))
|
|
2572
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
|
+
}
|
|
2573
3468
|
if (!includePermissions) doc = this.addEmptyPermissions(doc);
|
|
2574
3469
|
this.completeOp("findOne", startedAt, "success" /* Success */, _filter);
|
|
2575
3470
|
return { success: true, kind: "single", code: "success" /* Success */, data: doc, query, context };
|
|
@@ -2615,11 +3510,12 @@ var Service = class extends Base {
|
|
|
2615
3510
|
this.genAllowedFields({}, "list", this.baseFieldsExt)
|
|
2616
3511
|
]);
|
|
2617
3512
|
const finalSelect = normalizeSelect(_select);
|
|
2618
|
-
const filteredPopulate = (0,
|
|
2619
|
-
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);
|
|
2620
3516
|
const query = {
|
|
2621
3517
|
filter: _filter,
|
|
2622
|
-
select: finalSelect.concat(includeLocalFields),
|
|
3518
|
+
select: finalSelect.concat(includeLocalFields, correlatedReferenceFields),
|
|
2623
3519
|
populate: filteredPopulate,
|
|
2624
3520
|
sort,
|
|
2625
3521
|
...pagination
|
|
@@ -2628,7 +3524,7 @@ var Service = class extends Base {
|
|
|
2628
3524
|
sort,
|
|
2629
3525
|
skip: pagination.skip,
|
|
2630
3526
|
limit: pagination.limit,
|
|
2631
|
-
selectCount: finalSelect.concat(includeLocalFields).length,
|
|
3527
|
+
selectCount: finalSelect.concat(includeLocalFields, correlatedReferenceFields).length,
|
|
2632
3528
|
populateCount: Array.isArray(filteredPopulate) ? filteredPopulate.length : filteredPopulate ? 1 : 0
|
|
2633
3529
|
});
|
|
2634
3530
|
if (_filter === false) {
|
|
@@ -2652,9 +3548,13 @@ var Service = class extends Base {
|
|
|
2652
3548
|
originalDocumentSnapshot: toObject(doc),
|
|
2653
3549
|
resolvedQuery: query
|
|
2654
3550
|
}));
|
|
2655
|
-
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)));
|
|
2656
3553
|
try {
|
|
2657
3554
|
docs = await this.includeDocs(docs, includes);
|
|
3555
|
+
if (correlatedIncludes.length > 0) {
|
|
3556
|
+
docs = await this.includeCorrelatedDocs(docs, correlatedIncludes, correlatedSnapshots);
|
|
3557
|
+
}
|
|
2658
3558
|
} catch (error2) {
|
|
2659
3559
|
const result = this.getClientRequestErrorResult(error2);
|
|
2660
3560
|
if (result) {
|
|
@@ -2676,8 +3576,20 @@ var Service = class extends Base {
|
|
|
2676
3576
|
doc = await this.trimOutputFields(
|
|
2677
3577
|
doc,
|
|
2678
3578
|
"list",
|
|
2679
|
-
this.baseFieldsExt.concat(includePaths, normalizeSelect(overrideSelect))
|
|
3579
|
+
this.baseFieldsExt.concat(includePaths, correlatedOutputPaths, normalizeSelect(overrideSelect))
|
|
2680
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
|
+
}
|
|
2681
3593
|
doc = await _decorate(doc, contexts[i]);
|
|
2682
3594
|
if (!includePermissions) doc = this.addEmptyPermissions(doc);
|
|
2683
3595
|
return doc;
|
|
@@ -2753,16 +3665,16 @@ var Service = class extends Base {
|
|
|
2753
3665
|
resolvedQuery: resolvedPopulate.length > 0 ? { populate: resolvedPopulate } : {}
|
|
2754
3666
|
};
|
|
2755
3667
|
const allowedFields = await this.genAllowedFields(item, "create");
|
|
2756
|
-
const allowedData = (0,
|
|
3668
|
+
const allowedData = (0, import_utils15.pick)(item, allowedFields);
|
|
2757
3669
|
context.allowedFields = allowedFields;
|
|
2758
3670
|
context.allowedData = allowedData;
|
|
2759
3671
|
const validated = await this.validate(allowedData, "create", context);
|
|
2760
|
-
if ((0,
|
|
3672
|
+
if ((0, import_utils15.isBoolean)(validated)) {
|
|
2761
3673
|
if (!validated) {
|
|
2762
3674
|
validationErrors.push({ index, errors: [] });
|
|
2763
3675
|
return void 0;
|
|
2764
3676
|
}
|
|
2765
|
-
} else if ((0,
|
|
3677
|
+
} else if ((0, import_utils15.isArray)(validated)) {
|
|
2766
3678
|
if (validated.length > 0) {
|
|
2767
3679
|
validationErrors.push({
|
|
2768
3680
|
index,
|
|
@@ -2792,7 +3704,7 @@ var Service = class extends Base {
|
|
|
2792
3704
|
contexts[index].preparedData = preparedData;
|
|
2793
3705
|
return preparedData;
|
|
2794
3706
|
});
|
|
2795
|
-
const _decorate = (0,
|
|
3707
|
+
const _decorate = (0, import_utils15.isFunction)(decorate) ? decorate : (v) => v;
|
|
2796
3708
|
const createdDocs = await this.model.create(items);
|
|
2797
3709
|
const docs = await mapWithConcurrencyLimit(createdDocs, maxBulkConcurrency, async (doc, index) => {
|
|
2798
3710
|
contexts[index].currentDocument = doc;
|
|
@@ -2910,16 +3822,16 @@ var Service = class extends Base {
|
|
|
2910
3822
|
context.docPermissions = this.getDocPermissions(doc);
|
|
2911
3823
|
context.currentDocument = doc;
|
|
2912
3824
|
const allowedFields = await this.genAllowedFields(doc, "update");
|
|
2913
|
-
const allowedData = (0,
|
|
3825
|
+
const allowedData = (0, import_utils15.pick)(data, allowedFields);
|
|
2914
3826
|
context.allowedFields = allowedFields;
|
|
2915
3827
|
context.allowedData = allowedData;
|
|
2916
3828
|
const validated = await this.validate(allowedData, "update", context);
|
|
2917
|
-
if ((0,
|
|
3829
|
+
if ((0, import_utils15.isBoolean)(validated)) {
|
|
2918
3830
|
if (!validated) {
|
|
2919
3831
|
this.completeOp("updateOne", startedAt, "bad_request" /* BadRequest */, _filter);
|
|
2920
3832
|
return { success: false, kind: "error", code: "bad_request" /* BadRequest */ };
|
|
2921
3833
|
}
|
|
2922
|
-
} else if ((0,
|
|
3834
|
+
} else if ((0, import_utils15.isArray)(validated)) {
|
|
2923
3835
|
if (validated.length > 0) {
|
|
2924
3836
|
this.completeOp("updateOne", startedAt, "bad_request" /* BadRequest */, _filter);
|
|
2925
3837
|
return { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors: validated };
|
|
@@ -2935,10 +3847,10 @@ var Service = class extends Base {
|
|
|
2935
3847
|
const diffExcludeFields = [this.options.documentPermissionField, "__v"];
|
|
2936
3848
|
this.asServiceHookContext(context).diff = (d) => {
|
|
2937
3849
|
context.changes = (0, import_just_diff.diff)(
|
|
2938
|
-
(0,
|
|
2939
|
-
(0,
|
|
3850
|
+
(0, import_utils15.omit)(context.originalDocumentSnapshot, diffExcludeFields),
|
|
3851
|
+
(0, import_utils15.omit)(d.toObject({ virtuals: false }), diffExcludeFields)
|
|
2940
3852
|
) || [];
|
|
2941
|
-
context.modifiedPaths = (0,
|
|
3853
|
+
context.modifiedPaths = (0, import_utils15.uniq)(context.changes.map((di) => di.path.length > 0 ? String(di.path[0]) : ""));
|
|
2942
3854
|
};
|
|
2943
3855
|
doc = assertModelDocument(await this.afterPersist(doc, "update", context), this.modelName, "afterPersist");
|
|
2944
3856
|
context.currentDocument = doc;
|
|
@@ -2954,7 +3866,7 @@ var Service = class extends Base {
|
|
|
2954
3866
|
if (_populate) await populateDoc(doc, _populate);
|
|
2955
3867
|
doc = await this.trimOutputFields(doc, "read", this.baseFieldsExt);
|
|
2956
3868
|
let outputDoc = doc;
|
|
2957
|
-
if ((0,
|
|
3869
|
+
if ((0, import_utils15.isFunction)(decorate)) outputDoc = await decorate(outputDoc, context);
|
|
2958
3870
|
if (!includePermissions) outputDoc = this.addEmptyPermissions(outputDoc);
|
|
2959
3871
|
this.completeOp("updateOne", startedAt, "success" /* Success */, _filter);
|
|
2960
3872
|
return { success: true, kind: "single", code: "success" /* Success */, data: outputDoc, input: prepared };
|
|
@@ -3106,6 +4018,23 @@ var Service = class extends Base {
|
|
|
3106
4018
|
if (filter2 === false) return { success: false, kind: "error", code: "forbidden" /* Forbidden */, query };
|
|
3107
4019
|
return { success: true, kind: "single", code: "success" /* Success */, data: await this.model.countDocuments(filter2), query };
|
|
3108
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
|
+
}
|
|
3109
4038
|
async countByFieldValues(foreignField, values, filter2 = {}, access = "list") {
|
|
3110
4039
|
if (!isValidFieldPath(foreignField)) {
|
|
3111
4040
|
return {
|
|
@@ -3117,7 +4046,7 @@ var Service = class extends Base {
|
|
|
3117
4046
|
}
|
|
3118
4047
|
const filterErrors = this.validateClientFilter(filter2);
|
|
3119
4048
|
if (filterErrors.length > 0) return { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors: filterErrors };
|
|
3120
|
-
const uniqueValues = (0,
|
|
4049
|
+
const uniqueValues = (0, import_utils15.uniqBy)(values, (value) => String(value));
|
|
3121
4050
|
filter2 = await this.genFilter(access, {
|
|
3122
4051
|
...filter2,
|
|
3123
4052
|
[foreignField]: { $in: uniqueValues }
|
|
@@ -3193,7 +4122,7 @@ var Service = class extends Base {
|
|
|
3193
4122
|
return resolveExistsOptions(this, options);
|
|
3194
4123
|
}
|
|
3195
4124
|
async getFieldPermissionAccess(ids) {
|
|
3196
|
-
const uniqueIds = (0,
|
|
4125
|
+
const uniqueIds = (0, import_utils15.compact)((0, import_utils15.uniqBy)(ids, (id) => String(id)).map((id) => String(id)));
|
|
3197
4126
|
if (uniqueIds.length === 0) {
|
|
3198
4127
|
return {
|
|
3199
4128
|
readIds: /* @__PURE__ */ new Set(),
|
|
@@ -3223,7 +4152,7 @@ var Service = class extends Base {
|
|
|
3223
4152
|
return updateSub(this, id, sub, subId, data);
|
|
3224
4153
|
}
|
|
3225
4154
|
async bulkUpdateSub(id, sub, data) {
|
|
3226
|
-
return bulkUpdateSub(this, id, sub, (0,
|
|
4155
|
+
return bulkUpdateSub(this, id, sub, (0, import_utils15.castArray)(data));
|
|
3227
4156
|
}
|
|
3228
4157
|
async createSub(id, sub, data, options) {
|
|
3229
4158
|
return createSub(this, id, sub, data, options);
|
|
@@ -3237,7 +4166,7 @@ var Service = class extends Base {
|
|
|
3237
4166
|
};
|
|
3238
4167
|
|
|
3239
4168
|
// src/services/public-service.ts
|
|
3240
|
-
var
|
|
4169
|
+
var import_utils16 = require("@web-ts-toolkit/utils");
|
|
3241
4170
|
var PublicService = class extends Service {
|
|
3242
4171
|
async _list(filter2, args, options) {
|
|
3243
4172
|
const { select, populate, include, sort, skip, limit, page, pageSize, tasks } = this.resolvePublicListArgs(args);
|
|
@@ -3276,7 +4205,7 @@ var PublicService = class extends Service {
|
|
|
3276
4205
|
let d = toObject(doc);
|
|
3277
4206
|
d = await this.decorate(d, "create", context);
|
|
3278
4207
|
d = this.runTasks(d, tasks);
|
|
3279
|
-
if (select) d = (0,
|
|
4208
|
+
if (select) d = (0, import_utils16.pick)(d, [...normalizeSelect(select), ...this.baseFieldsExt]);
|
|
3280
4209
|
return d;
|
|
3281
4210
|
}
|
|
3282
4211
|
);
|
|
@@ -3381,8 +4310,8 @@ var PublicService = class extends Service {
|
|
|
3381
4310
|
let d = toObject(doc);
|
|
3382
4311
|
d = await this.decorate(d, "update", context);
|
|
3383
4312
|
d = this.runTasks(d, tasks);
|
|
3384
|
-
if (select) d = (0,
|
|
3385
|
-
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"]);
|
|
3386
4315
|
return d;
|
|
3387
4316
|
}
|
|
3388
4317
|
);
|
|
@@ -3504,7 +4433,7 @@ var PublicService = class extends Service {
|
|
|
3504
4433
|
};
|
|
3505
4434
|
|
|
3506
4435
|
// src/services/data-service.ts
|
|
3507
|
-
var
|
|
4436
|
+
var import_utils17 = require("@web-ts-toolkit/utils");
|
|
3508
4437
|
var DataService = class {
|
|
3509
4438
|
constructor(req, dataName) {
|
|
3510
4439
|
this.req = req;
|
|
@@ -3526,7 +4455,7 @@ var DataService = class {
|
|
|
3526
4455
|
let doc = await findElement(this.data, _filter);
|
|
3527
4456
|
if (!doc) return { success: false, kind: "error", code: "not_found" /* NotFound */, query };
|
|
3528
4457
|
doc = await this.trimOutputFields(doc, access);
|
|
3529
|
-
if (_select.length > 0) doc = (0,
|
|
4458
|
+
if (_select.length > 0) doc = (0, import_utils17.pick)(doc, _select);
|
|
3530
4459
|
return { success: true, kind: "single", code: "success" /* Success */, data: doc, query };
|
|
3531
4460
|
}
|
|
3532
4461
|
async findById(id, args, options) {
|
|
@@ -3566,13 +4495,13 @@ var DataService = class {
|
|
|
3566
4495
|
const totalCount = docs.length;
|
|
3567
4496
|
if (sort) {
|
|
3568
4497
|
const normalizedSort = normalizeSortForOrderBy(sort);
|
|
3569
|
-
docs = (0,
|
|
4498
|
+
docs = (0, import_utils17.orderBy)(docs, normalizedSort.fields, normalizedSort.orders);
|
|
3570
4499
|
}
|
|
3571
4500
|
const pagedDocs = docs.slice(query.skip, query.limit && query.skip + query.limit);
|
|
3572
4501
|
const { maxHookConcurrency } = this.getRequestComplexity();
|
|
3573
4502
|
const trimmed = await mapWithConcurrencyLimit(pagedDocs, maxHookConcurrency, async (doc) => {
|
|
3574
4503
|
doc = await this.trimOutputFields(doc, "list");
|
|
3575
|
-
if (_select.length > 0) doc = (0,
|
|
4504
|
+
if (_select.length > 0) doc = (0, import_utils17.pick)(doc, _select);
|
|
3576
4505
|
return doc;
|
|
3577
4506
|
});
|
|
3578
4507
|
if (!hasExplicitLimit && trimmed.length === totalCount) {
|
|
@@ -3621,30 +4550,65 @@ var DataService = class {
|
|
|
3621
4550
|
};
|
|
3622
4551
|
|
|
3623
4552
|
// src/processors.ts
|
|
3624
|
-
var
|
|
4553
|
+
var import_utils18 = require("@web-ts-toolkit/utils");
|
|
3625
4554
|
var UNSAFE_PATH_KEYS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
|
|
3626
|
-
|
|
4555
|
+
var DEFAULT_MAX_SLICE = 1e3;
|
|
4556
|
+
var DEFAULT_MASK_REPLACEMENT = "***";
|
|
4557
|
+
function failUnsafeSegment(path, fn = "copyAndDepopulate") {
|
|
3627
4558
|
throw new Error(
|
|
3628
|
-
|
|
4559
|
+
`${fn}: refusing path '${path}' because it contains an unsafe segment (__proto__, prototype, or constructor).`
|
|
3629
4560
|
);
|
|
3630
4561
|
}
|
|
3631
4562
|
function failMissingId(path) {
|
|
3632
4563
|
throw new Error(`copyAndDepopulate: path '${path}' resolved to a record missing the configured id field.`);
|
|
3633
4564
|
}
|
|
3634
|
-
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") {
|
|
3635
4569
|
const segs = path.split(".");
|
|
3636
4570
|
for (const seg of segs) {
|
|
3637
4571
|
if (seg.length === 0) continue;
|
|
3638
4572
|
if (UNSAFE_PATH_KEYS.has(seg)) {
|
|
3639
|
-
failUnsafeSegment(path);
|
|
4573
|
+
failUnsafeSegment(path, fn);
|
|
3640
4574
|
}
|
|
3641
4575
|
}
|
|
3642
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
|
+
}
|
|
3643
4607
|
var copyAndDepopulate = (docObject, operations, options = { mutable: true, idField: "_id" }) => {
|
|
3644
4608
|
const mutable = options.mutable !== false;
|
|
3645
4609
|
const idField = options.idField ?? "_id";
|
|
3646
|
-
const obj = mutable ? docObject : (0,
|
|
3647
|
-
(0,
|
|
4610
|
+
const obj = mutable ? docObject : (0, import_utils18.cloneDeep)(docObject);
|
|
4611
|
+
(0, import_utils18.forEach)(Array.isArray(operations) ? operations : [], (op) => {
|
|
3648
4612
|
if (!op || typeof op.src !== "string" || typeof op.dest !== "string") return;
|
|
3649
4613
|
const src = op.src;
|
|
3650
4614
|
const dest = op.dest;
|
|
@@ -3653,19 +4617,19 @@ var copyAndDepopulate = (docObject, operations, options = { mutable: true, idFie
|
|
|
3653
4617
|
assertSafePath(dest);
|
|
3654
4618
|
let targets = [obj];
|
|
3655
4619
|
const segs = src.split(".");
|
|
3656
|
-
(0,
|
|
4620
|
+
(0, import_utils18.forEach)(segs, (seg, ind) => {
|
|
3657
4621
|
if (segs.length === ind + 1) {
|
|
3658
|
-
(0,
|
|
3659
|
-
if (!(0,
|
|
4622
|
+
(0, import_utils18.forEach)(targets, (target) => {
|
|
4623
|
+
if (!(0, import_utils18.isPlainObject)(target)) return;
|
|
3660
4624
|
const targetObject = target;
|
|
3661
4625
|
const leaf = targetObject[seg];
|
|
3662
4626
|
const sourceForDest = leaf;
|
|
3663
4627
|
let depopulated;
|
|
3664
|
-
if ((0,
|
|
4628
|
+
if ((0, import_utils18.isArray)(leaf)) {
|
|
3665
4629
|
if (leaf.length === 0) {
|
|
3666
4630
|
depopulated = [];
|
|
3667
|
-
} else if (leaf.every((item) => (0,
|
|
3668
|
-
depopulated = (0,
|
|
4631
|
+
} else if (leaf.every((item) => (0, import_utils18.isPlainObject)(item))) {
|
|
4632
|
+
depopulated = (0, import_utils18.map)(leaf, idField).map((id, i) => {
|
|
3669
4633
|
if (id === void 0) {
|
|
3670
4634
|
failMissingId(`${src}[${i}]`);
|
|
3671
4635
|
}
|
|
@@ -3674,8 +4638,8 @@ var copyAndDepopulate = (docObject, operations, options = { mutable: true, idFie
|
|
|
3674
4638
|
} else {
|
|
3675
4639
|
return;
|
|
3676
4640
|
}
|
|
3677
|
-
} else if ((0,
|
|
3678
|
-
const id = (0,
|
|
4641
|
+
} else if ((0, import_utils18.isPlainObject)(leaf)) {
|
|
4642
|
+
const id = (0, import_utils18.get)(leaf, idField);
|
|
3679
4643
|
if (id === void 0) {
|
|
3680
4644
|
failMissingId(src);
|
|
3681
4645
|
}
|
|
@@ -3683,15 +4647,15 @@ var copyAndDepopulate = (docObject, operations, options = { mutable: true, idFie
|
|
|
3683
4647
|
} else {
|
|
3684
4648
|
return;
|
|
3685
4649
|
}
|
|
3686
|
-
(0,
|
|
3687
|
-
(0,
|
|
4650
|
+
(0, import_utils18.set)(targetObject, dest, sourceForDest);
|
|
4651
|
+
(0, import_utils18.set)(targetObject, seg, depopulated);
|
|
3688
4652
|
});
|
|
3689
4653
|
} else {
|
|
3690
4654
|
targets = targets.reduce((ret, target) => {
|
|
3691
|
-
if (!(0,
|
|
4655
|
+
if (!(0, import_utils18.isPlainObject)(target)) return ret;
|
|
3692
4656
|
const targetObject = target;
|
|
3693
4657
|
const next = targetObject[seg];
|
|
3694
|
-
if ((0,
|
|
4658
|
+
if ((0, import_utils18.isArray)(next)) {
|
|
3695
4659
|
for (const item of next) {
|
|
3696
4660
|
ret.push(item);
|
|
3697
4661
|
}
|
|
@@ -3703,6 +4667,125 @@ var copyAndDepopulate = (docObject, operations, options = { mutable: true, idFie
|
|
|
3703
4667
|
});
|
|
3704
4668
|
return obj;
|
|
3705
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
|
+
};
|
|
3706
4789
|
|
|
3707
4790
|
// src/symbols.ts
|
|
3708
4791
|
var MIDDLEWARE = /* @__PURE__ */ Symbol.for("@web-ts-toolkit/access-router/middleware");
|
|
@@ -3742,10 +4825,10 @@ var Cache = class {
|
|
|
3742
4825
|
};
|
|
3743
4826
|
|
|
3744
4827
|
// src/core-shared.ts
|
|
3745
|
-
var
|
|
4828
|
+
var import_utils20 = require("@web-ts-toolkit/utils");
|
|
3746
4829
|
|
|
3747
4830
|
// src/permission.ts
|
|
3748
|
-
var
|
|
4831
|
+
var import_utils19 = require("@web-ts-toolkit/utils");
|
|
3749
4832
|
var Permission = class {
|
|
3750
4833
|
constructor(permissions) {
|
|
3751
4834
|
this.permissions = { ...permissions };
|
|
@@ -3761,7 +4844,7 @@ var Permission = class {
|
|
|
3761
4844
|
return [permissionOrPermissions].concat(permissions);
|
|
3762
4845
|
}
|
|
3763
4846
|
hasKey(permission) {
|
|
3764
|
-
return (0,
|
|
4847
|
+
return (0, import_utils19.hasOwn)(this.permissions, permission);
|
|
3765
4848
|
}
|
|
3766
4849
|
has(permission) {
|
|
3767
4850
|
return this.permissions[permission] === true;
|
|
@@ -3781,10 +4864,10 @@ var permission_default = Permission;
|
|
|
3781
4864
|
|
|
3782
4865
|
// src/core-shared.ts
|
|
3783
4866
|
function isAndFilter(filter2) {
|
|
3784
|
-
return !!filter2 && (0,
|
|
4867
|
+
return !!filter2 && (0, import_utils20.isPlainObject)(filter2) && Object.keys(filter2).length === 1 && (0, import_utils20.isArray)(filter2.$and);
|
|
3785
4868
|
}
|
|
3786
4869
|
function isMergeableClause(filter2) {
|
|
3787
|
-
return !!filter2 && (0,
|
|
4870
|
+
return !!filter2 && (0, import_utils20.isPlainObject)(filter2) && Object.keys(filter2).every((key) => !key.startsWith("$"));
|
|
3788
4871
|
}
|
|
3789
4872
|
function optimizeAndFilter(clausesInput) {
|
|
3790
4873
|
const clauses = [];
|
|
@@ -3799,10 +4882,10 @@ function optimizeAndFilter(clausesInput) {
|
|
|
3799
4882
|
}
|
|
3800
4883
|
}
|
|
3801
4884
|
const dedupeKey = (value) => {
|
|
3802
|
-
if ((0,
|
|
4885
|
+
if ((0, import_utils20.isArray)(value)) {
|
|
3803
4886
|
return `[${value.map(dedupeKey).join(",")}]`;
|
|
3804
4887
|
}
|
|
3805
|
-
if ((0,
|
|
4888
|
+
if ((0, import_utils20.isPlainObject)(value)) {
|
|
3806
4889
|
return `{${Object.keys(value).sort().map((key) => `${key}:${dedupeKey(value[key])}`).join(",")}}`;
|
|
3807
4890
|
}
|
|
3808
4891
|
return JSON.stringify(value);
|
|
@@ -3826,7 +4909,7 @@ function optimizeAndFilter(clausesInput) {
|
|
|
3826
4909
|
}
|
|
3827
4910
|
let canMerge = true;
|
|
3828
4911
|
for (const [key, value] of Object.entries(clause)) {
|
|
3829
|
-
if ((0,
|
|
4912
|
+
if ((0, import_utils20.hasOwn)(mergedClause, key) && !(0, import_utils20.isEqual)(mergedClause[key], value)) {
|
|
3830
4913
|
canMerge = false;
|
|
3831
4914
|
break;
|
|
3832
4915
|
}
|
|
@@ -3837,24 +4920,24 @@ function optimizeAndFilter(clausesInput) {
|
|
|
3837
4920
|
}
|
|
3838
4921
|
Object.assign(mergedClause, clause);
|
|
3839
4922
|
}
|
|
3840
|
-
const finalClauses = (0,
|
|
4923
|
+
const finalClauses = (0, import_utils20.isEmpty)(mergedClause) ? remainingClauses : [mergedClause, ...remainingClauses];
|
|
3841
4924
|
if (finalClauses.length === 0) return null;
|
|
3842
4925
|
if (finalClauses.length === 1) return finalClauses[0];
|
|
3843
4926
|
return { $and: finalClauses };
|
|
3844
4927
|
}
|
|
3845
4928
|
function normalizeFilter(filter2) {
|
|
3846
4929
|
if (filter2 === false) return false;
|
|
3847
|
-
if (!filter2 || !(0,
|
|
4930
|
+
if (!filter2 || !(0, import_utils20.isPlainObject)(filter2) || (0, import_utils20.isEmpty)(filter2)) return null;
|
|
3848
4931
|
if (!isAndFilter(filter2)) {
|
|
3849
4932
|
return filter2;
|
|
3850
4933
|
}
|
|
3851
4934
|
return optimizeAndFilter(filter2.$and);
|
|
3852
4935
|
}
|
|
3853
4936
|
async function resolveIdentifierFilter(req, idField, resolveIdFilter, id) {
|
|
3854
|
-
if ((0,
|
|
4937
|
+
if ((0, import_utils20.isFunction)(resolveIdFilter)) {
|
|
3855
4938
|
return await resolveIdFilter.call(req, id);
|
|
3856
4939
|
}
|
|
3857
|
-
if ((0,
|
|
4940
|
+
if ((0, import_utils20.isString)(idField)) {
|
|
3858
4941
|
return { [idField]: id };
|
|
3859
4942
|
}
|
|
3860
4943
|
return { _id: id };
|
|
@@ -3870,12 +4953,12 @@ async function resolveAccessFilter({
|
|
|
3870
4953
|
}) {
|
|
3871
4954
|
let nextFilter = normalizeFilter(filter2);
|
|
3872
4955
|
const overrideFilterFn = getOption(`overrideFilter.${access}`, null);
|
|
3873
|
-
if (nextFilter !== false && (0,
|
|
4956
|
+
if (nextFilter !== false && (0, import_utils20.isFunction)(overrideFilterFn)) {
|
|
3874
4957
|
nextFilter = normalizeFilter(await overrideFilterFn.call(req, nextFilter, permissions));
|
|
3875
4958
|
}
|
|
3876
4959
|
if (nextFilter === false) return false;
|
|
3877
4960
|
const baseFilterFn = getOption(`baseFilter.${access}`, null);
|
|
3878
|
-
if (!(0,
|
|
4961
|
+
if (!(0, import_utils20.isFunction)(baseFilterFn)) return nextFilter ?? {};
|
|
3879
4962
|
const baseFilter = normalizeFilter(
|
|
3880
4963
|
cache.has(cacheKey) ? cache.get(cacheKey) : await baseFilterFn.call(req, permissions)
|
|
3881
4964
|
);
|
|
@@ -3909,34 +4992,34 @@ async function setRequestPermissions(req) {
|
|
|
3909
4992
|
if (owner === activeRuntime) return;
|
|
3910
4993
|
if (hasFieldValue && owner === void 0) return;
|
|
3911
4994
|
const globalPermissions = getGlobalOption("globalPermissions");
|
|
3912
|
-
if (!(0,
|
|
4995
|
+
if (!(0, import_utils20.isFunction)(globalPermissions)) return;
|
|
3913
4996
|
const permissions = await globalPermissions.call(req, req);
|
|
3914
|
-
if ((0,
|
|
3915
|
-
else if ((0,
|
|
3916
|
-
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 };
|
|
3917
5000
|
owners.set(requestPermissionField, activeRuntime);
|
|
3918
5001
|
}
|
|
3919
5002
|
async function evaluateRouteGuard(req, permissions, routeGuard) {
|
|
3920
5003
|
const phas = (key) => permissions.has(key);
|
|
3921
5004
|
const { stringHandler, arrayHandler } = createValidator(phas);
|
|
3922
|
-
if ((0,
|
|
5005
|
+
if ((0, import_utils20.isBoolean)(routeGuard)) {
|
|
3923
5006
|
return routeGuard === true;
|
|
3924
5007
|
}
|
|
3925
|
-
if ((0,
|
|
5008
|
+
if ((0, import_utils20.isString)(routeGuard)) {
|
|
3926
5009
|
return stringHandler(routeGuard);
|
|
3927
5010
|
}
|
|
3928
|
-
if ((0,
|
|
5011
|
+
if ((0, import_utils20.isArray)(routeGuard)) {
|
|
3929
5012
|
return arrayHandler(routeGuard);
|
|
3930
5013
|
}
|
|
3931
|
-
if ((0,
|
|
5014
|
+
if ((0, import_utils20.isFunction)(routeGuard)) {
|
|
3932
5015
|
return routeGuard.call(req, permissions);
|
|
3933
5016
|
}
|
|
3934
5017
|
return false;
|
|
3935
5018
|
}
|
|
3936
5019
|
async function callHookChain(req, hook, doc, permissions, context) {
|
|
3937
|
-
const hooks = (0,
|
|
5020
|
+
const hooks = (0, import_utils20.castArray)(hook);
|
|
3938
5021
|
for (let x = 0; x < hooks.length; x++) {
|
|
3939
|
-
if ((0,
|
|
5022
|
+
if ((0, import_utils20.isFunction)(hooks[x])) {
|
|
3940
5023
|
doc = await hooks[x].call(req, doc, permissions, context);
|
|
3941
5024
|
}
|
|
3942
5025
|
}
|
|
@@ -3959,13 +5042,13 @@ async function collectSchemaFields({
|
|
|
3959
5042
|
if (baseFields.includes(key)) continue;
|
|
3960
5043
|
const val = permissionSchema[key];
|
|
3961
5044
|
const value = val && val[access] || val;
|
|
3962
|
-
if ((0,
|
|
5045
|
+
if ((0, import_utils20.isBoolean)(value)) {
|
|
3963
5046
|
if (value) fields.push(key);
|
|
3964
|
-
} else if ((0,
|
|
5047
|
+
} else if ((0, import_utils20.isString)(value)) {
|
|
3965
5048
|
if (stringHandler(value)) fields.push(key);
|
|
3966
|
-
} else if ((0,
|
|
5049
|
+
} else if ((0, import_utils20.isArray)(value)) {
|
|
3967
5050
|
if (arrayHandler(value)) fields.push(key);
|
|
3968
|
-
} else if ((0,
|
|
5051
|
+
} else if ((0, import_utils20.isFunction)(value)) {
|
|
3969
5052
|
if (await value.call(req, ...functionArgs)) fields.push(key);
|
|
3970
5053
|
}
|
|
3971
5054
|
}
|
|
@@ -4051,7 +5134,7 @@ async function runDecorateAllHook({
|
|
|
4051
5134
|
}
|
|
4052
5135
|
|
|
4053
5136
|
// src/acl/select-resolution.ts
|
|
4054
|
-
var
|
|
5137
|
+
var import_utils21 = require("@web-ts-toolkit/utils");
|
|
4055
5138
|
async function collectAllowedFieldsForRequest({
|
|
4056
5139
|
req,
|
|
4057
5140
|
permissionSchema,
|
|
@@ -4119,15 +5202,15 @@ async function resolveSelectForRequest({
|
|
|
4119
5202
|
const excludeall = normalizedSelect.every((v) => v.startsWith("-"));
|
|
4120
5203
|
if (excludeall) {
|
|
4121
5204
|
normalizedSelect = normalizedSelect.map((v) => v.substring(1));
|
|
4122
|
-
fields = (0,
|
|
5205
|
+
fields = (0, import_utils21.difference)(fields, normalizedSelect);
|
|
4123
5206
|
if (excludeid) fields.push("-_id");
|
|
4124
5207
|
} else {
|
|
4125
|
-
fields = (0,
|
|
5208
|
+
fields = (0, import_utils21.intersection)(normalizedSelect, fields.concat(excludeid ? "-_id" : "_id"));
|
|
4126
5209
|
}
|
|
4127
5210
|
}
|
|
4128
5211
|
return fields.concat(alwaysSelectFields);
|
|
4129
5212
|
}
|
|
4130
|
-
return (0,
|
|
5213
|
+
return (0, import_utils21.intersection)(normalizedSelect, fields);
|
|
4131
5214
|
}
|
|
4132
5215
|
|
|
4133
5216
|
// src/core.ts
|
|
@@ -4142,10 +5225,10 @@ var Core = class {
|
|
|
4142
5225
|
getIdentifier(modelName) {
|
|
4143
5226
|
const resolveIdFilter = getModelOption(modelName, "resolveIdFilter");
|
|
4144
5227
|
const idField = getModelOption(modelName, "idField");
|
|
4145
|
-
if ((0,
|
|
5228
|
+
if ((0, import_utils22.isFunction)(resolveIdFilter)) {
|
|
4146
5229
|
return null;
|
|
4147
5230
|
}
|
|
4148
|
-
if ((0,
|
|
5231
|
+
if ((0, import_utils22.isString)(idField)) {
|
|
4149
5232
|
return idField;
|
|
4150
5233
|
}
|
|
4151
5234
|
return "_id";
|
|
@@ -4223,11 +5306,11 @@ var Core = class {
|
|
|
4223
5306
|
async genPopulate(modelName, access = "read", _populate = null, subPaths = []) {
|
|
4224
5307
|
if (!_populate) return [];
|
|
4225
5308
|
let populate = Array.isArray(_populate) ? _populate : [_populate];
|
|
4226
|
-
populate = (0,
|
|
5309
|
+
populate = (0, import_utils22.compact)(
|
|
4227
5310
|
await Promise.all(
|
|
4228
5311
|
populate.map(async (p) => {
|
|
4229
|
-
const populateAccess = !(0,
|
|
4230
|
-
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 } : {
|
|
4231
5314
|
path: p.path,
|
|
4232
5315
|
select: normalizeSelect(p.select)
|
|
4233
5316
|
};
|
|
@@ -4265,10 +5348,10 @@ var Core = class {
|
|
|
4265
5348
|
}
|
|
4266
5349
|
async validate(modelName, allowedData, access, context) {
|
|
4267
5350
|
const validate = getModelOption(modelName, `validate.${access}`, null);
|
|
4268
|
-
if ((0,
|
|
5351
|
+
if ((0, import_utils22.isFunction)(validate)) {
|
|
4269
5352
|
const permissions = this.getGlobalPermissions();
|
|
4270
5353
|
return validate.call(this.req, allowedData, permissions, context);
|
|
4271
|
-
} else if ((0,
|
|
5354
|
+
} else if ((0, import_utils22.isBoolean)(validate) || (0, import_utils22.isArray)(validate)) {
|
|
4272
5355
|
return validate;
|
|
4273
5356
|
} else {
|
|
4274
5357
|
return true;
|
|
@@ -4293,7 +5376,7 @@ var Core = class {
|
|
|
4293
5376
|
const changeOptions = getModelOption(modelName, `onChange`, {});
|
|
4294
5377
|
for (let x = 0; x < context.modifiedPaths.length; x++) {
|
|
4295
5378
|
const mpath = context.modifiedPaths[x];
|
|
4296
|
-
if ((0,
|
|
5379
|
+
if ((0, import_utils22.isFunction)(changeOptions[mpath])) {
|
|
4297
5380
|
await changeOptions[mpath].call(
|
|
4298
5381
|
this.req,
|
|
4299
5382
|
context.originalDocumentSnapshot[mpath],
|
|
@@ -4317,7 +5400,7 @@ var Core = class {
|
|
|
4317
5400
|
async genDocPermissions(modelName, doc, access, context) {
|
|
4318
5401
|
const docPermissionsFn = getModelOption(modelName, `docPermissions.${access}`, null);
|
|
4319
5402
|
let docPermissions = {};
|
|
4320
|
-
if ((0,
|
|
5403
|
+
if ((0, import_utils22.isFunction)(docPermissionsFn)) {
|
|
4321
5404
|
const permissions = this.getGlobalPermissions();
|
|
4322
5405
|
try {
|
|
4323
5406
|
docPermissions = await docPermissionsFn.call(this.req, doc, permissions, context);
|
|
@@ -4368,7 +5451,7 @@ var Core = class {
|
|
|
4368
5451
|
readExists ? this.genAllowedFields(modelName, doc, "read") : [],
|
|
4369
5452
|
updateExists ? this.genAllowedFields(modelName, doc, "update") : []
|
|
4370
5453
|
]);
|
|
4371
|
-
const viewObj = (0,
|
|
5454
|
+
const viewObj = (0, import_utils22.reduce)(
|
|
4372
5455
|
views,
|
|
4373
5456
|
(ret, view) => {
|
|
4374
5457
|
ret[view] = true;
|
|
@@ -4376,7 +5459,7 @@ var Core = class {
|
|
|
4376
5459
|
},
|
|
4377
5460
|
{}
|
|
4378
5461
|
);
|
|
4379
|
-
const editObj = (0,
|
|
5462
|
+
const editObj = (0, import_utils22.reduce)(
|
|
4380
5463
|
edits,
|
|
4381
5464
|
(ret, view) => {
|
|
4382
5465
|
ret[view] = true;
|
|
@@ -4400,9 +5483,9 @@ var Core = class {
|
|
|
4400
5483
|
return runDecorateAllHook({ req: this.req, hook: decorateAll, docs, permissions, context });
|
|
4401
5484
|
}
|
|
4402
5485
|
runTasks(modelName, docObject, task) {
|
|
4403
|
-
const tasks = (0,
|
|
5486
|
+
const tasks = (0, import_utils22.compact)((0, import_utils22.castArray)(task));
|
|
4404
5487
|
if (tasks.length === 0) return docObject;
|
|
4405
|
-
(0,
|
|
5488
|
+
(0, import_utils22.forEach)(tasks, (task2) => {
|
|
4406
5489
|
const { type, args, options } = task2;
|
|
4407
5490
|
switch (type) {
|
|
4408
5491
|
case "COPY_AND_DEPOPULATE":
|
|
@@ -4412,6 +5495,41 @@ var Core = class {
|
|
|
4412
5495
|
options
|
|
4413
5496
|
);
|
|
4414
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;
|
|
4415
5533
|
}
|
|
4416
5534
|
});
|
|
4417
5535
|
return docObject;
|
|
@@ -4435,9 +5553,9 @@ var Core = class {
|
|
|
4435
5553
|
}
|
|
4436
5554
|
const [, field, op] = keys2;
|
|
4437
5555
|
const subOption = getExactModelOption(modelName, `operationAccess.${access}`);
|
|
4438
|
-
if ((0,
|
|
5556
|
+
if ((0, import_utils22.isUndefined)(subOption)) {
|
|
4439
5557
|
const subFieldOption = getExactModelOption(modelName, `operationAccess.subs.${field}`);
|
|
4440
|
-
if ((0,
|
|
5558
|
+
if ((0, import_utils22.isUndefined)(subFieldOption)) {
|
|
4441
5559
|
const opOption = getModelOption(modelName, `operationAccess.${op}`);
|
|
4442
5560
|
return this.canActivate(opOption);
|
|
4443
5561
|
}
|
|
@@ -4489,25 +5607,25 @@ function guard(condition) {
|
|
|
4489
5607
|
const permissions = req[PERMISSIONS];
|
|
4490
5608
|
let cond = condition;
|
|
4491
5609
|
let phas = (key) => permissions.has(key);
|
|
4492
|
-
if ((0,
|
|
5610
|
+
if ((0, import_utils23.isPlainObject)(condition)) {
|
|
4493
5611
|
const { modelName, id, condition: _cond } = condition;
|
|
4494
5612
|
const svc = req.macl.getPublicService(modelName);
|
|
4495
5613
|
const select = getModelOption(modelName, `alwaysSelectFields.read`, void 0);
|
|
4496
|
-
let _id = (0,
|
|
4497
|
-
if ((0,
|
|
5614
|
+
let _id = (0, import_utils23.isString)(id) ? id : null;
|
|
5615
|
+
if ((0, import_utils23.isPlainObject)(id)) {
|
|
4498
5616
|
const { type = "param", key } = id;
|
|
4499
5617
|
if (type === "param") {
|
|
4500
5618
|
const paramValue = req.params[key];
|
|
4501
|
-
_id = (0,
|
|
5619
|
+
_id = (0, import_utils23.isArray)(paramValue) ? paramValue[0] ?? null : paramValue ?? null;
|
|
4502
5620
|
} else if (type === "query") {
|
|
4503
5621
|
const _qval = req.query[key];
|
|
4504
|
-
if ((0,
|
|
5622
|
+
if ((0, import_utils23.isArray)(_qval)) _id = _qval.length > 0 ? _qval[0] : null;
|
|
4505
5623
|
else _id = _qval;
|
|
4506
5624
|
} else {
|
|
4507
5625
|
_id = null;
|
|
4508
5626
|
}
|
|
4509
5627
|
}
|
|
4510
|
-
if (!(0,
|
|
5628
|
+
if (!(0, import_utils23.isString)(_id) || !_id) {
|
|
4511
5629
|
return next(new import_express_json_router2.default.clientErrors.BadRequestError());
|
|
4512
5630
|
}
|
|
4513
5631
|
const result = await svc._read(_id, { select });
|
|
@@ -4519,11 +5637,11 @@ function guard(condition) {
|
|
|
4519
5637
|
cond = _cond;
|
|
4520
5638
|
}
|
|
4521
5639
|
const { stringHandler, arrayHandler } = createValidator(phas);
|
|
4522
|
-
if ((0,
|
|
5640
|
+
if ((0, import_utils23.isString)(cond)) {
|
|
4523
5641
|
if (stringHandler(cond)) return next();
|
|
4524
|
-
} else if ((0,
|
|
5642
|
+
} else if ((0, import_utils23.isArray)(cond)) {
|
|
4525
5643
|
if (arrayHandler(cond)) return next();
|
|
4526
|
-
} else if ((0,
|
|
5644
|
+
} else if ((0, import_utils23.isFunction)(cond)) {
|
|
4527
5645
|
const result = await cond.call(req, permissions);
|
|
4528
5646
|
if (!!result) return next();
|
|
4529
5647
|
}
|
|
@@ -4536,7 +5654,7 @@ var import_express2 = __toESM(require("express"));
|
|
|
4536
5654
|
|
|
4537
5655
|
// src/routers/model-router.ts
|
|
4538
5656
|
var import_express_json_router5 = __toESM(require("@web-ts-toolkit/express-json-router"));
|
|
4539
|
-
var
|
|
5657
|
+
var import_utils27 = require("@web-ts-toolkit/utils");
|
|
4540
5658
|
|
|
4541
5659
|
// src/routers/router-mutation.ts
|
|
4542
5660
|
var MODEL_BUILD_TIME_OPTION_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -4665,6 +5783,7 @@ var import_express_json_router4 = __toESM(require("@web-ts-toolkit/express-json-
|
|
|
4665
5783
|
|
|
4666
5784
|
// src/validation/common.ts
|
|
4667
5785
|
var import_zod2 = require("zod");
|
|
5786
|
+
var import_utils24 = require("@web-ts-toolkit/utils");
|
|
4668
5787
|
var stringOrStringArray = import_zod2.z.union([import_zod2.z.string(), import_zod2.z.array(import_zod2.z.string())]);
|
|
4669
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);
|
|
4670
5789
|
var queryBooleanString = import_zod2.z.enum(["true", "false"]);
|
|
@@ -4725,7 +5844,8 @@ var subPopulateSchema = import_zod2.z.union([
|
|
|
4725
5844
|
select: projectionSchema.optional()
|
|
4726
5845
|
}).passthrough()
|
|
4727
5846
|
]);
|
|
4728
|
-
var
|
|
5847
|
+
var legacyIncludeItemSchema = import_zod2.z.object({
|
|
5848
|
+
mode: import_zod2.z.enum(["legacy"]).optional(),
|
|
4729
5849
|
model: import_zod2.z.string().min(1),
|
|
4730
5850
|
op: import_zod2.z.enum(["list", "read", "count"]),
|
|
4731
5851
|
path: import_zod2.z.string().min(1),
|
|
@@ -4734,8 +5854,139 @@ var includeItemSchema = import_zod2.z.object({
|
|
|
4734
5854
|
foreignField: import_zod2.z.string().min(1),
|
|
4735
5855
|
args: import_zod2.z.unknown().optional().describe("Forwarded to list/read includes. Count includes ignore pagination fields so counts remain exact."),
|
|
4736
5856
|
options: import_zod2.z.unknown().optional()
|
|
4737
|
-
}).passthrough()
|
|
4738
|
-
|
|
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
|
+
]);
|
|
4739
5990
|
var fieldsSchema = import_zod2.z.array(import_zod2.z.string().min(1));
|
|
4740
5991
|
var taskSchema = import_zod2.z.object({
|
|
4741
5992
|
type: import_zod2.z.string().min(1),
|
|
@@ -5272,7 +6523,7 @@ var listBodySchema = import_zod3.z.object({
|
|
|
5272
6523
|
}).passthrough().superRefine((body, ctx) => rejectKeys(body, ctx, ["query"]));
|
|
5273
6524
|
var countBodySchema = import_zod3.z.object({
|
|
5274
6525
|
filter: objectOrArraySchema.optional()
|
|
5275
|
-
}).passthrough().superRefine((body, ctx) => rejectKeys(body, ctx, ["query", "access", "options"]));
|
|
6526
|
+
}).passthrough().superRefine((body, ctx) => rejectKeys(body, ctx, ["query", "access", "options", "include"]));
|
|
5276
6527
|
var readFilterBodySchema = import_zod3.z.object({
|
|
5277
6528
|
filter: objectOrArraySchema.optional(),
|
|
5278
6529
|
select: projectionSchema.optional(),
|
|
@@ -5627,7 +6878,7 @@ var rootDataQueryEntrySchema = import_zod5.z.union([
|
|
|
5627
6878
|
var rootQuerySchema = import_zod5.z.array(import_zod5.z.union([rootModelQueryEntrySchema, rootDataQueryEntrySchema]));
|
|
5628
6879
|
|
|
5629
6880
|
// src/routers/shared.ts
|
|
5630
|
-
var
|
|
6881
|
+
var import_utils25 = require("@web-ts-toolkit/utils");
|
|
5631
6882
|
|
|
5632
6883
|
// src/routers/model-router-collection-routes.ts
|
|
5633
6884
|
function setModelCollectionRoutes(context) {
|
|
@@ -5645,14 +6896,14 @@ function setModelCollectionRoutes(context) {
|
|
|
5645
6896
|
await context.assertAllowed(req, "list");
|
|
5646
6897
|
const { skip, limit, page, page_size, skim, include_permissions, include_count, include_extra_headers } = parseQuery(requestSchemas.listQuery, req.query);
|
|
5647
6898
|
const svc = context.getPublicService(req);
|
|
5648
|
-
const includeCount = (0,
|
|
5649
|
-
const includeExtraHeaders = (0,
|
|
6899
|
+
const includeCount = (0, import_utils25.parseBooleanString)(include_count);
|
|
6900
|
+
const includeExtraHeaders = (0, import_utils25.parseBooleanString)(include_extra_headers);
|
|
5650
6901
|
const result = await svc._list(
|
|
5651
6902
|
{},
|
|
5652
6903
|
{ skip, limit, page, pageSize: page_size },
|
|
5653
6904
|
{
|
|
5654
|
-
skim: (0,
|
|
5655
|
-
includePermissions: (0,
|
|
6905
|
+
skim: (0, import_utils25.parseBooleanString)(skim),
|
|
6906
|
+
includePermissions: (0, import_utils25.parseBooleanString)(include_permissions),
|
|
5656
6907
|
includeCount
|
|
5657
6908
|
}
|
|
5658
6909
|
);
|
|
@@ -5707,7 +6958,7 @@ function setModelCollectionRoutes(context) {
|
|
|
5707
6958
|
context.getRequestSchema("requestSchemas.create")
|
|
5708
6959
|
);
|
|
5709
6960
|
const svc = context.getPublicService(req);
|
|
5710
|
-
const result = await svc._create(data, {}, { includePermissions: (0,
|
|
6961
|
+
const result = await svc._create(data, {}, { includePermissions: (0, import_utils25.parseBooleanString)(include_permissions) });
|
|
5711
6962
|
handleResultError(result);
|
|
5712
6963
|
return formatModelCreatedResponse(result);
|
|
5713
6964
|
});
|
|
@@ -5743,7 +6994,7 @@ function setModelCollectionRoutes(context) {
|
|
|
5743
6994
|
finalData,
|
|
5744
6995
|
{ select: finalSelect, populate: finalPopulate, tasks: finalTasks },
|
|
5745
6996
|
{
|
|
5746
|
-
includePermissions: includePermissions ?? (0,
|
|
6997
|
+
includePermissions: includePermissions ?? (0, import_utils25.parseBooleanString)(include_permissions),
|
|
5747
6998
|
populateAccess
|
|
5748
6999
|
}
|
|
5749
7000
|
);
|
|
@@ -5835,8 +7086,8 @@ function setModelDocumentRoutes(context) {
|
|
|
5835
7086
|
id,
|
|
5836
7087
|
{},
|
|
5837
7088
|
{
|
|
5838
|
-
includePermissions: (0,
|
|
5839
|
-
tryList: (0,
|
|
7089
|
+
includePermissions: (0, import_utils25.parseBooleanString)(include_permissions),
|
|
7090
|
+
tryList: (0, import_utils25.parseBooleanString)(try_list)
|
|
5840
7091
|
}
|
|
5841
7092
|
);
|
|
5842
7093
|
handleResultError(result);
|
|
@@ -5921,8 +7172,8 @@ function setModelDocumentRoutes(context) {
|
|
|
5921
7172
|
data,
|
|
5922
7173
|
{},
|
|
5923
7174
|
{
|
|
5924
|
-
returningAll: (0,
|
|
5925
|
-
includePermissions: (0,
|
|
7175
|
+
returningAll: (0, import_utils25.parseBooleanString)(returning_all),
|
|
7176
|
+
includePermissions: (0, import_utils25.parseBooleanString)(include_permissions)
|
|
5926
7177
|
}
|
|
5927
7178
|
);
|
|
5928
7179
|
handleResultError(result);
|
|
@@ -5968,8 +7219,8 @@ function setModelDocumentRoutes(context) {
|
|
|
5968
7219
|
finalData,
|
|
5969
7220
|
{ select: finalSelect, populate: finalPopulate, tasks: finalTasks },
|
|
5970
7221
|
{
|
|
5971
|
-
returningAll: returningAll ?? (0,
|
|
5972
|
-
includePermissions: includePermissions ?? (0,
|
|
7222
|
+
returningAll: returningAll ?? (0, import_utils25.parseBooleanString)(returning_all),
|
|
7223
|
+
includePermissions: includePermissions ?? (0, import_utils25.parseBooleanString)(include_permissions),
|
|
5973
7224
|
populateAccess
|
|
5974
7225
|
}
|
|
5975
7226
|
);
|
|
@@ -5997,8 +7248,8 @@ function setModelDocumentRoutes(context) {
|
|
|
5997
7248
|
body,
|
|
5998
7249
|
{},
|
|
5999
7250
|
{
|
|
6000
|
-
returningAll: (0,
|
|
6001
|
-
includePermissions: (0,
|
|
7251
|
+
returningAll: (0, import_utils25.parseBooleanString)(returning_all),
|
|
7252
|
+
includePermissions: (0, import_utils25.parseBooleanString)(include_permissions)
|
|
6002
7253
|
}
|
|
6003
7254
|
);
|
|
6004
7255
|
handleResultError(result);
|
|
@@ -6042,8 +7293,8 @@ function setModelDocumentRoutes(context) {
|
|
|
6042
7293
|
finalData,
|
|
6043
7294
|
{ select: finalSelect, populate: finalPopulate, tasks: finalTasks },
|
|
6044
7295
|
{
|
|
6045
|
-
returningAll: returningAll ?? (0,
|
|
6046
|
-
includePermissions: includePermissions ?? (0,
|
|
7296
|
+
returningAll: returningAll ?? (0, import_utils25.parseBooleanString)(returning_all),
|
|
7297
|
+
includePermissions: includePermissions ?? (0, import_utils25.parseBooleanString)(include_permissions),
|
|
6047
7298
|
populateAccess
|
|
6048
7299
|
}
|
|
6049
7300
|
);
|
|
@@ -6281,11 +7532,11 @@ function setModelSubDocumentRoutes(context) {
|
|
|
6281
7532
|
}
|
|
6282
7533
|
|
|
6283
7534
|
// src/openapi/route-registration.ts
|
|
6284
|
-
var
|
|
7535
|
+
var import_utils26 = require("@web-ts-toolkit/utils");
|
|
6285
7536
|
function registerOpenApiRoute(runtime, basePath, defaultTag, route) {
|
|
6286
7537
|
runtime.registerOpenApiRoute({
|
|
6287
7538
|
...route,
|
|
6288
|
-
path: (0,
|
|
7539
|
+
path: (0, import_utils26.normalizeUrlPath)(basePath + route.path),
|
|
6289
7540
|
tags: route.tags ?? [defaultTag]
|
|
6290
7541
|
});
|
|
6291
7542
|
}
|
|
@@ -6293,8 +7544,8 @@ function registerOpenApiRoute(runtime, basePath, defaultTag, route) {
|
|
|
6293
7544
|
// src/routers/model-router.ts
|
|
6294
7545
|
var clientErrors2 = import_express_json_router5.default.clientErrors;
|
|
6295
7546
|
function setOption(parentKey, optionKey, option) {
|
|
6296
|
-
const key = (0,
|
|
6297
|
-
const value = (0,
|
|
7547
|
+
const key = (0, import_utils27.isUndefined)(option) ? parentKey : `${parentKey}.${optionKey}`;
|
|
7548
|
+
const value = (0, import_utils27.isUndefined)(option) ? optionKey : option;
|
|
6298
7549
|
assertMutableRouterOption("model", key);
|
|
6299
7550
|
this.runtime.setModelOption(this.modelName, key, value);
|
|
6300
7551
|
return this;
|
|
@@ -6456,7 +7707,7 @@ var ModelRouter = class _ModelRouter {
|
|
|
6456
7707
|
this.runtime = runtime;
|
|
6457
7708
|
this.runtime.setModelOptions(modelName, initialOptions);
|
|
6458
7709
|
this.options = this.runtime.getModelOptions(modelName);
|
|
6459
|
-
this.fullBasePath = (0,
|
|
7710
|
+
this.fullBasePath = (0, import_utils27.normalizeUrlPath)(this.options.parentPath + this.options.basePath);
|
|
6460
7711
|
this.modelName = modelName;
|
|
6461
7712
|
this.router = new import_express_json_router5.default(this.options.basePath, createSetCore(this.runtime), accessRouterResponseHandler);
|
|
6462
7713
|
this.model = new model_default(modelName, this.runtime);
|
|
@@ -6536,13 +7787,13 @@ var ModelRouter = class _ModelRouter {
|
|
|
6536
7787
|
}
|
|
6537
7788
|
logEndpoints() {
|
|
6538
7789
|
runWithRuntime(this.runtime, () => {
|
|
6539
|
-
(0,
|
|
6540
|
-
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)}`);
|
|
6541
7792
|
});
|
|
6542
7793
|
});
|
|
6543
7794
|
}
|
|
6544
7795
|
set(keyOrOptions, value) {
|
|
6545
|
-
if (arguments.length === 2 && (0,
|
|
7796
|
+
if (arguments.length === 2 && (0, import_utils27.isString)(keyOrOptions)) {
|
|
6546
7797
|
assertMutableRouterOption("model", keyOrOptions);
|
|
6547
7798
|
this.runtime.setModelOption(
|
|
6548
7799
|
this.modelName,
|
|
@@ -6550,7 +7801,7 @@ var ModelRouter = class _ModelRouter {
|
|
|
6550
7801
|
value
|
|
6551
7802
|
);
|
|
6552
7803
|
}
|
|
6553
|
-
if (arguments.length === 1 && (0,
|
|
7804
|
+
if (arguments.length === 1 && (0, import_utils27.isPlainObject)(keyOrOptions)) {
|
|
6554
7805
|
assertMutableRouterOptions("model", keyOrOptions);
|
|
6555
7806
|
this.runtime.setModelOptions(this.modelName, keyOrOptions);
|
|
6556
7807
|
}
|
|
@@ -6573,7 +7824,7 @@ var ModelRouter = class _ModelRouter {
|
|
|
6573
7824
|
|
|
6574
7825
|
// src/routers/root-router.ts
|
|
6575
7826
|
var import_express_json_router6 = __toESM(require("@web-ts-toolkit/express-json-router"));
|
|
6576
|
-
var
|
|
7827
|
+
var import_utils28 = require("@web-ts-toolkit/utils");
|
|
6577
7828
|
|
|
6578
7829
|
// src/core-data.ts
|
|
6579
7830
|
var DataCore = class {
|
|
@@ -6839,7 +8090,7 @@ var RootRouter = class {
|
|
|
6839
8090
|
this.setRoutes();
|
|
6840
8091
|
}
|
|
6841
8092
|
registerOpenApiRoute(route) {
|
|
6842
|
-
registerOpenApiRoute(this.runtime, (0,
|
|
8093
|
+
registerOpenApiRoute(this.runtime, (0, import_utils28.normalizeUrlPath)(this.basename), "root", route);
|
|
6843
8094
|
}
|
|
6844
8095
|
wrapResult(index, item, result) {
|
|
6845
8096
|
return {
|
|
@@ -6900,7 +8151,7 @@ var RootRouter = class {
|
|
|
6900
8151
|
groupItemsByOrder(items) {
|
|
6901
8152
|
const groups = /* @__PURE__ */ new Map();
|
|
6902
8153
|
items.forEach((item, index) => {
|
|
6903
|
-
const order = (0,
|
|
8154
|
+
const order = (0, import_utils28.isNumber)(item.order) ? item.order : 0;
|
|
6904
8155
|
const group = groups.get(order);
|
|
6905
8156
|
if (group) {
|
|
6906
8157
|
group.push({ index, item });
|
|
@@ -6971,7 +8222,7 @@ var RootRouter = class {
|
|
|
6971
8222
|
const arrResult = await this.runGroup(req, groupedItems[x]);
|
|
6972
8223
|
results.push(...arrResult);
|
|
6973
8224
|
}
|
|
6974
|
-
return (0,
|
|
8225
|
+
return (0, import_utils28.orderBy)(results, ["index"], ["asc"]);
|
|
6975
8226
|
});
|
|
6976
8227
|
this.registerOpenApiRoute({
|
|
6977
8228
|
method: "post",
|
|
@@ -6989,11 +8240,11 @@ var RootRouter = class {
|
|
|
6989
8240
|
|
|
6990
8241
|
// src/routers/data-router.ts
|
|
6991
8242
|
var import_express_json_router7 = __toESM(require("@web-ts-toolkit/express-json-router"));
|
|
6992
|
-
var
|
|
8243
|
+
var import_utils29 = require("@web-ts-toolkit/utils");
|
|
6993
8244
|
var clientErrors4 = import_express_json_router7.default.clientErrors;
|
|
6994
8245
|
function setOption2(parentKey, optionKey, option) {
|
|
6995
|
-
const key = (0,
|
|
6996
|
-
const value = (0,
|
|
8246
|
+
const key = (0, import_utils29.isUndefined)(option) ? parentKey : `${parentKey}.${optionKey}`;
|
|
8247
|
+
const value = (0, import_utils29.isUndefined)(option) ? optionKey : option;
|
|
6997
8248
|
assertMutableRouterOption("data", key);
|
|
6998
8249
|
this.runtime.setDataOption(this.dataName, key, value);
|
|
6999
8250
|
return this;
|
|
@@ -7034,7 +8285,7 @@ var DataRouter = class {
|
|
|
7034
8285
|
this.runtime = runtime;
|
|
7035
8286
|
this.runtime.setDataOptions(dataName, initialOptions);
|
|
7036
8287
|
this.options = this.runtime.getDataOptions(dataName);
|
|
7037
|
-
this.fullBasePath = (0,
|
|
8288
|
+
this.fullBasePath = (0, import_utils29.normalizeUrlPath)(this.options.parentPath + this.options.basePath);
|
|
7038
8289
|
this.dataName = dataName;
|
|
7039
8290
|
this.router = new import_express_json_router7.default(this.options.basePath, createSetDataCore(this.runtime), accessRouterResponseHandler);
|
|
7040
8291
|
this.setCollectionRoutes();
|
|
@@ -7067,8 +8318,8 @@ var DataRouter = class {
|
|
|
7067
8318
|
req.query
|
|
7068
8319
|
);
|
|
7069
8320
|
const svc = this.getService(req);
|
|
7070
|
-
const includeCount = (0,
|
|
7071
|
-
const includeExtraHeaders = (0,
|
|
8321
|
+
const includeCount = (0, import_utils25.parseBooleanString)(include_count);
|
|
8322
|
+
const includeExtraHeaders = (0, import_utils25.parseBooleanString)(include_extra_headers);
|
|
7072
8323
|
const result = await svc.find(
|
|
7073
8324
|
{},
|
|
7074
8325
|
{ skip, limit, page, pageSize: page_size },
|
|
@@ -7186,11 +8437,11 @@ var DataRouter = class {
|
|
|
7186
8437
|
});
|
|
7187
8438
|
}
|
|
7188
8439
|
set(keyOrOptions, value) {
|
|
7189
|
-
if (arguments.length === 2 && (0,
|
|
8440
|
+
if (arguments.length === 2 && (0, import_utils29.isString)(keyOrOptions)) {
|
|
7190
8441
|
assertMutableRouterOption("data", keyOrOptions);
|
|
7191
8442
|
this.runtime.setDataOption(this.dataName, keyOrOptions, value);
|
|
7192
8443
|
}
|
|
7193
|
-
if (arguments.length === 1 && (0,
|
|
8444
|
+
if (arguments.length === 1 && (0, import_utils29.isPlainObject)(keyOrOptions)) {
|
|
7194
8445
|
assertMutableRouterOptions("data", keyOrOptions);
|
|
7195
8446
|
this.runtime.setDataOptions(this.dataName, keyOrOptions);
|
|
7196
8447
|
}
|
|
@@ -7313,7 +8564,7 @@ function createRuntimeApi(runtime) {
|
|
|
7313
8564
|
const wtt2 = runtimeMiddleware;
|
|
7314
8565
|
wtt2.runtime = runtime;
|
|
7315
8566
|
wtt2.createRouter = function(modelName, options) {
|
|
7316
|
-
if ((0,
|
|
8567
|
+
if ((0, import_utils30.isUndefined)(options)) {
|
|
7317
8568
|
return new RootRouter(modelName, runtime);
|
|
7318
8569
|
}
|
|
7319
8570
|
if (typeof modelName === "string") {
|
|
@@ -7334,10 +8585,10 @@ function createRuntimeApi(runtime) {
|
|
|
7334
8585
|
};
|
|
7335
8586
|
wtt2.combineRoutes = combineRoutes;
|
|
7336
8587
|
wtt2.set = function(keyOrOptions, value) {
|
|
7337
|
-
if (arguments.length === 2 && (0,
|
|
8588
|
+
if (arguments.length === 2 && (0, import_utils30.isString)(keyOrOptions)) {
|
|
7338
8589
|
return runtime.setGlobalOption(keyOrOptions, value);
|
|
7339
8590
|
}
|
|
7340
|
-
if (arguments.length === 1 && (0,
|
|
8591
|
+
if (arguments.length === 1 && (0, import_utils30.isPlainObject)(keyOrOptions)) {
|
|
7341
8592
|
return runtime.setGlobalOptions(keyOrOptions);
|
|
7342
8593
|
}
|
|
7343
8594
|
};
|