@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/README.md +51 -5
- package/advanced.d.mts +3 -3
- package/advanced.d.ts +3 -3
- package/advanced.js +170 -85
- package/advanced.mjs +144 -59
- package/index.d.mts +198 -3
- package/index.d.ts +198 -3
- package/index.js +519 -306
- package/index.mjs +540 -328
- package/llms.txt +25 -5
- package/package.json +3 -3
- package/{parsers--DZcTstL.d.mts → parsers-PMWujHUV.d.mts} +32 -204
- package/{parsers--DZcTstL.d.ts → parsers-PMWujHUV.d.ts} +32 -204
- package/processors.d.mts +20 -8
- package/processors.d.ts +20 -8
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
|
|
58
|
-
isString as
|
|
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
|
|
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) =>
|
|
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]) ||
|
|
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]) ||
|
|
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
|
|
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
|
|
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
|
|
200
|
+
return isPlainObject4(source) && source.kind === "objectProperties";
|
|
197
201
|
}
|
|
198
202
|
function isOpenApiSchemaResolver(source) {
|
|
199
|
-
return
|
|
203
|
+
return isPlainObject4(source) && source.kind === "schemaResolver" && typeof source.resolve === "function";
|
|
200
204
|
}
|
|
201
205
|
function isJsonSchemaLike(source) {
|
|
202
|
-
return
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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 ||
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 (!
|
|
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 (
|
|
696
|
+
if (isNil2(value)) {
|
|
629
697
|
return `/${pluralize(name)}`;
|
|
630
698
|
}
|
|
631
|
-
return
|
|
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 (
|
|
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 (
|
|
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 =
|
|
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
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
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:
|
|
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
|
-
|
|
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)
|
|
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(
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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:
|
|
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:
|
|
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 {
|
|
4
|
-
export {
|
|
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 };
|