@web-ts-toolkit/access-router 0.43.0 → 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.mjs CHANGED
@@ -784,6 +784,29 @@ var cloneConfigValue = (value) => {
784
784
  ])
785
785
  );
786
786
  };
787
+ var cloneForRead = (value) => {
788
+ if (Array.isArray(value)) {
789
+ if (Object.isFrozen(value)) {
790
+ return value;
791
+ }
792
+ return value.map((item) => cloneForRead(item));
793
+ }
794
+ if (!value || typeof value !== "object") {
795
+ return value;
796
+ }
797
+ if (Object.getPrototypeOf(value) !== Object.prototype) {
798
+ return value;
799
+ }
800
+ if (Object.isFrozen(value)) {
801
+ return value;
802
+ }
803
+ return Object.fromEntries(
804
+ Object.entries(value).map(([entryKey, entryValue]) => [
805
+ entryKey,
806
+ cloneForRead(entryValue)
807
+ ])
808
+ );
809
+ };
787
810
  var freezeConfigValue = (value) => {
788
811
  if (!value || typeof value !== "object") {
789
812
  return value;
@@ -799,6 +822,7 @@ var freezeConfigValue = (value) => {
799
822
  }
800
823
  return value;
801
824
  };
825
+ var cloneAndFreezeForStore = (value) => freezeConfigValue(cloneForRead(value));
802
826
  var getNestedOption = (manager, key, defaultValue) => {
803
827
  const keys2 = String(key).split(".");
804
828
  if (keys2.length === 1) {
@@ -828,6 +852,14 @@ var OptionsManager = class {
828
852
  target[keystr] = value;
829
853
  _this.listeners[keystr] && _this.listeners[keystr].call(_this, value, keystr, target, oldvalue);
830
854
  return true;
855
+ },
856
+ defineProperty(target, key, descriptor) {
857
+ const keystr = String(key);
858
+ const oldvalue = target[keystr];
859
+ const result = Reflect.defineProperty(target, key, descriptor);
860
+ const newValue = target[keystr];
861
+ _this.listeners[keystr] && _this.listeners[keystr].call(_this, newValue, keystr, target, oldvalue);
862
+ return result;
831
863
  }
832
864
  });
833
865
  }
@@ -841,18 +873,18 @@ var OptionsManager = class {
841
873
  set(key, value) {
842
874
  const path = toPath(key);
843
875
  if (path.length <= 1) {
844
- set2(this.currentOptions, path, this.preserveKeys.has(String(path[0])) ? value : cloneConfigValue(value));
876
+ set2(this.currentOptions, path, this.preserveKeys.has(String(path[0])) ? value : cloneAndFreezeForStore(value));
845
877
  return;
846
878
  }
847
879
  const [rootKey, ...nestedPath] = path;
848
880
  const currentRoot = get2(this.currentOptions, [rootKey], void 0);
849
881
  const baseRoot = isPlainObject8(currentRoot) ? currentRoot : {};
850
882
  const nextRoot = cloneConfigValue(baseRoot);
851
- set2(nextRoot, nestedPath, cloneConfigValue(value));
852
- set2(this.currentOptions, [rootKey], nextRoot);
883
+ set2(nextRoot, nestedPath, cloneForRead(value));
884
+ set2(this.currentOptions, [rootKey], freezeConfigValue(nextRoot));
853
885
  }
854
886
  fetch() {
855
- const cloned = cloneConfigValue(this.currentOptions);
887
+ const cloned = cloneForRead(this.currentOptions);
856
888
  for (const key of this.preserveKeys) {
857
889
  if (key in this.currentOptions) {
858
890
  cloned[key] = this.currentOptions[key];
@@ -861,20 +893,20 @@ var OptionsManager = class {
861
893
  return freezeConfigValue(cloned);
862
894
  }
863
895
  assign(options) {
864
- const cloned = cloneConfigValue(options);
896
+ const cloned = cloneForRead(options);
865
897
  for (const key of this.preserveKeys) {
866
898
  if (key in options) {
867
899
  cloned[key] = options[key];
868
900
  }
869
901
  }
870
- assign(this.currentOptions, cloned);
902
+ assign(this.currentOptions, freezeConfigValue(cloned));
871
903
  }
872
904
  onchange(key, func) {
873
905
  set2(this.listeners, key, func);
874
906
  return this;
875
907
  }
876
908
  snapshot() {
877
- const cloned = cloneConfigValue(this.currentOptions);
909
+ const cloned = cloneForRead(this.currentOptions);
878
910
  for (const key of this.preserveKeys) {
879
911
  if (key in this.currentOptions) {
880
912
  cloned[key] = this.currentOptions[key];
@@ -890,13 +922,13 @@ var OptionsManager = class {
890
922
  delete current[key];
891
923
  }
892
924
  }
893
- const cloned = cloneConfigValue(snap);
925
+ const cloned = cloneForRead(snap);
894
926
  for (const key of this.preserveKeys) {
895
927
  if (key in snap) {
896
928
  cloned[key] = snap[key];
897
929
  }
898
930
  }
899
- assign(current, cloned);
931
+ assign(current, freezeConfigValue(cloned));
900
932
  }
901
933
  };
902
934
 
@@ -1366,8 +1398,7 @@ var AccessRuntime = class {
1366
1398
  }
1367
1399
  setDataOptions(dataName, options) {
1368
1400
  const manager = this.getOrCreateDataOptions(dataName);
1369
- const currentDataOptions = manager.fetch();
1370
- manager.assign({ ...currentDataOptions, ...options });
1401
+ manager.assign(options);
1371
1402
  }
1372
1403
  setDataOption(dataName, key, value) {
1373
1404
  const manager = this.getOrCreateDataOptions(dataName);
@@ -1377,6 +1408,11 @@ var AccessRuntime = class {
1377
1408
  const manager = this.getOrCreateDataOptions(dataName);
1378
1409
  return manager.fetch();
1379
1410
  }
1411
+ getDataSnapshot(dataName) {
1412
+ const manager = this.getOrCreateDataOptions(dataName);
1413
+ const snapshot = manager.get("data");
1414
+ return snapshot ?? [];
1415
+ }
1380
1416
  getDataOption(dataName, key, defaultValue) {
1381
1417
  const manager = this.getOrCreateDataOptions(dataName);
1382
1418
  return getNestedOption(manager, key, defaultValue);
@@ -1498,6 +1534,9 @@ var getRuntime4 = () => getActiveRuntime() ?? defaultRuntime;
1498
1534
  var getDataOptions = (dataName) => {
1499
1535
  return getRuntime4().getDataOptions(dataName);
1500
1536
  };
1537
+ var getDataSnapshot = (dataName) => {
1538
+ return getRuntime4().getDataSnapshot(dataName);
1539
+ };
1501
1540
  var getDataOption = (dataName, key, defaultValue) => {
1502
1541
  return getRuntime4().getDataOption(dataName, key, defaultValue);
1503
1542
  };
@@ -1507,9 +1546,6 @@ var getRuntime5 = () => getActiveRuntime() ?? defaultRuntime;
1507
1546
  var getModelRef = (modelName, refPath) => {
1508
1547
  return getRuntime5().getModelRef(modelName, refPath);
1509
1548
  };
1510
- var getModelSub = (modelName) => {
1511
- return getRuntime5().getModelSub(modelName);
1512
- };
1513
1549
 
1514
1550
  // src/services/base.ts
1515
1551
  import {
@@ -3436,7 +3472,7 @@ var DataService = class {
3436
3472
  this.req = req;
3437
3473
  this.dataName = dataName;
3438
3474
  this.options = getDataOptions(dataName);
3439
- this.data = this.options.data ?? [];
3475
+ this.data = getDataSnapshot(dataName);
3440
3476
  }
3441
3477
  async findOne(filter2, args, options) {
3442
3478
  const filterErrors = validateClientFilter(filter2);
@@ -3617,8 +3653,11 @@ var copyAndDepopulate = (docObject, operations, options = { mutable: true, idFie
3617
3653
  if (!isPlainObject13(target)) return ret;
3618
3654
  const targetObject = target;
3619
3655
  const next = targetObject[seg];
3620
- if (isArray8(next)) ret.push(...next);
3621
- else if (next !== null && next !== void 0) ret.push(next);
3656
+ if (isArray8(next)) {
3657
+ for (const item of next) {
3658
+ ret.push(item);
3659
+ }
3660
+ } else if (next !== null && next !== void 0) ret.push(next);
3622
3661
  return ret;
3623
3662
  }, []);
3624
3663
  }
@@ -3804,11 +3843,12 @@ async function resolveAccessFilter({
3804
3843
  }) {
3805
3844
  let nextFilter = normalizeFilter(filter2);
3806
3845
  const overrideFilterFn = getOption(`overrideFilter.${access}`, null);
3807
- if (isFunction4(overrideFilterFn)) {
3846
+ if (nextFilter !== false && isFunction4(overrideFilterFn)) {
3808
3847
  nextFilter = normalizeFilter(await overrideFilterFn.call(req, nextFilter, permissions));
3809
3848
  }
3849
+ if (nextFilter === false) return false;
3810
3850
  const baseFilterFn = getOption(`baseFilter.${access}`, null);
3811
- if (!isFunction4(baseFilterFn)) return nextFilter || {};
3851
+ if (!isFunction4(baseFilterFn)) return nextFilter ?? {};
3812
3852
  const baseFilter = normalizeFilter(
3813
3853
  cache.has(cacheKey) ? cache.get(cacheKey) : await baseFilterFn.call(req, permissions)
3814
3854
  );
@@ -3816,7 +3856,7 @@ async function resolveAccessFilter({
3816
3856
  cache.set(cacheKey, baseFilter);
3817
3857
  }
3818
3858
  if (baseFilter === false) return false;
3819
- if (!baseFilter) return nextFilter || {};
3859
+ if (!baseFilter) return nextFilter ?? {};
3820
3860
  if (!nextFilter) return baseFilter;
3821
3861
  return optimizeAndFilter([baseFilter, nextFilter]);
3822
3862
  }
@@ -3824,15 +3864,30 @@ function getRequestPermissions(req) {
3824
3864
  const requestPermissionField = getGlobalOption("requestPermissionField");
3825
3865
  return new permission_default(req[requestPermissionField] || {});
3826
3866
  }
3867
+ var permissionFieldOwners = /* @__PURE__ */ new WeakMap();
3868
+ function getPermissionFieldOwnerMap(req) {
3869
+ let owners = permissionFieldOwners.get(req);
3870
+ if (!owners) {
3871
+ owners = /* @__PURE__ */ new Map();
3872
+ permissionFieldOwners.set(req, owners);
3873
+ }
3874
+ return owners;
3875
+ }
3827
3876
  async function setRequestPermissions(req) {
3828
3877
  const requestPermissionField = getGlobalOption("requestPermissionField");
3829
- if (req[requestPermissionField]) return;
3878
+ const activeRuntime = getActiveRuntime();
3879
+ const owners = getPermissionFieldOwnerMap(req);
3880
+ const owner = owners.get(requestPermissionField);
3881
+ const hasFieldValue = Boolean(req[requestPermissionField]);
3882
+ if (owner === activeRuntime) return;
3883
+ if (hasFieldValue && owner === void 0) return;
3830
3884
  const globalPermissions = getGlobalOption("globalPermissions");
3831
3885
  if (!isFunction4(globalPermissions)) return;
3832
3886
  const permissions = await globalPermissions.call(req, req);
3833
3887
  if (isPlainObject14(permissions)) req[requestPermissionField] = permissions;
3834
3888
  else if (isArray9(permissions)) req[requestPermissionField] = arrayToRecord(permissions);
3835
3889
  else if (isString5(permissions)) req[requestPermissionField] = { [permissions]: true };
3890
+ owners.set(requestPermissionField, activeRuntime);
3836
3891
  }
3837
3892
  async function evaluateRouteGuard(req, permissions, routeGuard) {
3838
3893
  const phas = (key) => permissions.has(key);
@@ -3900,17 +3955,14 @@ function getResolvedRequestPermissions(req) {
3900
3955
  async function setResolvedRequestPermissions(req) {
3901
3956
  await setRequestPermissions(req);
3902
3957
  }
3903
- async function canActivateRequest(req, routeGuard) {
3904
- const permissions = getGlobalPermissions(req);
3905
- return evaluateRouteGuard(req, permissions, routeGuard);
3906
- }
3907
3958
  async function initializeAclRequest({
3908
3959
  req,
3909
3960
  flag,
3961
+ runtime,
3910
3962
  createCore,
3911
3963
  assignCore
3912
3964
  }) {
3913
- if (req[flag]) {
3965
+ if (req[flag] === runtime) {
3914
3966
  return null;
3915
3967
  }
3916
3968
  const core = createCore(req);
@@ -3918,7 +3970,7 @@ async function initializeAclRequest({
3918
3970
  assignCore(core);
3919
3971
  req[PERMISSIONS] = core.getPermissions();
3920
3972
  req[PERMISSION_KEYS] = [...req[PERMISSIONS].keys];
3921
- req[flag] = true;
3973
+ req[flag] = runtime;
3922
3974
  return core;
3923
3975
  }
3924
3976
 
@@ -4054,6 +4106,7 @@ async function resolveSelectForRequest({
4054
4106
  // src/core.ts
4055
4107
  var Core = class {
4056
4108
  constructor(req) {
4109
+ this.cachedPermissions = null;
4057
4110
  this.req = req;
4058
4111
  this.caches = {
4059
4112
  baseFilter: new Cache()
@@ -4337,13 +4390,15 @@ var Core = class {
4337
4390
  return docObject;
4338
4391
  }
4339
4392
  getPermissions() {
4393
+ if (this.cachedPermissions) return this.cachedPermissions;
4340
4394
  return getResolvedRequestPermissions(this.req);
4341
4395
  }
4342
4396
  async setPermissions() {
4343
4397
  await setResolvedRequestPermissions(this.req);
4398
+ this.cachedPermissions = getResolvedRequestPermissions(this.req);
4344
4399
  }
4345
4400
  async canActivate(routeGuard) {
4346
- return canActivateRequest(this.req, routeGuard);
4401
+ return evaluateRouteGuard(this.req, this.getGlobalPermissions(), routeGuard);
4347
4402
  }
4348
4403
  async isAllowed(modelName, access) {
4349
4404
  if (access.startsWith("subs")) {
@@ -4379,6 +4434,7 @@ var Core = class {
4379
4434
  return this.getPublicService(modelName);
4380
4435
  }
4381
4436
  getGlobalPermissions() {
4437
+ if (this.cachedPermissions) return this.cachedPermissions;
4382
4438
  return getGlobalPermissions(this.req);
4383
4439
  }
4384
4440
  };
@@ -4388,6 +4444,7 @@ var createSetCore = (runtime = defaultRuntime) => {
4388
4444
  await initializeAclRequest({
4389
4445
  req,
4390
4446
  flag: MIDDLEWARE,
4447
+ runtime,
4391
4448
  createCore: (request) => new Core(request),
4392
4449
  assignCore: (core) => {
4393
4450
  req.macl = core;
@@ -4768,8 +4825,9 @@ function defineRequestSchema(validator, options = {}) {
4768
4825
  };
4769
4826
  }
4770
4827
  function fromZod(schema) {
4771
- return (value) => {
4772
- const result = schema.safeParse(value);
4828
+ return async (value) => {
4829
+ const candidate = schema;
4830
+ const result = typeof candidate.safeParseAsync === "function" ? await candidate.safeParseAsync.call(schema, value) : schema.safeParse(value);
4773
4831
  if (result.success) {
4774
4832
  return {
4775
4833
  success: true,
@@ -4806,6 +4864,9 @@ function fromYup(schema) {
4806
4864
  data
4807
4865
  };
4808
4866
  } catch (error2) {
4867
+ if (!isYupValidationError(error2)) {
4868
+ throw error2;
4869
+ }
4809
4870
  return {
4810
4871
  success: false,
4811
4872
  issues: normalizeYupIssues(error2)
@@ -4830,8 +4891,32 @@ function fromJoi(schema) {
4830
4891
  }
4831
4892
  function fromAjv(validator) {
4832
4893
  return async (value) => {
4833
- const valid = await validator(value);
4834
- if (valid) {
4894
+ const outcome = validator(value);
4895
+ if (isThenable(outcome)) {
4896
+ try {
4897
+ const data = await outcome;
4898
+ if (data === false) {
4899
+ return {
4900
+ success: false,
4901
+ issues: normalizeAjvIssues(validator.errors ?? [])
4902
+ };
4903
+ }
4904
+ return {
4905
+ success: true,
4906
+ data: data === true ? value : data
4907
+ };
4908
+ } catch (error2) {
4909
+ if (!isAjvValidationError(error2)) {
4910
+ throw error2;
4911
+ }
4912
+ return {
4913
+ success: false,
4914
+ issues: normalizeAjvIssues(error2.errors ?? [])
4915
+ };
4916
+ }
4917
+ }
4918
+ const errorsSnapshot = validator.errors ? [...validator.errors] : [];
4919
+ if (outcome) {
4835
4920
  return {
4836
4921
  success: true,
4837
4922
  data: value
@@ -4839,7 +4924,7 @@ function fromAjv(validator) {
4839
4924
  }
4840
4925
  return {
4841
4926
  success: false,
4842
- issues: normalizeAjvIssues(validator.errors ?? [])
4927
+ issues: normalizeAjvIssues(errorsSnapshot)
4843
4928
  };
4844
4929
  };
4845
4930
  }
@@ -4860,6 +4945,25 @@ function fromValibot(schema, safeParse) {
4860
4945
  }
4861
4946
  function fromArkType(type) {
4862
4947
  return async (value) => {
4948
+ if (isStandardSchema(type)) {
4949
+ const result2 = await type["~standard"].validate(value);
4950
+ if (isArkTypeErrors(result2)) {
4951
+ return {
4952
+ success: false,
4953
+ issues: normalizeArkTypeIssues(result2)
4954
+ };
4955
+ }
4956
+ if (isStandardSchemaFailure(result2)) {
4957
+ return {
4958
+ success: false,
4959
+ issues: normalizeArkTypeStandardIssues(result2.issues)
4960
+ };
4961
+ }
4962
+ return {
4963
+ success: true,
4964
+ data: result2.value
4965
+ };
4966
+ }
4863
4967
  const result = await type(value);
4864
4968
  if (!isArkTypeErrors(result)) {
4865
4969
  return {
@@ -4912,6 +5016,9 @@ function fromVine(validator) {
4912
5016
  data: output
4913
5017
  };
4914
5018
  } catch (error2) {
5019
+ if (!isVineValidationError(error2)) {
5020
+ throw error2;
5021
+ }
4915
5022
  return {
4916
5023
  success: false,
4917
5024
  issues: normalizeVineError(error2)
@@ -4972,6 +5079,13 @@ function normalizeArkTypeIssues(issues) {
4972
5079
  }));
4973
5080
  return normalized.length ? normalized : [{ message: issues.summary ?? "Validation failed" }];
4974
5081
  }
5082
+ function normalizeArkTypeStandardIssues(issues) {
5083
+ const normalized = issues.map((issue) => ({
5084
+ message: issue.message ?? issue.problem ?? "Validation failed",
5085
+ path: issue.path?.flatMap((segment) => normalizePathSegment(segment))
5086
+ }));
5087
+ return normalized.length ? normalized : [{ message: "Validation failed" }];
5088
+ }
4975
5089
  function normalizeIoTsIssues(issues) {
4976
5090
  return issues.map((issue) => ({
4977
5091
  message: issue.message ?? "Validation failed",
@@ -5006,25 +5120,64 @@ function normalizeVineField(message) {
5006
5120
  }
5007
5121
  return path.length ? path : void 0;
5008
5122
  }
5123
+ function decodeAjvSegment(segment) {
5124
+ return segment.replace(/~1/g, "/").replace(/~0/g, "~");
5125
+ }
5126
+ function encodePointerSegment(segment) {
5127
+ return encodeURIComponent(segment.replace(/~/g, "~0").replace(/\//g, "~1"));
5128
+ }
5009
5129
  function parseAjvPath(issue) {
5010
- const path = issue.instancePath?.split("/").filter(Boolean).map((segment) => /^\d+$/.test(segment) ? Number(segment) : segment);
5011
- if (issue.params?.missingProperty) {
5012
- return (path ?? []).concat(issue.params.missingProperty);
5130
+ const raw = issue.instancePath;
5131
+ const base = raw == null || raw === "" ? [] : (raw.startsWith("/") ? raw.slice(1) : raw).split("/").map(decodeAjvSegment);
5132
+ const missing = issue.params?.missingProperty;
5133
+ if (typeof missing === "string") {
5134
+ return [...base, missing];
5013
5135
  }
5014
- return path;
5136
+ return base;
5015
5137
  }
5016
5138
  function parsePathString(path) {
5017
5139
  if (!path) return void 0;
5018
5140
  return path.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean).map((segment) => /^\d+$/.test(segment) ? Number(segment) : segment);
5019
5141
  }
5020
5142
  function isYupValidationError(error2) {
5021
- return typeof error2 === "object" && error2 !== null && "message" in error2;
5143
+ if (typeof error2 !== "object" || error2 === null || !("message" in error2)) {
5144
+ return false;
5145
+ }
5146
+ const candidate = error2;
5147
+ return candidate.name === "ValidationError" && typeof candidate.message === "string" && Array.isArray(candidate.errors) && Array.isArray(candidate.inner);
5022
5148
  }
5023
5149
  function isArkTypeErrors(value) {
5024
- return Array.isArray(value);
5150
+ if (typeof value !== "object" || value === null) {
5151
+ return false;
5152
+ }
5153
+ const record = value;
5154
+ return record.arkKind === "errors" || record[" arkKind"] === "errors";
5155
+ }
5156
+ function isThenable(value) {
5157
+ return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
5158
+ }
5159
+ function isAjvValidationError(error2) {
5160
+ if (typeof error2 !== "object" || error2 === null || !("errors" in error2)) {
5161
+ return false;
5162
+ }
5163
+ const errors = error2.errors;
5164
+ return errors == null || Array.isArray(errors);
5025
5165
  }
5026
5166
  function isVineValidationError(error2) {
5027
- return typeof error2 === "object" && error2 !== null && "messages" in error2 && Array.isArray(error2.messages);
5167
+ if (typeof error2 !== "object" || error2 === null || !("messages" in error2)) {
5168
+ return false;
5169
+ }
5170
+ const candidate = error2;
5171
+ if (!Array.isArray(candidate.messages) || candidate.messages.length === 0) {
5172
+ return false;
5173
+ }
5174
+ const hasDiscriminator = candidate.code === "E_VALIDATION_ERROR" || candidate.name === "ValidationError";
5175
+ if (!hasDiscriminator) {
5176
+ return false;
5177
+ }
5178
+ return candidate.messages.every(
5179
+ (message) => typeof message === "object" && message !== null && typeof message.message === "string" && typeof message.field === "string"
5180
+ );
5028
5181
  }
5029
5182
  function formatIssue(issue, key, location = "pointer") {
5030
5183
  const path = (issue.path ?? []).map(String);
@@ -5041,7 +5194,7 @@ function formatIssue(issue, key, location = "pointer") {
5041
5194
  };
5042
5195
  }
5043
5196
  function buildPointer(path) {
5044
- return path.length === 0 ? "#" : `#/${path.join("/")}`;
5197
+ return path.length === 0 ? "#" : `#/${path.map(encodePointerSegment).join("/")}`;
5045
5198
  }
5046
5199
 
5047
5200
  // src/validation/model-router.ts
@@ -5542,24 +5695,26 @@ function setModelCollectionRoutes(context) {
5542
5695
  router.post(`/${options.mutationRouteSegment}`, async (req) => {
5543
5696
  await context.assertAllowed(req, "create");
5544
5697
  const { include_permissions } = parseQuery(requestSchemas.createQuery, req.query);
5545
- const body = await parseNestedBodyWithSchema(
5698
+ const intermediateBody = await parseNestedBodyWithSchema(
5546
5699
  advancedCreateBodySchema,
5547
5700
  req.body,
5548
5701
  "data",
5549
5702
  context.getRequestSchema("requestSchemas.advancedCreate.data")
5550
5703
  );
5551
- const { data, select, populate, tasks } = body;
5552
- const advancedOptions = body.options ?? {};
5553
- await parseBodyWithSchema(
5704
+ const { data: nestedData, select: nestedSelect, populate: nestedPopulate, tasks: nestedTasks } = intermediateBody;
5705
+ const nestedOptions = intermediateBody.options ?? {};
5706
+ const finalBody = await parseBodyWithSchema(
5554
5707
  advancedCreateBodySchema,
5555
- { data, select, populate, tasks, options: advancedOptions },
5708
+ { data: nestedData, select: nestedSelect, populate: nestedPopulate, tasks: nestedTasks, options: nestedOptions },
5556
5709
  context.getRequestSchema("requestSchemas.advancedCreate.default") ?? context.getRequestSchema("requestSchemas.advancedCreate")
5557
5710
  );
5558
- const { includePermissions, populateAccess } = advancedOptions;
5711
+ const { data: finalData, select: finalSelect, populate: finalPopulate, tasks: finalTasks } = finalBody;
5712
+ const finalOptions = finalBody.options ?? {};
5713
+ const { includePermissions, populateAccess } = finalOptions;
5559
5714
  const svc = context.getPublicService(req);
5560
5715
  const result = await svc._create(
5561
- data,
5562
- { select, populate, tasks },
5716
+ finalData,
5717
+ { select: finalSelect, populate: finalPopulate, tasks: finalTasks },
5563
5718
  {
5564
5719
  includePermissions: includePermissions ?? parseBooleanString(include_permissions),
5565
5720
  populateAccess
@@ -5758,25 +5913,33 @@ function setModelDocumentRoutes(context) {
5758
5913
  await context.assertAllowed(req, "update");
5759
5914
  const id = parsePathParam(req.params[options.idParam], options.idParam);
5760
5915
  const { returning_all, include_permissions } = parseQuery(requestSchemas.updateQuery, req.query);
5761
- const body = await parseNestedBodyWithSchema(
5916
+ const intermediateBody = await parseNestedBodyWithSchema(
5762
5917
  advancedUpdateBodySchema,
5763
5918
  req.body,
5764
5919
  "data",
5765
5920
  context.getRequestSchema("requestSchemas.advancedUpdate.data")
5766
5921
  );
5767
- const { data, select, populate, tasks } = body;
5768
- const advancedOptions = body.options ?? {};
5769
- await parseBodyWithSchema(
5922
+ const { data: nestedData, select: nestedSelect, populate: nestedPopulate, tasks: nestedTasks } = intermediateBody;
5923
+ const nestedOptions = intermediateBody.options ?? {};
5924
+ const finalBody = await parseBodyWithSchema(
5770
5925
  advancedUpdateBodySchema,
5771
- { data, select, populate, tasks, options: advancedOptions },
5926
+ {
5927
+ data: nestedData,
5928
+ select: nestedSelect,
5929
+ populate: nestedPopulate,
5930
+ tasks: nestedTasks,
5931
+ options: nestedOptions
5932
+ },
5772
5933
  context.getRequestSchema("requestSchemas.advancedUpdate.default") ?? context.getRequestSchema("requestSchemas.advancedUpdate")
5773
5934
  );
5774
- const { returningAll, includePermissions, populateAccess } = advancedOptions;
5935
+ const { data: finalData, select: finalSelect, populate: finalPopulate, tasks: finalTasks } = finalBody;
5936
+ const finalOptions = finalBody.options ?? {};
5937
+ const { returningAll, includePermissions, populateAccess } = finalOptions;
5775
5938
  const svc = context.getPublicService(req);
5776
5939
  const result = await svc._update(
5777
5940
  id,
5778
- data,
5779
- { select, populate, tasks },
5941
+ finalData,
5942
+ { select: finalSelect, populate: finalPopulate, tasks: finalTasks },
5780
5943
  {
5781
5944
  returningAll: returningAll ?? parseBooleanString(returning_all),
5782
5945
  includePermissions: includePermissions ?? parseBooleanString(include_permissions),
@@ -5826,23 +5989,31 @@ function setModelDocumentRoutes(context) {
5826
5989
  await context.assertAllowed(req, "upsert");
5827
5990
  const svc = context.getPublicService(req);
5828
5991
  const { returning_all, include_permissions } = parseQuery(requestSchemas.upsertQuery, req.query);
5829
- const body = await parseNestedBodyWithSchema(
5992
+ const intermediateBody = await parseNestedBodyWithSchema(
5830
5993
  advancedUpsertBodySchema,
5831
5994
  req.body,
5832
5995
  "data",
5833
5996
  context.getRequestSchema("requestSchemas.advancedUpsert.data")
5834
5997
  );
5835
- const { data, select, populate, tasks } = body;
5836
- const advancedOptions = body.options ?? {};
5837
- await parseBodyWithSchema(
5998
+ const { data: nestedData, select: nestedSelect, populate: nestedPopulate, tasks: nestedTasks } = intermediateBody;
5999
+ const nestedOptions = intermediateBody.options ?? {};
6000
+ const finalBody = await parseBodyWithSchema(
5838
6001
  advancedUpsertBodySchema,
5839
- { data, select, populate, tasks, options: advancedOptions },
6002
+ {
6003
+ data: nestedData,
6004
+ select: nestedSelect,
6005
+ populate: nestedPopulate,
6006
+ tasks: nestedTasks,
6007
+ options: nestedOptions
6008
+ },
5840
6009
  context.getRequestSchema("requestSchemas.advancedUpsert.default") ?? context.getRequestSchema("requestSchemas.advancedUpsert")
5841
6010
  );
5842
- const { returningAll, includePermissions, populateAccess } = advancedOptions;
6011
+ const { data: finalData, select: finalSelect, populate: finalPopulate, tasks: finalTasks } = finalBody;
6012
+ const finalOptions = finalBody.options ?? {};
6013
+ const { returningAll, includePermissions, populateAccess } = finalOptions;
5843
6014
  const result = await svc._upsert(
5844
- data,
5845
- { select, populate, tasks },
6015
+ finalData,
6016
+ { select: finalSelect, populate: finalPopulate, tasks: finalTasks },
5846
6017
  {
5847
6018
  returningAll: returningAll ?? parseBooleanString(returning_all),
5848
6019
  includePermissions: includePermissions ?? parseBooleanString(include_permissions),
@@ -5912,7 +6083,7 @@ function setModelDocumentRoutes(context) {
5912
6083
 
5913
6084
  // src/routers/model-router-subdocument-routes.ts
5914
6085
  function setModelSubDocumentRoutes(context) {
5915
- const subs = getModelSub(context.modelName);
6086
+ const subs = context.runtime.getModelSub(context.modelName);
5916
6087
  for (let x = 0; x < subs.length; x++) {
5917
6088
  const sub = subs[x];
5918
6089
  context.router.get(`/:${context.options.idParam}/${sub}`, async (req) => {
@@ -6137,7 +6308,9 @@ var ModelRouter = class _ModelRouter {
6137
6308
  "baseFilter"
6138
6309
  );
6139
6310
  /**
6140
- * The override filter definitions applied in every query transaction.
6311
+ * Trusted filter replacements applied before base-filter composition.
6312
+ * They may replace ordinary filters, but cannot replace an existing `false` denial.
6313
+ * Returning `false` denies the transaction.
6141
6314
  * @operation `list`, `read`, `update`, `delete`
6142
6315
  */
6143
6316
  this.overrideFilter = setOption.bind(this, "overrideFilter");
@@ -6297,6 +6470,7 @@ var ModelRouter = class _ModelRouter {
6297
6470
  modelName: this.modelName,
6298
6471
  router: this.router,
6299
6472
  options: this.options,
6473
+ runtime: this.runtime,
6300
6474
  getRequestSchema: this.getRequestSchema.bind(this),
6301
6475
  getPublicService: this.getPublicService.bind(this),
6302
6476
  assertAllowed: this.assertAllowed.bind(this),
@@ -6311,6 +6485,7 @@ var ModelRouter = class _ModelRouter {
6311
6485
  modelName: this.modelName,
6312
6486
  router: this.router,
6313
6487
  options: this.options,
6488
+ runtime: this.runtime,
6314
6489
  getRequestSchema: this.getRequestSchema.bind(this),
6315
6490
  getPublicService: this.getPublicService.bind(this),
6316
6491
  assertAllowed: this.assertAllowed.bind(this),
@@ -6325,6 +6500,7 @@ var ModelRouter = class _ModelRouter {
6325
6500
  modelName: this.modelName,
6326
6501
  router: this.router,
6327
6502
  options: this.options,
6503
+ runtime: this.runtime,
6328
6504
  getRequestSchema: this.getRequestSchema.bind(this),
6329
6505
  getPublicService: this.getPublicService.bind(this),
6330
6506
  assertAllowed: this.assertAllowed.bind(this),
@@ -6375,6 +6551,7 @@ import { isNumber as _isNumber, normalizeUrlPath as normalizeUrlPath3, orderBy a
6375
6551
  // src/core-data.ts
6376
6552
  var DataCore = class {
6377
6553
  constructor(req) {
6554
+ this.cachedPermissions = null;
6378
6555
  this.req = req;
6379
6556
  this.caches = {
6380
6557
  baseFilter: new Cache()
@@ -6468,13 +6645,15 @@ var DataCore = class {
6468
6645
  });
6469
6646
  }
6470
6647
  getPermissions() {
6648
+ if (this.cachedPermissions) return this.cachedPermissions;
6471
6649
  return getResolvedRequestPermissions(this.req);
6472
6650
  }
6473
6651
  async setPermissions() {
6474
6652
  await setResolvedRequestPermissions(this.req);
6653
+ this.cachedPermissions = getResolvedRequestPermissions(this.req);
6475
6654
  }
6476
6655
  async canActivate(routeGuard) {
6477
- return canActivateRequest(this.req, routeGuard);
6656
+ return evaluateRouteGuard(this.req, this.getGlobalPermissions(), routeGuard);
6478
6657
  }
6479
6658
  async isAllowed(dataName, access) {
6480
6659
  const operationAccess = getDataOption(dataName, `operationAccess.${access}`);
@@ -6490,6 +6669,7 @@ var DataCore = class {
6490
6669
  return this.getService(dataName);
6491
6670
  }
6492
6671
  getGlobalPermissions() {
6672
+ if (this.cachedPermissions) return this.cachedPermissions;
6493
6673
  return getGlobalPermissions(this.req);
6494
6674
  }
6495
6675
  };
@@ -6499,6 +6679,7 @@ var createSetDataCore = (runtime = defaultRuntime) => {
6499
6679
  await initializeAclRequest({
6500
6680
  req,
6501
6681
  flag: DATA_MIDDLEWARE,
6682
+ runtime,
6502
6683
  createCore: (request) => new DataCore(request),
6503
6684
  assignCore: (core) => {
6504
6685
  req.dacl = core;
@@ -6803,6 +6984,11 @@ var DataRouter = class {
6803
6984
  this,
6804
6985
  "baseFilter"
6805
6986
  );
6987
+ /**
6988
+ * Trusted filter replacements applied before base-filter composition.
6989
+ * They may replace ordinary filters, but cannot replace an existing `false` denial.
6990
+ * Returning `false` denies the transaction.
6991
+ */
6806
6992
  this.overrideFilter = setOption2.bind(this, "overrideFilter");
6807
6993
  this.decorate = setOption2.bind(
6808
6994
  this,