@web-ts-toolkit/access-router 0.38.0 → 0.39.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/advanced.mjs CHANGED
@@ -53,21 +53,25 @@ import {
53
53
  isArray as isArray4,
54
54
  isBoolean,
55
55
  isFunction,
56
- isNil,
57
- isPlainObject as isPlainObject7,
58
- isString as isString2,
56
+ isNil as isNil2,
57
+ isPlainObject as isPlainObject8,
58
+ isString as isString3,
59
59
  keys
60
60
  } from "@web-ts-toolkit/utils";
61
61
 
62
+ // src/helpers/query.ts
63
+ import { flattenDeep, isNil, isPlainObject, isString, reduce } from "@web-ts-toolkit/utils";
64
+ var DEFAULT_LIST_HARD_LIMIT = 1e3;
65
+
62
66
  // src/helpers/index.ts
63
- import { forEach, isArray, isEmpty, isObject, isPlainObject as isPlainObject2, isString, mapValuesAsync } from "@web-ts-toolkit/utils";
67
+ import { forEach, isArray, isEmpty, isObject, isPlainObject as isPlainObject3, isString as isString2 } from "@web-ts-toolkit/utils";
64
68
 
65
69
  // src/lib.ts
66
70
  import { Document, Schema } from "mongoose";
67
- import { isPlainObject } from "@web-ts-toolkit/utils";
71
+ import { isPlainObject as isPlainObject2 } from "@web-ts-toolkit/utils";
68
72
  var isSchema = (val) => val instanceof Schema;
69
73
  var isObjectIdType = (val) => val === "ObjectId" || val === Schema.Types.ObjectId;
70
- var isReference = (val) => isPlainObject(val) && !!val.ref && isObjectIdType(val.type);
74
+ var isReference = (val) => isPlainObject2(val) && !!val.ref && isObjectIdType(val.type);
71
75
 
72
76
  // src/helpers/index.ts
73
77
  function recurseObject(obj) {
@@ -97,7 +101,7 @@ function buildRefs(schema) {
97
101
  }
98
102
  const target = isObject(val) && "type" in val ? val.type ?? val : val;
99
103
  if (isArray(target) && target.length > 0) {
100
- if (isSchema(target[0]) || isPlainObject2(target[0])) {
104
+ if (isSchema(target[0]) || isPlainObject3(target[0])) {
101
105
  return;
102
106
  }
103
107
  }
@@ -110,7 +114,7 @@ function buildSubPaths(schema) {
110
114
  forEach(schema, (val, key) => {
111
115
  const target = isObject(val) && "type" in val ? val.type ?? val : val;
112
116
  if (isArray(target) && target.length > 0) {
113
- if (isSchema(target[0]) || isPlainObject2(target[0]) && !isReference(target[0])) {
117
+ if (isSchema(target[0]) || isPlainObject3(target[0]) && !isReference(target[0])) {
114
118
  subPaths.push(key);
115
119
  }
116
120
  }
@@ -119,7 +123,7 @@ function buildSubPaths(schema) {
119
123
  }
120
124
 
121
125
  // src/openapi/build-spec.ts
122
- import { isArray as isArray2, isPlainObject as isPlainObject4 } from "@web-ts-toolkit/utils";
126
+ import { isArray as isArray2, isPlainObject as isPlainObject5 } from "@web-ts-toolkit/utils";
123
127
 
124
128
  // src/openapi/responses.ts
125
129
  var json = (schema) => ({
@@ -190,19 +194,19 @@ var openApiResponses = {
190
194
 
191
195
  // src/openapi/schemas.ts
192
196
  import { z } from "zod";
193
- import { isPlainObject as isPlainObject3 } from "@web-ts-toolkit/utils";
197
+ import { isPlainObject as isPlainObject4 } from "@web-ts-toolkit/utils";
194
198
  var fallbackObjectSchema = { type: "object", additionalProperties: true };
195
199
  function isOpenApiSchemaPatch(source) {
196
- return isPlainObject3(source) && source.kind === "objectProperties";
200
+ return isPlainObject4(source) && source.kind === "objectProperties";
197
201
  }
198
202
  function isOpenApiSchemaResolver(source) {
199
- return isPlainObject3(source) && source.kind === "schemaResolver" && typeof source.resolve === "function";
203
+ return isPlainObject4(source) && source.kind === "schemaResolver" && typeof source.resolve === "function";
200
204
  }
201
205
  function isJsonSchemaLike(source) {
202
- return isPlainObject3(source) && ("type" in source || "properties" in source || "$ref" in source || "anyOf" in source);
206
+ return isPlainObject4(source) && ("type" in source || "properties" in source || "$ref" in source || "anyOf" in source);
203
207
  }
204
208
  function hasOpenApiSchema(source) {
205
- return isPlainObject3(source) && isJsonSchemaLike(source.openapi);
209
+ return isPlainObject4(source) && isJsonSchemaLike(source.openapi);
206
210
  }
207
211
  function toOpenApiSchema(source) {
208
212
  if (!source) return void 0;
@@ -210,7 +214,7 @@ function toOpenApiSchema(source) {
210
214
  if (!resolvedSource) return void 0;
211
215
  if (isOpenApiSchemaPatch(resolvedSource)) {
212
216
  const schema = toOpenApiSchema(resolvedSource.source) ?? { ...fallbackObjectSchema };
213
- const properties = isPlainObject3(schema.properties) ? { ...schema.properties } : {};
217
+ const properties = isPlainObject4(schema.properties) ? { ...schema.properties } : {};
214
218
  for (const [key, value] of Object.entries(resolvedSource.properties)) {
215
219
  properties[key] = toOpenApiSchema(value) ?? fallbackObjectSchema;
216
220
  }
@@ -243,7 +247,7 @@ var toOpenApiPath = (path) => normalizePath(path).replace(/:([A-Za-z0-9_]+)/g, "
243
247
  var schemaToQueryParameters = (schema) => {
244
248
  const jsonSchema = toOpenApiSchema(schema);
245
249
  if (!jsonSchema || jsonSchema.type !== "object") return [];
246
- const properties = isPlainObject4(jsonSchema.properties) ? jsonSchema.properties : {};
250
+ const properties = isPlainObject5(jsonSchema.properties) ? jsonSchema.properties : {};
247
251
  const required = new Set(
248
252
  isArray2(jsonSchema.required) ? jsonSchema.required.filter((item) => typeof item === "string") : []
249
253
  );
@@ -336,7 +340,7 @@ var OpenApiCollisionError = class extends Error {
336
340
  };
337
341
 
338
342
  // src/openapi/registry.ts
339
- var isPlainObject5 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
343
+ var isPlainObject6 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
340
344
  var stableKey = (value) => {
341
345
  if (value === null || typeof value === "undefined") return "null";
342
346
  if (typeof value === "function") return "[Function]";
@@ -371,7 +375,7 @@ var OpenApiRegistry = class {
371
375
  this.routes[existingIndex] = route;
372
376
  return;
373
377
  }
374
- if (descriptorsEqual(existing, route) && (route.idempotent || isPlainObject5(existing) && existing.idempotent)) {
378
+ if (descriptorsEqual(existing, route) && (route.idempotent || isPlainObject6(existing) && existing.idempotent)) {
375
379
  return;
376
380
  }
377
381
  if (this.options.rejectConflicts) {
@@ -450,6 +454,42 @@ var defaultLogger = createLogger({
450
454
 
451
455
  // src/options/manager.ts
452
456
  import { assign, get, set } from "@web-ts-toolkit/utils";
457
+ var toPath = (key) => {
458
+ if (Array.isArray(key)) return key;
459
+ return typeof key === "string" ? key.split(".") : [key];
460
+ };
461
+ var cloneConfigValue = (value) => {
462
+ if (Array.isArray(value)) {
463
+ return value.map((item) => cloneConfigValue(item));
464
+ }
465
+ if (!value || typeof value !== "object") {
466
+ return value;
467
+ }
468
+ if (Object.getPrototypeOf(value) !== Object.prototype) {
469
+ return value;
470
+ }
471
+ return Object.fromEntries(
472
+ Object.entries(value).map(([entryKey, entryValue]) => [
473
+ entryKey,
474
+ cloneConfigValue(entryValue)
475
+ ])
476
+ );
477
+ };
478
+ var freezeConfigValue = (value) => {
479
+ if (!value || typeof value !== "object") {
480
+ return value;
481
+ }
482
+ if (Object.isFrozen(value)) {
483
+ return value;
484
+ }
485
+ if (Array.isArray(value) || Object.getPrototypeOf(value) === Object.prototype) {
486
+ for (const nestedValue of Object.values(value)) {
487
+ freezeConfigValue(nestedValue);
488
+ }
489
+ return Object.freeze(value);
490
+ }
491
+ return value;
492
+ };
453
493
  var getNestedOption = (manager, key, defaultValue) => {
454
494
  const keys2 = String(key).split(".");
455
495
  if (keys2.length === 1) {
@@ -467,8 +507,9 @@ var getNestedOption = (manager, key, defaultValue) => {
467
507
  return option;
468
508
  };
469
509
  var OptionsManager = class {
470
- constructor(defaultOptions) {
510
+ constructor(defaultOptions, options = {}) {
471
511
  this.defaultOptions = defaultOptions;
512
+ this.preserveKeys = new Set(options.preserveKeys ?? []);
472
513
  this.listeners = {};
473
514
  const _this = this;
474
515
  this.currentOptions = new Proxy({}, {
@@ -489,13 +530,34 @@ var OptionsManager = class {
489
530
  return get(this.currentOptions, key, defaultValue);
490
531
  }
491
532
  set(key, value) {
492
- set(this.currentOptions, key, value);
533
+ const path = toPath(key);
534
+ if (path.length <= 1) {
535
+ set(this.currentOptions, path, this.preserveKeys.has(String(path[0])) ? value : cloneConfigValue(value));
536
+ return;
537
+ }
538
+ const [rootKey, ...nestedPath] = path;
539
+ const currentRoot = get(this.currentOptions, [rootKey], void 0);
540
+ const nextRoot = cloneConfigValue(currentRoot ?? {});
541
+ set(nextRoot, nestedPath, cloneConfigValue(value));
542
+ set(this.currentOptions, [rootKey], nextRoot);
493
543
  }
494
544
  fetch() {
495
- return { ...this.currentOptions };
545
+ const cloned = cloneConfigValue(this.currentOptions);
546
+ for (const key of this.preserveKeys) {
547
+ if (key in this.currentOptions) {
548
+ cloned[key] = this.currentOptions[key];
549
+ }
550
+ }
551
+ return freezeConfigValue(cloned);
496
552
  }
497
553
  assign(options) {
498
- assign(this.currentOptions, options);
554
+ const cloned = cloneConfigValue(options);
555
+ for (const key of this.preserveKeys) {
556
+ if (key in options) {
557
+ cloned[key] = options[key];
558
+ }
559
+ }
560
+ assign(this.currentOptions, cloned);
499
561
  }
500
562
  onchange(key, func) {
501
563
  set(this.listeners, key, func);
@@ -504,7 +566,7 @@ var OptionsManager = class {
504
566
  };
505
567
 
506
568
  // src/request-complexity.ts
507
- import { isArray as isArray3, isPlainObject as isPlainObject6 } from "@web-ts-toolkit/utils";
569
+ import { isArray as isArray3, isPlainObject as isPlainObject7 } from "@web-ts-toolkit/utils";
508
570
  var defaultRequestComplexity = {
509
571
  maxDepth: 8,
510
572
  maxNodes: 500,
@@ -513,7 +575,8 @@ var defaultRequestComplexity = {
513
575
  maxBulkItems: 100,
514
576
  maxIncludeCount: 10,
515
577
  maxSubQueryCount: 10,
516
- maxBulkConcurrency: 10
578
+ maxBulkConcurrency: 10,
579
+ maxHookConcurrency: 10
517
580
  };
518
581
  var normalizePositiveInteger = (value, fallback) => {
519
582
  return Number.isSafeInteger(value) && Number(value) > 0 ? Number(value) : fallback;
@@ -529,6 +592,10 @@ var resolveRequestComplexity = (options) => ({
529
592
  maxBulkConcurrency: normalizePositiveInteger(
530
593
  options?.maxBulkConcurrency,
531
594
  defaultRequestComplexity.maxBulkConcurrency
595
+ ),
596
+ maxHookConcurrency: normalizePositiveInteger(
597
+ options?.maxHookConcurrency,
598
+ defaultRequestComplexity.maxHookConcurrency
532
599
  )
533
600
  });
534
601
  function validateRequestComplexity(value, options, scope = "request") {
@@ -575,7 +642,7 @@ function validateRequestComplexity(value, options, scope = "request") {
575
642
  }
576
643
  return;
577
644
  }
578
- if (!isPlainObject6(current)) return;
645
+ if (!isPlainObject7(current)) return;
579
646
  for (const [key, child] of Object.entries(current)) {
580
647
  if (key === "__proto__" || key === "prototype" || key === "constructor") {
581
648
  pushError(`Unsupported key: ${key}`, path.concat(key));
@@ -622,24 +689,25 @@ var defaultModelOptions = {
622
689
  var defaultDataOptions = {
623
690
  basePath: null,
624
691
  parentPath: "/",
625
- queryRouteSegment: "__query"
692
+ queryRouteSegment: "__query",
693
+ listHardLimit: DEFAULT_LIST_HARD_LIMIT
626
694
  };
627
695
  var normalizeBasePath = (name, value) => {
628
- if (isNil(value)) {
696
+ if (isNil2(value)) {
629
697
  return `/${pluralize(name)}`;
630
698
  }
631
- return isString2(value) ? addLeadingSlash(value) : "";
699
+ return isString3(value) ? addLeadingSlash(value) : "";
632
700
  };
633
701
  var hasModelPermission = (value, modelPermissionPrefix) => {
634
702
  if (!modelPermissionPrefix) {
635
703
  return true;
636
704
  }
637
- if (isString2(value)) {
705
+ if (isString3(value)) {
638
706
  return value.trim().split(" ").some((item) => item.startsWith(modelPermissionPrefix));
639
707
  }
640
708
  if (isArray4(value)) {
641
709
  return value.some((item) => {
642
- if (isString2(item) || isArray4(item)) {
710
+ if (isString3(item) || isArray4(item)) {
643
711
  return hasModelPermission(item, modelPermissionPrefix);
644
712
  }
645
713
  return true;
@@ -653,7 +721,7 @@ var classifyPermissionSchema = (permissionSchema, modelPermissionPrefix) => {
653
721
  const modelPermissionKeys = {};
654
722
  forEach2(schemaKeys, (schemaKey) => {
655
723
  const schemaRule = permissionSchema[schemaKey];
656
- const ruleEntries = isPlainObject7(schemaRule) ? Object.entries(schemaRule) : FIELD_ACCESS_KEYS.map((accessKey) => [accessKey, schemaRule]);
724
+ const ruleEntries = isPlainObject8(schemaRule) ? Object.entries(schemaRule) : FIELD_ACCESS_KEYS.map((accessKey) => [accessKey, schemaRule]);
657
725
  for (let x = 0; x < ruleEntries.length; x++) {
658
726
  const [accessKey, value] = ruleEntries[x];
659
727
  if (!isArray4(globalPermissionKeys[accessKey])) {
@@ -679,17 +747,30 @@ var classifyPermissionSchema = (permissionSchema, modelPermissionPrefix) => {
679
747
  modelPermissionKeys
680
748
  };
681
749
  };
750
+ var refreshPermissionMetadata = (target) => {
751
+ const permissionSchema = target.permissionSchema ?? {};
752
+ const { schemaKeys, globalPermissionKeys, modelPermissionKeys } = classifyPermissionSchema(
753
+ permissionSchema,
754
+ target.modelPermissionPrefix ?? ""
755
+ );
756
+ target._permissionSchemaKeys = schemaKeys;
757
+ target._globalPermissionKeys = globalPermissionKeys;
758
+ target._modelPermissionKeys = modelPermissionKeys;
759
+ };
682
760
  var AccessRuntime = class {
683
- constructor() {
684
- this.globalOptions = new OptionsManager({
685
- requestPermissionField: "_permissions",
686
- globalPermissions: () => ({}),
687
- requireRegisteredPopulateModels: true,
688
- logger: defaultLogger,
689
- requestComplexity: defaultRequestComplexity
690
- }).build();
761
+ constructor(options = {}) {
762
+ this.globalOptions = new OptionsManager(
763
+ {
764
+ requestPermissionField: "_permissions",
765
+ globalPermissions: () => ({}),
766
+ requireRegisteredPopulateModels: true,
767
+ logger: defaultLogger,
768
+ requestComplexity: defaultRequestComplexity
769
+ },
770
+ { preserveKeys: ["logger"] }
771
+ ).build();
691
772
  this.defaultModelOptions = new OptionsManager({
692
- listHardLimit: 1e3,
773
+ listHardLimit: DEFAULT_LIST_HARD_LIMIT,
693
774
  documentPermissionField: "_permissions",
694
775
  idParam: "id",
695
776
  idField: "_id",
@@ -707,6 +788,7 @@ var AccessRuntime = class {
707
788
  this.modelAtts = {};
708
789
  this.modelInstances = {};
709
790
  this.openApiRegistry = new OpenApiRegistry();
791
+ this.allowGlobalModelLookup = options.allowGlobalModelLookup ?? false;
710
792
  ensureMongooseJsonSchemaInitialized();
711
793
  }
712
794
  registerModelInstance(modelName, model) {
@@ -722,13 +804,16 @@ var AccessRuntime = class {
722
804
  this.modelInstances[modelName] = model;
723
805
  }
724
806
  hasModelInstance(modelName) {
725
- return modelName in this.modelInstances;
807
+ return modelName in this.modelInstances || this.allowGlobalModelLookup && modelName in mongoose.models;
726
808
  }
727
809
  getModelInstance(modelName) {
728
810
  const registered = this.modelInstances[modelName];
729
811
  if (registered) return registered;
812
+ if (!this.allowGlobalModelLookup) return null;
730
813
  const global = mongoose.models[modelName];
731
- return global ?? null;
814
+ if (!global) return null;
815
+ this.registerModelInstance(modelName, global);
816
+ return global;
732
817
  }
733
818
  registerOpenApiRoute(route) {
734
819
  this.openApiRegistry.register(route);
@@ -777,24 +862,24 @@ var AccessRuntime = class {
777
862
  return this.defaultModelOptions.get(key, defaultValue);
778
863
  }
779
864
  createModelOptions(modelName) {
780
- const model = this.getModelInstance(modelName) ?? mongoose.model(modelName);
865
+ const model = this.getModelInstance(modelName);
866
+ if (!model) {
867
+ throw new Error(
868
+ `Runtime model registry missing model "${modelName}". Pass a mongoose.Model instance to createRouter() or register it with registerModelInstance() before using this isolated runtime.`
869
+ );
870
+ }
781
871
  const manager = new OptionsManager({
782
872
  ...defaultModelOptions,
783
873
  modelName
784
874
  });
785
- manager.onchange("permissionSchema", function(newval, key, target) {
786
- const permissionSchema = newval ?? {};
787
- const { schemaKeys, globalPermissionKeys, modelPermissionKeys } = classifyPermissionSchema(
788
- permissionSchema,
789
- target.modelPermissionPrefix ?? ""
790
- );
791
- target._permissionSchemaKeys = schemaKeys;
792
- target._globalPermissionKeys = globalPermissionKeys;
793
- target._modelPermissionKeys = modelPermissionKeys;
875
+ manager.onchange("permissionSchema", function(_newval, _key, target) {
876
+ refreshPermissionMetadata(target);
877
+ }).onchange("modelPermissionPrefix", function(_newval, _key, target) {
878
+ refreshPermissionMetadata(target);
794
879
  }).onchange("basePath", function(newval, key, target) {
795
880
  target[key] = normalizeBasePath(
796
881
  modelName,
797
- isString2(newval) || isNil(newval) ? newval : void 0
882
+ isString3(newval) || isNil2(newval) ? newval : void 0
798
883
  );
799
884
  }).build();
800
885
  this.modelJsonSchemas[modelName] = model.jsonSchema();
@@ -852,7 +937,7 @@ var AccessRuntime = class {
852
937
  manager.onchange("basePath", function(newval, key, target) {
853
938
  target[key] = normalizeBasePath(
854
939
  dataName,
855
- isString2(newval) || isNil(newval) ? newval : void 0
940
+ isString3(newval) || isNil2(newval) ? newval : void 0
856
941
  );
857
942
  }).build();
858
943
  return manager;
@@ -908,7 +993,7 @@ var AccessRuntime = class {
908
993
  getModelRef(modelName, refPath) {
909
994
  this.ensureModelMeta(modelName);
910
995
  const value = get2(this.modelRefs, `${modelName}.${refPath}`, null);
911
- return isString2(value) ? value : null;
996
+ return isString3(value) ? value : null;
912
997
  }
913
998
  getModelSub(modelName) {
914
999
  this.ensureModelMeta(modelName);
@@ -919,7 +1004,7 @@ var AccessRuntime = class {
919
1004
  return get2(this.modelAtts, modelName, []);
920
1005
  }
921
1006
  };
922
- var defaultRuntime = new AccessRuntime();
1007
+ var defaultRuntime = new AccessRuntime({ allowGlobalModelLookup: true });
923
1008
 
924
1009
  // src/runtime-context.ts
925
1010
  import { AsyncLocalStorage } from "async_hooks";
@@ -1003,7 +1088,7 @@ var includeItemSchema = z2.object({
1003
1088
  filter: z2.record(z2.string(), z2.unknown()).optional(),
1004
1089
  localField: z2.string().min(1),
1005
1090
  foreignField: z2.string().min(1),
1006
- args: z2.unknown().optional(),
1091
+ args: z2.unknown().optional().describe("Forwarded to list/read includes. Count includes ignore pagination fields so counts remain exact."),
1007
1092
  options: z2.unknown().optional()
1008
1093
  }).passthrough();
1009
1094
  var includeSchema = z2.union([includeItemSchema, z2.array(includeItemSchema)]);
@@ -1540,7 +1625,7 @@ import { z as z4 } from "zod";
1540
1625
  var dataListBodySchema = z4.object({
1541
1626
  filter: objectOrArraySchema.optional(),
1542
1627
  select: projectionSchema.optional(),
1543
- sort: z4.string().optional(),
1628
+ sort: sortSchema.optional(),
1544
1629
  skip: z4.union([nonNegativeIntegerSchema, nonNegativeIntegerString]).optional(),
1545
1630
  limit: z4.union([positiveIntegerSchema, positiveIntegerString]).optional(),
1546
1631
  page: z4.union([nonNegativeIntegerSchema, nonNegativeIntegerString]).optional(),
@@ -1632,7 +1717,7 @@ var rootModelSubReadArgsSchema = z5.object({
1632
1717
  var rootModelCountOptionsSchema = z5.object({}).strict().optional();
1633
1718
  var rootDataListArgsSchema = z5.object({
1634
1719
  select: projectionSchema.optional(),
1635
- sort: z5.string().optional(),
1720
+ sort: sortSchema.optional(),
1636
1721
  skip: z5.union([nonNegativeIntegerSchema, nonNegativeIntegerString]).optional(),
1637
1722
  limit: z5.union([positiveIntegerSchema, positiveIntegerString]).optional(),
1638
1723
  page: z5.union([nonNegativeIntegerSchema, nonNegativeIntegerString]).optional(),
package/index.d.mts CHANGED
@@ -1,10 +1,173 @@
1
1
  import mongoose, { Schema } from 'mongoose';
2
2
  import { Response, NextFunction, RequestHandler, Router } from 'express';
3
- import { A as AccessRouterBaseRequest, aa as GuardHook, ay as ModelRequest, cG as AccessRuntime, N as DataRouterOptions, L as DataRequest, cH as DataService, cI as Model, az as ModelRouterOptions, cJ as Service, cK as PublicService, _ as ExtendedModelRouterOptions, cj as Validation, bu as RootRouterOptions, cL as OpenApiRouterOptions, cM as OpenApiRegistryOptions, cN as OpenApiRouteDescriptor, cO as OpenApiDocumentOptions, cP as OpenApiParameter, cQ as OpenApiSchema, cR as OpenApiResponses, a8 as GlobalOptions, R as DefaultModelRouterOptions, Z as ExtendedDefaultModelRouterOptions } from './parsers--DZcTstL.mjs';
4
- export { cS as AccessRouterPermissionMap, cT as AccessRouterPermissions, c as AccessRouterRequest, d as AccessRouterRequestExtensions, l as AjvErrorObjectLike, m as AjvValidatorLike, n as ArkTypeErrorsLike, o as ArkTypeLike, a9 as GlobalPermissionValue, ad as IoTsContextEntryLike, ae as IoTsDecodeErrorLike, af as IoTsDecoderLike, ag as JoiSchemaLike, ah as JoiValidationErrorDetailLike, am as MaybePromise, an as ModelBaseFilterHook, ap as ModelDeleteHook, aq as ModelDocPermissionsHook, as as ModelDocumentHook, at as ModelHook, av as ModelIdentifierHook, aw as ModelListHook, ax as ModelOverrideFilterHook, aA as ModelValidateHook, cU as OpenApiMethod, cV as Permissions, a_ as RequestSchemaAdapter, a$ as RequestSchemaFailure, b0 as RequestSchemaIssue, b1 as RequestSchemaLike, b2 as RequestSchemaOptions, b3 as RequestSchemaResult, b4 as RequestSchemaSuccess, b5 as RequestSchemaValidator, bF as StandardSchemaFailure, bG as StandardSchemaInferOutput, bH as StandardSchemaIssue, bI as StandardSchemaPathSegment, bJ as StandardSchemaResult, bK as StandardSchemaSuccess, bL as StandardSchemaV1, c0 as SuperstructFailureLike, c1 as SuperstructValidateLike, cd as ValibotIssueLike, ce as ValibotPathItemLike, cf as ValibotSafeParseLike, cg as ValibotSafeParseResult, cl as VineValidationErrorLike, cm as VineValidationMessageLike, cn as VineValidatorLike, co as YupSchemaLike, cp as YupValidationErrorLike, cW as defaultRuntime, cq as defineRequestSchema, cr as fromAjv, cs as fromArkType, ct as fromIoTs, cu as fromJoi, cv as fromStandardSchema, cw as fromSuperstruct, cx as fromValibot, cy as fromVine, cz as fromYup, cA as fromZod } from './parsers--DZcTstL.mjs';
3
+ import { a8 as GlobalOptions, R as DefaultModelRouterOptions, Z as ExtendedDefaultModelRouterOptions, az as ModelRouterOptions, _ as ExtendedModelRouterOptions, N as DataRouterOptions, Y as ExtendedDataRouterOptions, A as AccessRouterBaseRequest, aa as GuardHook, ay as ModelRequest, L as DataRequest, cG as DataService, $ as Filter, aI as Projection, bD as Sort, aF as Populate, cH as Service, cI as PublicService, cj as Validation, bu as RootRouterOptions } from './parsers-PMWujHUV.mjs';
4
+ export { cJ as AccessRouterPermissionMap, cK as AccessRouterPermissions, c as AccessRouterRequest, d as AccessRouterRequestExtensions, l as AjvErrorObjectLike, m as AjvValidatorLike, n as ArkTypeErrorsLike, o as ArkTypeLike, a9 as GlobalPermissionValue, ad as IoTsContextEntryLike, ae as IoTsDecodeErrorLike, af as IoTsDecoderLike, ag as JoiSchemaLike, ah as JoiValidationErrorDetailLike, am as MaybePromise, an as ModelBaseFilterHook, ap as ModelDeleteHook, aq as ModelDocPermissionsHook, as as ModelDocumentHook, at as ModelHook, av as ModelIdentifierHook, aw as ModelListHook, ax as ModelOverrideFilterHook, aA as ModelValidateHook, cL as Permissions, a_ as RequestSchemaAdapter, a$ as RequestSchemaFailure, b0 as RequestSchemaIssue, b1 as RequestSchemaLike, b2 as RequestSchemaOptions, b3 as RequestSchemaResult, b4 as RequestSchemaSuccess, b5 as RequestSchemaValidator, bF as StandardSchemaFailure, bG as StandardSchemaInferOutput, bH as StandardSchemaIssue, bI as StandardSchemaPathSegment, bJ as StandardSchemaResult, bK as StandardSchemaSuccess, bL as StandardSchemaV1, c0 as SuperstructFailureLike, c1 as SuperstructValidateLike, cd as ValibotIssueLike, ce as ValibotPathItemLike, cf as ValibotSafeParseLike, cg as ValibotSafeParseResult, cl as VineValidationErrorLike, cm as VineValidationMessageLike, cn as VineValidatorLike, co as YupSchemaLike, cp as YupValidationErrorLike, cq as defineRequestSchema, cr as fromAjv, cs as fromArkType, ct as fromIoTs, cu as fromJoi, cv as fromStandardSchema, cw as fromSuperstruct, cx as fromValibot, cy as fromVine, cz as fromYup, cA as fromZod } from './parsers-PMWujHUV.mjs';
5
5
  import JsonRouter from '@web-ts-toolkit/express-json-router';
6
6
  import 'zod';
7
7
 
8
+ type OpenApiMethod = 'get' | 'post' | 'put' | 'patch' | 'delete';
9
+ type OpenApiSchema = Record<string, unknown>;
10
+ type OpenApiSchemaSource = unknown;
11
+ type OpenApiParameter = {
12
+ name: string;
13
+ in: 'path' | 'query';
14
+ required?: boolean;
15
+ description?: string;
16
+ schema?: OpenApiSchema;
17
+ };
18
+ type OpenApiResponse = {
19
+ description: string;
20
+ content?: Record<string, {
21
+ schema: OpenApiSchema;
22
+ }>;
23
+ };
24
+ type OpenApiResponses = Record<string, OpenApiResponse>;
25
+ type OpenApiRouteDescriptor = {
26
+ method: OpenApiMethod;
27
+ path: string;
28
+ operationId?: string;
29
+ summary?: string;
30
+ description?: string;
31
+ tags?: string[];
32
+ acl?: string;
33
+ deprecated?: boolean;
34
+ query?: OpenApiSchemaSource;
35
+ body?: OpenApiSchemaSource;
36
+ pathParams?: Record<string, OpenApiSchemaSource>;
37
+ responses?: OpenApiResponses;
38
+ /**
39
+ * When true, replaces an existing registration with the same method/path
40
+ * even when the descriptors differ. When false (default), conflicting
41
+ * registrations throw at registration time so consumers can detect
42
+ * accidental route shadowing.
43
+ */
44
+ allowReplace?: boolean;
45
+ /**
46
+ * When true, this exact descriptor is idempotent and registering the same
47
+ * method/path with an equivalent descriptor again is silently accepted.
48
+ * Differing re-registrations still throw unless {@link allowReplace} is set.
49
+ * Additionally, an idempotent descriptor may share its operationId with
50
+ * another idempotent descriptor mounted at a different path on the same
51
+ * registry, so reserved operations (e.g. the root batch route) can be
52
+ * exposed from multiple base paths without triggering strict-mode
53
+ * duplicate-operationId rejection.
54
+ */
55
+ idempotent?: boolean;
56
+ };
57
+ type OpenApiRegistryOptions = {
58
+ /**
59
+ * Throw when the same method/path is registered twice with differing
60
+ * descriptors, instead of silently replacing. Default: true (strict).
61
+ * The {@link AccessRuntime} registry is constructed in strict mode by
62
+ * default; construct {@link OpenApiRegistry} directly with
63
+ * `rejectConflicts: false` for legacy replacement semantics.
64
+ */
65
+ rejectConflicts?: boolean;
66
+ /**
67
+ * Throw when the same operationId is registered more than once, including
68
+ * when {@link OpenApiRouteDescriptor.allowReplace} is set. Default: true.
69
+ */
70
+ rejectDuplicateOperationIds?: boolean;
71
+ };
72
+ type OpenApiDocumentOptions = {
73
+ title: string;
74
+ version: string;
75
+ description?: string;
76
+ servers?: Array<{
77
+ url: string;
78
+ description?: string;
79
+ }>;
80
+ };
81
+ type OpenApiRouterOptions = Partial<OpenApiDocumentOptions> & {
82
+ jsonPath?: string;
83
+ docsPath?: string | false;
84
+ swaggerUiCssUrl?: string;
85
+ swaggerUiBundleUrl?: string;
86
+ };
87
+
88
+ declare class AccessRuntime {
89
+ private readonly allowGlobalModelLookup;
90
+ constructor(options?: {
91
+ allowGlobalModelLookup?: boolean;
92
+ });
93
+ private readonly globalOptions;
94
+ private readonly defaultModelOptions;
95
+ private readonly modelOptions;
96
+ private readonly modelJsonSchemas;
97
+ private readonly dataOptions;
98
+ private readonly modelRefs;
99
+ private readonly modelSubs;
100
+ private readonly modelAtts;
101
+ private readonly modelInstances;
102
+ private readonly openApiRegistry;
103
+ registerModelInstance(modelName: string, model: mongoose.Model<unknown>): void;
104
+ hasModelInstance(modelName: string): boolean;
105
+ getModelInstance(modelName: string): mongoose.Model<unknown> | null;
106
+ registerOpenApiRoute(route: OpenApiRouteDescriptor): void;
107
+ /**
108
+ * Enable strict OpenAPI collision detection for the remainder of this
109
+ * runtime's lifetime. After this call, conflicting method/path
110
+ * registrations and duplicate operationIds throw {@link OpenApiCollisionError}
111
+ * instead of silently replacing. Existing already-registered routes are
112
+ * not retroactively re-validated.
113
+ */
114
+ enableOpenApiCollisionDetection(): void;
115
+ getOpenApiRoutes(): OpenApiRouteDescriptor[];
116
+ clearOpenApiRoutes(): void;
117
+ getOpenApiSpec(info: OpenApiDocumentOptions): {
118
+ openapi: string;
119
+ info: Omit<OpenApiDocumentOptions, "servers">;
120
+ servers?: OpenApiDocumentOptions["servers"];
121
+ paths: Record<string, Record<string, {
122
+ [key: `x-${string}`]: unknown;
123
+ operationId?: string;
124
+ summary?: string;
125
+ description?: string;
126
+ tags?: string[];
127
+ deprecated?: boolean;
128
+ parameters?: OpenApiParameter[];
129
+ requestBody?: {
130
+ required?: boolean;
131
+ content: Record<string, {
132
+ schema: OpenApiSchema;
133
+ }>;
134
+ };
135
+ responses: OpenApiResponses;
136
+ }>>;
137
+ };
138
+ setGlobalOptions(options: GlobalOptions): void;
139
+ setGlobalOption<K extends keyof GlobalOptions>(key: K, value: GlobalOptions[K]): void;
140
+ getGlobalOptions(): GlobalOptions;
141
+ getGlobalOption<K extends keyof GlobalOptions>(key: K, defaultValue?: GlobalOptions[K]): GlobalOptions[K];
142
+ setDefaultModelOptions(options: DefaultModelRouterOptions): void;
143
+ setDefaultModelOption<K extends keyof ExtendedDefaultModelRouterOptions>(key: K, value: ExtendedDefaultModelRouterOptions[K]): void;
144
+ getDefaultModelOptions(): DefaultModelRouterOptions<unknown>;
145
+ getDefaultModelOption<K extends keyof ExtendedDefaultModelRouterOptions>(key: K, defaultValue?: ExtendedDefaultModelRouterOptions[K]): ExtendedDefaultModelRouterOptions<unknown>[K];
146
+ private createModelOptions;
147
+ private getOrCreateModelOptions;
148
+ setModelOptions<TModel = unknown>(modelName: string, options: ModelRouterOptions<TModel>): void;
149
+ setModelOption<K extends keyof ExtendedModelRouterOptions<TModel>, TModel = unknown>(modelName: string, key: K, value: ExtendedModelRouterOptions<TModel>[K]): void;
150
+ getModelOptions<TModel = unknown>(modelName: string): ModelRouterOptions<TModel>;
151
+ getModelOption<K extends keyof ExtendedModelRouterOptions<TModel>, TModel = unknown>(modelName: string, key: K | string, defaultValue?: ExtendedModelRouterOptions<TModel>[K]): ExtendedModelRouterOptions<TModel>[K];
152
+ getExactModelOption<K extends keyof ExtendedModelRouterOptions<TModel>, TModel = unknown>(modelName: string, key: K | string): ExtendedModelRouterOptions<TModel>[K];
153
+ getModelNames(): string[];
154
+ hasModel(modelName: string): boolean;
155
+ getModelJsonSchema(modelName: string): Record<string, unknown>;
156
+ private createDataOptions;
157
+ private getOrCreateDataOptions;
158
+ setDataOptions<TData = unknown>(dataName: string, options: DataRouterOptions<TData>): void;
159
+ setDataOption<K extends keyof DataRouterOptions<TData>, TData = unknown>(dataName: string, key: K, value: DataRouterOptions<TData>[K]): void;
160
+ getDataOptions<TData = unknown>(dataName: string): DataRouterOptions<TData>;
161
+ getDataOption<K extends keyof DataRouterOptions<TData>, TData = unknown>(dataName: string, key: K | string, defaultValue?: DataRouterOptions<TData>[K]): DataRouterOptions<TData>[K];
162
+ getExactDataOption<K extends keyof ExtendedDataRouterOptions<TData>, TData = unknown>(dataName: string, key: K | string): ExtendedDataRouterOptions<TData>[K];
163
+ getDataNames(): string[];
164
+ ensureModelMeta(modelName: string): void;
165
+ getModelRef(modelName: string, refPath: string): string | null;
166
+ getModelSub(modelName: string): string[];
167
+ getModelAtt(modelName: string): string[];
168
+ }
169
+ declare const defaultRuntime: AccessRuntime;
170
+
8
171
  declare function macl(): (req: AccessRouterBaseRequest, res: Response, next: NextFunction) => Promise<void>;
9
172
  interface GuardModelConditionID {
10
173
  type: 'param' | 'query';
@@ -55,6 +218,38 @@ declare class DataRouter<TData = unknown> {
55
218
  get routes(): Router;
56
219
  }
57
220
 
221
+ interface FindProps {
222
+ filter: Filter;
223
+ select?: Projection;
224
+ sort?: Sort;
225
+ populate?: Populate[] | string;
226
+ limit?: string | number;
227
+ hardLimit?: number;
228
+ skip?: string | number;
229
+ lean?: boolean;
230
+ }
231
+ interface FindOneProps {
232
+ filter: Filter;
233
+ select?: Projection;
234
+ sort?: Sort;
235
+ populate?: Populate[] | string;
236
+ lean?: boolean;
237
+ }
238
+ declare class Model {
239
+ modelName: string;
240
+ mongooseModel: mongoose.Model<any>;
241
+ runtime: AccessRuntime | null;
242
+ constructor(modelName: string, runtime?: AccessRuntime);
243
+ new(): any;
244
+ create(data: unknown): Promise<any>;
245
+ find({ filter, select, sort, populate, limit, hardLimit, skip, lean }: FindProps): mongoose.Query<any[], any>;
246
+ findOne({ filter, select, sort, populate, lean }: FindOneProps): mongoose.Query<any, any>;
247
+ exists(filter: Filter): ReturnType<typeof Model.prototype.findOne> | null;
248
+ countDocuments(filter?: {}): mongoose.Query<number, any>;
249
+ distinct(field: string, conditions?: {}): mongoose.Query<any[], any>;
250
+ aggregate(pipeline: unknown[]): mongoose.Aggregate<any[]>;
251
+ }
252
+
58
253
  type SetTargetOption<TRouter, TOption> = {
59
254
  (option: TOption): TRouter;
60
255
  (key: string, option: unknown): TRouter;
@@ -379,4 +574,4 @@ type AccessRuntimeApi = typeof macl & Wtt;
379
574
  declare function createAccessRuntime(): AccessRuntimeApi;
380
575
  declare const acl: AccessRuntimeApi;
381
576
 
382
- export { AccessRuntime, type AccessRuntimeApi, type CombinedRouteInput, DataRouter, DataRouterOptions, ExtendedDefaultModelRouterOptions, ExtendedModelRouterOptions, GlobalOptions, type GuardModelCondition, type GuardModelConditionID, ModelRouter, ModelRouterOptions, type OpLogContext, OpenApiCollisionError, OpenApiDocumentOptions, OpenApiRegistry, OpenApiRegistryOptions, OpenApiRouteDescriptor, OpenApiRouterOptions, RootRouter, RootRouterOptions, acl, combineRoutes, createAccessRuntime, createOpenApiRouter, acl as default, getDefaultModelOption, getDefaultModelOptions, getGlobalOption, getGlobalOptions, getModelInstance, getModelJsonSchema, getModelNames, getModelOption, getModelOptions, guard, hasModelInstance, isLevelEnabled, permissionsPlugin, redactFilter, redactPayload, registerModelInstance, safeStringify, setDefaultModelOption, setDefaultModelOptions, setGlobalOption, setGlobalOptions, setModelOption, setModelOptions };
577
+ export { AccessRuntime, type AccessRuntimeApi, type CombinedRouteInput, DataRouter, DataRouterOptions, ExtendedDefaultModelRouterOptions, ExtendedModelRouterOptions, GlobalOptions, type GuardModelCondition, type GuardModelConditionID, ModelRouter, ModelRouterOptions, type OpLogContext, OpenApiCollisionError, type OpenApiDocumentOptions, type OpenApiMethod, OpenApiRegistry, type OpenApiRegistryOptions, type OpenApiRouteDescriptor, type OpenApiRouterOptions, RootRouter, RootRouterOptions, acl, combineRoutes, createAccessRuntime, createOpenApiRouter, acl as default, defaultRuntime, getDefaultModelOption, getDefaultModelOptions, getGlobalOption, getGlobalOptions, getModelInstance, getModelJsonSchema, getModelNames, getModelOption, getModelOptions, guard, hasModelInstance, isLevelEnabled, permissionsPlugin, redactFilter, redactPayload, registerModelInstance, safeStringify, setDefaultModelOption, setDefaultModelOptions, setGlobalOption, setGlobalOptions, setModelOption, setModelOptions };