@web-ts-toolkit/access-router 0.42.2 → 0.44.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
@@ -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
 
@@ -1423,8 +1455,7 @@ var AccessRuntime = class {
1423
1455
  }
1424
1456
  setDataOptions(dataName, options) {
1425
1457
  const manager = this.getOrCreateDataOptions(dataName);
1426
- const currentDataOptions = manager.fetch();
1427
- manager.assign({ ...currentDataOptions, ...options });
1458
+ manager.assign(options);
1428
1459
  }
1429
1460
  setDataOption(dataName, key, value) {
1430
1461
  const manager = this.getOrCreateDataOptions(dataName);
@@ -1434,6 +1465,11 @@ var AccessRuntime = class {
1434
1465
  const manager = this.getOrCreateDataOptions(dataName);
1435
1466
  return manager.fetch();
1436
1467
  }
1468
+ getDataSnapshot(dataName) {
1469
+ const manager = this.getOrCreateDataOptions(dataName);
1470
+ const snapshot = manager.get("data");
1471
+ return snapshot ?? [];
1472
+ }
1437
1473
  getDataOption(dataName, key, defaultValue) {
1438
1474
  const manager = this.getOrCreateDataOptions(dataName);
1439
1475
  return getNestedOption(manager, key, defaultValue);
@@ -1555,6 +1591,9 @@ var getRuntime4 = () => getActiveRuntime() ?? defaultRuntime;
1555
1591
  var getDataOptions = (dataName) => {
1556
1592
  return getRuntime4().getDataOptions(dataName);
1557
1593
  };
1594
+ var getDataSnapshot = (dataName) => {
1595
+ return getRuntime4().getDataSnapshot(dataName);
1596
+ };
1558
1597
  var getDataOption = (dataName, key, defaultValue) => {
1559
1598
  return getRuntime4().getDataOption(dataName, key, defaultValue);
1560
1599
  };
@@ -1564,9 +1603,6 @@ var getRuntime5 = () => getActiveRuntime() ?? defaultRuntime;
1564
1603
  var getModelRef = (modelName, refPath) => {
1565
1604
  return getRuntime5().getModelRef(modelName, refPath);
1566
1605
  };
1567
- var getModelSub = (modelName) => {
1568
- return getRuntime5().getModelSub(modelName);
1569
- };
1570
1606
 
1571
1607
  // src/services/base.ts
1572
1608
  var import_utils12 = require("@web-ts-toolkit/utils");
@@ -3474,7 +3510,7 @@ var DataService = class {
3474
3510
  this.req = req;
3475
3511
  this.dataName = dataName;
3476
3512
  this.options = getDataOptions(dataName);
3477
- this.data = this.options.data ?? [];
3513
+ this.data = getDataSnapshot(dataName);
3478
3514
  }
3479
3515
  async findOne(filter2, args, options) {
3480
3516
  const filterErrors = validateClientFilter(filter2);
@@ -3655,8 +3691,11 @@ var copyAndDepopulate = (docObject, operations, options = { mutable: true, idFie
3655
3691
  if (!(0, import_utils17.isPlainObject)(target)) return ret;
3656
3692
  const targetObject = target;
3657
3693
  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);
3694
+ if ((0, import_utils17.isArray)(next)) {
3695
+ for (const item of next) {
3696
+ ret.push(item);
3697
+ }
3698
+ } else if (next !== null && next !== void 0) ret.push(next);
3660
3699
  return ret;
3661
3700
  }, []);
3662
3701
  }
@@ -3831,11 +3870,12 @@ async function resolveAccessFilter({
3831
3870
  }) {
3832
3871
  let nextFilter = normalizeFilter(filter2);
3833
3872
  const overrideFilterFn = getOption(`overrideFilter.${access}`, null);
3834
- if ((0, import_utils19.isFunction)(overrideFilterFn)) {
3873
+ if (nextFilter !== false && (0, import_utils19.isFunction)(overrideFilterFn)) {
3835
3874
  nextFilter = normalizeFilter(await overrideFilterFn.call(req, nextFilter, permissions));
3836
3875
  }
3876
+ if (nextFilter === false) return false;
3837
3877
  const baseFilterFn = getOption(`baseFilter.${access}`, null);
3838
- if (!(0, import_utils19.isFunction)(baseFilterFn)) return nextFilter || {};
3878
+ if (!(0, import_utils19.isFunction)(baseFilterFn)) return nextFilter ?? {};
3839
3879
  const baseFilter = normalizeFilter(
3840
3880
  cache.has(cacheKey) ? cache.get(cacheKey) : await baseFilterFn.call(req, permissions)
3841
3881
  );
@@ -3843,7 +3883,7 @@ async function resolveAccessFilter({
3843
3883
  cache.set(cacheKey, baseFilter);
3844
3884
  }
3845
3885
  if (baseFilter === false) return false;
3846
- if (!baseFilter) return nextFilter || {};
3886
+ if (!baseFilter) return nextFilter ?? {};
3847
3887
  if (!nextFilter) return baseFilter;
3848
3888
  return optimizeAndFilter([baseFilter, nextFilter]);
3849
3889
  }
@@ -3851,15 +3891,30 @@ function getRequestPermissions(req) {
3851
3891
  const requestPermissionField = getGlobalOption("requestPermissionField");
3852
3892
  return new permission_default(req[requestPermissionField] || {});
3853
3893
  }
3894
+ var permissionFieldOwners = /* @__PURE__ */ new WeakMap();
3895
+ function getPermissionFieldOwnerMap(req) {
3896
+ let owners = permissionFieldOwners.get(req);
3897
+ if (!owners) {
3898
+ owners = /* @__PURE__ */ new Map();
3899
+ permissionFieldOwners.set(req, owners);
3900
+ }
3901
+ return owners;
3902
+ }
3854
3903
  async function setRequestPermissions(req) {
3855
3904
  const requestPermissionField = getGlobalOption("requestPermissionField");
3856
- if (req[requestPermissionField]) return;
3905
+ const activeRuntime = getActiveRuntime();
3906
+ const owners = getPermissionFieldOwnerMap(req);
3907
+ const owner = owners.get(requestPermissionField);
3908
+ const hasFieldValue = Boolean(req[requestPermissionField]);
3909
+ if (owner === activeRuntime) return;
3910
+ if (hasFieldValue && owner === void 0) return;
3857
3911
  const globalPermissions = getGlobalOption("globalPermissions");
3858
3912
  if (!(0, import_utils19.isFunction)(globalPermissions)) return;
3859
3913
  const permissions = await globalPermissions.call(req, req);
3860
3914
  if ((0, import_utils19.isPlainObject)(permissions)) req[requestPermissionField] = permissions;
3861
3915
  else if ((0, import_utils19.isArray)(permissions)) req[requestPermissionField] = (0, import_utils19.arrayToRecord)(permissions);
3862
3916
  else if ((0, import_utils19.isString)(permissions)) req[requestPermissionField] = { [permissions]: true };
3917
+ owners.set(requestPermissionField, activeRuntime);
3863
3918
  }
3864
3919
  async function evaluateRouteGuard(req, permissions, routeGuard) {
3865
3920
  const phas = (key) => permissions.has(key);
@@ -3927,17 +3982,14 @@ function getResolvedRequestPermissions(req) {
3927
3982
  async function setResolvedRequestPermissions(req) {
3928
3983
  await setRequestPermissions(req);
3929
3984
  }
3930
- async function canActivateRequest(req, routeGuard) {
3931
- const permissions = getGlobalPermissions(req);
3932
- return evaluateRouteGuard(req, permissions, routeGuard);
3933
- }
3934
3985
  async function initializeAclRequest({
3935
3986
  req,
3936
3987
  flag,
3988
+ runtime,
3937
3989
  createCore,
3938
3990
  assignCore
3939
3991
  }) {
3940
- if (req[flag]) {
3992
+ if (req[flag] === runtime) {
3941
3993
  return null;
3942
3994
  }
3943
3995
  const core = createCore(req);
@@ -3945,7 +3997,7 @@ async function initializeAclRequest({
3945
3997
  assignCore(core);
3946
3998
  req[PERMISSIONS] = core.getPermissions();
3947
3999
  req[PERMISSION_KEYS] = [...req[PERMISSIONS].keys];
3948
- req[flag] = true;
4000
+ req[flag] = runtime;
3949
4001
  return core;
3950
4002
  }
3951
4003
 
@@ -4081,6 +4133,7 @@ async function resolveSelectForRequest({
4081
4133
  // src/core.ts
4082
4134
  var Core = class {
4083
4135
  constructor(req) {
4136
+ this.cachedPermissions = null;
4084
4137
  this.req = req;
4085
4138
  this.caches = {
4086
4139
  baseFilter: new Cache()
@@ -4364,13 +4417,15 @@ var Core = class {
4364
4417
  return docObject;
4365
4418
  }
4366
4419
  getPermissions() {
4420
+ if (this.cachedPermissions) return this.cachedPermissions;
4367
4421
  return getResolvedRequestPermissions(this.req);
4368
4422
  }
4369
4423
  async setPermissions() {
4370
4424
  await setResolvedRequestPermissions(this.req);
4425
+ this.cachedPermissions = getResolvedRequestPermissions(this.req);
4371
4426
  }
4372
4427
  async canActivate(routeGuard) {
4373
- return canActivateRequest(this.req, routeGuard);
4428
+ return evaluateRouteGuard(this.req, this.getGlobalPermissions(), routeGuard);
4374
4429
  }
4375
4430
  async isAllowed(modelName, access) {
4376
4431
  if (access.startsWith("subs")) {
@@ -4406,6 +4461,7 @@ var Core = class {
4406
4461
  return this.getPublicService(modelName);
4407
4462
  }
4408
4463
  getGlobalPermissions() {
4464
+ if (this.cachedPermissions) return this.cachedPermissions;
4409
4465
  return getGlobalPermissions(this.req);
4410
4466
  }
4411
4467
  };
@@ -4415,6 +4471,7 @@ var createSetCore = (runtime = defaultRuntime) => {
4415
4471
  await initializeAclRequest({
4416
4472
  req,
4417
4473
  flag: MIDDLEWARE,
4474
+ runtime,
4418
4475
  createCore: (request) => new Core(request),
4419
4476
  assignCore: (core) => {
4420
4477
  req.macl = core;
@@ -4795,8 +4852,9 @@ function defineRequestSchema(validator, options = {}) {
4795
4852
  };
4796
4853
  }
4797
4854
  function fromZod(schema) {
4798
- return (value) => {
4799
- const result = schema.safeParse(value);
4855
+ return async (value) => {
4856
+ const candidate = schema;
4857
+ const result = typeof candidate.safeParseAsync === "function" ? await candidate.safeParseAsync.call(schema, value) : schema.safeParse(value);
4800
4858
  if (result.success) {
4801
4859
  return {
4802
4860
  success: true,
@@ -4833,6 +4891,9 @@ function fromYup(schema) {
4833
4891
  data
4834
4892
  };
4835
4893
  } catch (error2) {
4894
+ if (!isYupValidationError(error2)) {
4895
+ throw error2;
4896
+ }
4836
4897
  return {
4837
4898
  success: false,
4838
4899
  issues: normalizeYupIssues(error2)
@@ -4857,8 +4918,32 @@ function fromJoi(schema) {
4857
4918
  }
4858
4919
  function fromAjv(validator) {
4859
4920
  return async (value) => {
4860
- const valid = await validator(value);
4861
- if (valid) {
4921
+ const outcome = validator(value);
4922
+ if (isThenable(outcome)) {
4923
+ try {
4924
+ const data = await outcome;
4925
+ if (data === false) {
4926
+ return {
4927
+ success: false,
4928
+ issues: normalizeAjvIssues(validator.errors ?? [])
4929
+ };
4930
+ }
4931
+ return {
4932
+ success: true,
4933
+ data: data === true ? value : data
4934
+ };
4935
+ } catch (error2) {
4936
+ if (!isAjvValidationError(error2)) {
4937
+ throw error2;
4938
+ }
4939
+ return {
4940
+ success: false,
4941
+ issues: normalizeAjvIssues(error2.errors ?? [])
4942
+ };
4943
+ }
4944
+ }
4945
+ const errorsSnapshot = validator.errors ? [...validator.errors] : [];
4946
+ if (outcome) {
4862
4947
  return {
4863
4948
  success: true,
4864
4949
  data: value
@@ -4866,7 +4951,7 @@ function fromAjv(validator) {
4866
4951
  }
4867
4952
  return {
4868
4953
  success: false,
4869
- issues: normalizeAjvIssues(validator.errors ?? [])
4954
+ issues: normalizeAjvIssues(errorsSnapshot)
4870
4955
  };
4871
4956
  };
4872
4957
  }
@@ -4887,6 +4972,25 @@ function fromValibot(schema, safeParse) {
4887
4972
  }
4888
4973
  function fromArkType(type) {
4889
4974
  return async (value) => {
4975
+ if (isStandardSchema(type)) {
4976
+ const result2 = await type["~standard"].validate(value);
4977
+ if (isArkTypeErrors(result2)) {
4978
+ return {
4979
+ success: false,
4980
+ issues: normalizeArkTypeIssues(result2)
4981
+ };
4982
+ }
4983
+ if (isStandardSchemaFailure(result2)) {
4984
+ return {
4985
+ success: false,
4986
+ issues: normalizeArkTypeStandardIssues(result2.issues)
4987
+ };
4988
+ }
4989
+ return {
4990
+ success: true,
4991
+ data: result2.value
4992
+ };
4993
+ }
4890
4994
  const result = await type(value);
4891
4995
  if (!isArkTypeErrors(result)) {
4892
4996
  return {
@@ -4939,6 +5043,9 @@ function fromVine(validator) {
4939
5043
  data: output
4940
5044
  };
4941
5045
  } catch (error2) {
5046
+ if (!isVineValidationError(error2)) {
5047
+ throw error2;
5048
+ }
4942
5049
  return {
4943
5050
  success: false,
4944
5051
  issues: normalizeVineError(error2)
@@ -4999,6 +5106,13 @@ function normalizeArkTypeIssues(issues) {
4999
5106
  }));
5000
5107
  return normalized.length ? normalized : [{ message: issues.summary ?? "Validation failed" }];
5001
5108
  }
5109
+ function normalizeArkTypeStandardIssues(issues) {
5110
+ const normalized = issues.map((issue) => ({
5111
+ message: issue.message ?? issue.problem ?? "Validation failed",
5112
+ path: issue.path?.flatMap((segment) => normalizePathSegment(segment))
5113
+ }));
5114
+ return normalized.length ? normalized : [{ message: "Validation failed" }];
5115
+ }
5002
5116
  function normalizeIoTsIssues(issues) {
5003
5117
  return issues.map((issue) => ({
5004
5118
  message: issue.message ?? "Validation failed",
@@ -5033,25 +5147,64 @@ function normalizeVineField(message) {
5033
5147
  }
5034
5148
  return path.length ? path : void 0;
5035
5149
  }
5150
+ function decodeAjvSegment(segment) {
5151
+ return segment.replace(/~1/g, "/").replace(/~0/g, "~");
5152
+ }
5153
+ function encodePointerSegment(segment) {
5154
+ return encodeURIComponent(segment.replace(/~/g, "~0").replace(/\//g, "~1"));
5155
+ }
5036
5156
  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);
5157
+ const raw = issue.instancePath;
5158
+ const base = raw == null || raw === "" ? [] : (raw.startsWith("/") ? raw.slice(1) : raw).split("/").map(decodeAjvSegment);
5159
+ const missing = issue.params?.missingProperty;
5160
+ if (typeof missing === "string") {
5161
+ return [...base, missing];
5040
5162
  }
5041
- return path;
5163
+ return base;
5042
5164
  }
5043
5165
  function parsePathString(path) {
5044
5166
  if (!path) return void 0;
5045
5167
  return path.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean).map((segment) => /^\d+$/.test(segment) ? Number(segment) : segment);
5046
5168
  }
5047
5169
  function isYupValidationError(error2) {
5048
- return typeof error2 === "object" && error2 !== null && "message" in error2;
5170
+ if (typeof error2 !== "object" || error2 === null || !("message" in error2)) {
5171
+ return false;
5172
+ }
5173
+ const candidate = error2;
5174
+ return candidate.name === "ValidationError" && typeof candidate.message === "string" && Array.isArray(candidate.errors) && Array.isArray(candidate.inner);
5049
5175
  }
5050
5176
  function isArkTypeErrors(value) {
5051
- return Array.isArray(value);
5177
+ if (typeof value !== "object" || value === null) {
5178
+ return false;
5179
+ }
5180
+ const record = value;
5181
+ return record.arkKind === "errors" || record[" arkKind"] === "errors";
5182
+ }
5183
+ function isThenable(value) {
5184
+ return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
5185
+ }
5186
+ function isAjvValidationError(error2) {
5187
+ if (typeof error2 !== "object" || error2 === null || !("errors" in error2)) {
5188
+ return false;
5189
+ }
5190
+ const errors = error2.errors;
5191
+ return errors == null || Array.isArray(errors);
5052
5192
  }
5053
5193
  function isVineValidationError(error2) {
5054
- return typeof error2 === "object" && error2 !== null && "messages" in error2 && Array.isArray(error2.messages);
5194
+ if (typeof error2 !== "object" || error2 === null || !("messages" in error2)) {
5195
+ return false;
5196
+ }
5197
+ const candidate = error2;
5198
+ if (!Array.isArray(candidate.messages) || candidate.messages.length === 0) {
5199
+ return false;
5200
+ }
5201
+ const hasDiscriminator = candidate.code === "E_VALIDATION_ERROR" || candidate.name === "ValidationError";
5202
+ if (!hasDiscriminator) {
5203
+ return false;
5204
+ }
5205
+ return candidate.messages.every(
5206
+ (message) => typeof message === "object" && message !== null && typeof message.message === "string" && typeof message.field === "string"
5207
+ );
5055
5208
  }
5056
5209
  function formatIssue(issue, key, location = "pointer") {
5057
5210
  const path = (issue.path ?? []).map(String);
@@ -5068,7 +5221,7 @@ function formatIssue(issue, key, location = "pointer") {
5068
5221
  };
5069
5222
  }
5070
5223
  function buildPointer(path) {
5071
- return path.length === 0 ? "#" : `#/${path.join("/")}`;
5224
+ return path.length === 0 ? "#" : `#/${path.map(encodePointerSegment).join("/")}`;
5072
5225
  }
5073
5226
 
5074
5227
  // src/validation/model-router.ts
@@ -5569,24 +5722,26 @@ function setModelCollectionRoutes(context) {
5569
5722
  router.post(`/${options.mutationRouteSegment}`, async (req) => {
5570
5723
  await context.assertAllowed(req, "create");
5571
5724
  const { include_permissions } = parseQuery(requestSchemas.createQuery, req.query);
5572
- const body = await parseNestedBodyWithSchema(
5725
+ const intermediateBody = await parseNestedBodyWithSchema(
5573
5726
  advancedCreateBodySchema,
5574
5727
  req.body,
5575
5728
  "data",
5576
5729
  context.getRequestSchema("requestSchemas.advancedCreate.data")
5577
5730
  );
5578
- const { data, select, populate, tasks } = body;
5579
- const advancedOptions = body.options ?? {};
5580
- await parseBodyWithSchema(
5731
+ const { data: nestedData, select: nestedSelect, populate: nestedPopulate, tasks: nestedTasks } = intermediateBody;
5732
+ const nestedOptions = intermediateBody.options ?? {};
5733
+ const finalBody = await parseBodyWithSchema(
5581
5734
  advancedCreateBodySchema,
5582
- { data, select, populate, tasks, options: advancedOptions },
5735
+ { data: nestedData, select: nestedSelect, populate: nestedPopulate, tasks: nestedTasks, options: nestedOptions },
5583
5736
  context.getRequestSchema("requestSchemas.advancedCreate.default") ?? context.getRequestSchema("requestSchemas.advancedCreate")
5584
5737
  );
5585
- const { includePermissions, populateAccess } = advancedOptions;
5738
+ const { data: finalData, select: finalSelect, populate: finalPopulate, tasks: finalTasks } = finalBody;
5739
+ const finalOptions = finalBody.options ?? {};
5740
+ const { includePermissions, populateAccess } = finalOptions;
5586
5741
  const svc = context.getPublicService(req);
5587
5742
  const result = await svc._create(
5588
- data,
5589
- { select, populate, tasks },
5743
+ finalData,
5744
+ { select: finalSelect, populate: finalPopulate, tasks: finalTasks },
5590
5745
  {
5591
5746
  includePermissions: includePermissions ?? (0, import_utils23.parseBooleanString)(include_permissions),
5592
5747
  populateAccess
@@ -5785,25 +5940,33 @@ function setModelDocumentRoutes(context) {
5785
5940
  await context.assertAllowed(req, "update");
5786
5941
  const id = parsePathParam(req.params[options.idParam], options.idParam);
5787
5942
  const { returning_all, include_permissions } = parseQuery(requestSchemas.updateQuery, req.query);
5788
- const body = await parseNestedBodyWithSchema(
5943
+ const intermediateBody = await parseNestedBodyWithSchema(
5789
5944
  advancedUpdateBodySchema,
5790
5945
  req.body,
5791
5946
  "data",
5792
5947
  context.getRequestSchema("requestSchemas.advancedUpdate.data")
5793
5948
  );
5794
- const { data, select, populate, tasks } = body;
5795
- const advancedOptions = body.options ?? {};
5796
- await parseBodyWithSchema(
5949
+ const { data: nestedData, select: nestedSelect, populate: nestedPopulate, tasks: nestedTasks } = intermediateBody;
5950
+ const nestedOptions = intermediateBody.options ?? {};
5951
+ const finalBody = await parseBodyWithSchema(
5797
5952
  advancedUpdateBodySchema,
5798
- { data, select, populate, tasks, options: advancedOptions },
5953
+ {
5954
+ data: nestedData,
5955
+ select: nestedSelect,
5956
+ populate: nestedPopulate,
5957
+ tasks: nestedTasks,
5958
+ options: nestedOptions
5959
+ },
5799
5960
  context.getRequestSchema("requestSchemas.advancedUpdate.default") ?? context.getRequestSchema("requestSchemas.advancedUpdate")
5800
5961
  );
5801
- const { returningAll, includePermissions, populateAccess } = advancedOptions;
5962
+ const { data: finalData, select: finalSelect, populate: finalPopulate, tasks: finalTasks } = finalBody;
5963
+ const finalOptions = finalBody.options ?? {};
5964
+ const { returningAll, includePermissions, populateAccess } = finalOptions;
5802
5965
  const svc = context.getPublicService(req);
5803
5966
  const result = await svc._update(
5804
5967
  id,
5805
- data,
5806
- { select, populate, tasks },
5968
+ finalData,
5969
+ { select: finalSelect, populate: finalPopulate, tasks: finalTasks },
5807
5970
  {
5808
5971
  returningAll: returningAll ?? (0, import_utils23.parseBooleanString)(returning_all),
5809
5972
  includePermissions: includePermissions ?? (0, import_utils23.parseBooleanString)(include_permissions),
@@ -5853,23 +6016,31 @@ function setModelDocumentRoutes(context) {
5853
6016
  await context.assertAllowed(req, "upsert");
5854
6017
  const svc = context.getPublicService(req);
5855
6018
  const { returning_all, include_permissions } = parseQuery(requestSchemas.upsertQuery, req.query);
5856
- const body = await parseNestedBodyWithSchema(
6019
+ const intermediateBody = await parseNestedBodyWithSchema(
5857
6020
  advancedUpsertBodySchema,
5858
6021
  req.body,
5859
6022
  "data",
5860
6023
  context.getRequestSchema("requestSchemas.advancedUpsert.data")
5861
6024
  );
5862
- const { data, select, populate, tasks } = body;
5863
- const advancedOptions = body.options ?? {};
5864
- await parseBodyWithSchema(
6025
+ const { data: nestedData, select: nestedSelect, populate: nestedPopulate, tasks: nestedTasks } = intermediateBody;
6026
+ const nestedOptions = intermediateBody.options ?? {};
6027
+ const finalBody = await parseBodyWithSchema(
5865
6028
  advancedUpsertBodySchema,
5866
- { data, select, populate, tasks, options: advancedOptions },
6029
+ {
6030
+ data: nestedData,
6031
+ select: nestedSelect,
6032
+ populate: nestedPopulate,
6033
+ tasks: nestedTasks,
6034
+ options: nestedOptions
6035
+ },
5867
6036
  context.getRequestSchema("requestSchemas.advancedUpsert.default") ?? context.getRequestSchema("requestSchemas.advancedUpsert")
5868
6037
  );
5869
- const { returningAll, includePermissions, populateAccess } = advancedOptions;
6038
+ const { data: finalData, select: finalSelect, populate: finalPopulate, tasks: finalTasks } = finalBody;
6039
+ const finalOptions = finalBody.options ?? {};
6040
+ const { returningAll, includePermissions, populateAccess } = finalOptions;
5870
6041
  const result = await svc._upsert(
5871
- data,
5872
- { select, populate, tasks },
6042
+ finalData,
6043
+ { select: finalSelect, populate: finalPopulate, tasks: finalTasks },
5873
6044
  {
5874
6045
  returningAll: returningAll ?? (0, import_utils23.parseBooleanString)(returning_all),
5875
6046
  includePermissions: includePermissions ?? (0, import_utils23.parseBooleanString)(include_permissions),
@@ -5939,7 +6110,7 @@ function setModelDocumentRoutes(context) {
5939
6110
 
5940
6111
  // src/routers/model-router-subdocument-routes.ts
5941
6112
  function setModelSubDocumentRoutes(context) {
5942
- const subs = getModelSub(context.modelName);
6113
+ const subs = context.runtime.getModelSub(context.modelName);
5943
6114
  for (let x = 0; x < subs.length; x++) {
5944
6115
  const sub = subs[x];
5945
6116
  context.router.get(`/:${context.options.idParam}/${sub}`, async (req) => {
@@ -6164,7 +6335,9 @@ var ModelRouter = class _ModelRouter {
6164
6335
  "baseFilter"
6165
6336
  );
6166
6337
  /**
6167
- * The override filter definitions applied in every query transaction.
6338
+ * Trusted filter replacements applied before base-filter composition.
6339
+ * They may replace ordinary filters, but cannot replace an existing `false` denial.
6340
+ * Returning `false` denies the transaction.
6168
6341
  * @operation `list`, `read`, `update`, `delete`
6169
6342
  */
6170
6343
  this.overrideFilter = setOption.bind(this, "overrideFilter");
@@ -6324,6 +6497,7 @@ var ModelRouter = class _ModelRouter {
6324
6497
  modelName: this.modelName,
6325
6498
  router: this.router,
6326
6499
  options: this.options,
6500
+ runtime: this.runtime,
6327
6501
  getRequestSchema: this.getRequestSchema.bind(this),
6328
6502
  getPublicService: this.getPublicService.bind(this),
6329
6503
  assertAllowed: this.assertAllowed.bind(this),
@@ -6338,6 +6512,7 @@ var ModelRouter = class _ModelRouter {
6338
6512
  modelName: this.modelName,
6339
6513
  router: this.router,
6340
6514
  options: this.options,
6515
+ runtime: this.runtime,
6341
6516
  getRequestSchema: this.getRequestSchema.bind(this),
6342
6517
  getPublicService: this.getPublicService.bind(this),
6343
6518
  assertAllowed: this.assertAllowed.bind(this),
@@ -6352,6 +6527,7 @@ var ModelRouter = class _ModelRouter {
6352
6527
  modelName: this.modelName,
6353
6528
  router: this.router,
6354
6529
  options: this.options,
6530
+ runtime: this.runtime,
6355
6531
  getRequestSchema: this.getRequestSchema.bind(this),
6356
6532
  getPublicService: this.getPublicService.bind(this),
6357
6533
  assertAllowed: this.assertAllowed.bind(this),
@@ -6402,6 +6578,7 @@ var import_utils26 = require("@web-ts-toolkit/utils");
6402
6578
  // src/core-data.ts
6403
6579
  var DataCore = class {
6404
6580
  constructor(req) {
6581
+ this.cachedPermissions = null;
6405
6582
  this.req = req;
6406
6583
  this.caches = {
6407
6584
  baseFilter: new Cache()
@@ -6495,13 +6672,15 @@ var DataCore = class {
6495
6672
  });
6496
6673
  }
6497
6674
  getPermissions() {
6675
+ if (this.cachedPermissions) return this.cachedPermissions;
6498
6676
  return getResolvedRequestPermissions(this.req);
6499
6677
  }
6500
6678
  async setPermissions() {
6501
6679
  await setResolvedRequestPermissions(this.req);
6680
+ this.cachedPermissions = getResolvedRequestPermissions(this.req);
6502
6681
  }
6503
6682
  async canActivate(routeGuard) {
6504
- return canActivateRequest(this.req, routeGuard);
6683
+ return evaluateRouteGuard(this.req, this.getGlobalPermissions(), routeGuard);
6505
6684
  }
6506
6685
  async isAllowed(dataName, access) {
6507
6686
  const operationAccess = getDataOption(dataName, `operationAccess.${access}`);
@@ -6517,6 +6696,7 @@ var DataCore = class {
6517
6696
  return this.getService(dataName);
6518
6697
  }
6519
6698
  getGlobalPermissions() {
6699
+ if (this.cachedPermissions) return this.cachedPermissions;
6520
6700
  return getGlobalPermissions(this.req);
6521
6701
  }
6522
6702
  };
@@ -6526,6 +6706,7 @@ var createSetDataCore = (runtime = defaultRuntime) => {
6526
6706
  await initializeAclRequest({
6527
6707
  req,
6528
6708
  flag: DATA_MIDDLEWARE,
6709
+ runtime,
6529
6710
  createCore: (request) => new DataCore(request),
6530
6711
  assignCore: (core) => {
6531
6712
  req.dacl = core;
@@ -6830,6 +7011,11 @@ var DataRouter = class {
6830
7011
  this,
6831
7012
  "baseFilter"
6832
7013
  );
7014
+ /**
7015
+ * Trusted filter replacements applied before base-filter composition.
7016
+ * They may replace ordinary filters, but cannot replace an existing `false` denial.
7017
+ * Returning `false` denies the transaction.
7018
+ */
6833
7019
  this.overrideFilter = setOption2.bind(this, "overrideFilter");
6834
7020
  this.decorate = setOption2.bind(
6835
7021
  this,