@web-ts-toolkit/access-router 0.43.0 → 0.45.0

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