@web-ts-toolkit/access-router 0.41.0 → 0.42.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.js CHANGED
@@ -495,6 +495,17 @@ var OpenApiRegistry = class {
495
495
  getSpec(info) {
496
496
  return buildOpenApiSpec(this.routes, info);
497
497
  }
498
+ snapshot() {
499
+ return {
500
+ routes: [...this.routes],
501
+ options: { ...this.options }
502
+ };
503
+ }
504
+ restore(snapshot) {
505
+ this.routes.length = 0;
506
+ this.routes.push(...snapshot.routes);
507
+ this.options = { ...snapshot.options };
508
+ }
498
509
  };
499
510
 
500
511
  // src/logger-default.ts
@@ -608,7 +619,8 @@ var OptionsManager = class {
608
619
  }
609
620
  const [rootKey, ...nestedPath] = path;
610
621
  const currentRoot = (0, import_utils6.get)(this.currentOptions, [rootKey], void 0);
611
- const nextRoot = cloneConfigValue(currentRoot ?? {});
622
+ const baseRoot = (0, import_utils6.isPlainObject)(currentRoot) ? currentRoot : {};
623
+ const nextRoot = cloneConfigValue(baseRoot);
612
624
  (0, import_utils6.set)(nextRoot, nestedPath, cloneConfigValue(value));
613
625
  (0, import_utils6.set)(this.currentOptions, [rootKey], nextRoot);
614
626
  }
@@ -634,6 +646,31 @@ var OptionsManager = class {
634
646
  (0, import_utils6.set)(this.listeners, key, func);
635
647
  return this;
636
648
  }
649
+ snapshot() {
650
+ const cloned = cloneConfigValue(this.currentOptions);
651
+ for (const key of this.preserveKeys) {
652
+ if (key in this.currentOptions) {
653
+ cloned[key] = this.currentOptions[key];
654
+ }
655
+ }
656
+ return cloned;
657
+ }
658
+ restore(snapshot) {
659
+ const snap = snapshot;
660
+ const current = this.currentOptions;
661
+ for (const key of Object.keys(current)) {
662
+ if (!(key in snap)) {
663
+ delete current[key];
664
+ }
665
+ }
666
+ const cloned = cloneConfigValue(snap);
667
+ for (const key of this.preserveKeys) {
668
+ if (key in snap) {
669
+ cloned[key] = snap[key];
670
+ }
671
+ }
672
+ (0, import_utils6.assign)(current, cloned);
673
+ }
637
674
  };
638
675
 
639
676
  // src/request-complexity.ts
@@ -908,6 +945,85 @@ var AccessRuntime = class {
908
945
  getOpenApiSpec(info) {
909
946
  return this.openApiRegistry.getSpec(info);
910
947
  }
948
+ createBootstrapSnapshot() {
949
+ const modelOptionsSnap = {};
950
+ for (const [name, mgr] of Object.entries(this.modelOptions)) {
951
+ modelOptionsSnap[name] = mgr.snapshot();
952
+ }
953
+ const dataOptionsSnap = {};
954
+ for (const [name, mgr] of Object.entries(this.dataOptions)) {
955
+ dataOptionsSnap[name] = mgr.snapshot();
956
+ }
957
+ return {
958
+ globalOptions: this.globalOptions.snapshot(),
959
+ defaultModelOptions: this.defaultModelOptions.snapshot(),
960
+ modelOptions: modelOptionsSnap,
961
+ dataOptions: dataOptionsSnap,
962
+ modelInstances: { ...this.modelInstances },
963
+ modelJsonSchemas: { ...this.modelJsonSchemas },
964
+ modelRefs: Object.fromEntries(
965
+ Object.entries(this.modelRefs).map(([k, v]) => [k, JSON.parse(JSON.stringify(v))])
966
+ ),
967
+ modelSubs: Object.fromEntries(Object.entries(this.modelSubs).map(([k, v]) => [k, [...v]])),
968
+ modelAtts: Object.fromEntries(Object.entries(this.modelAtts).map(([k, v]) => [k, [...v]])),
969
+ openApi: this.openApiRegistry.snapshot()
970
+ };
971
+ }
972
+ restoreBootstrapSnapshot(snapshot) {
973
+ this.globalOptions.restore(snapshot.globalOptions);
974
+ this.defaultModelOptions.restore(snapshot.defaultModelOptions);
975
+ for (const name of Object.keys(this.modelOptions)) {
976
+ if (!(name in snapshot.modelOptions)) {
977
+ delete this.modelOptions[name];
978
+ }
979
+ }
980
+ for (const [name, opts] of Object.entries(snapshot.modelOptions)) {
981
+ const existing = this.modelOptions[name];
982
+ if (existing) {
983
+ existing.restore(opts);
984
+ } else {
985
+ const mgr = this.createModelOptions(name);
986
+ mgr.restore(opts);
987
+ this.modelOptions[name] = mgr;
988
+ }
989
+ }
990
+ for (const name of Object.keys(this.dataOptions)) {
991
+ if (!(name in snapshot.dataOptions)) {
992
+ delete this.dataOptions[name];
993
+ }
994
+ }
995
+ for (const [name, opts] of Object.entries(snapshot.dataOptions)) {
996
+ const existing = this.dataOptions[name];
997
+ if (existing) {
998
+ existing.restore(opts);
999
+ } else {
1000
+ const mgr = this.createDataOptions(name);
1001
+ mgr.restore(opts);
1002
+ this.dataOptions[name] = mgr;
1003
+ }
1004
+ }
1005
+ for (const k of Object.keys(this.modelInstances)) {
1006
+ if (!(k in snapshot.modelInstances)) delete this.modelInstances[k];
1007
+ }
1008
+ Object.assign(this.modelInstances, snapshot.modelInstances);
1009
+ for (const k of Object.keys(this.modelJsonSchemas)) {
1010
+ if (!(k in snapshot.modelJsonSchemas)) delete this.modelJsonSchemas[k];
1011
+ }
1012
+ Object.assign(this.modelJsonSchemas, snapshot.modelJsonSchemas);
1013
+ for (const k of Object.keys(this.modelRefs)) {
1014
+ if (!(k in snapshot.modelRefs)) delete this.modelRefs[k];
1015
+ }
1016
+ Object.assign(this.modelRefs, snapshot.modelRefs);
1017
+ for (const k of Object.keys(this.modelSubs)) {
1018
+ if (!(k in snapshot.modelSubs)) delete this.modelSubs[k];
1019
+ }
1020
+ Object.assign(this.modelSubs, snapshot.modelSubs);
1021
+ for (const k of Object.keys(this.modelAtts)) {
1022
+ if (!(k in snapshot.modelAtts)) delete this.modelAtts[k];
1023
+ }
1024
+ Object.assign(this.modelAtts, snapshot.modelAtts);
1025
+ this.openApiRegistry.restore(snapshot.openApi);
1026
+ }
911
1027
  setGlobalOptions(options) {
912
1028
  this.globalOptions.assign(options);
913
1029
  }
package/advanced.mjs CHANGED
@@ -54,7 +54,7 @@ import {
54
54
  isBoolean,
55
55
  isFunction,
56
56
  isNil as isNil2,
57
- isPlainObject as isPlainObject8,
57
+ isPlainObject as isPlainObject9,
58
58
  isString as isString3,
59
59
  keys
60
60
  } from "@web-ts-toolkit/utils";
@@ -424,6 +424,17 @@ var OpenApiRegistry = class {
424
424
  getSpec(info) {
425
425
  return buildOpenApiSpec(this.routes, info);
426
426
  }
427
+ snapshot() {
428
+ return {
429
+ routes: [...this.routes],
430
+ options: { ...this.options }
431
+ };
432
+ }
433
+ restore(snapshot) {
434
+ this.routes.length = 0;
435
+ this.routes.push(...snapshot.routes);
436
+ this.options = { ...snapshot.options };
437
+ }
427
438
  };
428
439
 
429
440
  // src/logger-default.ts
@@ -453,7 +464,7 @@ var defaultLogger = createLogger({
453
464
  });
454
465
 
455
466
  // src/options/manager.ts
456
- import { assign, get, set } from "@web-ts-toolkit/utils";
467
+ import { assign, get, isPlainObject as isPlainObject7, set } from "@web-ts-toolkit/utils";
457
468
  var toPath = (key) => {
458
469
  if (Array.isArray(key)) return key;
459
470
  return typeof key === "string" ? key.split(".") : [key];
@@ -537,7 +548,8 @@ var OptionsManager = class {
537
548
  }
538
549
  const [rootKey, ...nestedPath] = path;
539
550
  const currentRoot = get(this.currentOptions, [rootKey], void 0);
540
- const nextRoot = cloneConfigValue(currentRoot ?? {});
551
+ const baseRoot = isPlainObject7(currentRoot) ? currentRoot : {};
552
+ const nextRoot = cloneConfigValue(baseRoot);
541
553
  set(nextRoot, nestedPath, cloneConfigValue(value));
542
554
  set(this.currentOptions, [rootKey], nextRoot);
543
555
  }
@@ -563,10 +575,35 @@ var OptionsManager = class {
563
575
  set(this.listeners, key, func);
564
576
  return this;
565
577
  }
578
+ snapshot() {
579
+ const cloned = cloneConfigValue(this.currentOptions);
580
+ for (const key of this.preserveKeys) {
581
+ if (key in this.currentOptions) {
582
+ cloned[key] = this.currentOptions[key];
583
+ }
584
+ }
585
+ return cloned;
586
+ }
587
+ restore(snapshot) {
588
+ const snap = snapshot;
589
+ const current = this.currentOptions;
590
+ for (const key of Object.keys(current)) {
591
+ if (!(key in snap)) {
592
+ delete current[key];
593
+ }
594
+ }
595
+ const cloned = cloneConfigValue(snap);
596
+ for (const key of this.preserveKeys) {
597
+ if (key in snap) {
598
+ cloned[key] = snap[key];
599
+ }
600
+ }
601
+ assign(current, cloned);
602
+ }
566
603
  };
567
604
 
568
605
  // src/request-complexity.ts
569
- import { isArray as isArray3, isPlainObject as isPlainObject7 } from "@web-ts-toolkit/utils";
606
+ import { isArray as isArray3, isPlainObject as isPlainObject8 } from "@web-ts-toolkit/utils";
570
607
  var defaultRequestComplexity = {
571
608
  maxDepth: 8,
572
609
  maxNodes: 500,
@@ -642,7 +679,7 @@ function validateRequestComplexity(value, options, scope = "request") {
642
679
  }
643
680
  return;
644
681
  }
645
- if (!isPlainObject7(current)) return;
682
+ if (!isPlainObject8(current)) return;
646
683
  for (const [key, child] of Object.entries(current)) {
647
684
  if (key === "__proto__" || key === "prototype" || key === "constructor") {
648
685
  pushError(`Unsupported key: ${key}`, path.concat(key));
@@ -721,7 +758,7 @@ var classifyPermissionSchema = (permissionSchema, modelPermissionPrefix) => {
721
758
  const modelPermissionKeys = {};
722
759
  forEach2(schemaKeys, (schemaKey) => {
723
760
  const schemaRule = permissionSchema[schemaKey];
724
- const ruleEntries = isPlainObject8(schemaRule) ? Object.entries(schemaRule) : FIELD_ACCESS_KEYS.map((accessKey) => [accessKey, schemaRule]);
761
+ const ruleEntries = isPlainObject9(schemaRule) ? Object.entries(schemaRule) : FIELD_ACCESS_KEYS.map((accessKey) => [accessKey, schemaRule]);
725
762
  for (let x = 0; x < ruleEntries.length; x++) {
726
763
  const [accessKey, value] = ruleEntries[x];
727
764
  if (!isArray4(globalPermissionKeys[accessKey])) {
@@ -837,6 +874,85 @@ var AccessRuntime = class {
837
874
  getOpenApiSpec(info) {
838
875
  return this.openApiRegistry.getSpec(info);
839
876
  }
877
+ createBootstrapSnapshot() {
878
+ const modelOptionsSnap = {};
879
+ for (const [name, mgr] of Object.entries(this.modelOptions)) {
880
+ modelOptionsSnap[name] = mgr.snapshot();
881
+ }
882
+ const dataOptionsSnap = {};
883
+ for (const [name, mgr] of Object.entries(this.dataOptions)) {
884
+ dataOptionsSnap[name] = mgr.snapshot();
885
+ }
886
+ return {
887
+ globalOptions: this.globalOptions.snapshot(),
888
+ defaultModelOptions: this.defaultModelOptions.snapshot(),
889
+ modelOptions: modelOptionsSnap,
890
+ dataOptions: dataOptionsSnap,
891
+ modelInstances: { ...this.modelInstances },
892
+ modelJsonSchemas: { ...this.modelJsonSchemas },
893
+ modelRefs: Object.fromEntries(
894
+ Object.entries(this.modelRefs).map(([k, v]) => [k, JSON.parse(JSON.stringify(v))])
895
+ ),
896
+ modelSubs: Object.fromEntries(Object.entries(this.modelSubs).map(([k, v]) => [k, [...v]])),
897
+ modelAtts: Object.fromEntries(Object.entries(this.modelAtts).map(([k, v]) => [k, [...v]])),
898
+ openApi: this.openApiRegistry.snapshot()
899
+ };
900
+ }
901
+ restoreBootstrapSnapshot(snapshot) {
902
+ this.globalOptions.restore(snapshot.globalOptions);
903
+ this.defaultModelOptions.restore(snapshot.defaultModelOptions);
904
+ for (const name of Object.keys(this.modelOptions)) {
905
+ if (!(name in snapshot.modelOptions)) {
906
+ delete this.modelOptions[name];
907
+ }
908
+ }
909
+ for (const [name, opts] of Object.entries(snapshot.modelOptions)) {
910
+ const existing = this.modelOptions[name];
911
+ if (existing) {
912
+ existing.restore(opts);
913
+ } else {
914
+ const mgr = this.createModelOptions(name);
915
+ mgr.restore(opts);
916
+ this.modelOptions[name] = mgr;
917
+ }
918
+ }
919
+ for (const name of Object.keys(this.dataOptions)) {
920
+ if (!(name in snapshot.dataOptions)) {
921
+ delete this.dataOptions[name];
922
+ }
923
+ }
924
+ for (const [name, opts] of Object.entries(snapshot.dataOptions)) {
925
+ const existing = this.dataOptions[name];
926
+ if (existing) {
927
+ existing.restore(opts);
928
+ } else {
929
+ const mgr = this.createDataOptions(name);
930
+ mgr.restore(opts);
931
+ this.dataOptions[name] = mgr;
932
+ }
933
+ }
934
+ for (const k of Object.keys(this.modelInstances)) {
935
+ if (!(k in snapshot.modelInstances)) delete this.modelInstances[k];
936
+ }
937
+ Object.assign(this.modelInstances, snapshot.modelInstances);
938
+ for (const k of Object.keys(this.modelJsonSchemas)) {
939
+ if (!(k in snapshot.modelJsonSchemas)) delete this.modelJsonSchemas[k];
940
+ }
941
+ Object.assign(this.modelJsonSchemas, snapshot.modelJsonSchemas);
942
+ for (const k of Object.keys(this.modelRefs)) {
943
+ if (!(k in snapshot.modelRefs)) delete this.modelRefs[k];
944
+ }
945
+ Object.assign(this.modelRefs, snapshot.modelRefs);
946
+ for (const k of Object.keys(this.modelSubs)) {
947
+ if (!(k in snapshot.modelSubs)) delete this.modelSubs[k];
948
+ }
949
+ Object.assign(this.modelSubs, snapshot.modelSubs);
950
+ for (const k of Object.keys(this.modelAtts)) {
951
+ if (!(k in snapshot.modelAtts)) delete this.modelAtts[k];
952
+ }
953
+ Object.assign(this.modelAtts, snapshot.modelAtts);
954
+ this.openApiRegistry.restore(snapshot.openApi);
955
+ }
840
956
  setGlobalOptions(options) {
841
957
  this.globalOptions.assign(options);
842
958
  }
package/index.d.mts CHANGED
@@ -1,7 +1,7 @@
1
1
  import mongoose, { Schema } from 'mongoose';
2
2
  import { Response, NextFunction, RequestHandler, Router } from 'express';
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';
3
+ import { a8 as GlobalOptions, R as DefaultModelRouterOptions, az as ModelRouterOptions, N as DataRouterOptions, Z as ExtendedDefaultModelRouterOptions, _ as ExtendedModelRouterOptions, 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, bx as RouteGuardAccess, 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
 
@@ -85,6 +85,9 @@ type OpenApiRouterOptions = Partial<OpenApiDocumentOptions> & {
85
85
  swaggerUiBundleUrl?: string;
86
86
  };
87
87
 
88
+ type ModelReferenceMap = {
89
+ [key: string]: string | ModelReferenceMap;
90
+ };
88
91
  declare class AccessRuntime {
89
92
  private readonly allowGlobalModelLookup;
90
93
  constructor(options?: {
@@ -135,6 +138,22 @@ declare class AccessRuntime {
135
138
  responses: OpenApiResponses;
136
139
  }>>;
137
140
  };
141
+ createBootstrapSnapshot(): {
142
+ globalOptions: GlobalOptions;
143
+ defaultModelOptions: DefaultModelRouterOptions;
144
+ modelOptions: Record<string, ModelRouterOptions>;
145
+ dataOptions: Record<string, DataRouterOptions>;
146
+ modelInstances: Record<string, mongoose.Model<unknown>>;
147
+ modelJsonSchemas: Record<string, Record<string, unknown>>;
148
+ modelRefs: Record<string, ModelReferenceMap>;
149
+ modelSubs: Record<string, string[]>;
150
+ modelAtts: Record<string, string[]>;
151
+ openApi: {
152
+ routes: OpenApiRouteDescriptor[];
153
+ options: Required<OpenApiRegistryOptions>;
154
+ };
155
+ };
156
+ restoreBootstrapSnapshot(snapshot: ReturnType<AccessRuntime['createBootstrapSnapshot']>): void;
138
157
  setGlobalOptions(options: GlobalOptions): void;
139
158
  setGlobalOption<K extends keyof GlobalOptions>(key: K, value: GlobalOptions[K]): void;
140
159
  getGlobalOptions(): GlobalOptions;
@@ -456,6 +475,14 @@ declare class OpenApiRegistry {
456
475
  responses: OpenApiResponses;
457
476
  }>>;
458
477
  };
478
+ snapshot(): {
479
+ routes: OpenApiRouteDescriptor[];
480
+ options: Required<OpenApiRegistryOptions>;
481
+ };
482
+ restore(snapshot: {
483
+ routes: OpenApiRouteDescriptor[];
484
+ options: Required<OpenApiRegistryOptions>;
485
+ }): void;
459
486
  }
460
487
 
461
488
  type AccessRouterInstance = ModelRouter<any> | DataRouter<any> | RootRouter;
@@ -574,4 +601,4 @@ type AccessRuntimeApi = typeof macl & Wtt;
574
601
  declare function createAccessRuntime(): AccessRuntimeApi;
575
602
  declare const acl: AccessRuntimeApi;
576
603
 
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 };
604
+ export { AccessRuntime, type AccessRuntimeApi, type CombinedRouteInput, DataRouter, DataRouterOptions, ExtendedDefaultModelRouterOptions, ExtendedModelRouterOptions, GlobalOptions, GuardHook, type GuardModelCondition, type GuardModelConditionID, ModelRouter, ModelRouterOptions, type OpLogContext, OpenApiCollisionError, type OpenApiDocumentOptions, type OpenApiMethod, OpenApiRegistry, type OpenApiRegistryOptions, type OpenApiRouteDescriptor, type OpenApiRouterOptions, RootRouter, RootRouterOptions, Validation, 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 };
package/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import mongoose, { Schema } from 'mongoose';
2
2
  import { Response, NextFunction, RequestHandler, Router } from 'express';
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.js';
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.js';
3
+ import { a8 as GlobalOptions, R as DefaultModelRouterOptions, az as ModelRouterOptions, N as DataRouterOptions, Z as ExtendedDefaultModelRouterOptions, _ as ExtendedModelRouterOptions, 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.js';
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, bx as RouteGuardAccess, 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.js';
5
5
  import JsonRouter from '@web-ts-toolkit/express-json-router';
6
6
  import 'zod';
7
7
 
@@ -85,6 +85,9 @@ type OpenApiRouterOptions = Partial<OpenApiDocumentOptions> & {
85
85
  swaggerUiBundleUrl?: string;
86
86
  };
87
87
 
88
+ type ModelReferenceMap = {
89
+ [key: string]: string | ModelReferenceMap;
90
+ };
88
91
  declare class AccessRuntime {
89
92
  private readonly allowGlobalModelLookup;
90
93
  constructor(options?: {
@@ -135,6 +138,22 @@ declare class AccessRuntime {
135
138
  responses: OpenApiResponses;
136
139
  }>>;
137
140
  };
141
+ createBootstrapSnapshot(): {
142
+ globalOptions: GlobalOptions;
143
+ defaultModelOptions: DefaultModelRouterOptions;
144
+ modelOptions: Record<string, ModelRouterOptions>;
145
+ dataOptions: Record<string, DataRouterOptions>;
146
+ modelInstances: Record<string, mongoose.Model<unknown>>;
147
+ modelJsonSchemas: Record<string, Record<string, unknown>>;
148
+ modelRefs: Record<string, ModelReferenceMap>;
149
+ modelSubs: Record<string, string[]>;
150
+ modelAtts: Record<string, string[]>;
151
+ openApi: {
152
+ routes: OpenApiRouteDescriptor[];
153
+ options: Required<OpenApiRegistryOptions>;
154
+ };
155
+ };
156
+ restoreBootstrapSnapshot(snapshot: ReturnType<AccessRuntime['createBootstrapSnapshot']>): void;
138
157
  setGlobalOptions(options: GlobalOptions): void;
139
158
  setGlobalOption<K extends keyof GlobalOptions>(key: K, value: GlobalOptions[K]): void;
140
159
  getGlobalOptions(): GlobalOptions;
@@ -456,6 +475,14 @@ declare class OpenApiRegistry {
456
475
  responses: OpenApiResponses;
457
476
  }>>;
458
477
  };
478
+ snapshot(): {
479
+ routes: OpenApiRouteDescriptor[];
480
+ options: Required<OpenApiRegistryOptions>;
481
+ };
482
+ restore(snapshot: {
483
+ routes: OpenApiRouteDescriptor[];
484
+ options: Required<OpenApiRegistryOptions>;
485
+ }): void;
459
486
  }
460
487
 
461
488
  type AccessRouterInstance = ModelRouter<any> | DataRouter<any> | RootRouter;
@@ -574,4 +601,4 @@ type AccessRuntimeApi = typeof macl & Wtt;
574
601
  declare function createAccessRuntime(): AccessRuntimeApi;
575
602
  declare const acl: AccessRuntimeApi;
576
603
 
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 };
604
+ export { AccessRuntime, type AccessRuntimeApi, type CombinedRouteInput, DataRouter, DataRouterOptions, ExtendedDefaultModelRouterOptions, ExtendedModelRouterOptions, GlobalOptions, GuardHook, type GuardModelCondition, type GuardModelConditionID, ModelRouter, ModelRouterOptions, type OpLogContext, OpenApiCollisionError, type OpenApiDocumentOptions, type OpenApiMethod, OpenApiRegistry, type OpenApiRegistryOptions, type OpenApiRouteDescriptor, type OpenApiRouterOptions, RootRouter, RootRouterOptions, Validation, 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 };
package/index.js CHANGED
@@ -779,6 +779,17 @@ var OpenApiRegistry = class {
779
779
  getSpec(info) {
780
780
  return buildOpenApiSpec(this.routes, info);
781
781
  }
782
+ snapshot() {
783
+ return {
784
+ routes: [...this.routes],
785
+ options: { ...this.options }
786
+ };
787
+ }
788
+ restore(snapshot) {
789
+ this.routes.length = 0;
790
+ this.routes.push(...snapshot.routes);
791
+ this.options = { ...snapshot.options };
792
+ }
782
793
  };
783
794
 
784
795
  // src/logger-default.ts
@@ -892,7 +903,8 @@ var OptionsManager = class {
892
903
  }
893
904
  const [rootKey, ...nestedPath] = path;
894
905
  const currentRoot = (0, import_utils9.get)(this.currentOptions, [rootKey], void 0);
895
- const nextRoot = cloneConfigValue(currentRoot ?? {});
906
+ const baseRoot = (0, import_utils9.isPlainObject)(currentRoot) ? currentRoot : {};
907
+ const nextRoot = cloneConfigValue(baseRoot);
896
908
  (0, import_utils9.set)(nextRoot, nestedPath, cloneConfigValue(value));
897
909
  (0, import_utils9.set)(this.currentOptions, [rootKey], nextRoot);
898
910
  }
@@ -918,6 +930,31 @@ var OptionsManager = class {
918
930
  (0, import_utils9.set)(this.listeners, key, func);
919
931
  return this;
920
932
  }
933
+ snapshot() {
934
+ const cloned = cloneConfigValue(this.currentOptions);
935
+ for (const key of this.preserveKeys) {
936
+ if (key in this.currentOptions) {
937
+ cloned[key] = this.currentOptions[key];
938
+ }
939
+ }
940
+ return cloned;
941
+ }
942
+ restore(snapshot) {
943
+ const snap = snapshot;
944
+ const current = this.currentOptions;
945
+ for (const key of Object.keys(current)) {
946
+ if (!(key in snap)) {
947
+ delete current[key];
948
+ }
949
+ }
950
+ const cloned = cloneConfigValue(snap);
951
+ for (const key of this.preserveKeys) {
952
+ if (key in snap) {
953
+ cloned[key] = snap[key];
954
+ }
955
+ }
956
+ (0, import_utils9.assign)(current, cloned);
957
+ }
921
958
  };
922
959
 
923
960
  // src/request-complexity.ts
@@ -1192,6 +1229,85 @@ var AccessRuntime = class {
1192
1229
  getOpenApiSpec(info) {
1193
1230
  return this.openApiRegistry.getSpec(info);
1194
1231
  }
1232
+ createBootstrapSnapshot() {
1233
+ const modelOptionsSnap = {};
1234
+ for (const [name, mgr] of Object.entries(this.modelOptions)) {
1235
+ modelOptionsSnap[name] = mgr.snapshot();
1236
+ }
1237
+ const dataOptionsSnap = {};
1238
+ for (const [name, mgr] of Object.entries(this.dataOptions)) {
1239
+ dataOptionsSnap[name] = mgr.snapshot();
1240
+ }
1241
+ return {
1242
+ globalOptions: this.globalOptions.snapshot(),
1243
+ defaultModelOptions: this.defaultModelOptions.snapshot(),
1244
+ modelOptions: modelOptionsSnap,
1245
+ dataOptions: dataOptionsSnap,
1246
+ modelInstances: { ...this.modelInstances },
1247
+ modelJsonSchemas: { ...this.modelJsonSchemas },
1248
+ modelRefs: Object.fromEntries(
1249
+ Object.entries(this.modelRefs).map(([k, v]) => [k, JSON.parse(JSON.stringify(v))])
1250
+ ),
1251
+ modelSubs: Object.fromEntries(Object.entries(this.modelSubs).map(([k, v]) => [k, [...v]])),
1252
+ modelAtts: Object.fromEntries(Object.entries(this.modelAtts).map(([k, v]) => [k, [...v]])),
1253
+ openApi: this.openApiRegistry.snapshot()
1254
+ };
1255
+ }
1256
+ restoreBootstrapSnapshot(snapshot) {
1257
+ this.globalOptions.restore(snapshot.globalOptions);
1258
+ this.defaultModelOptions.restore(snapshot.defaultModelOptions);
1259
+ for (const name of Object.keys(this.modelOptions)) {
1260
+ if (!(name in snapshot.modelOptions)) {
1261
+ delete this.modelOptions[name];
1262
+ }
1263
+ }
1264
+ for (const [name, opts] of Object.entries(snapshot.modelOptions)) {
1265
+ const existing = this.modelOptions[name];
1266
+ if (existing) {
1267
+ existing.restore(opts);
1268
+ } else {
1269
+ const mgr = this.createModelOptions(name);
1270
+ mgr.restore(opts);
1271
+ this.modelOptions[name] = mgr;
1272
+ }
1273
+ }
1274
+ for (const name of Object.keys(this.dataOptions)) {
1275
+ if (!(name in snapshot.dataOptions)) {
1276
+ delete this.dataOptions[name];
1277
+ }
1278
+ }
1279
+ for (const [name, opts] of Object.entries(snapshot.dataOptions)) {
1280
+ const existing = this.dataOptions[name];
1281
+ if (existing) {
1282
+ existing.restore(opts);
1283
+ } else {
1284
+ const mgr = this.createDataOptions(name);
1285
+ mgr.restore(opts);
1286
+ this.dataOptions[name] = mgr;
1287
+ }
1288
+ }
1289
+ for (const k of Object.keys(this.modelInstances)) {
1290
+ if (!(k in snapshot.modelInstances)) delete this.modelInstances[k];
1291
+ }
1292
+ Object.assign(this.modelInstances, snapshot.modelInstances);
1293
+ for (const k of Object.keys(this.modelJsonSchemas)) {
1294
+ if (!(k in snapshot.modelJsonSchemas)) delete this.modelJsonSchemas[k];
1295
+ }
1296
+ Object.assign(this.modelJsonSchemas, snapshot.modelJsonSchemas);
1297
+ for (const k of Object.keys(this.modelRefs)) {
1298
+ if (!(k in snapshot.modelRefs)) delete this.modelRefs[k];
1299
+ }
1300
+ Object.assign(this.modelRefs, snapshot.modelRefs);
1301
+ for (const k of Object.keys(this.modelSubs)) {
1302
+ if (!(k in snapshot.modelSubs)) delete this.modelSubs[k];
1303
+ }
1304
+ Object.assign(this.modelSubs, snapshot.modelSubs);
1305
+ for (const k of Object.keys(this.modelAtts)) {
1306
+ if (!(k in snapshot.modelAtts)) delete this.modelAtts[k];
1307
+ }
1308
+ Object.assign(this.modelAtts, snapshot.modelAtts);
1309
+ this.openApiRegistry.restore(snapshot.openApi);
1310
+ }
1195
1311
  setGlobalOptions(options) {
1196
1312
  this.globalOptions.assign(options);
1197
1313
  }
package/index.mjs CHANGED
@@ -1,9 +1,9 @@
1
1
  // src/index.ts
2
- import { isPlainObject as isPlainObject17, isString as isString10, isUndefined as isUndefined4 } from "@web-ts-toolkit/utils";
2
+ import { isPlainObject as isPlainObject18, isString as isString10, isUndefined as isUndefined4 } from "@web-ts-toolkit/utils";
3
3
 
4
4
  // src/middleware.ts
5
5
  import JsonRouter2 from "@web-ts-toolkit/express-json-router";
6
- import { isArray as isArray11, isFunction as isFunction6, isPlainObject as isPlainObject14, isString as isString7 } from "@web-ts-toolkit/utils";
6
+ import { isArray as isArray11, isFunction as isFunction6, isPlainObject as isPlainObject15, isString as isString7 } from "@web-ts-toolkit/utils";
7
7
 
8
8
  // src/core.ts
9
9
  import {
@@ -29,7 +29,7 @@ import {
29
29
  isBoolean,
30
30
  isFunction,
31
31
  isNil as isNil2,
32
- isPlainObject as isPlainObject9,
32
+ isPlainObject as isPlainObject10,
33
33
  isString as isString4,
34
34
  keys
35
35
  } from "@web-ts-toolkit/utils";
@@ -722,6 +722,17 @@ var OpenApiRegistry = class {
722
722
  getSpec(info) {
723
723
  return buildOpenApiSpec(this.routes, info);
724
724
  }
725
+ snapshot() {
726
+ return {
727
+ routes: [...this.routes],
728
+ options: { ...this.options }
729
+ };
730
+ }
731
+ restore(snapshot) {
732
+ this.routes.length = 0;
733
+ this.routes.push(...snapshot.routes);
734
+ this.options = { ...snapshot.options };
735
+ }
725
736
  };
726
737
 
727
738
  // src/logger-default.ts
@@ -751,7 +762,7 @@ var defaultLogger = createLogger({
751
762
  });
752
763
 
753
764
  // src/options/manager.ts
754
- import { assign, get as get2, set as set2 } from "@web-ts-toolkit/utils";
765
+ import { assign, get as get2, isPlainObject as isPlainObject8, set as set2 } from "@web-ts-toolkit/utils";
755
766
  var toPath = (key) => {
756
767
  if (Array.isArray(key)) return key;
757
768
  return typeof key === "string" ? key.split(".") : [key];
@@ -835,7 +846,8 @@ var OptionsManager = class {
835
846
  }
836
847
  const [rootKey, ...nestedPath] = path;
837
848
  const currentRoot = get2(this.currentOptions, [rootKey], void 0);
838
- const nextRoot = cloneConfigValue(currentRoot ?? {});
849
+ const baseRoot = isPlainObject8(currentRoot) ? currentRoot : {};
850
+ const nextRoot = cloneConfigValue(baseRoot);
839
851
  set2(nextRoot, nestedPath, cloneConfigValue(value));
840
852
  set2(this.currentOptions, [rootKey], nextRoot);
841
853
  }
@@ -861,10 +873,35 @@ var OptionsManager = class {
861
873
  set2(this.listeners, key, func);
862
874
  return this;
863
875
  }
876
+ snapshot() {
877
+ const cloned = cloneConfigValue(this.currentOptions);
878
+ for (const key of this.preserveKeys) {
879
+ if (key in this.currentOptions) {
880
+ cloned[key] = this.currentOptions[key];
881
+ }
882
+ }
883
+ return cloned;
884
+ }
885
+ restore(snapshot) {
886
+ const snap = snapshot;
887
+ const current = this.currentOptions;
888
+ for (const key of Object.keys(current)) {
889
+ if (!(key in snap)) {
890
+ delete current[key];
891
+ }
892
+ }
893
+ const cloned = cloneConfigValue(snap);
894
+ for (const key of this.preserveKeys) {
895
+ if (key in snap) {
896
+ cloned[key] = snap[key];
897
+ }
898
+ }
899
+ assign(current, cloned);
900
+ }
864
901
  };
865
902
 
866
903
  // src/request-complexity.ts
867
- import { isArray as isArray4, isPlainObject as isPlainObject8 } from "@web-ts-toolkit/utils";
904
+ import { isArray as isArray4, isPlainObject as isPlainObject9 } from "@web-ts-toolkit/utils";
868
905
  var defaultRequestComplexity = {
869
906
  maxDepth: 8,
870
907
  maxNodes: 500,
@@ -940,7 +977,7 @@ function validateRequestComplexity(value, options, scope = "request") {
940
977
  }
941
978
  return;
942
979
  }
943
- if (!isPlainObject8(current)) return;
980
+ if (!isPlainObject9(current)) return;
944
981
  for (const [key, child] of Object.entries(current)) {
945
982
  if (key === "__proto__" || key === "prototype" || key === "constructor") {
946
983
  pushError(`Unsupported key: ${key}`, path.concat(key));
@@ -1019,7 +1056,7 @@ var classifyPermissionSchema = (permissionSchema, modelPermissionPrefix) => {
1019
1056
  const modelPermissionKeys = {};
1020
1057
  forEach2(schemaKeys, (schemaKey) => {
1021
1058
  const schemaRule = permissionSchema[schemaKey];
1022
- const ruleEntries = isPlainObject9(schemaRule) ? Object.entries(schemaRule) : FIELD_ACCESS_KEYS.map((accessKey) => [accessKey, schemaRule]);
1059
+ const ruleEntries = isPlainObject10(schemaRule) ? Object.entries(schemaRule) : FIELD_ACCESS_KEYS.map((accessKey) => [accessKey, schemaRule]);
1023
1060
  for (let x = 0; x < ruleEntries.length; x++) {
1024
1061
  const [accessKey, value] = ruleEntries[x];
1025
1062
  if (!isArray5(globalPermissionKeys[accessKey])) {
@@ -1135,6 +1172,85 @@ var AccessRuntime = class {
1135
1172
  getOpenApiSpec(info) {
1136
1173
  return this.openApiRegistry.getSpec(info);
1137
1174
  }
1175
+ createBootstrapSnapshot() {
1176
+ const modelOptionsSnap = {};
1177
+ for (const [name, mgr] of Object.entries(this.modelOptions)) {
1178
+ modelOptionsSnap[name] = mgr.snapshot();
1179
+ }
1180
+ const dataOptionsSnap = {};
1181
+ for (const [name, mgr] of Object.entries(this.dataOptions)) {
1182
+ dataOptionsSnap[name] = mgr.snapshot();
1183
+ }
1184
+ return {
1185
+ globalOptions: this.globalOptions.snapshot(),
1186
+ defaultModelOptions: this.defaultModelOptions.snapshot(),
1187
+ modelOptions: modelOptionsSnap,
1188
+ dataOptions: dataOptionsSnap,
1189
+ modelInstances: { ...this.modelInstances },
1190
+ modelJsonSchemas: { ...this.modelJsonSchemas },
1191
+ modelRefs: Object.fromEntries(
1192
+ Object.entries(this.modelRefs).map(([k, v]) => [k, JSON.parse(JSON.stringify(v))])
1193
+ ),
1194
+ modelSubs: Object.fromEntries(Object.entries(this.modelSubs).map(([k, v]) => [k, [...v]])),
1195
+ modelAtts: Object.fromEntries(Object.entries(this.modelAtts).map(([k, v]) => [k, [...v]])),
1196
+ openApi: this.openApiRegistry.snapshot()
1197
+ };
1198
+ }
1199
+ restoreBootstrapSnapshot(snapshot) {
1200
+ this.globalOptions.restore(snapshot.globalOptions);
1201
+ this.defaultModelOptions.restore(snapshot.defaultModelOptions);
1202
+ for (const name of Object.keys(this.modelOptions)) {
1203
+ if (!(name in snapshot.modelOptions)) {
1204
+ delete this.modelOptions[name];
1205
+ }
1206
+ }
1207
+ for (const [name, opts] of Object.entries(snapshot.modelOptions)) {
1208
+ const existing = this.modelOptions[name];
1209
+ if (existing) {
1210
+ existing.restore(opts);
1211
+ } else {
1212
+ const mgr = this.createModelOptions(name);
1213
+ mgr.restore(opts);
1214
+ this.modelOptions[name] = mgr;
1215
+ }
1216
+ }
1217
+ for (const name of Object.keys(this.dataOptions)) {
1218
+ if (!(name in snapshot.dataOptions)) {
1219
+ delete this.dataOptions[name];
1220
+ }
1221
+ }
1222
+ for (const [name, opts] of Object.entries(snapshot.dataOptions)) {
1223
+ const existing = this.dataOptions[name];
1224
+ if (existing) {
1225
+ existing.restore(opts);
1226
+ } else {
1227
+ const mgr = this.createDataOptions(name);
1228
+ mgr.restore(opts);
1229
+ this.dataOptions[name] = mgr;
1230
+ }
1231
+ }
1232
+ for (const k of Object.keys(this.modelInstances)) {
1233
+ if (!(k in snapshot.modelInstances)) delete this.modelInstances[k];
1234
+ }
1235
+ Object.assign(this.modelInstances, snapshot.modelInstances);
1236
+ for (const k of Object.keys(this.modelJsonSchemas)) {
1237
+ if (!(k in snapshot.modelJsonSchemas)) delete this.modelJsonSchemas[k];
1238
+ }
1239
+ Object.assign(this.modelJsonSchemas, snapshot.modelJsonSchemas);
1240
+ for (const k of Object.keys(this.modelRefs)) {
1241
+ if (!(k in snapshot.modelRefs)) delete this.modelRefs[k];
1242
+ }
1243
+ Object.assign(this.modelRefs, snapshot.modelRefs);
1244
+ for (const k of Object.keys(this.modelSubs)) {
1245
+ if (!(k in snapshot.modelSubs)) delete this.modelSubs[k];
1246
+ }
1247
+ Object.assign(this.modelSubs, snapshot.modelSubs);
1248
+ for (const k of Object.keys(this.modelAtts)) {
1249
+ if (!(k in snapshot.modelAtts)) delete this.modelAtts[k];
1250
+ }
1251
+ Object.assign(this.modelAtts, snapshot.modelAtts);
1252
+ this.openApiRegistry.restore(snapshot.openApi);
1253
+ }
1138
1254
  setGlobalOptions(options) {
1139
1255
  this.globalOptions.assign(options);
1140
1256
  }
@@ -1403,7 +1519,7 @@ import {
1403
1519
  forEach as forEach3,
1404
1520
  get as get4,
1405
1521
  isArray as isArray6,
1406
- isPlainObject as isPlainObject10,
1522
+ isPlainObject as isPlainObject11,
1407
1523
  uniq
1408
1524
  } from "@web-ts-toolkit/utils";
1409
1525
  var ClientRequestError = class extends Error {
@@ -1424,7 +1540,7 @@ function validateClientFilter(filter2) {
1424
1540
  value.forEach((item, index) => visit(item, `${path}[${index}]`));
1425
1541
  return;
1426
1542
  }
1427
- if (!isPlainObject10(value)) return;
1543
+ if (!isPlainObject11(value)) return;
1428
1544
  Object.entries(value).forEach(([key, child]) => {
1429
1545
  const nextPath = path ? `${path}.${key}` : key;
1430
1546
  if (blockedOperators.has(key)) {
@@ -1601,7 +1717,7 @@ var Base = class {
1601
1717
  return ids.size;
1602
1718
  }
1603
1719
  sanitizeIncludeArgs(args) {
1604
- if (!isPlainObject10(args)) return {};
1720
+ if (!isPlainObject11(args)) return {};
1605
1721
  const { overrides: _clientOverrides, ...trustedArgs } = args;
1606
1722
  return trustedArgs;
1607
1723
  }
@@ -3431,7 +3547,7 @@ var DataService = class {
3431
3547
  };
3432
3548
 
3433
3549
  // src/processors.ts
3434
- import { cloneDeep, forEach as forEach6, get as get7, isArray as isArray8, isPlainObject as isPlainObject12, map, set as set5 } from "@web-ts-toolkit/utils";
3550
+ import { cloneDeep, forEach as forEach6, get as get7, isArray as isArray8, isPlainObject as isPlainObject13, map, set as set5 } from "@web-ts-toolkit/utils";
3435
3551
  var UNSAFE_PATH_KEYS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
3436
3552
  function failUnsafeSegment(path) {
3437
3553
  throw new Error(
@@ -3466,7 +3582,7 @@ var copyAndDepopulate = (docObject, operations, options = { mutable: true, idFie
3466
3582
  forEach6(segs, (seg, ind) => {
3467
3583
  if (segs.length === ind + 1) {
3468
3584
  forEach6(targets, (target) => {
3469
- if (!isPlainObject12(target)) return;
3585
+ if (!isPlainObject13(target)) return;
3470
3586
  const targetObject = target;
3471
3587
  const leaf = targetObject[seg];
3472
3588
  const sourceForDest = leaf;
@@ -3474,7 +3590,7 @@ var copyAndDepopulate = (docObject, operations, options = { mutable: true, idFie
3474
3590
  if (isArray8(leaf)) {
3475
3591
  if (leaf.length === 0) {
3476
3592
  depopulated = [];
3477
- } else if (leaf.every((item) => isPlainObject12(item))) {
3593
+ } else if (leaf.every((item) => isPlainObject13(item))) {
3478
3594
  depopulated = map(leaf, idField).map((id, i) => {
3479
3595
  if (id === void 0) {
3480
3596
  failMissingId(`${src}[${i}]`);
@@ -3484,7 +3600,7 @@ var copyAndDepopulate = (docObject, operations, options = { mutable: true, idFie
3484
3600
  } else {
3485
3601
  return;
3486
3602
  }
3487
- } else if (isPlainObject12(leaf)) {
3603
+ } else if (isPlainObject13(leaf)) {
3488
3604
  const id = get7(leaf, idField);
3489
3605
  if (id === void 0) {
3490
3606
  failMissingId(src);
@@ -3498,7 +3614,7 @@ var copyAndDepopulate = (docObject, operations, options = { mutable: true, idFie
3498
3614
  });
3499
3615
  } else {
3500
3616
  targets = targets.reduce((ret, target) => {
3501
- if (!isPlainObject12(target)) return ret;
3617
+ if (!isPlainObject13(target)) return ret;
3502
3618
  const targetObject = target;
3503
3619
  const next = targetObject[seg];
3504
3620
  if (isArray8(next)) ret.push(...next);
@@ -3558,7 +3674,7 @@ import {
3558
3674
  isEmpty as isEmpty2,
3559
3675
  isEqual,
3560
3676
  isFunction as isFunction4,
3561
- isPlainObject as isPlainObject13,
3677
+ isPlainObject as isPlainObject14,
3562
3678
  isString as isString5
3563
3679
  } from "@web-ts-toolkit/utils";
3564
3680
 
@@ -3599,10 +3715,10 @@ var permission_default = Permission;
3599
3715
 
3600
3716
  // src/core-shared.ts
3601
3717
  function isAndFilter(filter2) {
3602
- return !!filter2 && isPlainObject13(filter2) && Object.keys(filter2).length === 1 && isArray9(filter2.$and);
3718
+ return !!filter2 && isPlainObject14(filter2) && Object.keys(filter2).length === 1 && isArray9(filter2.$and);
3603
3719
  }
3604
3720
  function isMergeableClause(filter2) {
3605
- return !!filter2 && isPlainObject13(filter2) && Object.keys(filter2).every((key) => !key.startsWith("$"));
3721
+ return !!filter2 && isPlainObject14(filter2) && Object.keys(filter2).every((key) => !key.startsWith("$"));
3606
3722
  }
3607
3723
  function optimizeAndFilter(clausesInput) {
3608
3724
  const clauses = [];
@@ -3620,7 +3736,7 @@ function optimizeAndFilter(clausesInput) {
3620
3736
  if (isArray9(value)) {
3621
3737
  return `[${value.map(dedupeKey).join(",")}]`;
3622
3738
  }
3623
- if (isPlainObject13(value)) {
3739
+ if (isPlainObject14(value)) {
3624
3740
  return `{${Object.keys(value).sort().map((key) => `${key}:${dedupeKey(value[key])}`).join(",")}}`;
3625
3741
  }
3626
3742
  return JSON.stringify(value);
@@ -3662,7 +3778,7 @@ function optimizeAndFilter(clausesInput) {
3662
3778
  }
3663
3779
  function normalizeFilter(filter2) {
3664
3780
  if (filter2 === false) return false;
3665
- if (!filter2 || !isPlainObject13(filter2) || isEmpty2(filter2)) return null;
3781
+ if (!filter2 || !isPlainObject14(filter2) || isEmpty2(filter2)) return null;
3666
3782
  if (!isAndFilter(filter2)) {
3667
3783
  return filter2;
3668
3784
  }
@@ -3714,7 +3830,7 @@ async function setRequestPermissions(req) {
3714
3830
  const globalPermissions = getGlobalOption("globalPermissions");
3715
3831
  if (!isFunction4(globalPermissions)) return;
3716
3832
  const permissions = await globalPermissions.call(req, req);
3717
- if (isPlainObject13(permissions)) req[requestPermissionField] = permissions;
3833
+ if (isPlainObject14(permissions)) req[requestPermissionField] = permissions;
3718
3834
  else if (isArray9(permissions)) req[requestPermissionField] = arrayToRecord(permissions);
3719
3835
  else if (isString5(permissions)) req[requestPermissionField] = { [permissions]: true };
3720
3836
  }
@@ -4289,12 +4405,12 @@ function guard(condition) {
4289
4405
  const permissions = req[PERMISSIONS];
4290
4406
  let cond = condition;
4291
4407
  let phas = (key) => permissions.has(key);
4292
- if (isPlainObject14(condition)) {
4408
+ if (isPlainObject15(condition)) {
4293
4409
  const { modelName, id, condition: _cond } = condition;
4294
4410
  const svc = req.macl.getPublicService(modelName);
4295
4411
  const select = getModelOption(modelName, `alwaysSelectFields.read`, void 0);
4296
4412
  let _id = isString7(id) ? id : null;
4297
- if (isPlainObject14(id)) {
4413
+ if (isPlainObject15(id)) {
4298
4414
  const { type = "param", key } = id;
4299
4415
  if (type === "param") {
4300
4416
  const paramValue = req.params[key];
@@ -4336,7 +4452,7 @@ import express2 from "express";
4336
4452
 
4337
4453
  // src/routers/model-router.ts
4338
4454
  import JsonRouter5 from "@web-ts-toolkit/express-json-router";
4339
- import { forEach as forEach8, isPlainObject as isPlainObject15, isString as isString8, isUndefined as isUndefined2, normalizeUrlPath as normalizeUrlPath2, padEnd } from "@web-ts-toolkit/utils";
4455
+ import { forEach as forEach8, isPlainObject as isPlainObject16, isString as isString8, isUndefined as isUndefined2, normalizeUrlPath as normalizeUrlPath2, padEnd } from "@web-ts-toolkit/utils";
4340
4456
 
4341
4457
  // src/routers/router-mutation.ts
4342
4458
  var MODEL_BUILD_TIME_OPTION_KEYS = /* @__PURE__ */ new Set([
@@ -6231,7 +6347,7 @@ var ModelRouter = class _ModelRouter {
6231
6347
  value
6232
6348
  );
6233
6349
  }
6234
- if (arguments.length === 1 && isPlainObject15(keyOrOptions)) {
6350
+ if (arguments.length === 1 && isPlainObject16(keyOrOptions)) {
6235
6351
  assertMutableRouterOptions("model", keyOrOptions);
6236
6352
  this.runtime.setModelOptions(this.modelName, keyOrOptions);
6237
6353
  }
@@ -6665,7 +6781,7 @@ var RootRouter = class {
6665
6781
 
6666
6782
  // src/routers/data-router.ts
6667
6783
  import JsonRouter7 from "@web-ts-toolkit/express-json-router";
6668
- import { isPlainObject as isPlainObject16, isString as isString9, isUndefined as isUndefined3, normalizeUrlPath as normalizeUrlPath4 } from "@web-ts-toolkit/utils";
6784
+ import { isPlainObject as isPlainObject17, isString as isString9, isUndefined as isUndefined3, normalizeUrlPath as normalizeUrlPath4 } from "@web-ts-toolkit/utils";
6669
6785
  var clientErrors4 = JsonRouter7.clientErrors;
6670
6786
  function setOption2(parentKey, optionKey, option) {
6671
6787
  const key = isUndefined3(option) ? parentKey : `${parentKey}.${optionKey}`;
@@ -6861,7 +6977,7 @@ var DataRouter = class {
6861
6977
  assertMutableRouterOption("data", keyOrOptions);
6862
6978
  this.runtime.setDataOption(this.dataName, keyOrOptions, value);
6863
6979
  }
6864
- if (arguments.length === 1 && isPlainObject16(keyOrOptions)) {
6980
+ if (arguments.length === 1 && isPlainObject17(keyOrOptions)) {
6865
6981
  assertMutableRouterOptions("data", keyOrOptions);
6866
6982
  this.runtime.setDataOptions(this.dataName, keyOrOptions);
6867
6983
  }
@@ -7008,7 +7124,7 @@ function createRuntimeApi(runtime) {
7008
7124
  if (arguments.length === 2 && isString10(keyOrOptions)) {
7009
7125
  return runtime.setGlobalOption(keyOrOptions, value);
7010
7126
  }
7011
- if (arguments.length === 1 && isPlainObject17(keyOrOptions)) {
7127
+ if (arguments.length === 1 && isPlainObject18(keyOrOptions)) {
7012
7128
  return runtime.setGlobalOptions(keyOrOptions);
7013
7129
  }
7014
7130
  };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@web-ts-toolkit/access-router",
3
3
  "description": "ACL-aware Express routers and in-memory data services for Mongoose-backed APIs",
4
4
  "homepage": "https://web-ts-toolkit.pages.dev/docs/packages/access-router",
5
- "version": "0.41.0",
5
+ "version": "0.42.0",
6
6
  "sideEffects": [
7
7
  "./**/index.js",
8
8
  "./**/index.mjs",
@@ -45,8 +45,8 @@
45
45
  "node": ">=22"
46
46
  },
47
47
  "dependencies": {
48
- "@web-ts-toolkit/express-json-router": "0.41.0",
49
- "@web-ts-toolkit/utils": "0.41.0",
48
+ "@web-ts-toolkit/express-json-router": "0.42.0",
49
+ "@web-ts-toolkit/utils": "0.42.0",
50
50
  "just-diff": "^6.0.2",
51
51
  "mongoose-schema-jsonschema": "^4.0.1",
52
52
  "sift": "^17.1.3",