@web-ts-toolkit/access-router 0.32.0 → 0.33.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 +101 -37
- package/advanced.d.mts +12 -1444
- package/advanced.d.ts +12 -1444
- package/advanced.js +1167 -269
- package/advanced.mjs +1178 -269
- package/index.d.mts +93 -1264
- package/index.d.ts +93 -1264
- package/index.js +1465 -489
- package/index.mjs +1354 -388
- package/llms.txt +34 -11
- package/package.json +10 -5
- package/parsers-D9B9dS0p.d.mts +1702 -0
- package/parsers-D9B9dS0p.d.ts +1702 -0
- package/processors.d.mts +72 -1
- package/processors.d.ts +72 -1
- package/processors.js +61 -13
- package/processors.mjs +62 -14
package/index.js
CHANGED
|
@@ -32,12 +32,15 @@ __export(index_exports, {
|
|
|
32
32
|
AccessRuntime: () => AccessRuntime,
|
|
33
33
|
DataRouter: () => DataRouter,
|
|
34
34
|
ModelRouter: () => ModelRouter,
|
|
35
|
+
OpenApiCollisionError: () => OpenApiCollisionError,
|
|
36
|
+
OpenApiRegistry: () => OpenApiRegistry,
|
|
35
37
|
RootRouter: () => RootRouter,
|
|
36
38
|
acl: () => acl,
|
|
37
39
|
combineRoutes: () => combineRoutes,
|
|
38
40
|
createAccessRuntime: () => createAccessRuntime,
|
|
39
41
|
createOpenApiRouter: () => createOpenApiRouter,
|
|
40
42
|
default: () => index_default,
|
|
43
|
+
defaultRuntime: () => defaultRuntime,
|
|
41
44
|
defineRequestSchema: () => defineRequestSchema,
|
|
42
45
|
fromAjv: () => fromAjv,
|
|
43
46
|
fromArkType: () => fromArkType,
|
|
@@ -53,12 +56,19 @@ __export(index_exports, {
|
|
|
53
56
|
getDefaultModelOptions: () => getDefaultModelOptions,
|
|
54
57
|
getGlobalOption: () => getGlobalOption,
|
|
55
58
|
getGlobalOptions: () => getGlobalOptions,
|
|
59
|
+
getModelInstance: () => getModelInstance,
|
|
56
60
|
getModelJsonSchema: () => getModelJsonSchema,
|
|
57
61
|
getModelNames: () => getModelNames,
|
|
58
62
|
getModelOption: () => getModelOption,
|
|
59
63
|
getModelOptions: () => getModelOptions,
|
|
60
64
|
guard: () => guard,
|
|
65
|
+
hasModelInstance: () => hasModelInstance,
|
|
66
|
+
isLevelEnabled: () => isLevelEnabled,
|
|
61
67
|
permissionsPlugin: () => permissionsPlugin,
|
|
68
|
+
redactFilter: () => redactFilter,
|
|
69
|
+
redactPayload: () => redactPayload,
|
|
70
|
+
registerModelInstance: () => registerModelInstance,
|
|
71
|
+
safeStringify: () => safeStringify,
|
|
62
72
|
setDefaultModelOption: () => setDefaultModelOption,
|
|
63
73
|
setDefaultModelOptions: () => setDefaultModelOptions,
|
|
64
74
|
setGlobalOption: () => setGlobalOption,
|
|
@@ -71,15 +81,15 @@ var import_utils28 = require("@web-ts-toolkit/utils");
|
|
|
71
81
|
|
|
72
82
|
// src/middleware.ts
|
|
73
83
|
var import_express_json_router2 = __toESM(require("@web-ts-toolkit/express-json-router"));
|
|
74
|
-
var
|
|
84
|
+
var import_utils22 = require("@web-ts-toolkit/utils");
|
|
75
85
|
|
|
76
86
|
// src/core.ts
|
|
77
|
-
var
|
|
87
|
+
var import_utils21 = require("@web-ts-toolkit/utils");
|
|
78
88
|
|
|
79
89
|
// src/runtime.ts
|
|
80
90
|
var import_mongoose3 = __toESM(require("mongoose"));
|
|
81
91
|
var import_mongoose_schema_jsonschema = __toESM(require("mongoose-schema-jsonschema"));
|
|
82
|
-
var
|
|
92
|
+
var import_utils11 = require("@web-ts-toolkit/utils");
|
|
83
93
|
|
|
84
94
|
// src/helpers/collection.ts
|
|
85
95
|
var import_sift = __toESM(require("sift"));
|
|
@@ -100,6 +110,12 @@ var import_utils3 = require("@web-ts-toolkit/utils");
|
|
|
100
110
|
|
|
101
111
|
// src/helpers/query.ts
|
|
102
112
|
var import_utils2 = require("@web-ts-toolkit/utils");
|
|
113
|
+
var normalizeSafeInteger = (value, min) => {
|
|
114
|
+
if ((0, import_utils2.isNil)(value)) return null;
|
|
115
|
+
const parsed = Number(value);
|
|
116
|
+
if (!Number.isSafeInteger(parsed) || parsed < min) return null;
|
|
117
|
+
return parsed;
|
|
118
|
+
};
|
|
103
119
|
function genPagination({
|
|
104
120
|
skip,
|
|
105
121
|
limit,
|
|
@@ -107,13 +123,21 @@ function genPagination({
|
|
|
107
123
|
pageSize
|
|
108
124
|
}, hardLimit) {
|
|
109
125
|
let _skip = 0;
|
|
110
|
-
let _limit =
|
|
111
|
-
if (
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
126
|
+
let _limit = normalizeSafeInteger(limit ?? pageSize, 1) ?? hardLimit;
|
|
127
|
+
if (!Number.isSafeInteger(_limit) || _limit > hardLimit) _limit = hardLimit;
|
|
128
|
+
const normalizedSkip = normalizeSafeInteger(skip, 0);
|
|
129
|
+
const normalizedPage = normalizeSafeInteger(page, 0);
|
|
130
|
+
if (normalizedSkip !== null) {
|
|
131
|
+
_skip = normalizedSkip;
|
|
132
|
+
} else if (normalizedPage !== null) {
|
|
133
|
+
const npage = normalizedPage;
|
|
134
|
+
if (npage > 1) {
|
|
135
|
+
const derivedSkip = (npage - 1) * _limit;
|
|
136
|
+
_skip = Number.isSafeInteger(derivedSkip) ? derivedSkip : Number.MAX_SAFE_INTEGER;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (!Number.isSafeInteger(_skip) || _skip < 0) {
|
|
140
|
+
return { skip: 0, limit: _limit };
|
|
117
141
|
}
|
|
118
142
|
return { skip: _skip, limit: _limit };
|
|
119
143
|
}
|
|
@@ -182,6 +206,7 @@ async function populateDoc(doc, target) {
|
|
|
182
206
|
if ((0, import_utils4.isPromise)(p)) return p;
|
|
183
207
|
return "execPopulate" in p && p.execPopulate?.();
|
|
184
208
|
}
|
|
209
|
+
var normalizeSubPopulate = (populate) => (0, import_utils4.isArray)(populate) ? populate.map((item) => (0, import_utils4.isString)(item) ? { path: item } : item) : (0, import_utils4.isString)(populate) ? { path: populate } : populate ?? [];
|
|
185
210
|
var genSubPopulate = (sub, popul) => {
|
|
186
211
|
if (!popul) return [];
|
|
187
212
|
let populate = (0, import_utils4.isArray)(popul) ? popul : [popul];
|
|
@@ -197,7 +222,7 @@ var genSubPopulate = (sub, popul) => {
|
|
|
197
222
|
|
|
198
223
|
// src/helpers/errors.ts
|
|
199
224
|
var import_express_json_router = __toESM(require("@web-ts-toolkit/express-json-router"));
|
|
200
|
-
var { BadRequestError, ForbiddenError, NotFoundError, UnprocessableEntityError } = import_express_json_router.default.clientErrors;
|
|
225
|
+
var { BadRequestError, ForbiddenError, NotFoundError, UnauthorizedError, UnprocessableEntityError } = import_express_json_router.default.clientErrors;
|
|
201
226
|
function mapCodeToMessage(code) {
|
|
202
227
|
switch (code) {
|
|
203
228
|
case "success" /* Success */:
|
|
@@ -206,6 +231,8 @@ function mapCodeToMessage(code) {
|
|
|
206
231
|
return "Created";
|
|
207
232
|
case "bad_request" /* BadRequest */:
|
|
208
233
|
return "Bad Request";
|
|
234
|
+
case "unauthorized" /* Unauthorized */:
|
|
235
|
+
return "Unauthorized";
|
|
209
236
|
case "forbidden" /* Forbidden */:
|
|
210
237
|
return "Forbidden";
|
|
211
238
|
case "not_found" /* NotFound */:
|
|
@@ -222,6 +249,8 @@ function mapCodeToStatusCode(code) {
|
|
|
222
249
|
return 201 /* Created */;
|
|
223
250
|
case "bad_request" /* BadRequest */:
|
|
224
251
|
return 400 /* BadRequest */;
|
|
252
|
+
case "unauthorized" /* Unauthorized */:
|
|
253
|
+
return 401 /* Unauthorized */;
|
|
225
254
|
case "forbidden" /* Forbidden */:
|
|
226
255
|
return 403 /* Forbidden */;
|
|
227
256
|
case "not_found" /* NotFound */:
|
|
@@ -232,11 +261,14 @@ function mapCodeToStatusCode(code) {
|
|
|
232
261
|
}
|
|
233
262
|
function handleResultError(result) {
|
|
234
263
|
if (result.success) return;
|
|
264
|
+
if (result.kind !== "error") return;
|
|
235
265
|
const { code, errors = [] } = result;
|
|
236
266
|
const errorOptions = { errors };
|
|
237
267
|
switch (code) {
|
|
238
268
|
case "bad_request" /* BadRequest */:
|
|
239
269
|
throw new BadRequestError("Bad Request", errorOptions);
|
|
270
|
+
case "unauthorized" /* Unauthorized */:
|
|
271
|
+
throw new UnauthorizedError("Unauthorized", errorOptions);
|
|
240
272
|
case "forbidden" /* Forbidden */:
|
|
241
273
|
throw new ForbiddenError("Forbidden", errorOptions);
|
|
242
274
|
case "not_found" /* NotFound */:
|
|
@@ -563,22 +595,102 @@ function buildOpenApiSpec(routes, options) {
|
|
|
563
595
|
};
|
|
564
596
|
}
|
|
565
597
|
|
|
598
|
+
// src/openapi/errors.ts
|
|
599
|
+
var OpenApiCollisionError = class extends Error {
|
|
600
|
+
constructor(message, details) {
|
|
601
|
+
super(message);
|
|
602
|
+
this.name = "OpenApiCollisionError";
|
|
603
|
+
this.collisionKind = details.collisionKind;
|
|
604
|
+
this.method = details.method;
|
|
605
|
+
this.path = details.path;
|
|
606
|
+
this.operationId = details.operationId;
|
|
607
|
+
this.existing = details.existing;
|
|
608
|
+
this.incoming = details.incoming;
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
|
|
566
612
|
// src/openapi/registry.ts
|
|
613
|
+
var isPlainObject7 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
614
|
+
var stableKey = (value) => {
|
|
615
|
+
if (value === null || typeof value === "undefined") return "null";
|
|
616
|
+
if (typeof value === "function") return "[Function]";
|
|
617
|
+
if (typeof value !== "object") return JSON.stringify(value);
|
|
618
|
+
if (Array.isArray(value)) {
|
|
619
|
+
return `[${value.map(stableKey).join(",")}]`;
|
|
620
|
+
}
|
|
621
|
+
const obj = value;
|
|
622
|
+
return `{${Object.keys(obj).sort().map((k) => `${JSON.stringify(k)}:${stableKey(obj[k])}`).join(",")}}`;
|
|
623
|
+
};
|
|
624
|
+
var descriptorsEqual = (a, b) => {
|
|
625
|
+
return a.method === b.method && a.path === b.path && (a.operationId ?? "") === (b.operationId ?? "") && (a.acl ?? "") === (b.acl ?? "") && stableKey(a.query) === stableKey(b.query) && stableKey(a.body) === stableKey(b.body) && stableKey(a.pathParams) === stableKey(b.pathParams) && stableKey(a.responses) === stableKey(b.responses) && stableKey(a.tags) === stableKey(b.tags) && (a.summary ?? "") === (b.summary ?? "") && (a.description ?? "") === (b.description ?? "") && Boolean(a.deprecated) === Boolean(b.deprecated);
|
|
626
|
+
};
|
|
567
627
|
var OpenApiRegistry = class {
|
|
568
|
-
constructor() {
|
|
628
|
+
constructor(options = {}) {
|
|
569
629
|
this.routes = [];
|
|
630
|
+
this.options = {
|
|
631
|
+
rejectConflicts: options.rejectConflicts ?? true,
|
|
632
|
+
rejectDuplicateOperationIds: options.rejectDuplicateOperationIds ?? true
|
|
633
|
+
};
|
|
570
634
|
}
|
|
571
635
|
register(route) {
|
|
572
636
|
const existingIndex = this.routes.findIndex((item) => item.method === route.method && item.path === route.path);
|
|
573
637
|
if (existingIndex === -1) {
|
|
638
|
+
this.assertOperationIdAvailable(route);
|
|
574
639
|
this.routes.push(route);
|
|
575
640
|
return;
|
|
576
641
|
}
|
|
642
|
+
const existing = this.routes[existingIndex];
|
|
643
|
+
if (route.allowReplace) {
|
|
644
|
+
this.assertOperationIdAvailable(route, existing);
|
|
645
|
+
this.routes[existingIndex] = route;
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
if (descriptorsEqual(existing, route) && (route.idempotent || isPlainObject7(existing) && existing.idempotent)) {
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
if (this.options.rejectConflicts) {
|
|
652
|
+
throw new OpenApiCollisionError(
|
|
653
|
+
`OpenAPI route collision: ${route.method.toUpperCase()} ${route.path} is already registered` + (existing.operationId ? ` as operationId="${existing.operationId}"` : "") + (route.operationId ? `; new registration uses operationId="${route.operationId}"` : "") + ". Set allowReplace:true on the new descriptor to override, or fix the duplicate registration.",
|
|
654
|
+
{
|
|
655
|
+
collisionKind: "path",
|
|
656
|
+
method: route.method,
|
|
657
|
+
path: route.path,
|
|
658
|
+
existing,
|
|
659
|
+
incoming: route
|
|
660
|
+
}
|
|
661
|
+
);
|
|
662
|
+
}
|
|
577
663
|
this.routes[existingIndex] = route;
|
|
578
664
|
}
|
|
665
|
+
assertOperationIdAvailable(route, replacing) {
|
|
666
|
+
if (!this.options.rejectDuplicateOperationIds) return;
|
|
667
|
+
if (!route.operationId) return;
|
|
668
|
+
const clash = this.routes.find(
|
|
669
|
+
(item) => item.operationId && item.operationId === route.operationId && item !== replacing
|
|
670
|
+
);
|
|
671
|
+
if (clash) {
|
|
672
|
+
if (route.idempotent && clash.idempotent) return;
|
|
673
|
+
throw new OpenApiCollisionError(
|
|
674
|
+
`OpenAPI operationId collision: "${route.operationId}" is already bound to ${clash.method.toUpperCase()} ${clash.path}. Each operationId must be unique across the spec.`,
|
|
675
|
+
{
|
|
676
|
+
collisionKind: "operationId",
|
|
677
|
+
operationId: route.operationId,
|
|
678
|
+
existing: clash,
|
|
679
|
+
incoming: route
|
|
680
|
+
}
|
|
681
|
+
);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
579
684
|
getRoutes() {
|
|
580
685
|
return [...this.routes];
|
|
581
686
|
}
|
|
687
|
+
clear() {
|
|
688
|
+
this.routes.length = 0;
|
|
689
|
+
}
|
|
690
|
+
setStrictMode(enabled) {
|
|
691
|
+
this.options.rejectConflicts = enabled;
|
|
692
|
+
this.options.rejectDuplicateOperationIds = enabled;
|
|
693
|
+
}
|
|
582
694
|
getSpec(info) {
|
|
583
695
|
return buildOpenApiSpec(this.routes, info);
|
|
584
696
|
}
|
|
@@ -600,8 +712,8 @@ var defaultLogger = (0, import_winston.createLogger)({
|
|
|
600
712
|
import_winston.format.label({ label: "[WTT]" }),
|
|
601
713
|
import_winston.format.timestamp(),
|
|
602
714
|
import_winston.format.printf(({ level, label, message, timestamp, stack, ...meta }) => {
|
|
603
|
-
const formattedLabel = colorizer.colorize(level, label);
|
|
604
|
-
const formattedLevel = colorizer.colorize(level, `[${level.toUpperCase()}]`);
|
|
715
|
+
const formattedLabel = colorizer.colorize(level, String(label));
|
|
716
|
+
const formattedLevel = colorizer.colorize(level, `[${String(level).toUpperCase()}]`);
|
|
605
717
|
const formattedMessage = colorizer.colorize(level, inspectValue(stack ?? message));
|
|
606
718
|
const formattedMeta = Object.keys(meta).length > 0 ? ` ${inspectValue(meta)}` : "";
|
|
607
719
|
return `${formattedLabel} ${timestamp} ${formattedLevel} ${formattedMessage}${formattedMeta}`;
|
|
@@ -636,8 +748,8 @@ var OptionsManager = class {
|
|
|
636
748
|
this.currentOptions = new Proxy({}, {
|
|
637
749
|
set(target, key, value) {
|
|
638
750
|
const keystr = String(key);
|
|
639
|
-
const oldvalue = target[
|
|
640
|
-
target[
|
|
751
|
+
const oldvalue = target[keystr];
|
|
752
|
+
target[keystr] = value;
|
|
641
753
|
_this.listeners[keystr] && _this.listeners[keystr].call(_this, value, keystr, target, oldvalue);
|
|
642
754
|
return true;
|
|
643
755
|
}
|
|
@@ -665,8 +777,116 @@ var OptionsManager = class {
|
|
|
665
777
|
}
|
|
666
778
|
};
|
|
667
779
|
|
|
780
|
+
// src/request-complexity.ts
|
|
781
|
+
var import_utils10 = require("@web-ts-toolkit/utils");
|
|
782
|
+
var defaultRequestComplexity = {
|
|
783
|
+
maxDepth: 8,
|
|
784
|
+
maxNodes: 500,
|
|
785
|
+
maxLogicalClauses: 50,
|
|
786
|
+
maxInValues: 100,
|
|
787
|
+
maxBulkItems: 100,
|
|
788
|
+
maxIncludeCount: 10,
|
|
789
|
+
maxSubQueryCount: 10,
|
|
790
|
+
maxBulkConcurrency: 10
|
|
791
|
+
};
|
|
792
|
+
var normalizePositiveInteger = (value, fallback) => {
|
|
793
|
+
return Number.isSafeInteger(value) && Number(value) > 0 ? Number(value) : fallback;
|
|
794
|
+
};
|
|
795
|
+
var resolveRequestComplexity = (options) => ({
|
|
796
|
+
maxDepth: normalizePositiveInteger(options?.maxDepth, defaultRequestComplexity.maxDepth),
|
|
797
|
+
maxNodes: normalizePositiveInteger(options?.maxNodes, defaultRequestComplexity.maxNodes),
|
|
798
|
+
maxLogicalClauses: normalizePositiveInteger(options?.maxLogicalClauses, defaultRequestComplexity.maxLogicalClauses),
|
|
799
|
+
maxInValues: normalizePositiveInteger(options?.maxInValues, defaultRequestComplexity.maxInValues),
|
|
800
|
+
maxBulkItems: normalizePositiveInteger(options?.maxBulkItems, defaultRequestComplexity.maxBulkItems),
|
|
801
|
+
maxIncludeCount: normalizePositiveInteger(options?.maxIncludeCount, defaultRequestComplexity.maxIncludeCount),
|
|
802
|
+
maxSubQueryCount: normalizePositiveInteger(options?.maxSubQueryCount, defaultRequestComplexity.maxSubQueryCount),
|
|
803
|
+
maxBulkConcurrency: normalizePositiveInteger(
|
|
804
|
+
options?.maxBulkConcurrency,
|
|
805
|
+
defaultRequestComplexity.maxBulkConcurrency
|
|
806
|
+
)
|
|
807
|
+
});
|
|
808
|
+
function validateRequestComplexity(value, options, scope = "request") {
|
|
809
|
+
const limits = resolveRequestComplexity(options);
|
|
810
|
+
const errors = [];
|
|
811
|
+
let nodes = 0;
|
|
812
|
+
let logicalClauses = 0;
|
|
813
|
+
let includeCount = 0;
|
|
814
|
+
let subQueryCount = 0;
|
|
815
|
+
const pointer = (path) => path.length === 0 ? "#" : `#/${path.join("/")}`;
|
|
816
|
+
const pushError = (detail, path) => {
|
|
817
|
+
if (errors.length === 0) {
|
|
818
|
+
errors.push({ detail, pointer: pointer(path) });
|
|
819
|
+
}
|
|
820
|
+
};
|
|
821
|
+
const visit = (current, path, depth, parentKey) => {
|
|
822
|
+
if (errors.length > 0) return;
|
|
823
|
+
nodes += 1;
|
|
824
|
+
if (nodes > limits.maxNodes) {
|
|
825
|
+
pushError(`Request exceeds maximum node budget of ${limits.maxNodes}`, path);
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
if (depth > limits.maxDepth) {
|
|
829
|
+
pushError(`Request exceeds maximum depth of ${limits.maxDepth}`, path);
|
|
830
|
+
return;
|
|
831
|
+
}
|
|
832
|
+
if ((0, import_utils10.isArray)(current)) {
|
|
833
|
+
if (parentKey === "$and" || parentKey === "$or" || parentKey === "$nor") {
|
|
834
|
+
logicalClauses += current.length;
|
|
835
|
+
if (logicalClauses > limits.maxLogicalClauses) {
|
|
836
|
+
pushError(`Filter exceeds maximum logical clause budget of ${limits.maxLogicalClauses}`, path);
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
if (parentKey === "$in") {
|
|
841
|
+
if (current.length > limits.maxInValues) {
|
|
842
|
+
pushError(`Filter exceeds maximum $in values of ${limits.maxInValues}`, path);
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
for (let index = 0; index < current.length; index++) {
|
|
847
|
+
visit(current[index], path.concat(index), depth + 1, parentKey);
|
|
848
|
+
if (errors.length > 0) return;
|
|
849
|
+
}
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
if (!(0, import_utils10.isPlainObject)(current)) return;
|
|
853
|
+
for (const [key, child] of Object.entries(current)) {
|
|
854
|
+
if (key === "__proto__" || key === "prototype" || key === "constructor") {
|
|
855
|
+
pushError(`Unsupported key: ${key}`, path.concat(key));
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
if (scope === "request" && key === "include") {
|
|
859
|
+
includeCount += (0, import_utils10.isArray)(child) ? child.length : 1;
|
|
860
|
+
if (includeCount > limits.maxIncludeCount) {
|
|
861
|
+
pushError(`Request exceeds maximum include count of ${limits.maxIncludeCount}`, path.concat(key));
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
if (key === "$$sq") {
|
|
866
|
+
subQueryCount += 1;
|
|
867
|
+
if (subQueryCount > limits.maxSubQueryCount) {
|
|
868
|
+
pushError(`Request exceeds maximum subquery count of ${limits.maxSubQueryCount}`, path.concat(key));
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
visit(child, path.concat(key), depth + 1, key);
|
|
872
|
+
if (errors.length > 0) return;
|
|
873
|
+
continue;
|
|
874
|
+
}
|
|
875
|
+
visit(child, path.concat(key), depth + 1, key);
|
|
876
|
+
if (errors.length > 0) return;
|
|
877
|
+
}
|
|
878
|
+
};
|
|
879
|
+
visit(value, [], 0);
|
|
880
|
+
return errors;
|
|
881
|
+
}
|
|
882
|
+
|
|
668
883
|
// src/runtime.ts
|
|
669
|
-
|
|
884
|
+
var mongooseJsonSchemaInitialized = false;
|
|
885
|
+
var ensureMongooseJsonSchemaInitialized = () => {
|
|
886
|
+
if (mongooseJsonSchemaInitialized) return;
|
|
887
|
+
(0, import_mongoose_schema_jsonschema.default)(import_mongoose3.default);
|
|
888
|
+
mongooseJsonSchemaInitialized = true;
|
|
889
|
+
};
|
|
670
890
|
var pluralize = import_mongoose3.default.pluralize();
|
|
671
891
|
var FIELD_ACCESS_KEYS = ["list", "create", "read", "update"];
|
|
672
892
|
var defaultModelOptions = {
|
|
@@ -675,47 +895,48 @@ var defaultModelOptions = {
|
|
|
675
895
|
};
|
|
676
896
|
var defaultDataOptions = {
|
|
677
897
|
basePath: null,
|
|
898
|
+
parentPath: "/",
|
|
678
899
|
queryRouteSegment: "__query"
|
|
679
900
|
};
|
|
680
901
|
var normalizeBasePath = (name, value) => {
|
|
681
|
-
if ((0,
|
|
902
|
+
if ((0, import_utils11.isNil)(value)) {
|
|
682
903
|
return `/${pluralize(name)}`;
|
|
683
904
|
}
|
|
684
|
-
return (0,
|
|
905
|
+
return (0, import_utils11.isString)(value) ? (0, import_utils11.addLeadingSlash)(value) : "";
|
|
685
906
|
};
|
|
686
907
|
var hasModelPermission = (value, modelPermissionPrefix) => {
|
|
687
908
|
if (!modelPermissionPrefix) {
|
|
688
909
|
return true;
|
|
689
910
|
}
|
|
690
|
-
if ((0,
|
|
911
|
+
if ((0, import_utils11.isString)(value)) {
|
|
691
912
|
return value.trim().split(" ").some((item) => item.startsWith(modelPermissionPrefix));
|
|
692
913
|
}
|
|
693
|
-
if ((0,
|
|
914
|
+
if ((0, import_utils11.isArray)(value)) {
|
|
694
915
|
return value.some((item) => {
|
|
695
|
-
if ((0,
|
|
916
|
+
if ((0, import_utils11.isString)(item) || (0, import_utils11.isArray)(item)) {
|
|
696
917
|
return hasModelPermission(item, modelPermissionPrefix);
|
|
697
918
|
}
|
|
698
919
|
return true;
|
|
699
920
|
});
|
|
700
921
|
}
|
|
701
|
-
return (0,
|
|
922
|
+
return (0, import_utils11.isFunction)(value);
|
|
702
923
|
};
|
|
703
924
|
var classifyPermissionSchema = (permissionSchema, modelPermissionPrefix) => {
|
|
704
925
|
const schemaKeys = Object.keys(permissionSchema);
|
|
705
926
|
const globalPermissionKeys = {};
|
|
706
927
|
const modelPermissionKeys = {};
|
|
707
|
-
(0,
|
|
928
|
+
(0, import_utils11.forEach)(schemaKeys, (schemaKey) => {
|
|
708
929
|
const schemaRule = permissionSchema[schemaKey];
|
|
709
|
-
const ruleEntries = (0,
|
|
930
|
+
const ruleEntries = (0, import_utils11.isPlainObject)(schemaRule) ? Object.entries(schemaRule) : FIELD_ACCESS_KEYS.map((accessKey) => [accessKey, schemaRule]);
|
|
710
931
|
for (let x = 0; x < ruleEntries.length; x++) {
|
|
711
932
|
const [accessKey, value] = ruleEntries[x];
|
|
712
|
-
if (!(0,
|
|
933
|
+
if (!(0, import_utils11.isArray)(globalPermissionKeys[accessKey])) {
|
|
713
934
|
globalPermissionKeys[accessKey] = [];
|
|
714
935
|
}
|
|
715
|
-
if (!(0,
|
|
936
|
+
if (!(0, import_utils11.isArray)(modelPermissionKeys[accessKey])) {
|
|
716
937
|
modelPermissionKeys[accessKey] = [];
|
|
717
938
|
}
|
|
718
|
-
if ((0,
|
|
939
|
+
if ((0, import_utils11.isBoolean)(value)) {
|
|
719
940
|
globalPermissionKeys[accessKey].push(schemaKey);
|
|
720
941
|
return;
|
|
721
942
|
}
|
|
@@ -737,7 +958,8 @@ var AccessRuntime = class {
|
|
|
737
958
|
this.globalOptions = new OptionsManager({
|
|
738
959
|
requestPermissionField: "_permissions",
|
|
739
960
|
globalPermissions: () => ({}),
|
|
740
|
-
logger: defaultLogger
|
|
961
|
+
logger: defaultLogger,
|
|
962
|
+
requestComplexity: defaultRequestComplexity
|
|
741
963
|
}).build();
|
|
742
964
|
this.defaultModelOptions = new OptionsManager({
|
|
743
965
|
listHardLimit: 1e3,
|
|
@@ -756,14 +978,50 @@ var AccessRuntime = class {
|
|
|
756
978
|
this.modelRefs = {};
|
|
757
979
|
this.modelSubs = {};
|
|
758
980
|
this.modelAtts = {};
|
|
981
|
+
this.modelInstances = {};
|
|
759
982
|
this.openApiRegistry = new OpenApiRegistry();
|
|
983
|
+
ensureMongooseJsonSchemaInitialized();
|
|
984
|
+
}
|
|
985
|
+
registerModelInstance(modelName, model) {
|
|
986
|
+
if (!modelName || typeof modelName !== "string") {
|
|
987
|
+
throw new TypeError(`registerModelInstance: modelName must be a non-empty string, received ${typeof modelName}`);
|
|
988
|
+
}
|
|
989
|
+
const existing = this.modelInstances[modelName];
|
|
990
|
+
if (existing && existing !== model) {
|
|
991
|
+
throw new Error(
|
|
992
|
+
`Runtime model registry conflict: model "${modelName}" is already registered to a different mongoose.Model instance on this runtime. Use a distinct model name or a separate runtime.`
|
|
993
|
+
);
|
|
994
|
+
}
|
|
995
|
+
this.modelInstances[modelName] = model;
|
|
996
|
+
}
|
|
997
|
+
hasModelInstance(modelName) {
|
|
998
|
+
return modelName in this.modelInstances;
|
|
999
|
+
}
|
|
1000
|
+
getModelInstance(modelName) {
|
|
1001
|
+
const registered = this.modelInstances[modelName];
|
|
1002
|
+
if (registered) return registered;
|
|
1003
|
+
const global = import_mongoose3.default.models[modelName];
|
|
1004
|
+
return global ?? null;
|
|
760
1005
|
}
|
|
761
1006
|
registerOpenApiRoute(route) {
|
|
762
1007
|
this.openApiRegistry.register(route);
|
|
763
1008
|
}
|
|
1009
|
+
/**
|
|
1010
|
+
* Enable strict OpenAPI collision detection for the remainder of this
|
|
1011
|
+
* runtime's lifetime. After this call, conflicting method/path
|
|
1012
|
+
* registrations and duplicate operationIds throw {@link OpenApiCollisionError}
|
|
1013
|
+
* instead of silently replacing. Existing already-registered routes are
|
|
1014
|
+
* not retroactively re-validated.
|
|
1015
|
+
*/
|
|
1016
|
+
enableOpenApiCollisionDetection() {
|
|
1017
|
+
this.openApiRegistry.setStrictMode(true);
|
|
1018
|
+
}
|
|
764
1019
|
getOpenApiRoutes() {
|
|
765
1020
|
return this.openApiRegistry.getRoutes();
|
|
766
1021
|
}
|
|
1022
|
+
clearOpenApiRoutes() {
|
|
1023
|
+
this.openApiRegistry.clear();
|
|
1024
|
+
}
|
|
767
1025
|
getOpenApiSpec(info) {
|
|
768
1026
|
return this.openApiRegistry.getSpec(info);
|
|
769
1027
|
}
|
|
@@ -792,7 +1050,7 @@ var AccessRuntime = class {
|
|
|
792
1050
|
return this.defaultModelOptions.get(key, defaultValue);
|
|
793
1051
|
}
|
|
794
1052
|
createModelOptions(modelName) {
|
|
795
|
-
const model = import_mongoose3.default.model(modelName);
|
|
1053
|
+
const model = this.getModelInstance(modelName) ?? import_mongoose3.default.model(modelName);
|
|
796
1054
|
const manager = new OptionsManager({
|
|
797
1055
|
...defaultModelOptions,
|
|
798
1056
|
modelName
|
|
@@ -809,7 +1067,7 @@ var AccessRuntime = class {
|
|
|
809
1067
|
}).onchange("basePath", function(newval, key, target) {
|
|
810
1068
|
target[key] = normalizeBasePath(
|
|
811
1069
|
modelName,
|
|
812
|
-
(0,
|
|
1070
|
+
(0, import_utils11.isString)(newval) || (0, import_utils11.isNil)(newval) ? newval : void 0
|
|
813
1071
|
);
|
|
814
1072
|
}).build();
|
|
815
1073
|
this.modelJsonSchemas[modelName] = model.jsonSchema();
|
|
@@ -867,7 +1125,7 @@ var AccessRuntime = class {
|
|
|
867
1125
|
manager.onchange("basePath", function(newval, key, target) {
|
|
868
1126
|
target[key] = normalizeBasePath(
|
|
869
1127
|
dataName,
|
|
870
|
-
(0,
|
|
1128
|
+
(0, import_utils11.isString)(newval) || (0, import_utils11.isNil)(newval) ? newval : void 0
|
|
871
1129
|
);
|
|
872
1130
|
}).build();
|
|
873
1131
|
return manager;
|
|
@@ -908,7 +1166,7 @@ var AccessRuntime = class {
|
|
|
908
1166
|
if (modelName in this.modelRefs && modelName in this.modelSubs && modelName in this.modelAtts) {
|
|
909
1167
|
return;
|
|
910
1168
|
}
|
|
911
|
-
const model =
|
|
1169
|
+
const model = this.getModelInstance(modelName);
|
|
912
1170
|
if (!model) {
|
|
913
1171
|
this.modelRefs[modelName] = this.modelRefs[modelName] ?? {};
|
|
914
1172
|
this.modelSubs[modelName] = this.modelSubs[modelName] ?? [];
|
|
@@ -918,28 +1176,26 @@ var AccessRuntime = class {
|
|
|
918
1176
|
const schema = model.schema;
|
|
919
1177
|
this.modelRefs[modelName] = buildRefs(schema.tree);
|
|
920
1178
|
this.modelSubs[modelName] = buildSubPaths(schema.tree);
|
|
921
|
-
this.modelAtts[modelName] = (0,
|
|
1179
|
+
this.modelAtts[modelName] = (0, import_utils11.keys)(schema.obj);
|
|
922
1180
|
}
|
|
923
1181
|
getModelRef(modelName, refPath) {
|
|
924
1182
|
this.ensureModelMeta(modelName);
|
|
925
|
-
const value = (0,
|
|
926
|
-
return (0,
|
|
1183
|
+
const value = (0, import_utils11.get)(this.modelRefs, `${modelName}.${refPath}`, null);
|
|
1184
|
+
return (0, import_utils11.isString)(value) ? value : null;
|
|
927
1185
|
}
|
|
928
1186
|
getModelSub(modelName) {
|
|
929
1187
|
this.ensureModelMeta(modelName);
|
|
930
|
-
return (0,
|
|
1188
|
+
return (0, import_utils11.get)(this.modelSubs, modelName, []);
|
|
931
1189
|
}
|
|
932
1190
|
getModelAtt(modelName) {
|
|
933
1191
|
this.ensureModelMeta(modelName);
|
|
934
|
-
return (0,
|
|
1192
|
+
return (0, import_utils11.get)(this.modelAtts, modelName, []);
|
|
935
1193
|
}
|
|
936
1194
|
};
|
|
937
1195
|
var defaultRuntime = new AccessRuntime();
|
|
938
1196
|
|
|
939
1197
|
// src/runtime-context.ts
|
|
940
1198
|
var import_node_async_hooks = require("async_hooks");
|
|
941
|
-
var import_mongoose4 = __toESM(require("mongoose"));
|
|
942
|
-
var ACCESS_RUNTIME = /* @__PURE__ */ Symbol("access-runtime");
|
|
943
1199
|
var runtimeStorage = new import_node_async_hooks.AsyncLocalStorage();
|
|
944
1200
|
function runWithRuntime(runtime, callback) {
|
|
945
1201
|
return runtimeStorage.run(runtime, callback);
|
|
@@ -947,16 +1203,6 @@ function runWithRuntime(runtime, callback) {
|
|
|
947
1203
|
function getActiveRuntime() {
|
|
948
1204
|
return runtimeStorage.getStore() ?? null;
|
|
949
1205
|
}
|
|
950
|
-
function attachRuntimeToModel(modelName, runtime) {
|
|
951
|
-
const model = import_mongoose4.default.models[modelName];
|
|
952
|
-
if (model) {
|
|
953
|
-
model[ACCESS_RUNTIME] = runtime;
|
|
954
|
-
}
|
|
955
|
-
}
|
|
956
|
-
function getRuntimeForModelName(modelName) {
|
|
957
|
-
const model = import_mongoose4.default.models[modelName];
|
|
958
|
-
return model?.[ACCESS_RUNTIME] ?? null;
|
|
959
|
-
}
|
|
960
1206
|
|
|
961
1207
|
// src/options/global-options.ts
|
|
962
1208
|
var getRuntime = () => getActiveRuntime() ?? defaultRuntime;
|
|
@@ -974,27 +1220,36 @@ var getGlobalOption = (key, defaultValue) => {
|
|
|
974
1220
|
};
|
|
975
1221
|
|
|
976
1222
|
// src/options/model-options.ts
|
|
977
|
-
var getRuntime2 = (
|
|
1223
|
+
var getRuntime2 = () => getActiveRuntime() ?? defaultRuntime;
|
|
978
1224
|
var setModelOptions = (modelName, options) => {
|
|
979
|
-
getRuntime2(
|
|
1225
|
+
getRuntime2().setModelOptions(modelName, options);
|
|
980
1226
|
};
|
|
981
1227
|
var setModelOption = (modelName, key, value) => {
|
|
982
|
-
getRuntime2(
|
|
1228
|
+
getRuntime2().setModelOption(modelName, key, value);
|
|
983
1229
|
};
|
|
984
1230
|
var getModelOptions = (modelName) => {
|
|
985
|
-
return getRuntime2(
|
|
1231
|
+
return getRuntime2().getModelOptions(modelName);
|
|
986
1232
|
};
|
|
987
1233
|
var getModelOption = (modelName, key, defaultValue) => {
|
|
988
|
-
return getRuntime2(
|
|
1234
|
+
return getRuntime2().getModelOption(modelName, key, defaultValue);
|
|
989
1235
|
};
|
|
990
1236
|
var getExactModelOption = (modelName, key) => {
|
|
991
|
-
return getRuntime2(
|
|
1237
|
+
return getRuntime2().getExactModelOption(modelName, key);
|
|
992
1238
|
};
|
|
993
1239
|
var getModelNames = () => {
|
|
994
1240
|
return (getActiveRuntime() ?? defaultRuntime).getModelNames();
|
|
995
1241
|
};
|
|
996
1242
|
var getModelJsonSchema = (modelName) => {
|
|
997
|
-
return getRuntime2(
|
|
1243
|
+
return getRuntime2().getModelJsonSchema(modelName);
|
|
1244
|
+
};
|
|
1245
|
+
var registerModelInstance = (modelName, model) => {
|
|
1246
|
+
getRuntime2().registerModelInstance(modelName, model);
|
|
1247
|
+
};
|
|
1248
|
+
var hasModelInstance = (modelName) => {
|
|
1249
|
+
return getRuntime2().hasModelInstance(modelName);
|
|
1250
|
+
};
|
|
1251
|
+
var getModelInstance = (modelName) => {
|
|
1252
|
+
return getRuntime2().getModelInstance(modelName);
|
|
998
1253
|
};
|
|
999
1254
|
|
|
1000
1255
|
// src/options/default-model-options.ts
|
|
@@ -1022,25 +1277,35 @@ var getDataOption = (dataName, key, defaultValue) => {
|
|
|
1022
1277
|
};
|
|
1023
1278
|
|
|
1024
1279
|
// src/meta.ts
|
|
1025
|
-
var getRuntime5 = (
|
|
1280
|
+
var getRuntime5 = () => getActiveRuntime() ?? defaultRuntime;
|
|
1026
1281
|
var getModelRef = (modelName, refPath) => {
|
|
1027
|
-
return getRuntime5(
|
|
1282
|
+
return getRuntime5().getModelRef(modelName, refPath);
|
|
1028
1283
|
};
|
|
1029
1284
|
var getModelSub = (modelName) => {
|
|
1030
|
-
return getRuntime5(
|
|
1285
|
+
return getRuntime5().getModelSub(modelName);
|
|
1031
1286
|
};
|
|
1032
1287
|
|
|
1033
1288
|
// src/services/base.ts
|
|
1034
|
-
var
|
|
1289
|
+
var import_utils12 = require("@web-ts-toolkit/utils");
|
|
1290
|
+
var ClientRequestError = class extends Error {
|
|
1291
|
+
constructor(result) {
|
|
1292
|
+
super(String(result.code));
|
|
1293
|
+
this.result = result;
|
|
1294
|
+
}
|
|
1295
|
+
};
|
|
1035
1296
|
function validateClientFilter(filter2) {
|
|
1036
1297
|
const errors = [];
|
|
1298
|
+
const complexityErrors = validateRequestComplexity(filter2, getGlobalOption("requestComplexity"), "filter");
|
|
1299
|
+
if (complexityErrors.length > 0) {
|
|
1300
|
+
return complexityErrors.map((error2) => error2.detail);
|
|
1301
|
+
}
|
|
1037
1302
|
const blockedOperators = /* @__PURE__ */ new Set(["$where", "$expr", "$function", "$accumulator"]);
|
|
1038
1303
|
const visit = (value, path) => {
|
|
1039
|
-
if ((0,
|
|
1304
|
+
if ((0, import_utils12.isArray)(value)) {
|
|
1040
1305
|
value.forEach((item, index) => visit(item, `${path}[${index}]`));
|
|
1041
1306
|
return;
|
|
1042
1307
|
}
|
|
1043
|
-
if (!(0,
|
|
1308
|
+
if (!(0, import_utils12.isPlainObject)(value)) return;
|
|
1044
1309
|
Object.entries(value).forEach(([key, child]) => {
|
|
1045
1310
|
const nextPath = path ? `${path}.${key}` : key;
|
|
1046
1311
|
if (blockedOperators.has(key)) {
|
|
@@ -1134,18 +1399,43 @@ var Base = class {
|
|
|
1134
1399
|
validateClientFilter(filter2) {
|
|
1135
1400
|
return validateClientFilter(filter2);
|
|
1136
1401
|
}
|
|
1402
|
+
getRequestComplexity() {
|
|
1403
|
+
return resolveRequestComplexity(getGlobalOption("requestComplexity"));
|
|
1404
|
+
}
|
|
1405
|
+
getClientRequestErrorResult(error2) {
|
|
1406
|
+
return error2 instanceof ClientRequestError ? error2.result : null;
|
|
1407
|
+
}
|
|
1408
|
+
throwClientRequestError(code, detail) {
|
|
1409
|
+
throw new ClientRequestError({
|
|
1410
|
+
success: false,
|
|
1411
|
+
kind: "error",
|
|
1412
|
+
code,
|
|
1413
|
+
errors: [{ detail }]
|
|
1414
|
+
});
|
|
1415
|
+
}
|
|
1416
|
+
async getAuthorizedTargetService(modelName, op) {
|
|
1417
|
+
const runtime = getActiveRuntime();
|
|
1418
|
+
if (runtime && !runtime.hasModel(modelName)) {
|
|
1419
|
+
this.throwClientRequestError("bad_request" /* BadRequest */, `Model ${modelName} not found`);
|
|
1420
|
+
}
|
|
1421
|
+
const allowed = await this.req.macl.isAllowed(modelName, op);
|
|
1422
|
+
if (!allowed) {
|
|
1423
|
+
this.throwClientRequestError("unauthorized" /* Unauthorized */, "Unauthorized");
|
|
1424
|
+
}
|
|
1425
|
+
return this.req.macl.getPublicService(modelName);
|
|
1426
|
+
}
|
|
1137
1427
|
processInclude(include) {
|
|
1138
|
-
const includes = (0,
|
|
1428
|
+
const includes = (0, import_utils12.compact)((0, import_utils12.castArray)(include)).filter(({ model, op, path, localField, foreignField }) => {
|
|
1139
1429
|
return model && op && path && localField && foreignField;
|
|
1140
1430
|
});
|
|
1141
1431
|
let includeLocalFields = [];
|
|
1142
1432
|
let includePaths = [];
|
|
1143
|
-
(0,
|
|
1433
|
+
(0, import_utils12.forEach)(includes, (inc) => {
|
|
1144
1434
|
includeLocalFields.push(inc.localField);
|
|
1145
1435
|
includePaths.push(inc.path);
|
|
1146
1436
|
});
|
|
1147
|
-
includeLocalFields = (0,
|
|
1148
|
-
includePaths = (0,
|
|
1437
|
+
includeLocalFields = (0, import_utils12.uniq)((0, import_utils12.compact)(includeLocalFields));
|
|
1438
|
+
includePaths = (0, import_utils12.uniq)((0, import_utils12.compact)(includePaths));
|
|
1149
1439
|
return {
|
|
1150
1440
|
includes,
|
|
1151
1441
|
includeLocalFields,
|
|
@@ -1154,72 +1444,80 @@ var Base = class {
|
|
|
1154
1444
|
}
|
|
1155
1445
|
async includeDocs(docs, include) {
|
|
1156
1446
|
if (!include) return docs;
|
|
1157
|
-
const includes = (0,
|
|
1447
|
+
const includes = (0, import_utils12.compact)((0, import_utils12.castArray)(include));
|
|
1158
1448
|
if (includes.length === 0) return docs;
|
|
1159
|
-
const isSingle = !(0,
|
|
1160
|
-
|
|
1449
|
+
const isSingle = !(0, import_utils12.isArray)(docs);
|
|
1450
|
+
let docList = isSingle ? [docs] : docs;
|
|
1161
1451
|
for (let x = 0; x < includes.length; x++) {
|
|
1162
1452
|
const include2 = includes[x];
|
|
1163
1453
|
if (include2.op === "count") {
|
|
1164
|
-
|
|
1454
|
+
docList = await this.includeDocsCount(docList, include2);
|
|
1165
1455
|
} else {
|
|
1166
|
-
|
|
1456
|
+
docList = await this.includeDocsList(docList, include2);
|
|
1167
1457
|
}
|
|
1168
1458
|
}
|
|
1169
|
-
return isSingle ?
|
|
1459
|
+
return isSingle ? docList[0] : docList;
|
|
1170
1460
|
}
|
|
1171
1461
|
async includeDocsList(docs, include) {
|
|
1172
1462
|
const { model, op, path, localField, foreignField, filter: _filters, args = {}, options = {} } = include;
|
|
1173
|
-
const svc = this.
|
|
1174
|
-
if (!svc) return docs;
|
|
1463
|
+
const svc = await this.getAuthorizedTargetService(model, op);
|
|
1175
1464
|
const includeLocalValues = [];
|
|
1176
|
-
(0,
|
|
1177
|
-
includeLocalValues.push((0,
|
|
1465
|
+
(0, import_utils12.forEach)(docs, (doc, i) => {
|
|
1466
|
+
includeLocalValues.push((0, import_utils12.get)(doc, localField));
|
|
1178
1467
|
});
|
|
1179
|
-
const filter2 = { ..._filters ?? {}, [foreignField]: { $in: (0,
|
|
1180
|
-
const
|
|
1468
|
+
const filter2 = { ..._filters ?? {}, [foreignField]: { $in: (0, import_utils12.flatten)(includeLocalValues) } };
|
|
1469
|
+
const authorizedFilter = await svc.genFilter(op, filter2);
|
|
1470
|
+
const trustedArgs = {
|
|
1471
|
+
...args,
|
|
1472
|
+
overrides: {
|
|
1473
|
+
filter: authorizedFilter
|
|
1474
|
+
}
|
|
1475
|
+
};
|
|
1476
|
+
const trustedOptions = {
|
|
1181
1477
|
...options,
|
|
1182
1478
|
lean: true,
|
|
1183
1479
|
includePermissions: false,
|
|
1184
1480
|
includeCount: false
|
|
1185
|
-
}
|
|
1186
|
-
|
|
1481
|
+
};
|
|
1482
|
+
const trustedResult = await svc.find(filter2, trustedArgs, trustedOptions);
|
|
1483
|
+
if (!trustedResult.success) return docs;
|
|
1187
1484
|
for (let y = 0; y < docs.length; y++) {
|
|
1188
1485
|
const doc = docs[y];
|
|
1189
|
-
const localValue = (0,
|
|
1190
|
-
const filterFn = (row) => (0,
|
|
1191
|
-
const matches =
|
|
1486
|
+
const localValue = (0, import_utils12.get)(doc, localField);
|
|
1487
|
+
const filterFn = (row) => (0, import_utils12.intersectionBy)((0, import_utils12.castArray)(localValue), (0, import_utils12.castArray)((0, import_utils12.get)(row, foreignField)), String).length > 0;
|
|
1488
|
+
const matches = trustedResult.data.filter(filterFn);
|
|
1192
1489
|
setDocValue(doc, path, op === "list" ? matches : matches[0]);
|
|
1193
1490
|
}
|
|
1194
1491
|
return docs;
|
|
1195
1492
|
}
|
|
1196
1493
|
async includeDocsCount(docs, include) {
|
|
1197
1494
|
const { model, path, localField, foreignField, filter: _filters, args = {}, options = {} } = include;
|
|
1198
|
-
const svc = this.
|
|
1199
|
-
if (!svc) return docs;
|
|
1495
|
+
const svc = await this.getAuthorizedTargetService(model, "count");
|
|
1200
1496
|
const includeLocalValues = [];
|
|
1201
|
-
(0,
|
|
1202
|
-
includeLocalValues.push((0,
|
|
1497
|
+
(0, import_utils12.forEach)(docs, (doc) => {
|
|
1498
|
+
includeLocalValues.push((0, import_utils12.get)(doc, localField));
|
|
1203
1499
|
});
|
|
1204
|
-
const filter2 = { ..._filters ?? {}, [foreignField]: { $in: (0,
|
|
1205
|
-
const
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
{
|
|
1212
|
-
...options,
|
|
1213
|
-
lean: true,
|
|
1214
|
-
includePermissions: false,
|
|
1215
|
-
includeCount: false
|
|
1500
|
+
const filter2 = { ..._filters ?? {}, [foreignField]: { $in: (0, import_utils12.flatten)(includeLocalValues) } };
|
|
1501
|
+
const authorizedFilter = await svc.genFilter("list", filter2);
|
|
1502
|
+
const trustedArgs = {
|
|
1503
|
+
...args,
|
|
1504
|
+
select: [foreignField],
|
|
1505
|
+
overrides: {
|
|
1506
|
+
filter: authorizedFilter
|
|
1216
1507
|
}
|
|
1217
|
-
|
|
1508
|
+
};
|
|
1509
|
+
const trustedOptions = {
|
|
1510
|
+
...options,
|
|
1511
|
+
lean: true,
|
|
1512
|
+
includePermissions: false,
|
|
1513
|
+
includeCount: false
|
|
1514
|
+
};
|
|
1515
|
+
const result = await svc.find(filter2, trustedArgs, trustedOptions);
|
|
1218
1516
|
if (!result.success) return docs;
|
|
1219
1517
|
for (let y = 0; y < docs.length; y++) {
|
|
1220
1518
|
const doc = docs[y];
|
|
1221
|
-
const localValue = (0,
|
|
1222
|
-
const filterFn = (row) => (0,
|
|
1519
|
+
const localValue = (0, import_utils12.get)(doc, localField);
|
|
1520
|
+
const filterFn = (row) => (0, import_utils12.intersectionBy)((0, import_utils12.castArray)(localValue), (0, import_utils12.castArray)((0, import_utils12.get)(row, foreignField)), String).length > 0;
|
|
1223
1521
|
setDocValue(doc, path, result.data.filter(filterFn).length);
|
|
1224
1522
|
}
|
|
1225
1523
|
return docs;
|
|
@@ -1239,29 +1537,32 @@ var Base = class {
|
|
|
1239
1537
|
}
|
|
1240
1538
|
async handleSubQuery(sq, key) {
|
|
1241
1539
|
const { model, op, id, filter: filter2, args, options, sqOptions = {} } = sq;
|
|
1242
|
-
const svc = this.req.macl.getPublicService(model);
|
|
1243
|
-
if (!svc) return null;
|
|
1244
1540
|
let result;
|
|
1245
1541
|
if (op === "list") {
|
|
1246
|
-
|
|
1542
|
+
const svc = await this.getAuthorizedTargetService(model, "list");
|
|
1543
|
+
result = await svc._list(filter2, args, options);
|
|
1247
1544
|
} else if (op === "read") {
|
|
1545
|
+
const svc = await this.getAuthorizedTargetService(model, "read");
|
|
1546
|
+
const readOptions = { ...options, tryList: false };
|
|
1248
1547
|
if (id) {
|
|
1249
|
-
result = await svc.
|
|
1548
|
+
result = await svc._read(id, args, readOptions);
|
|
1250
1549
|
} else if (filter2) {
|
|
1251
|
-
result = await svc.
|
|
1550
|
+
result = await svc._readFilter(filter2, args, readOptions);
|
|
1252
1551
|
} else {
|
|
1253
|
-
|
|
1552
|
+
this.throwClientRequestError("bad_request" /* BadRequest */, `Subquery for field ${key} requires an id or filter`);
|
|
1254
1553
|
}
|
|
1255
1554
|
} else {
|
|
1256
|
-
|
|
1555
|
+
this.throwClientRequestError("bad_request" /* BadRequest */, `Unsupported subquery operation: ${op}`);
|
|
1556
|
+
}
|
|
1557
|
+
if (!result.success) {
|
|
1558
|
+
throw new ClientRequestError(result);
|
|
1257
1559
|
}
|
|
1258
|
-
if (!result.success) return null;
|
|
1259
1560
|
let ret = result.data;
|
|
1260
1561
|
if (sqOptions.path) {
|
|
1261
|
-
ret = (0,
|
|
1562
|
+
ret = (0, import_utils12.isArray)(ret) ? (0, import_utils12.flatten)(ret.map((v) => (0, import_utils12.get)(v, sqOptions.path))) : (0, import_utils12.get)(ret, sqOptions.path);
|
|
1262
1563
|
}
|
|
1263
1564
|
if (sqOptions.compact) {
|
|
1264
|
-
ret = (0,
|
|
1565
|
+
ret = (0, import_utils12.compact)((0, import_utils12.castArray)(ret));
|
|
1265
1566
|
}
|
|
1266
1567
|
return ret;
|
|
1267
1568
|
}
|
|
@@ -1273,34 +1574,203 @@ var Base = class {
|
|
|
1273
1574
|
};
|
|
1274
1575
|
|
|
1275
1576
|
// src/services/service.ts
|
|
1276
|
-
var
|
|
1577
|
+
var import_utils14 = require("@web-ts-toolkit/utils");
|
|
1277
1578
|
var import_just_diff = require("just-diff");
|
|
1278
1579
|
|
|
1279
1580
|
// src/model.ts
|
|
1280
|
-
var
|
|
1581
|
+
var import_mongoose4 = __toESM(require("mongoose"));
|
|
1281
1582
|
|
|
1282
|
-
// src/logger.ts
|
|
1283
|
-
var
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1583
|
+
// src/logger-helpers.ts
|
|
1584
|
+
var SENSITIVE_FILTER_KEYS = /* @__PURE__ */ new Set([
|
|
1585
|
+
"password",
|
|
1586
|
+
"pwd",
|
|
1587
|
+
"secret",
|
|
1588
|
+
"token",
|
|
1589
|
+
"access_token",
|
|
1590
|
+
"refreshtoken",
|
|
1591
|
+
"api_key",
|
|
1592
|
+
"apikey",
|
|
1593
|
+
"authorization",
|
|
1594
|
+
"authtoken",
|
|
1595
|
+
"credentials",
|
|
1596
|
+
"privatekey",
|
|
1597
|
+
"ssn",
|
|
1598
|
+
"creditcard",
|
|
1599
|
+
"cardnumber",
|
|
1600
|
+
"cvv",
|
|
1601
|
+
"tenantid",
|
|
1602
|
+
"tenant"
|
|
1603
|
+
]);
|
|
1604
|
+
var SENSITIVE_PAYLOAD_KEYS = new Set(SENSITIVE_FILTER_KEYS);
|
|
1605
|
+
var safeReplace = (value, seen) => {
|
|
1606
|
+
if (value === null || typeof value === "undefined") return value;
|
|
1607
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") {
|
|
1608
|
+
return value;
|
|
1609
|
+
}
|
|
1610
|
+
if (typeof value === "function") return "[Function]";
|
|
1611
|
+
if (typeof value === "object") {
|
|
1612
|
+
if (seen.has(value)) return "[Circular]";
|
|
1613
|
+
seen.add(value);
|
|
1614
|
+
try {
|
|
1615
|
+
if (Array.isArray(value)) {
|
|
1616
|
+
return value.map((entry) => safeReplace(entry, seen));
|
|
1617
|
+
}
|
|
1618
|
+
const out = {};
|
|
1619
|
+
for (const [k, v] of Object.entries(value)) {
|
|
1620
|
+
out[k] = safeReplace(v, seen);
|
|
1621
|
+
}
|
|
1622
|
+
return out;
|
|
1623
|
+
} finally {
|
|
1624
|
+
seen.delete(value);
|
|
1625
|
+
}
|
|
1626
|
+
}
|
|
1627
|
+
return value;
|
|
1628
|
+
};
|
|
1629
|
+
var redactObjectKeys = (value, sensitiveKeys, seen) => {
|
|
1630
|
+
if (value === null || typeof value !== "object") return value;
|
|
1631
|
+
if (seen.has(value)) return "[Circular]";
|
|
1632
|
+
seen.add(value);
|
|
1633
|
+
try {
|
|
1634
|
+
if (Array.isArray(value)) {
|
|
1635
|
+
return value.map((entry) => redactObjectKeys(entry, sensitiveKeys, seen));
|
|
1636
|
+
}
|
|
1637
|
+
const out = {};
|
|
1638
|
+
for (const [k, v] of Object.entries(value)) {
|
|
1639
|
+
const lowered = String(k).toLowerCase();
|
|
1640
|
+
if (sensitiveKeys.has(lowered)) {
|
|
1641
|
+
out[k] = "[REDACTED]";
|
|
1642
|
+
} else {
|
|
1643
|
+
out[k] = redactObjectKeys(v, sensitiveKeys, seen);
|
|
1644
|
+
}
|
|
1645
|
+
}
|
|
1646
|
+
return out;
|
|
1647
|
+
} finally {
|
|
1648
|
+
seen.delete(value);
|
|
1649
|
+
}
|
|
1650
|
+
};
|
|
1651
|
+
var redactFilter = (filter2) => {
|
|
1652
|
+
return redactObjectKeys(filter2, SENSITIVE_FILTER_KEYS, /* @__PURE__ */ new WeakSet());
|
|
1653
|
+
};
|
|
1654
|
+
var redactPayload = (payload) => {
|
|
1655
|
+
return redactObjectKeys(payload, SENSITIVE_PAYLOAD_KEYS, /* @__PURE__ */ new WeakSet());
|
|
1656
|
+
};
|
|
1657
|
+
var safeStringify = (value) => {
|
|
1658
|
+
try {
|
|
1659
|
+
return JSON.stringify(safeReplace(value, /* @__PURE__ */ new WeakSet()));
|
|
1660
|
+
} catch {
|
|
1661
|
+
return "[Unserializable]";
|
|
1662
|
+
}
|
|
1663
|
+
};
|
|
1664
|
+
var countFilterKeys = (filter2) => {
|
|
1665
|
+
if (filter2 === null || typeof filter2 === "undefined") return 0;
|
|
1666
|
+
if (typeof filter2 !== "object") return 0;
|
|
1667
|
+
if (Array.isArray(filter2)) return 0;
|
|
1668
|
+
let count = 0;
|
|
1669
|
+
const stack = [filter2];
|
|
1670
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
1671
|
+
while (stack.length > 0) {
|
|
1672
|
+
const current = stack.pop();
|
|
1673
|
+
if (current === null || typeof current !== "object" || Array.isArray(current)) continue;
|
|
1674
|
+
if (seen.has(current)) continue;
|
|
1675
|
+
seen.add(current);
|
|
1676
|
+
for (const [key, value] of Object.entries(current)) {
|
|
1677
|
+
count += 1;
|
|
1678
|
+
if (typeof key === "string" && key.startsWith("$") && value !== null && typeof value === "object") {
|
|
1679
|
+
if (Array.isArray(value)) {
|
|
1680
|
+
for (const entry of value) {
|
|
1681
|
+
if (entry !== null && typeof entry === "object" && !Array.isArray(entry)) {
|
|
1682
|
+
stack.push(entry);
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
} else {
|
|
1686
|
+
stack.push(value);
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
1690
|
+
}
|
|
1691
|
+
return count;
|
|
1692
|
+
};
|
|
1693
|
+
var summarizeFilter = (filter2) => {
|
|
1694
|
+
return { filterKeyValueCount: countFilterKeys(filter2) };
|
|
1287
1695
|
};
|
|
1288
|
-
var
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1696
|
+
var resolveLogger = () => {
|
|
1697
|
+
return getGlobalOption("logger") ?? defaultLogger;
|
|
1698
|
+
};
|
|
1699
|
+
var isLevelEnabled = (level) => {
|
|
1700
|
+
const target = resolveLogger();
|
|
1701
|
+
const fn = target?.isLevelEnabled;
|
|
1702
|
+
if (typeof fn === "function") {
|
|
1703
|
+
try {
|
|
1704
|
+
return !!fn.call(target, level);
|
|
1705
|
+
} catch {
|
|
1706
|
+
return true;
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
return true;
|
|
1710
|
+
};
|
|
1711
|
+
var buildOpMessage = (ctx) => {
|
|
1712
|
+
const trimmed = { op: ctx.op };
|
|
1713
|
+
if (ctx.modelName) trimmed.modelName = ctx.modelName;
|
|
1714
|
+
if (ctx.dataName) trimmed.dataName = ctx.dataName;
|
|
1715
|
+
if (ctx.page != null) trimmed.page = ctx.page;
|
|
1716
|
+
if (ctx.pageSize != null) trimmed.pageSize = ctx.pageSize;
|
|
1717
|
+
if (ctx.skip != null) trimmed.skip = ctx.skip;
|
|
1718
|
+
if (ctx.limit != null) trimmed.limit = ctx.limit;
|
|
1719
|
+
if (ctx.sort != null) trimmed.sort = ctx.sort;
|
|
1720
|
+
if (ctx.populateCount != null) trimmed.populateCount = ctx.populateCount;
|
|
1721
|
+
if (ctx.selectCount != null) trimmed.selectCount = ctx.selectCount;
|
|
1722
|
+
if (ctx.filterKeyValueCount != null) trimmed.filterKeyValueCount = ctx.filterKeyValueCount;
|
|
1723
|
+
if (ctx.startedAt != null && ctx.durationMs == null) {
|
|
1724
|
+
trimmed.startedAt = ctx.startedAt;
|
|
1725
|
+
}
|
|
1726
|
+
if (ctx.durationMs != null) trimmed.durationMs = ctx.durationMs;
|
|
1727
|
+
if (ctx.resultCode != null) trimmed.resultCode = ctx.resultCode;
|
|
1728
|
+
return safeStringify(trimmed);
|
|
1729
|
+
};
|
|
1730
|
+
var safeApply = (fn, thisArg, args) => {
|
|
1731
|
+
try {
|
|
1732
|
+
fn.apply(thisArg, args);
|
|
1733
|
+
} catch {
|
|
1734
|
+
}
|
|
1735
|
+
};
|
|
1736
|
+
var debug = (ctx) => {
|
|
1737
|
+
if (!isLevelEnabled("debug")) return;
|
|
1738
|
+
const target = resolveLogger();
|
|
1739
|
+
const fn = target?.debug ?? defaultLogger.debug;
|
|
1740
|
+
const message = buildOpMessage(ctx);
|
|
1741
|
+
safeApply(fn, target ?? defaultLogger, [message]);
|
|
1742
|
+
};
|
|
1743
|
+
var warn = (message, meta) => {
|
|
1744
|
+
const target = resolveLogger();
|
|
1745
|
+
const fn = target?.warn ?? defaultLogger.warn;
|
|
1746
|
+
safeApply(fn, target ?? defaultLogger, meta != null ? [message, meta] : [message]);
|
|
1747
|
+
};
|
|
1748
|
+
var logInfoMessage = (message, ...args) => {
|
|
1749
|
+
const target = resolveLogger();
|
|
1750
|
+
const fn = target?.info ?? defaultLogger.info;
|
|
1751
|
+
const payload = args.length > 0 ? [message, ...args] : [message];
|
|
1752
|
+
safeApply(fn, target ?? defaultLogger, payload);
|
|
1753
|
+
};
|
|
1754
|
+
var error = (error2) => {
|
|
1755
|
+
const target = resolveLogger();
|
|
1756
|
+
const fn = target?.error ?? defaultLogger.error;
|
|
1757
|
+
safeApply(fn, target ?? defaultLogger, [error2]);
|
|
1293
1758
|
};
|
|
1294
1759
|
|
|
1295
1760
|
// src/model.ts
|
|
1761
|
+
var safeSortLogError = (msg, ...args) => {
|
|
1762
|
+
const message = args.length > 0 ? `${msg} ${safeStringify(args.length === 1 ? args[0] : args)}` : msg;
|
|
1763
|
+
error(message);
|
|
1764
|
+
};
|
|
1296
1765
|
var Model = class {
|
|
1297
|
-
constructor(modelName) {
|
|
1766
|
+
constructor(modelName, runtime) {
|
|
1298
1767
|
this.modelName = modelName;
|
|
1299
|
-
this.
|
|
1768
|
+
this.runtime = runtime ?? null;
|
|
1769
|
+
const resolvedRuntime = runtime ?? getActiveRuntime();
|
|
1770
|
+
const registered = resolvedRuntime?.getModelInstance(modelName) ?? null;
|
|
1771
|
+
const global = import_mongoose4.default.models[modelName];
|
|
1772
|
+
this.model = registered ?? global ?? import_mongoose4.default.model(modelName);
|
|
1300
1773
|
if (!this.model) return;
|
|
1301
|
-
this.model.schema.set("optimisticConcurrency", true);
|
|
1302
|
-
const currVersionKey = this.model.schema.get("versionKey");
|
|
1303
|
-
if (!currVersionKey) this.model.schema.set("versionKey", "__v");
|
|
1304
1774
|
}
|
|
1305
1775
|
new() {
|
|
1306
1776
|
const doc = new this.model();
|
|
@@ -1309,20 +1779,25 @@ var Model = class {
|
|
|
1309
1779
|
create(data) {
|
|
1310
1780
|
return this.model.create(data);
|
|
1311
1781
|
}
|
|
1312
|
-
find({ filter: filter2, select, sort, populate, limit, skip, lean }) {
|
|
1782
|
+
find({ filter: filter2, select, sort, populate, limit, hardLimit, skip, lean }) {
|
|
1313
1783
|
if (!this.validateSort(sort)) {
|
|
1314
1784
|
sort = null;
|
|
1315
1785
|
}
|
|
1316
1786
|
const builder = this.model.find(filter2);
|
|
1317
1787
|
if (select) builder.select(select);
|
|
1318
1788
|
if (skip) builder.skip(Number(skip));
|
|
1319
|
-
|
|
1789
|
+
const normalizedLimit = Number(limit);
|
|
1790
|
+
if (Number.isSafeInteger(normalizedLimit) && normalizedLimit > 0) {
|
|
1791
|
+
builder.limit(normalizedLimit);
|
|
1792
|
+
} else if (Number.isSafeInteger(hardLimit) && Number(hardLimit) > 0) {
|
|
1793
|
+
builder.limit(Number(hardLimit));
|
|
1794
|
+
}
|
|
1320
1795
|
if (sort) builder.sort(sort);
|
|
1321
1796
|
if (populate) builder.populate(populate);
|
|
1322
1797
|
if (lean) builder.lean();
|
|
1323
1798
|
return builder;
|
|
1324
1799
|
}
|
|
1325
|
-
validateSort(sort, logError =
|
|
1800
|
+
validateSort(sort, logError = safeSortLogError) {
|
|
1326
1801
|
const validSortOrders = [1, -1, "asc", "ascending", "desc", "descending"];
|
|
1327
1802
|
const fieldPathPattern = /^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*)*$/;
|
|
1328
1803
|
const isValidFieldPath = (field) => fieldPathPattern.test(field);
|
|
@@ -1399,7 +1874,7 @@ var Model = class {
|
|
|
1399
1874
|
}
|
|
1400
1875
|
exists(filter2) {
|
|
1401
1876
|
if (!filter2) return null;
|
|
1402
|
-
return this.findOne(filter2).select("_id").lean();
|
|
1877
|
+
return this.findOne({ filter: filter2 }).select("_id").lean();
|
|
1403
1878
|
}
|
|
1404
1879
|
// see https://mongoosejs.com/docs/api.html#query_Query-countDocuments
|
|
1405
1880
|
countDocuments(filter2 = {}) {
|
|
@@ -1413,98 +1888,122 @@ var Model = class {
|
|
|
1413
1888
|
var model_default = Model;
|
|
1414
1889
|
|
|
1415
1890
|
// src/services/model-subdocument-service.ts
|
|
1416
|
-
var
|
|
1891
|
+
var import_utils13 = require("@web-ts-toolkit/utils");
|
|
1417
1892
|
async function listSub(service, id, sub, options) {
|
|
1418
1893
|
const { filter: ft, select } = options ?? {};
|
|
1894
|
+
const filterErrors = validateClientFilter(ft);
|
|
1895
|
+
if (filterErrors.length > 0) return { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors: filterErrors };
|
|
1419
1896
|
const parentDoc = await getParentDoc(service, id, sub, null, { access: "read" });
|
|
1420
|
-
if (!parentDoc) return { success: false, code: "not_found" /* NotFound */ };
|
|
1421
|
-
let result = (0,
|
|
1897
|
+
if (!parentDoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
1898
|
+
let result = (0, import_utils13.get)(parentDoc, sub);
|
|
1422
1899
|
const [subFilter, subSelect] = await Promise.all([
|
|
1423
1900
|
service.genFilter(`subs.${sub}.list`, ft),
|
|
1424
1901
|
service.genQuerySelect("list", select, false, [sub, "sub"])
|
|
1425
1902
|
]);
|
|
1426
|
-
if (subFilter === false) return { success: false, code: "forbidden" /* Forbidden */ };
|
|
1903
|
+
if (subFilter === false) return { success: false, kind: "error", code: "forbidden" /* Forbidden */ };
|
|
1427
1904
|
result = filterCollection(result, subFilter);
|
|
1428
|
-
if (subSelect) result = result.map((v) => (0,
|
|
1905
|
+
if (subSelect) result = result.map((v) => (0, import_utils13.pick)(toObject(v), subSelect.concat("_id")));
|
|
1429
1906
|
return { success: true, kind: "list", code: "success" /* Success */, data: result, count: result.length };
|
|
1430
1907
|
}
|
|
1431
1908
|
async function readSub(service, id, sub, subId, options) {
|
|
1432
1909
|
const { select, populate } = options ?? {};
|
|
1433
1910
|
const parentDoc = await getParentDoc(service, id, sub, { populate }, { access: "read" });
|
|
1434
|
-
if (!parentDoc) return { success: false, code: "not_found" /* NotFound */ };
|
|
1435
|
-
const result = (0,
|
|
1911
|
+
if (!parentDoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
1912
|
+
const result = (0, import_utils13.get)(parentDoc, sub);
|
|
1436
1913
|
const [subFilter, subSelect] = await Promise.all([
|
|
1437
1914
|
service.genFilter(`subs.${sub}.read`, { _id: subId }),
|
|
1438
1915
|
service.genQuerySelect("read", select, false, [sub, "sub"])
|
|
1439
1916
|
]);
|
|
1440
|
-
if (subFilter === false) return { success: false, code: "forbidden" /* Forbidden */ };
|
|
1917
|
+
if (subFilter === false) return { success: false, kind: "error", code: "forbidden" /* Forbidden */ };
|
|
1441
1918
|
let subdoc = findElement(result, subFilter);
|
|
1442
|
-
if (!subdoc) return { success: false, code: "not_found" /* NotFound */ };
|
|
1443
|
-
if (subSelect) subdoc = (0,
|
|
1919
|
+
if (!subdoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
1920
|
+
if (subSelect) subdoc = (0, import_utils13.pick)(toObject(subdoc), subSelect.concat(["_id"]));
|
|
1444
1921
|
return { success: true, kind: "single", code: "success" /* Success */, data: subdoc };
|
|
1445
1922
|
}
|
|
1446
1923
|
async function updateSub(service, id, sub, subId, data) {
|
|
1447
1924
|
const parentDoc = await getParentDoc(service, id, sub, null, { access: "update" });
|
|
1448
|
-
if (!parentDoc) return { success: false, code: "not_found" /* NotFound */ };
|
|
1449
|
-
const result = (0,
|
|
1925
|
+
if (!parentDoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
1926
|
+
const result = (0, import_utils13.get)(parentDoc, sub);
|
|
1450
1927
|
const [subFilter, subReadSelect, subUpdateSelect] = await Promise.all([
|
|
1451
1928
|
service.genFilter(`subs.${sub}.update`, { _id: subId }),
|
|
1452
1929
|
service.genQuerySelect("read", null, false, [sub, "sub"]),
|
|
1453
1930
|
service.genQuerySelect("update", null, false, [sub, "sub"])
|
|
1454
1931
|
]);
|
|
1455
|
-
if (subFilter === false) return { success: false, code: "forbidden" /* Forbidden */ };
|
|
1932
|
+
if (subFilter === false) return { success: false, kind: "error", code: "forbidden" /* Forbidden */ };
|
|
1456
1933
|
let subdoc = findElement(result, subFilter);
|
|
1457
|
-
if (!subdoc) return { success: false, code: "not_found" /* NotFound */ };
|
|
1458
|
-
const allowedData = (0,
|
|
1934
|
+
if (!subdoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
1935
|
+
const allowedData = (0, import_utils13.pick)(data, subUpdateSelect);
|
|
1459
1936
|
Object.assign(subdoc, allowedData);
|
|
1460
1937
|
await parentDoc.save();
|
|
1461
|
-
if (subReadSelect) subdoc = (0,
|
|
1938
|
+
if (subReadSelect) subdoc = (0, import_utils13.pick)(toObject(subdoc), subReadSelect.concat(["_id"]));
|
|
1462
1939
|
return { success: true, kind: "single", code: "success" /* Success */, data: subdoc };
|
|
1463
1940
|
}
|
|
1464
1941
|
async function bulkUpdateSub(service, id, sub, data) {
|
|
1942
|
+
const { maxBulkItems } = service.getRequestComplexity();
|
|
1943
|
+
if (data.length > maxBulkItems) {
|
|
1944
|
+
return {
|
|
1945
|
+
success: false,
|
|
1946
|
+
kind: "error",
|
|
1947
|
+
code: "bad_request" /* BadRequest */,
|
|
1948
|
+
errors: [{ detail: `Bulk subdocument update exceeds maximum item count of ${maxBulkItems}` }]
|
|
1949
|
+
};
|
|
1950
|
+
}
|
|
1465
1951
|
const parentDoc = await getParentDoc(service, id, sub, null, { access: "update" });
|
|
1466
|
-
if (!parentDoc) return { success: false, code: "not_found" /* NotFound */ };
|
|
1467
|
-
let result = (0,
|
|
1952
|
+
if (!parentDoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
1953
|
+
let result = (0, import_utils13.get)(parentDoc, sub);
|
|
1468
1954
|
const [subFilter, subReadSelect, subUpdateSelect] = await Promise.all([
|
|
1469
1955
|
service.genFilter(`subs.${sub}.update`, { _id: { $in: data.map((v) => v._id) } }),
|
|
1470
1956
|
service.genQuerySelect("read", null, false, [sub, "sub"]),
|
|
1471
1957
|
service.genQuerySelect("update", null, false, [sub, "sub"])
|
|
1472
1958
|
]);
|
|
1473
|
-
if (subFilter === false) return { success: false, code: "forbidden" /* Forbidden */ };
|
|
1959
|
+
if (subFilter === false) return { success: false, kind: "error", code: "forbidden" /* Forbidden */ };
|
|
1474
1960
|
result = filterCollection(result, subFilter);
|
|
1475
|
-
(0,
|
|
1961
|
+
(0, import_utils13.forEach)(result, (subdoc) => {
|
|
1476
1962
|
const tdata = findElementById(data, subdoc._id);
|
|
1477
1963
|
if (!tdata) return;
|
|
1478
|
-
const allowedData = (0,
|
|
1964
|
+
const allowedData = (0, import_utils13.pick)(tdata, subUpdateSelect);
|
|
1479
1965
|
Object.assign(subdoc, allowedData);
|
|
1480
1966
|
});
|
|
1481
1967
|
await parentDoc.save();
|
|
1482
|
-
if (subReadSelect) result = result.map((v) => (0,
|
|
1968
|
+
if (subReadSelect) result = result.map((v) => (0, import_utils13.pick)(toObject(v), subReadSelect.concat(["_id"])));
|
|
1483
1969
|
return { success: true, kind: "list", code: "success" /* Success */, data: result, count: result.length };
|
|
1484
1970
|
}
|
|
1485
1971
|
async function createSub(service, id, sub, data, options) {
|
|
1486
1972
|
const { addFirst } = options ?? {};
|
|
1973
|
+
const { maxBulkItems } = service.getRequestComplexity();
|
|
1974
|
+
if (Array.isArray(data) && data.length > maxBulkItems) {
|
|
1975
|
+
return {
|
|
1976
|
+
success: false,
|
|
1977
|
+
kind: "error",
|
|
1978
|
+
code: "bad_request" /* BadRequest */,
|
|
1979
|
+
errors: [{ detail: `Bulk subdocument create exceeds maximum item count of ${maxBulkItems}` }]
|
|
1980
|
+
};
|
|
1981
|
+
}
|
|
1487
1982
|
const parentDoc = await getParentDoc(service, id, sub, null, { access: "update" });
|
|
1488
|
-
if (!parentDoc) return { success: false, code: "not_found" /* NotFound */ };
|
|
1489
|
-
let result = (0,
|
|
1983
|
+
if (!parentDoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
1984
|
+
let result = (0, import_utils13.get)(parentDoc, sub);
|
|
1490
1985
|
const [subCreateSelect, subReadSelect] = await Promise.all([
|
|
1491
1986
|
service.genQuerySelect("create", null, false, [sub, "sub"]),
|
|
1492
1987
|
service.genQuerySelect("read", null, false, [sub, "sub"])
|
|
1493
1988
|
]);
|
|
1494
|
-
const allowedData = (0,
|
|
1495
|
-
|
|
1989
|
+
const allowedData = Array.isArray(data) ? data.map((row) => (0, import_utils13.pick)(row, subCreateSelect)) : (0, import_utils13.pick)(data, subCreateSelect);
|
|
1990
|
+
if (Array.isArray(allowedData)) {
|
|
1991
|
+
addFirst === true ? result.unshift(...allowedData) : result.push(...allowedData);
|
|
1992
|
+
} else {
|
|
1993
|
+
addFirst === true ? result.unshift(allowedData) : result.push(allowedData);
|
|
1994
|
+
}
|
|
1496
1995
|
await parentDoc.save();
|
|
1497
|
-
if (subReadSelect) result = result.map((v) => (0,
|
|
1996
|
+
if (subReadSelect) result = result.map((v) => (0, import_utils13.pick)(toObject(v), subReadSelect.concat(["_id"])));
|
|
1498
1997
|
return { success: true, kind: "list", code: "created" /* Created */, data: result, count: result.length };
|
|
1499
1998
|
}
|
|
1500
1999
|
async function deleteSub(service, id, sub, subId) {
|
|
1501
2000
|
const parentDoc = await getParentDoc(service, id, sub, null, { access: "update" });
|
|
1502
|
-
if (!parentDoc) return { success: false, code: "not_found" /* NotFound */ };
|
|
1503
|
-
const result = (0,
|
|
2001
|
+
if (!parentDoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
2002
|
+
const result = (0, import_utils13.get)(parentDoc, sub);
|
|
1504
2003
|
const subFilter = await service.genFilter(`subs.${sub}.delete`, { _id: subId });
|
|
1505
|
-
if (subFilter === false) return { success: false, code: "forbidden" /* Forbidden */ };
|
|
2004
|
+
if (subFilter === false) return { success: false, kind: "error", code: "forbidden" /* Forbidden */ };
|
|
1506
2005
|
const subdoc = findElement(result, subFilter);
|
|
1507
|
-
if (!subdoc) return { success: false, code: "not_found" /* NotFound */ };
|
|
2006
|
+
if (!subdoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
1508
2007
|
await ("deleteOne" in subdoc ? subdoc.deleteOne?.() : subdoc.remove?.());
|
|
1509
2008
|
await parentDoc.save();
|
|
1510
2009
|
return { success: true, kind: "single", code: "success" /* Success */, data: subdoc._id };
|
|
@@ -1514,7 +2013,7 @@ async function getParentDoc(service, id, sub, args, options) {
|
|
|
1514
2013
|
const { access = "read", lean = false } = options ?? {};
|
|
1515
2014
|
const parentFilter = await service.genFilter(access, await service.genIDFilter(id));
|
|
1516
2015
|
if (parentFilter === false) return null;
|
|
1517
|
-
return service.
|
|
2016
|
+
return service.findRawParentDoc({ filter: parentFilter, select: sub, populate: genSubPopulate(sub, populate), lean });
|
|
1518
2017
|
}
|
|
1519
2018
|
|
|
1520
2019
|
// src/services/model-service-defaults.ts
|
|
@@ -1640,10 +2139,53 @@ var assertModelDocument = (value, modelName, hookName) => {
|
|
|
1640
2139
|
}
|
|
1641
2140
|
throw new Error(`${hookName} hook for model=${modelName} must return a Mongoose document instance`);
|
|
1642
2141
|
};
|
|
2142
|
+
var mapWithConcurrencyLimit = async (items, limit, iteratee) => {
|
|
2143
|
+
const results = new Array(items.length);
|
|
2144
|
+
let cursor = 0;
|
|
2145
|
+
const workerCount = Math.min(Math.max(limit, 1), items.length || 1);
|
|
2146
|
+
await Promise.all(
|
|
2147
|
+
Array.from({ length: workerCount }, async () => {
|
|
2148
|
+
while (cursor < items.length) {
|
|
2149
|
+
const current = cursor;
|
|
2150
|
+
cursor += 1;
|
|
2151
|
+
results[current] = await iteratee(items[current], current);
|
|
2152
|
+
}
|
|
2153
|
+
})
|
|
2154
|
+
);
|
|
2155
|
+
return results;
|
|
2156
|
+
};
|
|
1643
2157
|
var Service = class extends Base {
|
|
2158
|
+
findRawParentDoc(args) {
|
|
2159
|
+
return this.model.findOne({
|
|
2160
|
+
...args,
|
|
2161
|
+
filter: args.filter,
|
|
2162
|
+
populate: args.populate
|
|
2163
|
+
});
|
|
2164
|
+
}
|
|
1644
2165
|
asServiceHookContext(context) {
|
|
1645
2166
|
return context;
|
|
1646
2167
|
}
|
|
2168
|
+
beginOp(op, filter2, extra) {
|
|
2169
|
+
const startedAt = Date.now();
|
|
2170
|
+
debug({
|
|
2171
|
+
op,
|
|
2172
|
+
modelName: this.modelName,
|
|
2173
|
+
filterKeyValueCount: summarizeFilter(filter2).filterKeyValueCount,
|
|
2174
|
+
startedAt,
|
|
2175
|
+
...extra ?? {}
|
|
2176
|
+
});
|
|
2177
|
+
return startedAt;
|
|
2178
|
+
}
|
|
2179
|
+
completeOp(op, startedAt, resultCode, filter2, extra) {
|
|
2180
|
+
debug({
|
|
2181
|
+
op,
|
|
2182
|
+
modelName: this.modelName,
|
|
2183
|
+
filterKeyValueCount: summarizeFilter(filter2).filterKeyValueCount,
|
|
2184
|
+
durationMs: Date.now() - startedAt,
|
|
2185
|
+
resultCode,
|
|
2186
|
+
...extra ?? {}
|
|
2187
|
+
});
|
|
2188
|
+
}
|
|
1647
2189
|
constructor(req, modelName) {
|
|
1648
2190
|
super(req, modelName);
|
|
1649
2191
|
this.model = new model_default(modelName);
|
|
@@ -1654,12 +2196,20 @@ var Service = class extends Base {
|
|
|
1654
2196
|
}
|
|
1655
2197
|
async findOne(filter2, args, options) {
|
|
1656
2198
|
const filterErrors = this.validateClientFilter(filter2);
|
|
1657
|
-
if (filterErrors.length > 0) return { success: false, code: "bad_request" /* BadRequest */, errors: filterErrors };
|
|
2199
|
+
if (filterErrors.length > 0) return { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors: filterErrors };
|
|
1658
2200
|
const { select, sort, populate, include, overrides } = this.resolveFindOneArgs(args);
|
|
1659
2201
|
const { skim, includePermissions, access, populateAccess, lean } = this.resolveFindOneOptions(options);
|
|
1660
2202
|
const { filter: overrideFilter, select: overrideSelect, populate: overridePopulate } = overrides ?? {};
|
|
2203
|
+
let parsedFilter;
|
|
2204
|
+
try {
|
|
2205
|
+
parsedFilter = await this.parseClientData(filter2);
|
|
2206
|
+
} catch (error2) {
|
|
2207
|
+
const result = this.getClientRequestErrorResult(error2);
|
|
2208
|
+
if (result) return result;
|
|
2209
|
+
throw error2;
|
|
2210
|
+
}
|
|
1661
2211
|
let [_filter, _select, _populate] = await Promise.all([
|
|
1662
|
-
overrideFilter || this.genFilter(access,
|
|
2212
|
+
overrideFilter || this.genFilter(access, parsedFilter),
|
|
1663
2213
|
overrideSelect || this.genQuerySelect(access, select),
|
|
1664
2214
|
overridePopulate || this.genPopulate(populateAccess || access, populate)
|
|
1665
2215
|
]);
|
|
@@ -1671,10 +2221,20 @@ var Service = class extends Base {
|
|
|
1671
2221
|
sort,
|
|
1672
2222
|
populate: _populate
|
|
1673
2223
|
};
|
|
1674
|
-
|
|
1675
|
-
|
|
2224
|
+
const startedAt = this.beginOp("findOne", _filter, {
|
|
2225
|
+
sort,
|
|
2226
|
+
selectCount: finalSelect.length,
|
|
2227
|
+
populateCount: Array.isArray(_populate) ? _populate.length : _populate ? 1 : 0
|
|
2228
|
+
});
|
|
2229
|
+
if (_filter === false) {
|
|
2230
|
+
this.completeOp("findOne", startedAt, "forbidden" /* Forbidden */, _filter);
|
|
2231
|
+
return { success: false, kind: "error", code: "forbidden" /* Forbidden */, query };
|
|
2232
|
+
}
|
|
1676
2233
|
let doc = await this.model.findOne({ ...query, lean });
|
|
1677
|
-
if (!doc)
|
|
2234
|
+
if (!doc) {
|
|
2235
|
+
this.completeOp("findOne", startedAt, "not_found" /* NotFound */, _filter);
|
|
2236
|
+
return { success: false, kind: "error", code: "not_found" /* NotFound */, query };
|
|
2237
|
+
}
|
|
1678
2238
|
const context = {
|
|
1679
2239
|
mongooseModel: this.model.model,
|
|
1680
2240
|
modelName: this.modelName,
|
|
@@ -1682,7 +2242,16 @@ var Service = class extends Base {
|
|
|
1682
2242
|
originalDocumentSnapshot: toObject(doc),
|
|
1683
2243
|
resolvedQuery: query
|
|
1684
2244
|
};
|
|
1685
|
-
|
|
2245
|
+
try {
|
|
2246
|
+
doc = await this.includeDocs(doc, includes);
|
|
2247
|
+
} catch (error2) {
|
|
2248
|
+
const result = this.getClientRequestErrorResult(error2);
|
|
2249
|
+
if (result) {
|
|
2250
|
+
this.completeOp("findOne", startedAt, result.code, _filter);
|
|
2251
|
+
return { ...result, query };
|
|
2252
|
+
}
|
|
2253
|
+
throw error2;
|
|
2254
|
+
}
|
|
1686
2255
|
let includeDocPermissions = includePermissions;
|
|
1687
2256
|
if (!includeDocPermissions && !skim) {
|
|
1688
2257
|
includeDocPermissions = this.checkIfModelPermissionExists([access, "read", "update"]);
|
|
@@ -1695,6 +2264,7 @@ var Service = class extends Base {
|
|
|
1695
2264
|
this.baseFieldsExt.concat(includePaths, normalizeSelect(overrideSelect))
|
|
1696
2265
|
);
|
|
1697
2266
|
if (!includePermissions) doc = this.addEmptyPermissions(doc);
|
|
2267
|
+
this.completeOp("findOne", startedAt, "success" /* Success */, _filter);
|
|
1698
2268
|
return { success: true, kind: "single", code: "success" /* Success */, data: doc, query, context };
|
|
1699
2269
|
}
|
|
1700
2270
|
async findById(id, args, options) {
|
|
@@ -1718,18 +2288,26 @@ var Service = class extends Base {
|
|
|
1718
2288
|
}
|
|
1719
2289
|
async find(filter2, args, options, decorate) {
|
|
1720
2290
|
const filterErrors = this.validateClientFilter(filter2);
|
|
1721
|
-
if (filterErrors.length > 0) return { success: false, code: "bad_request" /* BadRequest */, errors: filterErrors };
|
|
2291
|
+
if (filterErrors.length > 0) return { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors: filterErrors };
|
|
1722
2292
|
const { select, populate, include, sort, skip, limit, page, pageSize, overrides } = this.resolveFindArgs(args);
|
|
1723
2293
|
const { skim, includePermissions, includeCount, populateAccess, lean } = this.resolveFindOptions(options);
|
|
1724
2294
|
const { filter: overrideFilter, select: overrideSelect, populate: overridePopulate } = overrides ?? {};
|
|
2295
|
+
let parsedFilter;
|
|
2296
|
+
try {
|
|
2297
|
+
parsedFilter = await this.parseClientData(filter2);
|
|
2298
|
+
} catch (error2) {
|
|
2299
|
+
const result = this.getClientRequestErrorResult(error2);
|
|
2300
|
+
if (result) return result;
|
|
2301
|
+
throw error2;
|
|
2302
|
+
}
|
|
1725
2303
|
const [_filter, _select, _populate, pagination] = await Promise.all([
|
|
1726
|
-
overrideFilter || this.genFilter("list",
|
|
2304
|
+
overrideFilter || this.genFilter("list", parsedFilter),
|
|
1727
2305
|
overrideSelect || this.genQuerySelect("list", select),
|
|
1728
2306
|
overridePopulate || this.genPopulate(populateAccess, populate),
|
|
1729
2307
|
genPagination({ skip, limit, page, pageSize }, this.options.listHardLimit)
|
|
1730
2308
|
]);
|
|
1731
2309
|
const finalSelect = normalizeSelect(_select);
|
|
1732
|
-
const filteredPopulate = (0,
|
|
2310
|
+
const filteredPopulate = (0, import_utils14.isArray)(finalSelect) && (0, import_utils14.isArray)(_populate) ? _populate.filter((p) => finalSelect.includes(p.path.split(".")[0])) : _populate;
|
|
1733
2311
|
const { includes, includeLocalFields, includePaths } = this.processInclude(include);
|
|
1734
2312
|
const query = {
|
|
1735
2313
|
filter: _filter,
|
|
@@ -1738,10 +2316,20 @@ var Service = class extends Base {
|
|
|
1738
2316
|
sort,
|
|
1739
2317
|
...pagination
|
|
1740
2318
|
};
|
|
1741
|
-
|
|
1742
|
-
|
|
2319
|
+
const startedAt = this.beginOp("find", _filter, {
|
|
2320
|
+
sort,
|
|
2321
|
+
skip: pagination.skip,
|
|
2322
|
+
limit: pagination.limit,
|
|
2323
|
+
selectCount: finalSelect.concat(includeLocalFields).length,
|
|
2324
|
+
populateCount: Array.isArray(filteredPopulate) ? filteredPopulate.length : filteredPopulate ? 1 : 0
|
|
2325
|
+
});
|
|
2326
|
+
if (_filter === false) {
|
|
2327
|
+
this.completeOp("find", startedAt, "forbidden" /* Forbidden */, _filter);
|
|
2328
|
+
return { success: false, kind: "error", code: "forbidden" /* Forbidden */, query };
|
|
2329
|
+
}
|
|
1743
2330
|
let docs = await this.model.find({
|
|
1744
2331
|
...query,
|
|
2332
|
+
hardLimit: this.options.listHardLimit,
|
|
1745
2333
|
lean
|
|
1746
2334
|
});
|
|
1747
2335
|
const contexts = docs.map((doc) => ({
|
|
@@ -1751,8 +2339,17 @@ var Service = class extends Base {
|
|
|
1751
2339
|
originalDocumentSnapshot: toObject(doc),
|
|
1752
2340
|
resolvedQuery: query
|
|
1753
2341
|
}));
|
|
1754
|
-
const _decorate = (0,
|
|
1755
|
-
|
|
2342
|
+
const _decorate = (0, import_utils14.isFunction)(decorate) ? decorate : (v) => v;
|
|
2343
|
+
try {
|
|
2344
|
+
docs = await this.includeDocs(docs, includes);
|
|
2345
|
+
} catch (error2) {
|
|
2346
|
+
const result = this.getClientRequestErrorResult(error2);
|
|
2347
|
+
if (result) {
|
|
2348
|
+
this.completeOp("find", startedAt, result.code, _filter);
|
|
2349
|
+
return { ...result, query };
|
|
2350
|
+
}
|
|
2351
|
+
throw error2;
|
|
2352
|
+
}
|
|
1756
2353
|
const fieldPermissionAccess = includePermissions ? await this.getFieldPermissionAccess(docs.map((doc) => doc._id)) : void 0;
|
|
1757
2354
|
docs = await Promise.all(
|
|
1758
2355
|
docs.map(async (doc, i) => {
|
|
@@ -1773,6 +2370,7 @@ var Service = class extends Base {
|
|
|
1773
2370
|
return doc;
|
|
1774
2371
|
})
|
|
1775
2372
|
);
|
|
2373
|
+
this.completeOp("find", startedAt, "success" /* Success */, _filter);
|
|
1776
2374
|
return {
|
|
1777
2375
|
success: true,
|
|
1778
2376
|
kind: "list",
|
|
@@ -1789,67 +2387,96 @@ var Service = class extends Base {
|
|
|
1789
2387
|
const { skim, includePermissions, populateAccess } = this.resolveCreateOptions(options);
|
|
1790
2388
|
const isArr = Array.isArray(data);
|
|
1791
2389
|
let dataArr = isArr ? data : [data];
|
|
1792
|
-
|
|
2390
|
+
const { maxBulkItems, maxBulkConcurrency } = this.getRequestComplexity();
|
|
2391
|
+
if (dataArr.length > maxBulkItems) {
|
|
2392
|
+
return {
|
|
2393
|
+
success: false,
|
|
2394
|
+
kind: "error",
|
|
2395
|
+
code: "bad_request" /* BadRequest */,
|
|
2396
|
+
errors: [{ detail: `Bulk create exceeds maximum item count of ${maxBulkItems}` }]
|
|
2397
|
+
};
|
|
2398
|
+
}
|
|
2399
|
+
try {
|
|
2400
|
+
dataArr = await Promise.all(dataArr.map((d) => this.parseClientData(d)));
|
|
2401
|
+
} catch (error2) {
|
|
2402
|
+
const result = this.getClientRequestErrorResult(error2);
|
|
2403
|
+
if (result) return result;
|
|
2404
|
+
throw error2;
|
|
2405
|
+
}
|
|
1793
2406
|
const resolvedPopulate = populate ? await this.genPopulate(populateAccess, populate) : [];
|
|
1794
2407
|
const contexts = [];
|
|
1795
|
-
|
|
1796
|
-
const
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
if (
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
return;
|
|
1814
|
-
}
|
|
1815
|
-
} else if ((0, import_utils13.isArray)(validated)) {
|
|
1816
|
-
if (validated.length > 0) {
|
|
1817
|
-
validationError = { success: false, code: "bad_request" /* BadRequest */, errors: validated };
|
|
1818
|
-
return;
|
|
1819
|
-
}
|
|
2408
|
+
const validationErrors = [];
|
|
2409
|
+
const validationItems = await mapWithConcurrencyLimit(dataArr, maxBulkConcurrency, async (item, index) => {
|
|
2410
|
+
const context = {
|
|
2411
|
+
mongooseModel: this.model.model,
|
|
2412
|
+
modelName: this.modelName,
|
|
2413
|
+
operation: "create",
|
|
2414
|
+
originalData: item,
|
|
2415
|
+
resolvedQuery: resolvedPopulate.length > 0 ? { populate: resolvedPopulate } : {}
|
|
2416
|
+
};
|
|
2417
|
+
const allowedFields = await this.genAllowedFields(item, "create");
|
|
2418
|
+
const allowedData = (0, import_utils14.pick)(item, allowedFields);
|
|
2419
|
+
context.allowedFields = allowedFields;
|
|
2420
|
+
context.allowedData = allowedData;
|
|
2421
|
+
const validated = await this.validate(allowedData, "create", context);
|
|
2422
|
+
if ((0, import_utils14.isBoolean)(validated)) {
|
|
2423
|
+
if (!validated) {
|
|
2424
|
+
validationErrors.push({ index, errors: [] });
|
|
2425
|
+
return void 0;
|
|
1820
2426
|
}
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
const _decorate = (0, import_utils13.isFunction)(decorate) ? decorate : (v) => v;
|
|
1829
|
-
const createdDocs = await this.model.create(items);
|
|
1830
|
-
const docs = await Promise.all(
|
|
1831
|
-
createdDocs.map(async (doc, index) => {
|
|
1832
|
-
contexts[index].currentDocument = doc;
|
|
1833
|
-
doc = assertModelDocument(
|
|
1834
|
-
await this.afterPersist(doc, "create", contexts[index]),
|
|
1835
|
-
this.modelName,
|
|
1836
|
-
"afterPersist"
|
|
1837
|
-
);
|
|
1838
|
-
contexts[index].currentDocument = doc;
|
|
1839
|
-
contexts[index].finalDocumentSnapshot = doc.toObject({ virtuals: false });
|
|
1840
|
-
let includeDocPermissions = includePermissions;
|
|
1841
|
-
if (!includeDocPermissions && !skim) {
|
|
1842
|
-
includeDocPermissions = this.checkIfModelPermissionExists(["create", "read", "update"]);
|
|
2427
|
+
} else if ((0, import_utils14.isArray)(validated)) {
|
|
2428
|
+
if (validated.length > 0) {
|
|
2429
|
+
validationErrors.push({
|
|
2430
|
+
index,
|
|
2431
|
+
errors: isArr ? validated.map((issue) => this.formatBulkValidationIssue(issue, index)) : validated
|
|
2432
|
+
});
|
|
2433
|
+
return void 0;
|
|
1843
2434
|
}
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
2435
|
+
}
|
|
2436
|
+
contexts[index] = context;
|
|
2437
|
+
return allowedData;
|
|
2438
|
+
});
|
|
2439
|
+
if (validationErrors.length > 0) {
|
|
2440
|
+
const aggregate = validationErrors.slice().sort((a, b) => a.index - b.index).flatMap((entry) => entry.errors);
|
|
2441
|
+
if (isArr) {
|
|
2442
|
+
return { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors: aggregate };
|
|
2443
|
+
}
|
|
2444
|
+
const single = validationErrors[0];
|
|
2445
|
+
return {
|
|
2446
|
+
success: false,
|
|
2447
|
+
kind: "error",
|
|
2448
|
+
code: "bad_request" /* BadRequest */,
|
|
2449
|
+
errors: single?.errors ?? []
|
|
2450
|
+
};
|
|
2451
|
+
}
|
|
2452
|
+
const items = await mapWithConcurrencyLimit(validationItems, maxBulkConcurrency, async (allowedData, index) => {
|
|
2453
|
+
const preparedData = await this.prepare(allowedData, "create", contexts[index]);
|
|
2454
|
+
contexts[index].preparedData = preparedData;
|
|
2455
|
+
return preparedData;
|
|
2456
|
+
});
|
|
2457
|
+
const _decorate = (0, import_utils14.isFunction)(decorate) ? decorate : (v) => v;
|
|
2458
|
+
const createdDocs = await this.model.create(items);
|
|
2459
|
+
const docs = await mapWithConcurrencyLimit(createdDocs, maxBulkConcurrency, async (doc, index) => {
|
|
2460
|
+
contexts[index].currentDocument = doc;
|
|
2461
|
+
doc = assertModelDocument(
|
|
2462
|
+
await this.afterPersist(doc, "create", contexts[index]),
|
|
2463
|
+
this.modelName,
|
|
2464
|
+
"afterPersist"
|
|
2465
|
+
);
|
|
2466
|
+
contexts[index].currentDocument = doc;
|
|
2467
|
+
contexts[index].finalDocumentSnapshot = doc.toObject({ virtuals: false });
|
|
2468
|
+
let includeDocPermissions = includePermissions;
|
|
2469
|
+
if (!includeDocPermissions && !skim) {
|
|
2470
|
+
includeDocPermissions = this.checkIfModelPermissionExists(["create", "read", "update"]);
|
|
2471
|
+
}
|
|
2472
|
+
if (includeDocPermissions) doc = await this.addDocPermissions(doc, "create", contexts[index]);
|
|
2473
|
+
if (includePermissions) doc = await this.addFieldPermissions(doc, "read", contexts[index]);
|
|
2474
|
+
if (resolvedPopulate.length > 0) await populateDoc(doc, resolvedPopulate);
|
|
2475
|
+
doc = await this.trimOutputFields(doc, "read", this.baseFieldsExt);
|
|
2476
|
+
let outputDoc = await _decorate(doc, contexts[index]);
|
|
2477
|
+
if (!includePermissions) outputDoc = this.addEmptyPermissions(outputDoc);
|
|
2478
|
+
return outputDoc;
|
|
2479
|
+
});
|
|
1853
2480
|
return {
|
|
1854
2481
|
success: true,
|
|
1855
2482
|
kind: "list",
|
|
@@ -1859,18 +2486,50 @@ var Service = class extends Base {
|
|
|
1859
2486
|
count: docs.length
|
|
1860
2487
|
};
|
|
1861
2488
|
}
|
|
1862
|
-
|
|
2489
|
+
formatBulkValidationIssue(issue, index) {
|
|
2490
|
+
if (!issue || typeof issue !== "object") {
|
|
2491
|
+
return {
|
|
2492
|
+
detail: typeof issue === "string" && issue.length > 0 ? issue : "Bad Request",
|
|
2493
|
+
...index === null ? {} : { pointer: `#/${index}` }
|
|
2494
|
+
};
|
|
2495
|
+
}
|
|
2496
|
+
const typedIssue = issue;
|
|
2497
|
+
const detail = typedIssue.detail ?? typedIssue.message ?? "Bad Request";
|
|
2498
|
+
if (index === null) {
|
|
2499
|
+
return typedIssue.pointer || typedIssue.path ? {
|
|
2500
|
+
detail,
|
|
2501
|
+
...typedIssue.pointer ? { pointer: typedIssue.pointer } : {}
|
|
2502
|
+
} : { detail };
|
|
2503
|
+
}
|
|
2504
|
+
if (typedIssue.pointer?.startsWith("#/")) {
|
|
2505
|
+
return { ...typedIssue, detail, pointer: `#/${index}${typedIssue.pointer.slice(1)}` };
|
|
2506
|
+
}
|
|
2507
|
+
if (typedIssue.path) {
|
|
2508
|
+
return { ...typedIssue, detail, pointer: `#/${[index, ...typedIssue.path].join("/")}` };
|
|
2509
|
+
}
|
|
2510
|
+
return { ...typedIssue, detail, pointer: `#/${index}` };
|
|
2511
|
+
}
|
|
2512
|
+
async new(args, options) {
|
|
2513
|
+
const { skim, includePermissions } = options ?? {};
|
|
1863
2514
|
const data = await this.model.new();
|
|
2515
|
+
let doc = data;
|
|
2516
|
+
doc = await this.trimOutputFields(doc, "create", this.baseFieldsExt);
|
|
2517
|
+
let includeDocPermissions = includePermissions;
|
|
2518
|
+
if (!includeDocPermissions && !skim) {
|
|
2519
|
+
includeDocPermissions = this.checkIfModelPermissionExists(["create", "read", "update"]);
|
|
2520
|
+
}
|
|
2521
|
+
if (includeDocPermissions) doc = await this.addDocPermissions(doc, "create", {});
|
|
2522
|
+
if (!includePermissions) doc = this.addEmptyPermissions(doc);
|
|
1864
2523
|
return {
|
|
1865
2524
|
success: true,
|
|
1866
2525
|
kind: "single",
|
|
1867
2526
|
code: "success" /* Success */,
|
|
1868
|
-
data
|
|
2527
|
+
data: doc
|
|
1869
2528
|
};
|
|
1870
2529
|
}
|
|
1871
2530
|
async updateOne(filter2, data, args, options, decorate) {
|
|
1872
2531
|
const filterErrors = this.validateClientFilter(filter2);
|
|
1873
|
-
if (filterErrors.length > 0) return { success: false, code: "bad_request" /* BadRequest */, errors: filterErrors };
|
|
2532
|
+
if (filterErrors.length > 0) return { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors: filterErrors };
|
|
1874
2533
|
const { populate, overrides } = this.resolveUpdateOneArgs(args);
|
|
1875
2534
|
const { skim, includePermissions, populateAccess } = this.resolveUpdateOneOptions(options);
|
|
1876
2535
|
const { filter: overrideFilter, populate: overridePopulate } = overrides ?? {};
|
|
@@ -1879,31 +2538,54 @@ var Service = class extends Base {
|
|
|
1879
2538
|
overridePopulate || this.genPopulate(populateAccess, populate)
|
|
1880
2539
|
]);
|
|
1881
2540
|
const query = { filter: _filter, populate: _populate };
|
|
1882
|
-
|
|
1883
|
-
|
|
2541
|
+
const startedAt = this.beginOp("updateOne", _filter, {
|
|
2542
|
+
populateCount: Array.isArray(_populate) ? _populate.length : _populate ? 1 : 0
|
|
2543
|
+
});
|
|
2544
|
+
if (_filter === false) {
|
|
2545
|
+
this.completeOp("updateOne", startedAt, "forbidden" /* Forbidden */, _filter);
|
|
2546
|
+
return { success: false, kind: "error", code: "forbidden" /* Forbidden */, query };
|
|
2547
|
+
}
|
|
1884
2548
|
let doc = await this.model.findOne({ filter: _filter });
|
|
1885
|
-
if (!doc)
|
|
2549
|
+
if (!doc) {
|
|
2550
|
+
this.completeOp("updateOne", startedAt, "not_found" /* NotFound */, _filter);
|
|
2551
|
+
return { success: false, kind: "error", code: "not_found" /* NotFound */, query };
|
|
2552
|
+
}
|
|
1886
2553
|
const context = {
|
|
1887
2554
|
mongooseModel: this.model.model,
|
|
1888
2555
|
modelName: this.modelName,
|
|
1889
2556
|
operation: "update",
|
|
1890
2557
|
resolvedQuery: query
|
|
1891
2558
|
};
|
|
1892
|
-
|
|
2559
|
+
try {
|
|
2560
|
+
data = await this.parseClientData(data);
|
|
2561
|
+
} catch (error2) {
|
|
2562
|
+
const result = this.getClientRequestErrorResult(error2);
|
|
2563
|
+
if (result) {
|
|
2564
|
+
this.completeOp("updateOne", startedAt, result.code, _filter);
|
|
2565
|
+
return result;
|
|
2566
|
+
}
|
|
2567
|
+
throw error2;
|
|
2568
|
+
}
|
|
1893
2569
|
context.originalDocumentSnapshot = doc.toObject({ virtuals: false });
|
|
1894
2570
|
context.originalData = data;
|
|
1895
2571
|
doc = await this.addDocPermissions(doc, "update", context);
|
|
1896
2572
|
context.docPermissions = this.getDocPermissions(doc);
|
|
1897
2573
|
context.currentDocument = doc;
|
|
1898
2574
|
const allowedFields = await this.genAllowedFields(doc, "update");
|
|
1899
|
-
const allowedData = (0,
|
|
2575
|
+
const allowedData = (0, import_utils14.pick)(data, allowedFields);
|
|
1900
2576
|
context.allowedFields = allowedFields;
|
|
1901
2577
|
context.allowedData = allowedData;
|
|
1902
2578
|
const validated = await this.validate(allowedData, "update", context);
|
|
1903
|
-
if ((0,
|
|
1904
|
-
if (!validated)
|
|
1905
|
-
|
|
1906
|
-
|
|
2579
|
+
if ((0, import_utils14.isBoolean)(validated)) {
|
|
2580
|
+
if (!validated) {
|
|
2581
|
+
this.completeOp("updateOne", startedAt, "bad_request" /* BadRequest */, _filter);
|
|
2582
|
+
return { success: false, kind: "error", code: "bad_request" /* BadRequest */ };
|
|
2583
|
+
}
|
|
2584
|
+
} else if ((0, import_utils14.isArray)(validated)) {
|
|
2585
|
+
if (validated.length > 0) {
|
|
2586
|
+
this.completeOp("updateOne", startedAt, "bad_request" /* BadRequest */, _filter);
|
|
2587
|
+
return { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors: validated };
|
|
2588
|
+
}
|
|
1907
2589
|
}
|
|
1908
2590
|
const prepared = await this.prepare(allowedData, "update", context);
|
|
1909
2591
|
context.preparedData = prepared;
|
|
@@ -1915,10 +2597,10 @@ var Service = class extends Base {
|
|
|
1915
2597
|
const diffExcludeFields = [this.options.documentPermissionField, "__v"];
|
|
1916
2598
|
this.asServiceHookContext(context).diff = (d) => {
|
|
1917
2599
|
context.changes = (0, import_just_diff.diff)(
|
|
1918
|
-
(0,
|
|
1919
|
-
(0,
|
|
2600
|
+
(0, import_utils14.omit)(context.originalDocumentSnapshot, diffExcludeFields),
|
|
2601
|
+
(0, import_utils14.omit)(d.toObject({ virtuals: false }), diffExcludeFields)
|
|
1920
2602
|
) || [];
|
|
1921
|
-
context.modifiedPaths = (0,
|
|
2603
|
+
context.modifiedPaths = (0, import_utils14.uniq)(context.changes.map((di) => di.path.length > 0 ? String(di.path[0]) : ""));
|
|
1922
2604
|
};
|
|
1923
2605
|
doc = assertModelDocument(await this.afterPersist(doc, "update", context), this.modelName, "afterPersist");
|
|
1924
2606
|
context.currentDocument = doc;
|
|
@@ -1934,8 +2616,9 @@ var Service = class extends Base {
|
|
|
1934
2616
|
if (_populate) await populateDoc(doc, _populate);
|
|
1935
2617
|
doc = await this.trimOutputFields(doc, "read", this.baseFieldsExt);
|
|
1936
2618
|
let outputDoc = doc;
|
|
1937
|
-
if ((0,
|
|
2619
|
+
if ((0, import_utils14.isFunction)(decorate)) outputDoc = await decorate(outputDoc, context);
|
|
1938
2620
|
if (!includePermissions) outputDoc = this.addEmptyPermissions(outputDoc);
|
|
2621
|
+
this.completeOp("updateOne", startedAt, "success" /* Success */, _filter);
|
|
1939
2622
|
return { success: true, kind: "single", code: "success" /* Success */, data: outputDoc, input: prepared };
|
|
1940
2623
|
}
|
|
1941
2624
|
async updateById(id, data, args = {}, options = {}, decorate) {
|
|
@@ -1958,17 +2641,21 @@ var Service = class extends Base {
|
|
|
1958
2641
|
}
|
|
1959
2642
|
async upsert(filter2, data, args, options, decorate) {
|
|
1960
2643
|
const filterErrors = this.validateClientFilter(filter2);
|
|
1961
|
-
if (filterErrors.length > 0) return { success: false, code: "bad_request" /* BadRequest */, errors: filterErrors };
|
|
2644
|
+
if (filterErrors.length > 0) return { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors: filterErrors };
|
|
1962
2645
|
const { populate, overrides } = this.resolveUpsertArgs(args);
|
|
1963
2646
|
const { skim, includePermissions, populateAccess } = this.resolveUpsertOptions(options);
|
|
1964
2647
|
const { filter: overrideFilter, populate: overridePopulate } = overrides ?? {};
|
|
1965
2648
|
const _filter = await (overrideFilter || this.genFilter("update", filter2));
|
|
1966
2649
|
const query = { filter: _filter };
|
|
1967
|
-
|
|
1968
|
-
if (_filter === false)
|
|
2650
|
+
const startedAt = this.beginOp("upsert", _filter);
|
|
2651
|
+
if (_filter === false) {
|
|
2652
|
+
this.completeOp("upsert", startedAt, "forbidden" /* Forbidden */, _filter);
|
|
2653
|
+
return { success: false, kind: "error", code: "forbidden" /* Forbidden */, query };
|
|
2654
|
+
}
|
|
1969
2655
|
const theone = await this.model.findOne({ filter: _filter });
|
|
2656
|
+
let result;
|
|
1970
2657
|
if (theone) {
|
|
1971
|
-
|
|
2658
|
+
result = await this.updateOne(
|
|
1972
2659
|
null,
|
|
1973
2660
|
data,
|
|
1974
2661
|
{
|
|
@@ -1982,7 +2669,7 @@ var Service = class extends Base {
|
|
|
1982
2669
|
decorate
|
|
1983
2670
|
);
|
|
1984
2671
|
} else {
|
|
1985
|
-
|
|
2672
|
+
result = await this.create(
|
|
1986
2673
|
data,
|
|
1987
2674
|
{ populate },
|
|
1988
2675
|
{
|
|
@@ -1993,14 +2680,22 @@ var Service = class extends Base {
|
|
|
1993
2680
|
decorate
|
|
1994
2681
|
);
|
|
1995
2682
|
}
|
|
2683
|
+
this.completeOp("upsert", startedAt, result.code, _filter);
|
|
2684
|
+
return result;
|
|
1996
2685
|
}
|
|
1997
2686
|
async delete(id) {
|
|
1998
2687
|
const filter2 = await this.genFilter("delete", await this.genIDFilter(id));
|
|
1999
2688
|
const query = { filter: filter2 };
|
|
2000
|
-
|
|
2001
|
-
if (filter2 === false)
|
|
2689
|
+
const startedAt = this.beginOp("delete", filter2);
|
|
2690
|
+
if (filter2 === false) {
|
|
2691
|
+
this.completeOp("delete", startedAt, "forbidden" /* Forbidden */, filter2);
|
|
2692
|
+
return { success: false, kind: "error", code: "forbidden" /* Forbidden */, query };
|
|
2693
|
+
}
|
|
2002
2694
|
let doc = await this.model.findOne({ filter: filter2 });
|
|
2003
|
-
if (!doc)
|
|
2695
|
+
if (!doc) {
|
|
2696
|
+
this.completeOp("delete", startedAt, "not_found" /* NotFound */, filter2);
|
|
2697
|
+
return { success: false, kind: "error", code: "not_found" /* NotFound */, query };
|
|
2698
|
+
}
|
|
2004
2699
|
const context = {
|
|
2005
2700
|
mongooseModel: this.model.model,
|
|
2006
2701
|
modelName: this.modelName,
|
|
@@ -2013,11 +2708,12 @@ var Service = class extends Base {
|
|
|
2013
2708
|
await ("deleteOne" in doc ? doc.deleteOne() : doc.remove());
|
|
2014
2709
|
context.finalDocumentSnapshot = toObject(doc);
|
|
2015
2710
|
await this.afterDelete(doc, context);
|
|
2711
|
+
this.completeOp("delete", startedAt, "success" /* Success */, filter2);
|
|
2016
2712
|
return { success: true, kind: "single", code: "success" /* Success */, data: doc._id, query };
|
|
2017
2713
|
}
|
|
2018
2714
|
async exists(filter2, options) {
|
|
2019
2715
|
const filterErrors = this.validateClientFilter(filter2);
|
|
2020
|
-
if (filterErrors.length > 0) return { success: false, code: "bad_request" /* BadRequest */, errors: filterErrors };
|
|
2716
|
+
if (filterErrors.length > 0) return { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors: filterErrors };
|
|
2021
2717
|
const { access, includeId } = this.resolveExistsOptions(options);
|
|
2022
2718
|
filter2 = await this.genFilter(access, filter2);
|
|
2023
2719
|
const result = await this.model.exists(filter2);
|
|
@@ -2029,22 +2725,58 @@ var Service = class extends Base {
|
|
|
2029
2725
|
query: { filter: filter2 }
|
|
2030
2726
|
};
|
|
2031
2727
|
}
|
|
2728
|
+
isValidDistinctFieldName(field) {
|
|
2729
|
+
if (typeof field !== "string" || field.length === 0) return false;
|
|
2730
|
+
if (field.includes("$")) return false;
|
|
2731
|
+
if (field.includes("..")) return false;
|
|
2732
|
+
if (field.startsWith(".") || field.endsWith(".")) return false;
|
|
2733
|
+
if (/\s/.test(field)) return false;
|
|
2734
|
+
return true;
|
|
2735
|
+
}
|
|
2736
|
+
async authorizeDistinctField(field) {
|
|
2737
|
+
const allowedFields = await this.genAllowedFields(null, "read");
|
|
2738
|
+
const isAllowed = allowedFields.some((allowed) => {
|
|
2739
|
+
if (allowed === field) return true;
|
|
2740
|
+
if (allowed.startsWith(`${field}.`)) return true;
|
|
2741
|
+
if (field.startsWith(`${allowed}.`)) return true;
|
|
2742
|
+
return false;
|
|
2743
|
+
});
|
|
2744
|
+
if (!isAllowed) {
|
|
2745
|
+
return {
|
|
2746
|
+
success: false,
|
|
2747
|
+
kind: "error",
|
|
2748
|
+
code: "forbidden" /* Forbidden */,
|
|
2749
|
+
errors: [{ detail: `Distinct field not allowed: ${field}` }]
|
|
2750
|
+
};
|
|
2751
|
+
}
|
|
2752
|
+
return null;
|
|
2753
|
+
}
|
|
2032
2754
|
async distinct(field, args) {
|
|
2755
|
+
if (!this.isValidDistinctFieldName(field)) {
|
|
2756
|
+
return {
|
|
2757
|
+
success: false,
|
|
2758
|
+
kind: "error",
|
|
2759
|
+
code: "bad_request" /* BadRequest */,
|
|
2760
|
+
errors: [{ detail: `Invalid distinct field: ${field}` }]
|
|
2761
|
+
};
|
|
2762
|
+
}
|
|
2763
|
+
const fieldError = await this.authorizeDistinctField(field);
|
|
2764
|
+
if (fieldError) return fieldError;
|
|
2033
2765
|
let { filter: filter2 } = args ?? {};
|
|
2034
2766
|
const filterErrors = this.validateClientFilter(filter2);
|
|
2035
|
-
if (filterErrors.length > 0) return { success: false, code: "bad_request" /* BadRequest */, errors: filterErrors };
|
|
2767
|
+
if (filterErrors.length > 0) return { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors: filterErrors };
|
|
2036
2768
|
filter2 = await this.genFilter("read", filter2);
|
|
2037
2769
|
const query = { filter: filter2 };
|
|
2038
|
-
if (filter2 === false) return { success: false, code: "forbidden" /* Forbidden */, query };
|
|
2770
|
+
if (filter2 === false) return { success: false, kind: "error", code: "forbidden" /* Forbidden */, query };
|
|
2039
2771
|
const result = await this.model.distinct(field, filter2);
|
|
2040
2772
|
return { success: true, kind: "list", code: "success" /* Success */, data: result, count: result.length, query };
|
|
2041
2773
|
}
|
|
2042
2774
|
async count(filter2, access = "list") {
|
|
2043
2775
|
const filterErrors = this.validateClientFilter(filter2);
|
|
2044
|
-
if (filterErrors.length > 0) return { success: false, code: "bad_request" /* BadRequest */, errors: filterErrors };
|
|
2776
|
+
if (filterErrors.length > 0) return { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors: filterErrors };
|
|
2045
2777
|
filter2 = await this.genFilter(access, filter2);
|
|
2046
2778
|
const query = { filter: filter2 };
|
|
2047
|
-
if (filter2 === false) return { success: false, code: "forbidden" /* Forbidden */, query };
|
|
2779
|
+
if (filter2 === false) return { success: false, kind: "error", code: "forbidden" /* Forbidden */, query };
|
|
2048
2780
|
return { success: true, kind: "single", code: "success" /* Success */, data: await this.model.countDocuments(filter2), query };
|
|
2049
2781
|
}
|
|
2050
2782
|
getDocPermissions(doc) {
|
|
@@ -2096,7 +2828,7 @@ var Service = class extends Base {
|
|
|
2096
2828
|
return resolveExistsOptions(this, options);
|
|
2097
2829
|
}
|
|
2098
2830
|
async getFieldPermissionAccess(ids) {
|
|
2099
|
-
const uniqueIds = (0,
|
|
2831
|
+
const uniqueIds = (0, import_utils14.compact)((0, import_utils14.uniqBy)(ids, (id) => String(id)).map((id) => String(id)));
|
|
2100
2832
|
if (uniqueIds.length === 0) {
|
|
2101
2833
|
return {
|
|
2102
2834
|
readIds: /* @__PURE__ */ new Set(),
|
|
@@ -2126,7 +2858,7 @@ var Service = class extends Base {
|
|
|
2126
2858
|
return updateSub(this, id, sub, subId, data);
|
|
2127
2859
|
}
|
|
2128
2860
|
async bulkUpdateSub(id, sub, data) {
|
|
2129
|
-
return bulkUpdateSub(this, id, sub, (0,
|
|
2861
|
+
return bulkUpdateSub(this, id, sub, (0, import_utils14.castArray)(data));
|
|
2130
2862
|
}
|
|
2131
2863
|
async createSub(id, sub, data, options) {
|
|
2132
2864
|
return createSub(this, id, sub, data, options);
|
|
@@ -2140,7 +2872,7 @@ var Service = class extends Base {
|
|
|
2140
2872
|
};
|
|
2141
2873
|
|
|
2142
2874
|
// src/services/public-service.ts
|
|
2143
|
-
var
|
|
2875
|
+
var import_utils15 = require("@web-ts-toolkit/utils");
|
|
2144
2876
|
var PublicService = class extends Service {
|
|
2145
2877
|
async _list(filter2, args, options) {
|
|
2146
2878
|
const { select, populate, include, sort, skip, limit, page, pageSize, tasks } = this.resolvePublicListArgs(args);
|
|
@@ -2176,11 +2908,11 @@ var PublicService = class extends Service {
|
|
|
2176
2908
|
{ populate },
|
|
2177
2909
|
{ skim, includePermissions, populateAccess },
|
|
2178
2910
|
async (doc, context) => {
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
if (select)
|
|
2183
|
-
return
|
|
2911
|
+
let d = toObject(doc);
|
|
2912
|
+
d = await this.decorate(d, "create", context);
|
|
2913
|
+
d = this.runTasks(d, tasks);
|
|
2914
|
+
if (select) d = (0, import_utils15.pick)(d, [...normalizeSelect(select), ...this.baseFieldsExt]);
|
|
2915
|
+
return d;
|
|
2184
2916
|
}
|
|
2185
2917
|
);
|
|
2186
2918
|
if (!result.success) {
|
|
@@ -2188,8 +2920,8 @@ var PublicService = class extends Service {
|
|
|
2188
2920
|
}
|
|
2189
2921
|
return result;
|
|
2190
2922
|
}
|
|
2191
|
-
async _new() {
|
|
2192
|
-
return this.new();
|
|
2923
|
+
async _new(args, options) {
|
|
2924
|
+
return this.new(args, options);
|
|
2193
2925
|
}
|
|
2194
2926
|
async _read(id, args, options) {
|
|
2195
2927
|
const { select, populate, include, tasks } = this.resolvePublicReadArgs(args);
|
|
@@ -2271,12 +3003,12 @@ var PublicService = class extends Service {
|
|
|
2271
3003
|
{ populate },
|
|
2272
3004
|
{ skim, includePermissions, populateAccess },
|
|
2273
3005
|
async (doc, context) => {
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
if (select)
|
|
2278
|
-
else if (!returningAll)
|
|
2279
|
-
return
|
|
3006
|
+
let d = toObject(doc);
|
|
3007
|
+
d = await this.decorate(d, "update", context);
|
|
3008
|
+
d = this.runTasks(d, tasks);
|
|
3009
|
+
if (select) d = (0, import_utils15.pick)(d, [...normalizeSelect(select), ...this.baseFieldsExt]);
|
|
3010
|
+
else if (!returningAll) d = (0, import_utils15.pick)(d, [...Object.keys(data), "_id"]);
|
|
3011
|
+
return d;
|
|
2280
3012
|
}
|
|
2281
3013
|
);
|
|
2282
3014
|
if (!result.success) {
|
|
@@ -2287,7 +3019,7 @@ var PublicService = class extends Service {
|
|
|
2287
3019
|
async _upsert(data, args, options) {
|
|
2288
3020
|
const idKey = this.getIdentifier();
|
|
2289
3021
|
if (idKey !== "_id") {
|
|
2290
|
-
return { success: false, code: "bad_request" /* BadRequest */, errors: ["not supported custom id field"] };
|
|
3022
|
+
return { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors: ["not supported custom id field"] };
|
|
2291
3023
|
}
|
|
2292
3024
|
const { [idKey]: idVal, ...otherData } = data;
|
|
2293
3025
|
const upsertId = typeof idVal === "string" ? idVal : void 0;
|
|
@@ -2297,7 +3029,7 @@ var PublicService = class extends Service {
|
|
|
2297
3029
|
return existing;
|
|
2298
3030
|
}
|
|
2299
3031
|
if (!existing.data) {
|
|
2300
|
-
return { success: false, code: "unauthorized" /* Unauthorized */, errors: ["Unauthorized"] };
|
|
3032
|
+
return { success: false, kind: "error", code: "unauthorized" /* Unauthorized */, errors: ["Unauthorized"] };
|
|
2301
3033
|
}
|
|
2302
3034
|
return this._update(upsertId, otherData, args, options);
|
|
2303
3035
|
}
|
|
@@ -2397,7 +3129,7 @@ var PublicService = class extends Service {
|
|
|
2397
3129
|
};
|
|
2398
3130
|
|
|
2399
3131
|
// src/services/data-service.ts
|
|
2400
|
-
var
|
|
3132
|
+
var import_utils16 = require("@web-ts-toolkit/utils");
|
|
2401
3133
|
var DataService = class {
|
|
2402
3134
|
constructor(req, dataName) {
|
|
2403
3135
|
this.req = req;
|
|
@@ -2407,7 +3139,7 @@ var DataService = class {
|
|
|
2407
3139
|
}
|
|
2408
3140
|
async findOne(filter2, args, options) {
|
|
2409
3141
|
const filterErrors = validateClientFilter(filter2);
|
|
2410
|
-
if (filterErrors.length > 0) return { success: false, code: "bad_request" /* BadRequest */, errors: filterErrors };
|
|
3142
|
+
if (filterErrors.length > 0) return { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors: filterErrors };
|
|
2411
3143
|
const { select } = args ?? {};
|
|
2412
3144
|
const { access = "read" } = options ?? {};
|
|
2413
3145
|
let [_filter, _select] = await Promise.all([this.genFilter(access, filter2), this.genQuerySelect(access, select)]);
|
|
@@ -2415,11 +3147,11 @@ var DataService = class {
|
|
|
2415
3147
|
filter: _filter,
|
|
2416
3148
|
select: _select
|
|
2417
3149
|
};
|
|
2418
|
-
if (_filter === false) return { success: false, code: "forbidden" /* Forbidden */, query };
|
|
3150
|
+
if (_filter === false) return { success: false, kind: "error", code: "forbidden" /* Forbidden */, query };
|
|
2419
3151
|
let doc = await findElement(this.data, _filter);
|
|
2420
|
-
if (!doc) return { success: false, code: "not_found" /* NotFound */, query };
|
|
3152
|
+
if (!doc) return { success: false, kind: "error", code: "not_found" /* NotFound */, query };
|
|
2421
3153
|
doc = await this.trimOutputFields(doc, access);
|
|
2422
|
-
if (_select.length > 0) doc = (0,
|
|
3154
|
+
if (_select.length > 0) doc = (0, import_utils16.pick)(doc, _select);
|
|
2423
3155
|
return { success: true, kind: "single", code: "success" /* Success */, data: doc, query };
|
|
2424
3156
|
}
|
|
2425
3157
|
async findById(id, args, options) {
|
|
@@ -2436,7 +3168,7 @@ var DataService = class {
|
|
|
2436
3168
|
}
|
|
2437
3169
|
async find(filter2, args, options) {
|
|
2438
3170
|
const filterErrors = validateClientFilter(filter2);
|
|
2439
|
-
if (filterErrors.length > 0) return { success: false, code: "bad_request" /* BadRequest */, errors: filterErrors };
|
|
3171
|
+
if (filterErrors.length > 0) return { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors: filterErrors };
|
|
2440
3172
|
const { select, sort, skip, limit, page, pageSize } = args ?? {};
|
|
2441
3173
|
const [_filter, _select, pagination] = await Promise.all([
|
|
2442
3174
|
this.genFilter("list", filter2),
|
|
@@ -2449,27 +3181,33 @@ var DataService = class {
|
|
|
2449
3181
|
sort,
|
|
2450
3182
|
...pagination
|
|
2451
3183
|
};
|
|
2452
|
-
if (_filter === false) return { success: false, code: "forbidden" /* Forbidden */, query };
|
|
3184
|
+
if (_filter === false) return { success: false, kind: "error", code: "forbidden" /* Forbidden */, query };
|
|
3185
|
+
if (sort) {
|
|
3186
|
+
const sortFieldErrors = await this.validateSortFields(sort, "list");
|
|
3187
|
+
if (sortFieldErrors.length > 0) {
|
|
3188
|
+
return { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors: sortFieldErrors, query };
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
2453
3191
|
let docs = await filterCollection(this.data, _filter);
|
|
2454
3192
|
const totalCount = docs.length;
|
|
2455
|
-
|
|
2456
|
-
|
|
3193
|
+
if (sort) {
|
|
3194
|
+
const { sortKey, sortOrder } = parseSortString(sort);
|
|
3195
|
+
docs = (0, import_utils16.orderBy)(docs, [sortKey], [sortOrder]);
|
|
3196
|
+
}
|
|
3197
|
+
const pagedDocs = docs.slice(query.skip, query.limit && query.skip + query.limit);
|
|
3198
|
+
const trimmed = await Promise.all(
|
|
3199
|
+
pagedDocs.map(async (doc) => {
|
|
2457
3200
|
doc = await this.trimOutputFields(doc, "list");
|
|
2458
|
-
if (_select.length > 0) doc = (0,
|
|
3201
|
+
if (_select.length > 0) doc = (0, import_utils16.pick)(doc, _select);
|
|
2459
3202
|
return doc;
|
|
2460
3203
|
})
|
|
2461
3204
|
);
|
|
2462
|
-
if (sort) {
|
|
2463
|
-
const { sortKey, sortOrder } = parseSortString(sort);
|
|
2464
|
-
docs = (0, import_utils15.orderBy)(docs, [sortKey], [sortOrder]);
|
|
2465
|
-
}
|
|
2466
|
-
docs = docs.slice(query.skip, query.limit && query.skip + query.limit);
|
|
2467
3205
|
return {
|
|
2468
3206
|
success: true,
|
|
2469
3207
|
kind: "list",
|
|
2470
3208
|
code: "success" /* Success */,
|
|
2471
|
-
data:
|
|
2472
|
-
count:
|
|
3209
|
+
data: trimmed,
|
|
3210
|
+
count: trimmed.length,
|
|
2473
3211
|
totalCount,
|
|
2474
3212
|
query
|
|
2475
3213
|
};
|
|
@@ -2501,33 +3239,101 @@ var DataService = class {
|
|
|
2501
3239
|
trimOutputFields(doc, access, baseFields) {
|
|
2502
3240
|
return this.pickAllowedFields(doc, access, baseFields);
|
|
2503
3241
|
}
|
|
3242
|
+
async validateSortFields(sort, access) {
|
|
3243
|
+
const fieldPathPattern = /^[A-Za-z_][A-Za-z0-9_$]*(\.[A-Za-z_][A-Za-z0-9_$]*)*$/;
|
|
3244
|
+
const fields = sort.trim().split(/\s+/).filter(Boolean).map((field) => ({
|
|
3245
|
+
raw: field,
|
|
3246
|
+
name: field.startsWith("-") ? field.slice(1) : field
|
|
3247
|
+
}));
|
|
3248
|
+
const errors = [];
|
|
3249
|
+
for (const { raw, name } of fields) {
|
|
3250
|
+
if (!fieldPathPattern.test(name)) {
|
|
3251
|
+
errors.push({ detail: `Invalid sort field: ${raw}`, pointer: `#/sort` });
|
|
3252
|
+
continue;
|
|
3253
|
+
}
|
|
3254
|
+
if (name === "id" || name === "_id") continue;
|
|
3255
|
+
const allowed = await this.genAllowedFields({}, access);
|
|
3256
|
+
if (!allowed.includes(name)) {
|
|
3257
|
+
errors.push({ detail: `Sort field is not allowed: ${name}`, pointer: `#/sort` });
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3260
|
+
return errors;
|
|
3261
|
+
}
|
|
2504
3262
|
};
|
|
2505
3263
|
|
|
2506
3264
|
// src/processors.ts
|
|
2507
|
-
var
|
|
3265
|
+
var import_utils17 = require("@web-ts-toolkit/utils");
|
|
3266
|
+
var UNSAFE_PATH_KEYS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
|
|
3267
|
+
function failUnsafeSegment(path) {
|
|
3268
|
+
throw new Error(
|
|
3269
|
+
`copyAndDepopulate: refusing path '${path}' because it contains an unsafe segment (__proto__, prototype, or constructor).`
|
|
3270
|
+
);
|
|
3271
|
+
}
|
|
3272
|
+
function failMissingId(path) {
|
|
3273
|
+
throw new Error(`copyAndDepopulate: path '${path}' resolved to a record missing the configured id field.`);
|
|
3274
|
+
}
|
|
3275
|
+
function assertSafePath(path) {
|
|
3276
|
+
const segs = path.split(".");
|
|
3277
|
+
for (const seg of segs) {
|
|
3278
|
+
if (seg.length === 0) continue;
|
|
3279
|
+
if (UNSAFE_PATH_KEYS.has(seg)) {
|
|
3280
|
+
failUnsafeSegment(path);
|
|
3281
|
+
}
|
|
3282
|
+
}
|
|
3283
|
+
}
|
|
2508
3284
|
var copyAndDepopulate = (docObject, operations, options = { mutable: true, idField: "_id" }) => {
|
|
2509
|
-
const
|
|
2510
|
-
const idField =
|
|
2511
|
-
|
|
2512
|
-
|
|
3285
|
+
const mutable = options.mutable !== false;
|
|
3286
|
+
const idField = options.idField ?? "_id";
|
|
3287
|
+
const obj = mutable ? docObject : (0, import_utils17.cloneDeep)(docObject);
|
|
3288
|
+
(0, import_utils17.forEach)(Array.isArray(operations) ? operations : [], (op) => {
|
|
3289
|
+
if (!op || typeof op.src !== "string" || typeof op.dest !== "string") return;
|
|
3290
|
+
const src = op.src;
|
|
3291
|
+
const dest = op.dest;
|
|
3292
|
+
if (src.length === 0 || dest.length === 0) return;
|
|
3293
|
+
assertSafePath(src);
|
|
3294
|
+
assertSafePath(dest);
|
|
2513
3295
|
let targets = [obj];
|
|
2514
|
-
const segs =
|
|
2515
|
-
(0,
|
|
3296
|
+
const segs = src.split(".");
|
|
3297
|
+
(0, import_utils17.forEach)(segs, (seg, ind) => {
|
|
2516
3298
|
if (segs.length === ind + 1) {
|
|
2517
|
-
(0,
|
|
3299
|
+
(0, import_utils17.forEach)(targets, (target) => {
|
|
3300
|
+
if (!(0, import_utils17.isPlainObject)(target)) return;
|
|
2518
3301
|
const targetObject = target;
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
3302
|
+
const leaf = targetObject[seg];
|
|
3303
|
+
const sourceForDest = leaf;
|
|
3304
|
+
let depopulated;
|
|
3305
|
+
if ((0, import_utils17.isArray)(leaf)) {
|
|
3306
|
+
if (leaf.length === 0) {
|
|
3307
|
+
depopulated = [];
|
|
3308
|
+
} else if (leaf.every((item) => (0, import_utils17.isPlainObject)(item))) {
|
|
3309
|
+
depopulated = (0, import_utils17.map)(leaf, idField).map((id, i) => {
|
|
3310
|
+
if (id === void 0) {
|
|
3311
|
+
failMissingId(`${src}[${i}]`);
|
|
3312
|
+
}
|
|
3313
|
+
return id;
|
|
3314
|
+
});
|
|
3315
|
+
} else {
|
|
3316
|
+
return;
|
|
3317
|
+
}
|
|
3318
|
+
} else if ((0, import_utils17.isPlainObject)(leaf)) {
|
|
3319
|
+
const id = (0, import_utils17.get)(leaf, idField);
|
|
3320
|
+
if (id === void 0) {
|
|
3321
|
+
failMissingId(src);
|
|
3322
|
+
}
|
|
3323
|
+
depopulated = id;
|
|
3324
|
+
} else {
|
|
3325
|
+
return;
|
|
3326
|
+
}
|
|
3327
|
+
(0, import_utils17.set)(targetObject, dest, sourceForDest);
|
|
3328
|
+
(0, import_utils17.set)(targetObject, seg, depopulated);
|
|
2525
3329
|
});
|
|
2526
3330
|
} else {
|
|
2527
3331
|
targets = targets.reduce((ret, target) => {
|
|
3332
|
+
if (!(0, import_utils17.isPlainObject)(target)) return ret;
|
|
2528
3333
|
const targetObject = target;
|
|
2529
|
-
|
|
2530
|
-
|
|
3334
|
+
const next = targetObject[seg];
|
|
3335
|
+
if ((0, import_utils17.isArray)(next)) ret.push(...next);
|
|
3336
|
+
else if (next !== null && next !== void 0) ret.push(next);
|
|
2531
3337
|
return ret;
|
|
2532
3338
|
}, []);
|
|
2533
3339
|
}
|
|
@@ -2574,10 +3380,10 @@ var Cache = class {
|
|
|
2574
3380
|
};
|
|
2575
3381
|
|
|
2576
3382
|
// src/core-shared.ts
|
|
2577
|
-
var
|
|
3383
|
+
var import_utils19 = require("@web-ts-toolkit/utils");
|
|
2578
3384
|
|
|
2579
3385
|
// src/permission.ts
|
|
2580
|
-
var
|
|
3386
|
+
var import_utils18 = require("@web-ts-toolkit/utils");
|
|
2581
3387
|
var Permission = class {
|
|
2582
3388
|
constructor(permissions) {
|
|
2583
3389
|
this.permissions = { ...permissions };
|
|
@@ -2593,7 +3399,7 @@ var Permission = class {
|
|
|
2593
3399
|
return [permissionOrPermissions].concat(permissions);
|
|
2594
3400
|
}
|
|
2595
3401
|
hasKey(permission) {
|
|
2596
|
-
return (0,
|
|
3402
|
+
return (0, import_utils18.hasOwn)(this.permissions, permission);
|
|
2597
3403
|
}
|
|
2598
3404
|
has(permission) {
|
|
2599
3405
|
return this.permissions[permission] === true;
|
|
@@ -2613,10 +3419,10 @@ var permission_default = Permission;
|
|
|
2613
3419
|
|
|
2614
3420
|
// src/core-shared.ts
|
|
2615
3421
|
function isAndFilter(filter2) {
|
|
2616
|
-
return !!filter2 && (0,
|
|
3422
|
+
return !!filter2 && (0, import_utils19.isPlainObject)(filter2) && Object.keys(filter2).length === 1 && (0, import_utils19.isArray)(filter2.$and);
|
|
2617
3423
|
}
|
|
2618
3424
|
function isMergeableClause(filter2) {
|
|
2619
|
-
return !!filter2 && (0,
|
|
3425
|
+
return !!filter2 && (0, import_utils19.isPlainObject)(filter2) && Object.keys(filter2).every((key) => !key.startsWith("$"));
|
|
2620
3426
|
}
|
|
2621
3427
|
function optimizeAndFilter(clausesInput) {
|
|
2622
3428
|
const clauses = [];
|
|
@@ -2630,9 +3436,24 @@ function optimizeAndFilter(clausesInput) {
|
|
|
2630
3436
|
clauses.push(clause);
|
|
2631
3437
|
}
|
|
2632
3438
|
}
|
|
2633
|
-
const
|
|
2634
|
-
|
|
2635
|
-
|
|
3439
|
+
const dedupeKey = (value) => {
|
|
3440
|
+
if ((0, import_utils19.isArray)(value)) {
|
|
3441
|
+
return `[${value.map(dedupeKey).join(",")}]`;
|
|
3442
|
+
}
|
|
3443
|
+
if ((0, import_utils19.isPlainObject)(value)) {
|
|
3444
|
+
return `{${Object.keys(value).sort().map((key) => `${key}:${dedupeKey(value[key])}`).join(",")}}`;
|
|
3445
|
+
}
|
|
3446
|
+
return JSON.stringify(value);
|
|
3447
|
+
};
|
|
3448
|
+
const dedupedClauses = [];
|
|
3449
|
+
const seenClauses = /* @__PURE__ */ new Set();
|
|
3450
|
+
for (let x = 0; x < clauses.length; x++) {
|
|
3451
|
+
const clause = clauses[x];
|
|
3452
|
+
const key = dedupeKey(clause);
|
|
3453
|
+
if (seenClauses.has(key)) continue;
|
|
3454
|
+
seenClauses.add(key);
|
|
3455
|
+
dedupedClauses.push(clause);
|
|
3456
|
+
}
|
|
2636
3457
|
const mergedClause = {};
|
|
2637
3458
|
const remainingClauses = [];
|
|
2638
3459
|
for (let x = 0; x < dedupedClauses.length; x++) {
|
|
@@ -2643,7 +3464,7 @@ function optimizeAndFilter(clausesInput) {
|
|
|
2643
3464
|
}
|
|
2644
3465
|
let canMerge = true;
|
|
2645
3466
|
for (const [key, value] of Object.entries(clause)) {
|
|
2646
|
-
if ((0,
|
|
3467
|
+
if ((0, import_utils19.hasOwn)(mergedClause, key) && !(0, import_utils19.isEqual)(mergedClause[key], value)) {
|
|
2647
3468
|
canMerge = false;
|
|
2648
3469
|
break;
|
|
2649
3470
|
}
|
|
@@ -2654,24 +3475,24 @@ function optimizeAndFilter(clausesInput) {
|
|
|
2654
3475
|
}
|
|
2655
3476
|
Object.assign(mergedClause, clause);
|
|
2656
3477
|
}
|
|
2657
|
-
const finalClauses = (0,
|
|
3478
|
+
const finalClauses = (0, import_utils19.isEmpty)(mergedClause) ? remainingClauses : [mergedClause, ...remainingClauses];
|
|
2658
3479
|
if (finalClauses.length === 0) return null;
|
|
2659
3480
|
if (finalClauses.length === 1) return finalClauses[0];
|
|
2660
3481
|
return { $and: finalClauses };
|
|
2661
3482
|
}
|
|
2662
3483
|
function normalizeFilter(filter2) {
|
|
2663
3484
|
if (filter2 === false) return false;
|
|
2664
|
-
if (!filter2 || !(0,
|
|
3485
|
+
if (!filter2 || !(0, import_utils19.isPlainObject)(filter2) || (0, import_utils19.isEmpty)(filter2)) return null;
|
|
2665
3486
|
if (!isAndFilter(filter2)) {
|
|
2666
3487
|
return filter2;
|
|
2667
3488
|
}
|
|
2668
3489
|
return optimizeAndFilter(filter2.$and);
|
|
2669
3490
|
}
|
|
2670
3491
|
async function resolveIdentifierFilter(req, idField, resolveIdFilter, id) {
|
|
2671
|
-
if ((0,
|
|
3492
|
+
if ((0, import_utils19.isFunction)(resolveIdFilter)) {
|
|
2672
3493
|
return await resolveIdFilter.call(req, id);
|
|
2673
3494
|
}
|
|
2674
|
-
if ((0,
|
|
3495
|
+
if ((0, import_utils19.isString)(idField)) {
|
|
2675
3496
|
return { [idField]: id };
|
|
2676
3497
|
}
|
|
2677
3498
|
return { _id: id };
|
|
@@ -2687,11 +3508,11 @@ async function resolveAccessFilter({
|
|
|
2687
3508
|
}) {
|
|
2688
3509
|
let nextFilter = normalizeFilter(filter2);
|
|
2689
3510
|
const overrideFilterFn = getOption(`overrideFilter.${access}`, null);
|
|
2690
|
-
if ((0,
|
|
3511
|
+
if ((0, import_utils19.isFunction)(overrideFilterFn)) {
|
|
2691
3512
|
nextFilter = normalizeFilter(await overrideFilterFn.call(req, nextFilter, permissions));
|
|
2692
3513
|
}
|
|
2693
3514
|
const baseFilterFn = getOption(`baseFilter.${access}`, null);
|
|
2694
|
-
if (!(0,
|
|
3515
|
+
if (!(0, import_utils19.isFunction)(baseFilterFn)) return nextFilter || {};
|
|
2695
3516
|
const baseFilter = normalizeFilter(
|
|
2696
3517
|
cache.has(cacheKey) ? cache.get(cacheKey) : await baseFilterFn.call(req, permissions)
|
|
2697
3518
|
);
|
|
@@ -2711,33 +3532,33 @@ async function setRequestPermissions(req) {
|
|
|
2711
3532
|
const requestPermissionField = getGlobalOption("requestPermissionField");
|
|
2712
3533
|
if (req[requestPermissionField]) return;
|
|
2713
3534
|
const globalPermissions = getGlobalOption("globalPermissions");
|
|
2714
|
-
if (!(0,
|
|
3535
|
+
if (!(0, import_utils19.isFunction)(globalPermissions)) return;
|
|
2715
3536
|
const permissions = await globalPermissions.call(req, req);
|
|
2716
|
-
if ((0,
|
|
2717
|
-
else if ((0,
|
|
2718
|
-
else if ((0,
|
|
3537
|
+
if ((0, import_utils19.isPlainObject)(permissions)) req[requestPermissionField] = permissions;
|
|
3538
|
+
else if ((0, import_utils19.isArray)(permissions)) req[requestPermissionField] = (0, import_utils19.arrayToRecord)(permissions);
|
|
3539
|
+
else if ((0, import_utils19.isString)(permissions)) req[requestPermissionField] = { [permissions]: true };
|
|
2719
3540
|
}
|
|
2720
3541
|
async function evaluateRouteGuard(req, permissions, routeGuard) {
|
|
2721
3542
|
const phas = (key) => permissions.has(key);
|
|
2722
3543
|
const { stringHandler, arrayHandler } = createValidator(phas);
|
|
2723
|
-
if ((0,
|
|
3544
|
+
if ((0, import_utils19.isBoolean)(routeGuard)) {
|
|
2724
3545
|
return routeGuard === true;
|
|
2725
3546
|
}
|
|
2726
|
-
if ((0,
|
|
3547
|
+
if ((0, import_utils19.isString)(routeGuard)) {
|
|
2727
3548
|
return stringHandler(routeGuard);
|
|
2728
3549
|
}
|
|
2729
|
-
if ((0,
|
|
3550
|
+
if ((0, import_utils19.isArray)(routeGuard)) {
|
|
2730
3551
|
return arrayHandler(routeGuard);
|
|
2731
3552
|
}
|
|
2732
|
-
if ((0,
|
|
3553
|
+
if ((0, import_utils19.isFunction)(routeGuard)) {
|
|
2733
3554
|
return routeGuard.call(req, permissions);
|
|
2734
3555
|
}
|
|
2735
3556
|
return false;
|
|
2736
3557
|
}
|
|
2737
3558
|
async function callHookChain(req, hook, doc, permissions, context) {
|
|
2738
|
-
const hooks = (0,
|
|
3559
|
+
const hooks = (0, import_utils19.castArray)(hook);
|
|
2739
3560
|
for (let x = 0; x < hooks.length; x++) {
|
|
2740
|
-
if ((0,
|
|
3561
|
+
if ((0, import_utils19.isFunction)(hooks[x])) {
|
|
2741
3562
|
doc = await hooks[x].call(req, doc, permissions, context);
|
|
2742
3563
|
}
|
|
2743
3564
|
}
|
|
@@ -2760,13 +3581,13 @@ async function collectSchemaFields({
|
|
|
2760
3581
|
if (baseFields.includes(key)) continue;
|
|
2761
3582
|
const val = permissionSchema[key];
|
|
2762
3583
|
const value = val && val[access] || val;
|
|
2763
|
-
if ((0,
|
|
3584
|
+
if ((0, import_utils19.isBoolean)(value)) {
|
|
2764
3585
|
if (value) fields.push(key);
|
|
2765
|
-
} else if ((0,
|
|
3586
|
+
} else if ((0, import_utils19.isString)(value)) {
|
|
2766
3587
|
if (stringHandler(value)) fields.push(key);
|
|
2767
|
-
} else if ((0,
|
|
3588
|
+
} else if ((0, import_utils19.isArray)(value)) {
|
|
2768
3589
|
if (arrayHandler(value)) fields.push(key);
|
|
2769
|
-
} else if ((0,
|
|
3590
|
+
} else if ((0, import_utils19.isFunction)(value)) {
|
|
2770
3591
|
if (await value.call(req, ...functionArgs)) fields.push(key);
|
|
2771
3592
|
}
|
|
2772
3593
|
}
|
|
@@ -2800,7 +3621,7 @@ async function initializeAclRequest({
|
|
|
2800
3621
|
await core.setPermissions();
|
|
2801
3622
|
assignCore(core);
|
|
2802
3623
|
req[PERMISSIONS] = core.getPermissions();
|
|
2803
|
-
req[PERMISSION_KEYS] = req[PERMISSIONS].keys;
|
|
3624
|
+
req[PERMISSION_KEYS] = [...req[PERMISSIONS].keys];
|
|
2804
3625
|
req[flag] = true;
|
|
2805
3626
|
return core;
|
|
2806
3627
|
}
|
|
@@ -2855,7 +3676,7 @@ async function runDecorateAllHook({
|
|
|
2855
3676
|
}
|
|
2856
3677
|
|
|
2857
3678
|
// src/acl/select-resolution.ts
|
|
2858
|
-
var
|
|
3679
|
+
var import_utils20 = require("@web-ts-toolkit/utils");
|
|
2859
3680
|
async function collectAllowedFieldsForRequest({
|
|
2860
3681
|
req,
|
|
2861
3682
|
permissionSchema,
|
|
@@ -2923,15 +3744,15 @@ async function resolveSelectForRequest({
|
|
|
2923
3744
|
const excludeall = normalizedSelect.every((v) => v.startsWith("-"));
|
|
2924
3745
|
if (excludeall) {
|
|
2925
3746
|
normalizedSelect = normalizedSelect.map((v) => v.substring(1));
|
|
2926
|
-
fields = (0,
|
|
3747
|
+
fields = (0, import_utils20.difference)(fields, normalizedSelect);
|
|
2927
3748
|
if (excludeid) fields.push("-_id");
|
|
2928
3749
|
} else {
|
|
2929
|
-
fields = (0,
|
|
3750
|
+
fields = (0, import_utils20.intersection)(normalizedSelect, fields.concat(excludeid ? "-_id" : "_id"));
|
|
2930
3751
|
}
|
|
2931
3752
|
}
|
|
2932
3753
|
return fields.concat(alwaysSelectFields);
|
|
2933
3754
|
}
|
|
2934
|
-
return (0,
|
|
3755
|
+
return (0, import_utils20.intersection)(normalizedSelect, fields);
|
|
2935
3756
|
}
|
|
2936
3757
|
|
|
2937
3758
|
// src/core.ts
|
|
@@ -2945,10 +3766,10 @@ var Core = class {
|
|
|
2945
3766
|
getIdentifier(modelName) {
|
|
2946
3767
|
const resolveIdFilter = getModelOption(modelName, "resolveIdFilter");
|
|
2947
3768
|
const idField = getModelOption(modelName, "idField");
|
|
2948
|
-
if ((0,
|
|
3769
|
+
if ((0, import_utils21.isFunction)(resolveIdFilter)) {
|
|
2949
3770
|
return null;
|
|
2950
3771
|
}
|
|
2951
|
-
if ((0,
|
|
3772
|
+
if ((0, import_utils21.isString)(idField)) {
|
|
2952
3773
|
return idField;
|
|
2953
3774
|
}
|
|
2954
3775
|
return "_id";
|
|
@@ -3026,11 +3847,11 @@ var Core = class {
|
|
|
3026
3847
|
async genPopulate(modelName, access = "read", _populate = null) {
|
|
3027
3848
|
if (!_populate) return [];
|
|
3028
3849
|
let populate = Array.isArray(_populate) ? _populate : [_populate];
|
|
3029
|
-
populate = (0,
|
|
3850
|
+
populate = (0, import_utils21.compact)(
|
|
3030
3851
|
await Promise.all(
|
|
3031
3852
|
populate.map(async (p) => {
|
|
3032
|
-
const populateAccess = !(0,
|
|
3033
|
-
const ret = (0,
|
|
3853
|
+
const populateAccess = !(0, import_utils21.isString)(p) && p.access ? p.access : access;
|
|
3854
|
+
const ret = (0, import_utils21.isString)(p) ? { path: p } : {
|
|
3034
3855
|
path: p.path,
|
|
3035
3856
|
select: normalizeSelect(p.select)
|
|
3036
3857
|
};
|
|
@@ -3044,6 +3865,14 @@ var Core = class {
|
|
|
3044
3865
|
if (!allowedParentPaths.includes(parentPath)) return null;
|
|
3045
3866
|
const refModelName = getModelRef(modelName, ret.path);
|
|
3046
3867
|
if (!refModelName) return null;
|
|
3868
|
+
const runtime = getActiveRuntime();
|
|
3869
|
+
if (runtime && !runtime.hasModel(refModelName)) {
|
|
3870
|
+
return null;
|
|
3871
|
+
}
|
|
3872
|
+
const allowedTargetOperation = await this.req.macl.isAllowed(refModelName, populateAccess);
|
|
3873
|
+
if (!allowedTargetOperation) {
|
|
3874
|
+
return null;
|
|
3875
|
+
}
|
|
3047
3876
|
ret.select = await this.genSelect(refModelName, populateAccess, ret.select, false);
|
|
3048
3877
|
const filter2 = await this.genFilter(refModelName, populateAccess, null);
|
|
3049
3878
|
if (filter2 === false) return null;
|
|
@@ -3056,10 +3885,10 @@ var Core = class {
|
|
|
3056
3885
|
}
|
|
3057
3886
|
async validate(modelName, allowedData, access, context) {
|
|
3058
3887
|
const validate = getModelOption(modelName, `validate.${access}`, null);
|
|
3059
|
-
if ((0,
|
|
3888
|
+
if ((0, import_utils21.isFunction)(validate)) {
|
|
3060
3889
|
const permissions = this.getGlobalPermissions();
|
|
3061
3890
|
return validate.call(this.req, allowedData, permissions, context);
|
|
3062
|
-
} else if ((0,
|
|
3891
|
+
} else if ((0, import_utils21.isBoolean)(validate) || (0, import_utils21.isArray)(validate)) {
|
|
3063
3892
|
return validate;
|
|
3064
3893
|
} else {
|
|
3065
3894
|
return true;
|
|
@@ -3084,7 +3913,7 @@ var Core = class {
|
|
|
3084
3913
|
const changeOptions = getModelOption(modelName, `onChange`, {});
|
|
3085
3914
|
for (let x = 0; x < context.modifiedPaths.length; x++) {
|
|
3086
3915
|
const mpath = context.modifiedPaths[x];
|
|
3087
|
-
if ((0,
|
|
3916
|
+
if ((0, import_utils21.isFunction)(changeOptions[mpath])) {
|
|
3088
3917
|
await changeOptions[mpath].call(
|
|
3089
3918
|
this.req,
|
|
3090
3919
|
context.originalDocumentSnapshot[mpath],
|
|
@@ -3108,14 +3937,17 @@ var Core = class {
|
|
|
3108
3937
|
async genDocPermissions(modelName, doc, access, context) {
|
|
3109
3938
|
const docPermissionsFn = getModelOption(modelName, `docPermissions.${access}`, null);
|
|
3110
3939
|
let docPermissions = {};
|
|
3111
|
-
if ((0,
|
|
3940
|
+
if ((0, import_utils21.isFunction)(docPermissionsFn)) {
|
|
3112
3941
|
const permissions = this.getGlobalPermissions();
|
|
3113
3942
|
try {
|
|
3114
3943
|
docPermissions = await docPermissionsFn.call(this.req, doc, permissions, context);
|
|
3115
|
-
} catch (
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3944
|
+
} catch (error2) {
|
|
3945
|
+
warn("docPermissions hook failed; applying empty document permissions", {
|
|
3946
|
+
modelName,
|
|
3947
|
+
access,
|
|
3948
|
+
operation: context.operation,
|
|
3949
|
+
error: error2 instanceof Error ? error2.message : String(error2)
|
|
3950
|
+
});
|
|
3119
3951
|
}
|
|
3120
3952
|
}
|
|
3121
3953
|
return docPermissions;
|
|
@@ -3156,7 +3988,7 @@ var Core = class {
|
|
|
3156
3988
|
readExists ? this.genAllowedFields(modelName, doc, "read") : [],
|
|
3157
3989
|
updateExists ? this.genAllowedFields(modelName, doc, "update") : []
|
|
3158
3990
|
]);
|
|
3159
|
-
const viewObj = (0,
|
|
3991
|
+
const viewObj = (0, import_utils21.reduce)(
|
|
3160
3992
|
views,
|
|
3161
3993
|
(ret, view) => {
|
|
3162
3994
|
ret[view] = true;
|
|
@@ -3164,7 +3996,7 @@ var Core = class {
|
|
|
3164
3996
|
},
|
|
3165
3997
|
{}
|
|
3166
3998
|
);
|
|
3167
|
-
const editObj = (0,
|
|
3999
|
+
const editObj = (0, import_utils21.reduce)(
|
|
3168
4000
|
edits,
|
|
3169
4001
|
(ret, view) => {
|
|
3170
4002
|
ret[view] = true;
|
|
@@ -3188,9 +4020,9 @@ var Core = class {
|
|
|
3188
4020
|
return runDecorateAllHook({ req: this.req, hook: decorateAll, docs, permissions, context });
|
|
3189
4021
|
}
|
|
3190
4022
|
runTasks(modelName, docObject, task) {
|
|
3191
|
-
const tasks = (0,
|
|
4023
|
+
const tasks = (0, import_utils21.compact)((0, import_utils21.castArray)(task));
|
|
3192
4024
|
if (tasks.length === 0) return docObject;
|
|
3193
|
-
(0,
|
|
4025
|
+
(0, import_utils21.forEach)(tasks, (task2) => {
|
|
3194
4026
|
const { type, args, options } = task2;
|
|
3195
4027
|
switch (type) {
|
|
3196
4028
|
case "COPY_AND_DEPOPULATE":
|
|
@@ -3221,9 +4053,9 @@ var Core = class {
|
|
|
3221
4053
|
}
|
|
3222
4054
|
const [, field, op] = keys2;
|
|
3223
4055
|
const subOption = getExactModelOption(modelName, `operationAccess.${access}`);
|
|
3224
|
-
if ((0,
|
|
4056
|
+
if ((0, import_utils21.isUndefined)(subOption)) {
|
|
3225
4057
|
const subFieldOption = getExactModelOption(modelName, `operationAccess.subs.${field}`);
|
|
3226
|
-
if ((0,
|
|
4058
|
+
if ((0, import_utils21.isUndefined)(subFieldOption)) {
|
|
3227
4059
|
const opOption = getModelOption(modelName, `operationAccess.${op}`);
|
|
3228
4060
|
return this.canActivate(opOption);
|
|
3229
4061
|
}
|
|
@@ -3273,25 +4105,25 @@ function guard(condition) {
|
|
|
3273
4105
|
const permissions = req[PERMISSIONS];
|
|
3274
4106
|
let cond = condition;
|
|
3275
4107
|
let phas = (key) => permissions.has(key);
|
|
3276
|
-
if ((0,
|
|
4108
|
+
if ((0, import_utils22.isPlainObject)(condition)) {
|
|
3277
4109
|
const { modelName, id, condition: _cond } = condition;
|
|
3278
4110
|
const svc = req.macl.getPublicService(modelName);
|
|
3279
4111
|
const select = getModelOption(modelName, `alwaysSelectFields.read`, void 0);
|
|
3280
|
-
let _id = (0,
|
|
3281
|
-
if ((0,
|
|
4112
|
+
let _id = (0, import_utils22.isString)(id) ? id : null;
|
|
4113
|
+
if ((0, import_utils22.isPlainObject)(id)) {
|
|
3282
4114
|
const { type = "param", key } = id;
|
|
3283
4115
|
if (type === "param") {
|
|
3284
4116
|
const paramValue = req.params[key];
|
|
3285
|
-
_id = (0,
|
|
4117
|
+
_id = (0, import_utils22.isArray)(paramValue) ? paramValue[0] ?? null : paramValue ?? null;
|
|
3286
4118
|
} else if (type === "query") {
|
|
3287
4119
|
const _qval = req.query[key];
|
|
3288
|
-
if ((0,
|
|
4120
|
+
if ((0, import_utils22.isArray)(_qval)) _id = _qval.length > 0 ? _qval[0] : null;
|
|
3289
4121
|
else _id = _qval;
|
|
3290
4122
|
} else {
|
|
3291
4123
|
_id = null;
|
|
3292
4124
|
}
|
|
3293
4125
|
}
|
|
3294
|
-
if (!(0,
|
|
4126
|
+
if (!(0, import_utils22.isString)(_id) || !_id) {
|
|
3295
4127
|
return next(new import_express_json_router2.default.clientErrors.BadRequestError());
|
|
3296
4128
|
}
|
|
3297
4129
|
const result = await svc._read(_id, { select });
|
|
@@ -3303,11 +4135,11 @@ function guard(condition) {
|
|
|
3303
4135
|
cond = _cond;
|
|
3304
4136
|
}
|
|
3305
4137
|
const { stringHandler, arrayHandler } = createValidator(phas);
|
|
3306
|
-
if ((0,
|
|
4138
|
+
if ((0, import_utils22.isString)(cond)) {
|
|
3307
4139
|
if (stringHandler(cond)) return next();
|
|
3308
|
-
} else if ((0,
|
|
4140
|
+
} else if ((0, import_utils22.isArray)(cond)) {
|
|
3309
4141
|
if (arrayHandler(cond)) return next();
|
|
3310
|
-
} else if ((0,
|
|
4142
|
+
} else if ((0, import_utils22.isFunction)(cond)) {
|
|
3311
4143
|
const result = await cond.call(req, permissions);
|
|
3312
4144
|
if (!!result) return next();
|
|
3313
4145
|
}
|
|
@@ -3350,9 +4182,37 @@ function assertMutableRouterOptions(kind, options) {
|
|
|
3350
4182
|
// src/http/response-pipelines/model-response.ts
|
|
3351
4183
|
var import_express_json_router3 = __toESM(require("@web-ts-toolkit/express-json-router"));
|
|
3352
4184
|
|
|
4185
|
+
// src/http/response-pipelines/service-result.ts
|
|
4186
|
+
var toPublicErrorResult = (result) => ({
|
|
4187
|
+
success: false,
|
|
4188
|
+
code: result.code,
|
|
4189
|
+
errors: result.errors
|
|
4190
|
+
});
|
|
4191
|
+
var toPublicSingleResult = (result) => ({
|
|
4192
|
+
success: true,
|
|
4193
|
+
kind: "single",
|
|
4194
|
+
code: result.code,
|
|
4195
|
+
data: result.data
|
|
4196
|
+
});
|
|
4197
|
+
var toPublicListResult = (result) => ({
|
|
4198
|
+
success: true,
|
|
4199
|
+
kind: "list",
|
|
4200
|
+
code: result.code,
|
|
4201
|
+
data: result.data,
|
|
4202
|
+
count: result.count,
|
|
4203
|
+
totalCount: result.totalCount ?? null
|
|
4204
|
+
});
|
|
4205
|
+
var toPublicServiceResult = (result) => {
|
|
4206
|
+
if (result.kind === "error") {
|
|
4207
|
+
return toPublicErrorResult(result);
|
|
4208
|
+
}
|
|
4209
|
+
return result.kind === "list" ? toPublicListResult(result) : toPublicSingleResult(result);
|
|
4210
|
+
};
|
|
4211
|
+
|
|
3353
4212
|
// src/http/response-pipelines/list-response.ts
|
|
3354
4213
|
var formatListResponse = (req, result, includeCount, includeExtraHeaders) => {
|
|
3355
|
-
const
|
|
4214
|
+
const publicResult = toPublicListResult(result);
|
|
4215
|
+
const { data, count: returnedCount, totalCount } = publicResult;
|
|
3356
4216
|
const query = result.query ?? {};
|
|
3357
4217
|
const rawLimit = Number(query.limit ?? 0);
|
|
3358
4218
|
const skip = Number(query.skip ?? 0);
|
|
@@ -3387,16 +4247,18 @@ var formatListResponse = (req, result, includeCount, includeExtraHeaders) => {
|
|
|
3387
4247
|
return { data, meta };
|
|
3388
4248
|
};
|
|
3389
4249
|
var formatCreatedData = (result) => {
|
|
3390
|
-
|
|
4250
|
+
const publicResult = toPublicListResult(result);
|
|
4251
|
+
return publicResult.count === 1 ? publicResult.data[0] : publicResult.data;
|
|
3391
4252
|
};
|
|
3392
4253
|
var formatUpsertCreatedData = (result) => {
|
|
3393
|
-
|
|
4254
|
+
const publicResult = toPublicListResult(result);
|
|
4255
|
+
return publicResult.data.length > 0 ? publicResult.data[0] : null;
|
|
3394
4256
|
};
|
|
3395
4257
|
|
|
3396
4258
|
// src/http/response-pipelines/model-response.ts
|
|
3397
4259
|
var success = import_express_json_router3.default.success;
|
|
3398
4260
|
function unwrapServiceData(result) {
|
|
3399
|
-
return result.data;
|
|
4261
|
+
return result.kind === "list" ? toPublicListResult(result).data : toPublicSingleResult(result).data;
|
|
3400
4262
|
}
|
|
3401
4263
|
function formatModelListResponse(req, result, includeCount, includeExtraHeaders) {
|
|
3402
4264
|
return formatListResponse(req, result, includeCount, includeExtraHeaders);
|
|
@@ -3405,7 +4267,13 @@ function formatModelCreatedResponse(result) {
|
|
|
3405
4267
|
return new success.Created(formatCreatedData(result));
|
|
3406
4268
|
}
|
|
3407
4269
|
function formatModelUpsertResponse(result) {
|
|
3408
|
-
|
|
4270
|
+
if (result.kind === "list") {
|
|
4271
|
+
return new success.Created(formatUpsertCreatedData(result));
|
|
4272
|
+
}
|
|
4273
|
+
if (result.kind === "single") {
|
|
4274
|
+
return toPublicSingleResult(result).data;
|
|
4275
|
+
}
|
|
4276
|
+
throw new Error(`Unexpected ServiceResult kind: ${String(result.kind)}`);
|
|
3409
4277
|
}
|
|
3410
4278
|
|
|
3411
4279
|
// src/validation/parsers.ts
|
|
@@ -3414,9 +4282,12 @@ var import_express_json_router4 = __toESM(require("@web-ts-toolkit/express-json-
|
|
|
3414
4282
|
// src/validation/common.ts
|
|
3415
4283
|
var import_zod2 = require("zod");
|
|
3416
4284
|
var stringOrStringArray = import_zod2.z.union([import_zod2.z.string(), import_zod2.z.array(import_zod2.z.string())]);
|
|
4285
|
+
var safeIntegerString = (min, message) => import_zod2.z.string().regex(/^\d+$/, message).refine((value) => Number.isSafeInteger(Number(value)), "Expected a safe integer").refine((value) => Number(value) >= min, message);
|
|
3417
4286
|
var queryBooleanString = import_zod2.z.enum(["true", "false"]);
|
|
3418
|
-
var
|
|
3419
|
-
var
|
|
4287
|
+
var nonNegativeIntegerString = safeIntegerString(0, "Expected a non-negative integer");
|
|
4288
|
+
var positiveIntegerString = safeIntegerString(1, "Expected a positive integer");
|
|
4289
|
+
var nonNegativeIntegerSchema = import_zod2.z.number().int().min(0).max(Number.MAX_SAFE_INTEGER);
|
|
4290
|
+
var positiveIntegerSchema = import_zod2.z.number().int().min(1).max(Number.MAX_SAFE_INTEGER);
|
|
3420
4291
|
var unknownRecord = import_zod2.z.record(import_zod2.z.string(), import_zod2.z.unknown());
|
|
3421
4292
|
var projectionObjectSchema = import_zod2.z.record(import_zod2.z.string(), import_zod2.z.union([import_zod2.z.literal(1), import_zod2.z.literal(-1)]));
|
|
3422
4293
|
var projectionSchema = import_zod2.z.union([import_zod2.z.string(), import_zod2.z.array(import_zod2.z.string()), projectionObjectSchema]);
|
|
@@ -3517,6 +4388,10 @@ function parsePathParam(value, parameter) {
|
|
|
3517
4388
|
return param;
|
|
3518
4389
|
}
|
|
3519
4390
|
function parseQuery(schema, value) {
|
|
4391
|
+
const complexityErrors = validateRequestComplexity(value, getGlobalOption("requestComplexity"), "request");
|
|
4392
|
+
if (complexityErrors.length > 0) {
|
|
4393
|
+
throw new clientErrors.BadRequestError("Bad Request", { errors: complexityErrors });
|
|
4394
|
+
}
|
|
3520
4395
|
const result = schema.safeParse(value);
|
|
3521
4396
|
if (!result.success) {
|
|
3522
4397
|
throwValidationError(normalizeIssues(result.error.issues), void 0, "parameter");
|
|
@@ -3524,6 +4399,10 @@ function parseQuery(schema, value) {
|
|
|
3524
4399
|
return result.data;
|
|
3525
4400
|
}
|
|
3526
4401
|
function parseBody(schema, value) {
|
|
4402
|
+
const complexityErrors = validateRequestComplexity(value ?? {}, getGlobalOption("requestComplexity"), "request");
|
|
4403
|
+
if (complexityErrors.length > 0) {
|
|
4404
|
+
throw new clientErrors.BadRequestError("Bad Request", { errors: complexityErrors });
|
|
4405
|
+
}
|
|
3527
4406
|
const result = schema.safeParse(value ?? {});
|
|
3528
4407
|
if (!result.success) {
|
|
3529
4408
|
throwValidationError(normalizeIssues(result.error.issues), void 0, "pointer");
|
|
@@ -3626,10 +4505,10 @@ function fromYup(schema) {
|
|
|
3626
4505
|
success: true,
|
|
3627
4506
|
data
|
|
3628
4507
|
};
|
|
3629
|
-
} catch (
|
|
4508
|
+
} catch (error2) {
|
|
3630
4509
|
return {
|
|
3631
4510
|
success: false,
|
|
3632
|
-
issues: normalizeYupIssues(
|
|
4511
|
+
issues: normalizeYupIssues(error2)
|
|
3633
4512
|
};
|
|
3634
4513
|
}
|
|
3635
4514
|
};
|
|
@@ -3732,10 +4611,10 @@ function fromVine(validator) {
|
|
|
3732
4611
|
success: true,
|
|
3733
4612
|
data: output
|
|
3734
4613
|
};
|
|
3735
|
-
} catch (
|
|
4614
|
+
} catch (error2) {
|
|
3736
4615
|
return {
|
|
3737
4616
|
success: false,
|
|
3738
|
-
issues: normalizeVineError(
|
|
4617
|
+
issues: normalizeVineError(error2)
|
|
3739
4618
|
};
|
|
3740
4619
|
}
|
|
3741
4620
|
};
|
|
@@ -3756,11 +4635,11 @@ function normalizePathSegment(segment) {
|
|
|
3756
4635
|
function isStandardSchemaPathSegment(segment) {
|
|
3757
4636
|
return typeof segment === "object" && segment !== null && "key" in segment;
|
|
3758
4637
|
}
|
|
3759
|
-
function normalizeYupIssues(
|
|
3760
|
-
if (!isYupValidationError(
|
|
4638
|
+
function normalizeYupIssues(error2) {
|
|
4639
|
+
if (!isYupValidationError(error2)) {
|
|
3761
4640
|
return [{ message: "Validation failed" }];
|
|
3762
4641
|
}
|
|
3763
|
-
const issues =
|
|
4642
|
+
const issues = error2.inner?.length ? error2.inner : [error2];
|
|
3764
4643
|
return issues.map((issue) => ({
|
|
3765
4644
|
message: issue.message,
|
|
3766
4645
|
path: parsePathString(issue.path)
|
|
@@ -3811,11 +4690,11 @@ function normalizeSuperstructPath(failure) {
|
|
|
3811
4690
|
if (typeof failure.key === "string" || typeof failure.key === "number") return [failure.key];
|
|
3812
4691
|
return void 0;
|
|
3813
4692
|
}
|
|
3814
|
-
function normalizeVineError(
|
|
3815
|
-
if (!isVineValidationError(
|
|
4693
|
+
function normalizeVineError(error2) {
|
|
4694
|
+
if (!isVineValidationError(error2)) {
|
|
3816
4695
|
return [{ message: "Validation failed" }];
|
|
3817
4696
|
}
|
|
3818
|
-
return
|
|
4697
|
+
return error2.messages.map((message) => ({
|
|
3819
4698
|
message: message.message,
|
|
3820
4699
|
path: normalizeVineField(message)
|
|
3821
4700
|
}));
|
|
@@ -3838,14 +4717,14 @@ function parsePathString(path) {
|
|
|
3838
4717
|
if (!path) return void 0;
|
|
3839
4718
|
return path.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean).map((segment) => /^\d+$/.test(segment) ? Number(segment) : segment);
|
|
3840
4719
|
}
|
|
3841
|
-
function isYupValidationError(
|
|
3842
|
-
return typeof
|
|
4720
|
+
function isYupValidationError(error2) {
|
|
4721
|
+
return typeof error2 === "object" && error2 !== null && "message" in error2;
|
|
3843
4722
|
}
|
|
3844
4723
|
function isArkTypeErrors(value) {
|
|
3845
4724
|
return Array.isArray(value);
|
|
3846
4725
|
}
|
|
3847
|
-
function isVineValidationError(
|
|
3848
|
-
return typeof
|
|
4726
|
+
function isVineValidationError(error2) {
|
|
4727
|
+
return typeof error2 === "object" && error2 !== null && "messages" in error2 && Array.isArray(error2.messages);
|
|
3849
4728
|
}
|
|
3850
4729
|
function formatIssue(issue, key, location = "pointer") {
|
|
3851
4730
|
const path = (issue.path ?? []).map(String);
|
|
@@ -3868,9 +4747,9 @@ function buildPointer(path) {
|
|
|
3868
4747
|
// src/validation/model-router.ts
|
|
3869
4748
|
var import_zod3 = require("zod");
|
|
3870
4749
|
var listQuerySchema = import_zod3.z.object({
|
|
3871
|
-
skip:
|
|
4750
|
+
skip: nonNegativeIntegerString.optional(),
|
|
3872
4751
|
limit: positiveIntegerString.optional(),
|
|
3873
|
-
page:
|
|
4752
|
+
page: nonNegativeIntegerString.optional(),
|
|
3874
4753
|
page_size: positiveIntegerString.optional(),
|
|
3875
4754
|
skim: queryBooleanString.optional(),
|
|
3876
4755
|
include_permissions: queryBooleanString.optional(),
|
|
@@ -3899,10 +4778,10 @@ var listBodySchema = import_zod3.z.object({
|
|
|
3899
4778
|
populate: populateSchema.optional(),
|
|
3900
4779
|
include: includeSchema.optional(),
|
|
3901
4780
|
tasks: tasksSchema.optional(),
|
|
3902
|
-
skip: import_zod3.z.union([nonNegativeIntegerSchema,
|
|
3903
|
-
limit: import_zod3.z.union([
|
|
3904
|
-
page: import_zod3.z.union([nonNegativeIntegerSchema,
|
|
3905
|
-
pageSize: import_zod3.z.union([
|
|
4781
|
+
skip: import_zod3.z.union([nonNegativeIntegerSchema, nonNegativeIntegerString]).optional(),
|
|
4782
|
+
limit: import_zod3.z.union([positiveIntegerSchema, positiveIntegerString]).optional(),
|
|
4783
|
+
page: import_zod3.z.union([nonNegativeIntegerSchema, nonNegativeIntegerString]).optional(),
|
|
4784
|
+
pageSize: import_zod3.z.union([positiveIntegerSchema, positiveIntegerString]).optional(),
|
|
3906
4785
|
options: import_zod3.z.object({
|
|
3907
4786
|
skim: import_zod3.z.boolean().optional(),
|
|
3908
4787
|
includePermissions: import_zod3.z.boolean().optional(),
|
|
@@ -3912,11 +4791,8 @@ var listBodySchema = import_zod3.z.object({
|
|
|
3912
4791
|
}).passthrough().optional()
|
|
3913
4792
|
}).passthrough().superRefine((body, ctx) => rejectKeys(body, ctx, ["query"]));
|
|
3914
4793
|
var countBodySchema = import_zod3.z.object({
|
|
3915
|
-
filter: objectOrArraySchema.optional()
|
|
3916
|
-
|
|
3917
|
-
access: import_zod3.z.unknown().optional()
|
|
3918
|
-
}).passthrough().optional()
|
|
3919
|
-
}).passthrough().superRefine((body, ctx) => rejectKeys(body, ctx, ["query", "access"]));
|
|
4794
|
+
filter: objectOrArraySchema.optional()
|
|
4795
|
+
}).passthrough().superRefine((body, ctx) => rejectKeys(body, ctx, ["query", "access", "options"]));
|
|
3920
4796
|
var readFilterBodySchema = import_zod3.z.object({
|
|
3921
4797
|
filter: objectOrArraySchema.optional(),
|
|
3922
4798
|
select: projectionSchema.optional(),
|
|
@@ -4010,10 +4886,10 @@ var dataListBodySchema = import_zod4.z.object({
|
|
|
4010
4886
|
filter: objectOrArraySchema.optional(),
|
|
4011
4887
|
select: projectionSchema.optional(),
|
|
4012
4888
|
sort: import_zod4.z.string().optional(),
|
|
4013
|
-
skip: import_zod4.z.union([nonNegativeIntegerSchema,
|
|
4014
|
-
limit: import_zod4.z.union([
|
|
4015
|
-
page: import_zod4.z.union([nonNegativeIntegerSchema,
|
|
4016
|
-
pageSize: import_zod4.z.union([
|
|
4889
|
+
skip: import_zod4.z.union([nonNegativeIntegerSchema, nonNegativeIntegerString]).optional(),
|
|
4890
|
+
limit: import_zod4.z.union([positiveIntegerSchema, positiveIntegerString]).optional(),
|
|
4891
|
+
page: import_zod4.z.union([nonNegativeIntegerSchema, nonNegativeIntegerString]).optional(),
|
|
4892
|
+
pageSize: import_zod4.z.union([positiveIntegerSchema, positiveIntegerString]).optional(),
|
|
4017
4893
|
options: import_zod4.z.object({
|
|
4018
4894
|
includeCount: import_zod4.z.boolean().optional(),
|
|
4019
4895
|
includeExtraHeaders: import_zod4.z.boolean().optional()
|
|
@@ -4032,17 +4908,17 @@ var import_zod5 = require("zod");
|
|
|
4032
4908
|
var rootEntryBaseSchema = {
|
|
4033
4909
|
target: import_zod5.z.enum(["model", "data"]),
|
|
4034
4910
|
name: import_zod5.z.string().min(1),
|
|
4035
|
-
order:
|
|
4911
|
+
order: nonNegativeIntegerSchema.optional()
|
|
4036
4912
|
};
|
|
4037
4913
|
var rootModelListArgsSchema = import_zod5.z.object({
|
|
4038
4914
|
select: projectionSchema.optional(),
|
|
4039
4915
|
populate: populateSchema.optional(),
|
|
4040
4916
|
include: includeSchema.optional(),
|
|
4041
4917
|
sort: sortSchema.optional(),
|
|
4042
|
-
skip: import_zod5.z.union([nonNegativeIntegerSchema,
|
|
4043
|
-
limit: import_zod5.z.union([
|
|
4044
|
-
page: import_zod5.z.union([nonNegativeIntegerSchema,
|
|
4045
|
-
pageSize: import_zod5.z.union([
|
|
4918
|
+
skip: import_zod5.z.union([nonNegativeIntegerSchema, nonNegativeIntegerString]).optional(),
|
|
4919
|
+
limit: import_zod5.z.union([positiveIntegerSchema, positiveIntegerString]).optional(),
|
|
4920
|
+
page: import_zod5.z.union([nonNegativeIntegerSchema, nonNegativeIntegerString]).optional(),
|
|
4921
|
+
pageSize: import_zod5.z.union([positiveIntegerSchema, positiveIntegerString]).optional(),
|
|
4046
4922
|
tasks: tasksSchema.optional()
|
|
4047
4923
|
}).passthrough();
|
|
4048
4924
|
var rootModelListOptionsSchema = import_zod5.z.object({
|
|
@@ -4098,16 +4974,14 @@ var rootModelSubReadArgsSchema = import_zod5.z.object({
|
|
|
4098
4974
|
select: fieldsSchema.optional(),
|
|
4099
4975
|
populate: subPopulateSchema.optional()
|
|
4100
4976
|
}).passthrough();
|
|
4101
|
-
var rootModelCountOptionsSchema = import_zod5.z.object({
|
|
4102
|
-
access: import_zod5.z.unknown().optional()
|
|
4103
|
-
}).passthrough();
|
|
4977
|
+
var rootModelCountOptionsSchema = import_zod5.z.object({}).strict().optional();
|
|
4104
4978
|
var rootDataListArgsSchema = import_zod5.z.object({
|
|
4105
4979
|
select: projectionSchema.optional(),
|
|
4106
4980
|
sort: import_zod5.z.string().optional(),
|
|
4107
|
-
skip: import_zod5.z.union([nonNegativeIntegerSchema,
|
|
4108
|
-
limit: import_zod5.z.union([
|
|
4109
|
-
page: import_zod5.z.union([nonNegativeIntegerSchema,
|
|
4110
|
-
pageSize: import_zod5.z.union([
|
|
4981
|
+
skip: import_zod5.z.union([nonNegativeIntegerSchema, nonNegativeIntegerString]).optional(),
|
|
4982
|
+
limit: import_zod5.z.union([positiveIntegerSchema, positiveIntegerString]).optional(),
|
|
4983
|
+
page: import_zod5.z.union([nonNegativeIntegerSchema, nonNegativeIntegerString]).optional(),
|
|
4984
|
+
pageSize: import_zod5.z.union([positiveIntegerSchema, positiveIntegerString]).optional()
|
|
4111
4985
|
}).passthrough();
|
|
4112
4986
|
var rootDataListOptionsSchema = import_zod5.z.object({
|
|
4113
4987
|
includeCount: import_zod5.z.boolean().optional()
|
|
@@ -4115,8 +4989,21 @@ var rootDataListOptionsSchema = import_zod5.z.object({
|
|
|
4115
4989
|
var rootDataReadArgsSchema = import_zod5.z.object({
|
|
4116
4990
|
select: projectionSchema.optional()
|
|
4117
4991
|
}).passthrough();
|
|
4992
|
+
var rootModelNewArgsSchema = import_zod5.z.object({
|
|
4993
|
+
select: projectionSchema.optional()
|
|
4994
|
+
}).passthrough();
|
|
4995
|
+
var rootModelNewOptionsSchema = import_zod5.z.object({
|
|
4996
|
+
skim: import_zod5.z.boolean().optional(),
|
|
4997
|
+
includePermissions: import_zod5.z.boolean().optional()
|
|
4998
|
+
}).passthrough();
|
|
4118
4999
|
var rootModelQueryEntrySchema = import_zod5.z.union([
|
|
4119
|
-
import_zod5.z.object({
|
|
5000
|
+
import_zod5.z.object({
|
|
5001
|
+
...rootEntryBaseSchema,
|
|
5002
|
+
target: import_zod5.z.literal("model"),
|
|
5003
|
+
op: import_zod5.z.literal("new"),
|
|
5004
|
+
args: rootModelNewArgsSchema.optional(),
|
|
5005
|
+
options: rootModelNewOptionsSchema.optional()
|
|
5006
|
+
}).passthrough(),
|
|
4120
5007
|
import_zod5.z.object({
|
|
4121
5008
|
...rootEntryBaseSchema,
|
|
4122
5009
|
target: import_zod5.z.literal("model"),
|
|
@@ -4231,7 +5118,7 @@ var rootModelQueryEntrySchema = import_zod5.z.union([
|
|
|
4231
5118
|
op: import_zod5.z.literal("count"),
|
|
4232
5119
|
filter: objectOrArraySchema.optional(),
|
|
4233
5120
|
options: rootModelCountOptionsSchema.optional()
|
|
4234
|
-
}).passthrough()
|
|
5121
|
+
}).passthrough().superRefine((body, ctx) => rejectKeys(body, ctx, ["access"]))
|
|
4235
5122
|
]);
|
|
4236
5123
|
var rootDataQueryEntrySchema = import_zod5.z.union([
|
|
4237
5124
|
import_zod5.z.object({
|
|
@@ -4260,7 +5147,7 @@ var rootDataQueryEntrySchema = import_zod5.z.union([
|
|
|
4260
5147
|
var rootQuerySchema = import_zod5.z.array(import_zod5.z.union([rootModelQueryEntrySchema, rootDataQueryEntrySchema]));
|
|
4261
5148
|
|
|
4262
5149
|
// src/routers/shared.ts
|
|
4263
|
-
var
|
|
5150
|
+
var import_utils23 = require("@web-ts-toolkit/utils");
|
|
4264
5151
|
|
|
4265
5152
|
// src/routers/model-router-collection-routes.ts
|
|
4266
5153
|
function setModelCollectionRoutes(context) {
|
|
@@ -4278,14 +5165,14 @@ function setModelCollectionRoutes(context) {
|
|
|
4278
5165
|
await context.assertAllowed(req, "list");
|
|
4279
5166
|
const { skip, limit, page, page_size, skim, include_permissions, include_count, include_extra_headers } = parseQuery(requestSchemas.listQuery, req.query);
|
|
4280
5167
|
const svc = context.getPublicService(req);
|
|
4281
|
-
const includeCount = (0,
|
|
4282
|
-
const includeExtraHeaders = (0,
|
|
5168
|
+
const includeCount = (0, import_utils23.parseBooleanString)(include_count);
|
|
5169
|
+
const includeExtraHeaders = (0, import_utils23.parseBooleanString)(include_extra_headers);
|
|
4283
5170
|
const result = await svc._list(
|
|
4284
5171
|
{},
|
|
4285
5172
|
{ skip, limit, page, pageSize: page_size },
|
|
4286
5173
|
{
|
|
4287
|
-
skim: (0,
|
|
4288
|
-
includePermissions: (0,
|
|
5174
|
+
skim: (0, import_utils23.parseBooleanString)(skim),
|
|
5175
|
+
includePermissions: (0, import_utils23.parseBooleanString)(include_permissions),
|
|
4289
5176
|
includeCount
|
|
4290
5177
|
}
|
|
4291
5178
|
);
|
|
@@ -4340,7 +5227,7 @@ function setModelCollectionRoutes(context) {
|
|
|
4340
5227
|
context.getRequestSchema("requestSchemas.create")
|
|
4341
5228
|
);
|
|
4342
5229
|
const svc = context.getPublicService(req);
|
|
4343
|
-
const result = await svc._create(data, {}, { includePermissions: (0,
|
|
5230
|
+
const result = await svc._create(data, {}, { includePermissions: (0, import_utils23.parseBooleanString)(include_permissions) });
|
|
4344
5231
|
handleResultError(result);
|
|
4345
5232
|
return formatModelCreatedResponse(result);
|
|
4346
5233
|
});
|
|
@@ -4374,7 +5261,7 @@ function setModelCollectionRoutes(context) {
|
|
|
4374
5261
|
data,
|
|
4375
5262
|
{ select, populate, tasks },
|
|
4376
5263
|
{
|
|
4377
|
-
includePermissions: includePermissions ?? (0,
|
|
5264
|
+
includePermissions: includePermissions ?? (0, import_utils23.parseBooleanString)(include_permissions),
|
|
4378
5265
|
populateAccess
|
|
4379
5266
|
}
|
|
4380
5267
|
);
|
|
@@ -4390,8 +5277,9 @@ function setModelCollectionRoutes(context) {
|
|
|
4390
5277
|
body: defineOpenApiSchemaResolver(getAdvancedCreateOpenApiBody)
|
|
4391
5278
|
});
|
|
4392
5279
|
router.get("/new", async (req) => {
|
|
5280
|
+
await context.assertAllowed(req, "new");
|
|
4393
5281
|
const svc = context.getPublicService(req);
|
|
4394
|
-
const result = await svc._new();
|
|
5282
|
+
const result = await svc._new({}, { includePermissions: false });
|
|
4395
5283
|
handleResultError(result);
|
|
4396
5284
|
return unwrapServiceData(result);
|
|
4397
5285
|
});
|
|
@@ -4439,16 +5327,13 @@ function setModelDocumentRoutes(context) {
|
|
|
4439
5327
|
});
|
|
4440
5328
|
router.post("/count", async (req) => {
|
|
4441
5329
|
await context.assertAllowed(req, "count");
|
|
4442
|
-
const { filter: filter2
|
|
5330
|
+
const { filter: filter2 } = await parseBodyWithSchema(
|
|
4443
5331
|
countBodySchema,
|
|
4444
5332
|
req.body,
|
|
4445
5333
|
context.getRequestSchema("requestSchemas.count")
|
|
4446
5334
|
);
|
|
4447
5335
|
const svc = context.getPublicService(req);
|
|
4448
|
-
const result = await svc._count(
|
|
4449
|
-
filter2 ?? {},
|
|
4450
|
-
countOptions.access
|
|
4451
|
-
);
|
|
5336
|
+
const result = await svc._count(filter2 ?? {});
|
|
4452
5337
|
handleResultError(result);
|
|
4453
5338
|
return unwrapServiceData(result);
|
|
4454
5339
|
});
|
|
@@ -4468,8 +5353,8 @@ function setModelDocumentRoutes(context) {
|
|
|
4468
5353
|
id,
|
|
4469
5354
|
{},
|
|
4470
5355
|
{
|
|
4471
|
-
includePermissions: (0,
|
|
4472
|
-
tryList: (0,
|
|
5356
|
+
includePermissions: (0, import_utils23.parseBooleanString)(include_permissions),
|
|
5357
|
+
tryList: (0, import_utils23.parseBooleanString)(try_list)
|
|
4473
5358
|
}
|
|
4474
5359
|
);
|
|
4475
5360
|
handleResultError(result);
|
|
@@ -4554,8 +5439,8 @@ function setModelDocumentRoutes(context) {
|
|
|
4554
5439
|
data,
|
|
4555
5440
|
{},
|
|
4556
5441
|
{
|
|
4557
|
-
returningAll: (0,
|
|
4558
|
-
includePermissions: (0,
|
|
5442
|
+
returningAll: (0, import_utils23.parseBooleanString)(returning_all),
|
|
5443
|
+
includePermissions: (0, import_utils23.parseBooleanString)(include_permissions)
|
|
4559
5444
|
}
|
|
4560
5445
|
);
|
|
4561
5446
|
handleResultError(result);
|
|
@@ -4593,8 +5478,8 @@ function setModelDocumentRoutes(context) {
|
|
|
4593
5478
|
data,
|
|
4594
5479
|
{ select, populate, tasks },
|
|
4595
5480
|
{
|
|
4596
|
-
returningAll: returningAll ?? (0,
|
|
4597
|
-
includePermissions: includePermissions ?? (0,
|
|
5481
|
+
returningAll: returningAll ?? (0, import_utils23.parseBooleanString)(returning_all),
|
|
5482
|
+
includePermissions: includePermissions ?? (0, import_utils23.parseBooleanString)(include_permissions),
|
|
4598
5483
|
populateAccess
|
|
4599
5484
|
}
|
|
4600
5485
|
);
|
|
@@ -4622,8 +5507,8 @@ function setModelDocumentRoutes(context) {
|
|
|
4622
5507
|
body,
|
|
4623
5508
|
{},
|
|
4624
5509
|
{
|
|
4625
|
-
returningAll: (0,
|
|
4626
|
-
includePermissions: (0,
|
|
5510
|
+
returningAll: (0, import_utils23.parseBooleanString)(returning_all),
|
|
5511
|
+
includePermissions: (0, import_utils23.parseBooleanString)(include_permissions)
|
|
4627
5512
|
}
|
|
4628
5513
|
);
|
|
4629
5514
|
handleResultError(result);
|
|
@@ -4659,8 +5544,8 @@ function setModelDocumentRoutes(context) {
|
|
|
4659
5544
|
data,
|
|
4660
5545
|
{ select, populate, tasks },
|
|
4661
5546
|
{
|
|
4662
|
-
returningAll: returningAll ?? (0,
|
|
4663
|
-
includePermissions: includePermissions ?? (0,
|
|
5547
|
+
returningAll: returningAll ?? (0, import_utils23.parseBooleanString)(returning_all),
|
|
5548
|
+
includePermissions: includePermissions ?? (0, import_utils23.parseBooleanString)(include_permissions),
|
|
4664
5549
|
populateAccess
|
|
4665
5550
|
}
|
|
4666
5551
|
);
|
|
@@ -4726,7 +5611,6 @@ function setModelDocumentRoutes(context) {
|
|
|
4726
5611
|
}
|
|
4727
5612
|
|
|
4728
5613
|
// src/routers/model-router-subdocument-routes.ts
|
|
4729
|
-
var import_utils23 = require("@web-ts-toolkit/utils");
|
|
4730
5614
|
function setModelSubDocumentRoutes(context) {
|
|
4731
5615
|
const subs = getModelSub(context.modelName);
|
|
4732
5616
|
for (let x = 0; x < subs.length; x++) {
|
|
@@ -4756,7 +5640,10 @@ function setModelSubDocumentRoutes(context) {
|
|
|
4756
5640
|
context.getRequestSchema("requestSchemas.subList")
|
|
4757
5641
|
);
|
|
4758
5642
|
const svc = context.getPublicService(req);
|
|
4759
|
-
const result = await svc.listSub(id, sub, {
|
|
5643
|
+
const result = await svc.listSub(id, sub, {
|
|
5644
|
+
filter: body.filter ?? {},
|
|
5645
|
+
select: body.select ?? []
|
|
5646
|
+
});
|
|
4760
5647
|
handleResultError(result);
|
|
4761
5648
|
return unwrapServiceData(result);
|
|
4762
5649
|
}
|
|
@@ -4816,10 +5703,11 @@ function setModelSubDocumentRoutes(context) {
|
|
|
4816
5703
|
req.body,
|
|
4817
5704
|
context.getRequestSchema("requestSchemas.subRead")
|
|
4818
5705
|
);
|
|
4819
|
-
const populate = body.populate;
|
|
4820
|
-
const normalizedPopulate = Array.isArray(populate) ? populate.map((item) => (0, import_utils23.isString)(item) ? { path: item } : item) : (0, import_utils23.isString)(populate) ? { path: populate } : populate ?? [];
|
|
4821
5706
|
const svc = context.getPublicService(req);
|
|
4822
|
-
const result = await svc.readSub(id, sub, subId, {
|
|
5707
|
+
const result = await svc.readSub(id, sub, subId, {
|
|
5708
|
+
select: body.select ?? [],
|
|
5709
|
+
populate: normalizeSubPopulate(body.populate)
|
|
5710
|
+
});
|
|
4823
5711
|
handleResultError(result);
|
|
4824
5712
|
return unwrapServiceData(result);
|
|
4825
5713
|
}
|
|
@@ -4913,7 +5801,7 @@ function setOption(parentKey, optionKey, option) {
|
|
|
4913
5801
|
this.runtime.setModelOption(this.modelName, key, value);
|
|
4914
5802
|
return this;
|
|
4915
5803
|
}
|
|
4916
|
-
var ModelRouter = class {
|
|
5804
|
+
var ModelRouter = class _ModelRouter {
|
|
4917
5805
|
constructor(modelName, initialOptions, runtime = defaultRuntime) {
|
|
4918
5806
|
/**
|
|
4919
5807
|
* The maximum limit of the number of documents returned from the `list` operation.
|
|
@@ -5067,17 +5955,21 @@ var ModelRouter = class {
|
|
|
5067
5955
|
);
|
|
5068
5956
|
this.runtime = runtime;
|
|
5069
5957
|
this.runtime.setModelOptions(modelName, initialOptions);
|
|
5070
|
-
attachRuntimeToModel(modelName, this.runtime);
|
|
5071
5958
|
this.options = this.runtime.getModelOptions(modelName);
|
|
5072
5959
|
this.fullBasePath = (0, import_utils25.normalizeUrlPath)(this.options.parentPath + this.options.basePath);
|
|
5073
5960
|
this.modelName = modelName;
|
|
5074
5961
|
this.router = new import_express_json_router5.default(this.options.basePath, createSetCore(this.runtime), accessRouterResponseHandler);
|
|
5075
|
-
this.model = new model_default(modelName);
|
|
5962
|
+
this.model = new model_default(modelName, this.runtime);
|
|
5076
5963
|
this.setCollectionRoutes();
|
|
5077
5964
|
this.setDocumentRoutes();
|
|
5078
5965
|
this.setSubDocumentRoutes();
|
|
5079
5966
|
this.logEndpoints();
|
|
5080
5967
|
}
|
|
5968
|
+
static fromModel(model, initialOptions, runtime = defaultRuntime) {
|
|
5969
|
+
const modelName = String(model.modelName);
|
|
5970
|
+
runtime.registerModelInstance(modelName, model);
|
|
5971
|
+
return new _ModelRouter(modelName, initialOptions, runtime);
|
|
5972
|
+
}
|
|
5081
5973
|
getRequestSchema(key) {
|
|
5082
5974
|
return this.runtime.getExactModelOption(
|
|
5083
5975
|
this.modelName,
|
|
@@ -5141,8 +6033,8 @@ var ModelRouter = class {
|
|
|
5141
6033
|
}
|
|
5142
6034
|
logEndpoints() {
|
|
5143
6035
|
runWithRuntime(this.runtime, () => {
|
|
5144
|
-
(0, import_utils25.forEach)(this.router.
|
|
5145
|
-
|
|
6036
|
+
(0, import_utils25.forEach)(this.router.getEndpoints(), ({ method, path }) => {
|
|
6037
|
+
logInfoMessage(`${(0, import_utils25.padEnd)(method, 6)} ${(0, import_utils25.normalizeUrlPath)(this.options.parentPath + path)}`);
|
|
5146
6038
|
});
|
|
5147
6039
|
});
|
|
5148
6040
|
}
|
|
@@ -5346,16 +6238,19 @@ async function decorateDataSingleResult(svc, result, context = {}) {
|
|
|
5346
6238
|
|
|
5347
6239
|
// src/routers/root-router.ts
|
|
5348
6240
|
var clientErrors3 = import_express_json_router6.default.clientErrors;
|
|
6241
|
+
var DEFAULT_MAX_BATCH_ENTRIES = 100;
|
|
6242
|
+
var DEFAULT_MAX_ORDER_GROUPS = 25;
|
|
6243
|
+
var DEFAULT_MAX_CONCURRENT_OPERATIONS = 10;
|
|
5349
6244
|
var createErrorResult = (code, detail) => ({
|
|
5350
6245
|
success: false,
|
|
6246
|
+
kind: "error",
|
|
5351
6247
|
code,
|
|
5352
6248
|
errors: [{ detail }]
|
|
5353
6249
|
});
|
|
5354
|
-
var normalizeSubPopulate = (populate) => Array.isArray(populate) ? populate.map((item) => (0, import_utils26.isString)(item) ? { path: item } : item) : (0, import_utils26.isString)(populate) ? { path: populate } : populate ?? [];
|
|
5355
6250
|
var RootRouter = class {
|
|
5356
6251
|
constructor(options = { basePath: "", operationAccess: true }, runtime = defaultRuntime) {
|
|
5357
6252
|
this.modelOperations = {
|
|
5358
|
-
new: async (req, item) => req.macl.getPublicService(item.name)._new(),
|
|
6253
|
+
new: async (req, item) => req.macl.getPublicService(item.name)._new(item.args, item.options),
|
|
5359
6254
|
list: async (req, item) => req.macl.getPublicService(item.name)._list(item.filter ?? {}, item.args, item.options),
|
|
5360
6255
|
read: async (req, item) => {
|
|
5361
6256
|
const svc = req.macl.getPublicService(item.name);
|
|
@@ -5378,7 +6273,7 @@ var RootRouter = class {
|
|
|
5378
6273
|
subBulkUpdate: async (req, item) => req.macl.getPublicService(item.name).bulkUpdateSub(item.id, item.sub, item.data),
|
|
5379
6274
|
subDelete: async (req, item) => req.macl.getPublicService(item.name).deleteSub(item.id, item.sub, item.subId),
|
|
5380
6275
|
distinct: async (req, item) => req.macl.getPublicService(item.name)._distinct(item.field, { filter: item.filter }),
|
|
5381
|
-
count: async (req, item) => req.macl.getPublicService(item.name)._count(item.filter ?? {}
|
|
6276
|
+
count: async (req, item) => req.macl.getPublicService(item.name)._count(item.filter ?? {})
|
|
5382
6277
|
};
|
|
5383
6278
|
this.dataOperations = {
|
|
5384
6279
|
list: async (req, item) => {
|
|
@@ -5413,10 +6308,16 @@ var RootRouter = class {
|
|
|
5413
6308
|
return decorateDataSingleResult(svc, result, { dataName: item.name });
|
|
5414
6309
|
}
|
|
5415
6310
|
};
|
|
5416
|
-
const { basePath, operationAccess } = options;
|
|
6311
|
+
const { basePath, operationAccess, maxBatchEntries, maxOrderGroups, maxConcurrentOperations } = options;
|
|
5417
6312
|
this.runtime = runtime;
|
|
5418
6313
|
this.basename = basePath || "";
|
|
5419
6314
|
this.operationAccess = operationAccess;
|
|
6315
|
+
this.maxBatchEntries = this.normalizePositiveLimit(maxBatchEntries, DEFAULT_MAX_BATCH_ENTRIES);
|
|
6316
|
+
this.maxOrderGroups = this.normalizePositiveLimit(maxOrderGroups, DEFAULT_MAX_ORDER_GROUPS);
|
|
6317
|
+
this.maxConcurrentOperations = this.normalizePositiveLimit(
|
|
6318
|
+
maxConcurrentOperations,
|
|
6319
|
+
DEFAULT_MAX_CONCURRENT_OPERATIONS
|
|
6320
|
+
);
|
|
5420
6321
|
this.router = new import_express_json_router6.default(
|
|
5421
6322
|
this.basename,
|
|
5422
6323
|
[createSetCore(this.runtime), createSetDataCore(this.runtime)],
|
|
@@ -5433,7 +6334,7 @@ var RootRouter = class {
|
|
|
5433
6334
|
target: item.target,
|
|
5434
6335
|
name: item.name,
|
|
5435
6336
|
op: item.op,
|
|
5436
|
-
result,
|
|
6337
|
+
result: toPublicServiceResult(result),
|
|
5437
6338
|
message: mapCodeToMessage(result.code),
|
|
5438
6339
|
statusCode: mapCodeToStatusCode(result.code)
|
|
5439
6340
|
};
|
|
@@ -5484,17 +6385,63 @@ var RootRouter = class {
|
|
|
5484
6385
|
return this.wrapResult(index, item, await handler(req, item));
|
|
5485
6386
|
}
|
|
5486
6387
|
groupItemsByOrder(items) {
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
|
|
5490
|
-
|
|
6388
|
+
const groups = /* @__PURE__ */ new Map();
|
|
6389
|
+
items.forEach((item, index) => {
|
|
6390
|
+
const order = (0, import_utils26.isNumber)(item.order) ? item.order : 0;
|
|
6391
|
+
const group = groups.get(order);
|
|
6392
|
+
if (group) {
|
|
6393
|
+
group.push({ index, item });
|
|
6394
|
+
return;
|
|
6395
|
+
}
|
|
6396
|
+
groups.set(order, [{ index, item }]);
|
|
6397
|
+
});
|
|
6398
|
+
return Array.from(groups.entries()).sort(([leftOrder], [rightOrder]) => leftOrder - rightOrder).map(([, group]) => group);
|
|
6399
|
+
}
|
|
6400
|
+
normalizePositiveLimit(value, fallback) {
|
|
6401
|
+
return Number.isSafeInteger(value) && Number(value) > 0 ? Number(value) : fallback;
|
|
6402
|
+
}
|
|
6403
|
+
validateBatch(items) {
|
|
6404
|
+
if (items.length > this.maxBatchEntries) {
|
|
6405
|
+
throw new clientErrors3.BadRequestError("Bad Request", {
|
|
6406
|
+
errors: [{ detail: `Root batch exceeds maximum size of ${this.maxBatchEntries}` }]
|
|
6407
|
+
});
|
|
6408
|
+
}
|
|
6409
|
+
const orders = /* @__PURE__ */ new Set();
|
|
6410
|
+
for (const item of items) {
|
|
6411
|
+
const order = item.order ?? 0;
|
|
6412
|
+
if (!Number.isSafeInteger(order) || order < 0) {
|
|
6413
|
+
throw new clientErrors3.BadRequestError("Bad Request", {
|
|
6414
|
+
errors: [{ detail: "Root batch order must be a non-negative safe integer" }]
|
|
6415
|
+
});
|
|
5491
6416
|
}
|
|
5492
|
-
if (
|
|
5493
|
-
|
|
6417
|
+
if (order > this.maxOrderGroups) {
|
|
6418
|
+
throw new clientErrors3.BadRequestError("Bad Request", {
|
|
6419
|
+
errors: [{ detail: `Root batch order exceeds maximum group index of ${this.maxOrderGroups}` }]
|
|
6420
|
+
});
|
|
5494
6421
|
}
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
6422
|
+
orders.add(order);
|
|
6423
|
+
}
|
|
6424
|
+
if (orders.size > this.maxOrderGroups) {
|
|
6425
|
+
throw new clientErrors3.BadRequestError("Bad Request", {
|
|
6426
|
+
errors: [{ detail: `Root batch exceeds maximum order groups of ${this.maxOrderGroups}` }]
|
|
6427
|
+
});
|
|
6428
|
+
}
|
|
6429
|
+
}
|
|
6430
|
+
async runGroup(req, entries) {
|
|
6431
|
+
const results = new Array(entries.length);
|
|
6432
|
+
let cursor = 0;
|
|
6433
|
+
const workerCount = Math.min(this.maxConcurrentOperations, entries.length);
|
|
6434
|
+
await Promise.all(
|
|
6435
|
+
Array.from({ length: workerCount }, async () => {
|
|
6436
|
+
while (cursor < entries.length) {
|
|
6437
|
+
const current = cursor;
|
|
6438
|
+
cursor += 1;
|
|
6439
|
+
const { item, index } = entries[current];
|
|
6440
|
+
results[current] = await this.processOp(req, item, index);
|
|
6441
|
+
}
|
|
6442
|
+
})
|
|
6443
|
+
);
|
|
6444
|
+
return results;
|
|
5498
6445
|
}
|
|
5499
6446
|
async assertAllowed(req) {
|
|
5500
6447
|
const allowed = await req.macl.canActivate(this.operationAccess);
|
|
@@ -5504,10 +6451,11 @@ var RootRouter = class {
|
|
|
5504
6451
|
this.router.post("", async (req) => {
|
|
5505
6452
|
await this.assertAllowed(req);
|
|
5506
6453
|
const items = parseBody(rootQuerySchema, req.body);
|
|
6454
|
+
this.validateBatch(items);
|
|
5507
6455
|
const groupedItems = this.groupItemsByOrder(items);
|
|
5508
6456
|
const results = [];
|
|
5509
6457
|
for (let x = 0; x < groupedItems.length; x++) {
|
|
5510
|
-
const arrResult = await
|
|
6458
|
+
const arrResult = await this.runGroup(req, groupedItems[x]);
|
|
5511
6459
|
results.push(...arrResult);
|
|
5512
6460
|
}
|
|
5513
6461
|
return (0, import_utils26.orderBy)(results, ["index"], ["asc"]);
|
|
@@ -5517,7 +6465,8 @@ var RootRouter = class {
|
|
|
5517
6465
|
path: "",
|
|
5518
6466
|
operationId: "root.query",
|
|
5519
6467
|
summary: "Execute batched model and data operations",
|
|
5520
|
-
body: rootQuerySchema
|
|
6468
|
+
body: rootQuerySchema,
|
|
6469
|
+
idempotent: true
|
|
5521
6470
|
});
|
|
5522
6471
|
}
|
|
5523
6472
|
get routes() {
|
|
@@ -5600,8 +6549,8 @@ var DataRouter = class {
|
|
|
5600
6549
|
req.query
|
|
5601
6550
|
);
|
|
5602
6551
|
const svc = this.getService(req);
|
|
5603
|
-
const includeCount = (0,
|
|
5604
|
-
const includeExtraHeaders = (0,
|
|
6552
|
+
const includeCount = (0, import_utils23.parseBooleanString)(include_count);
|
|
6553
|
+
const includeExtraHeaders = (0, import_utils23.parseBooleanString)(include_extra_headers);
|
|
5605
6554
|
const result = await svc.find(
|
|
5606
6555
|
{},
|
|
5607
6556
|
{ skip, limit, page, pageSize: page_size },
|
|
@@ -5664,7 +6613,7 @@ var DataRouter = class {
|
|
|
5664
6613
|
const result = await svc.findById(id, {}, {});
|
|
5665
6614
|
handleResultError(result);
|
|
5666
6615
|
const decoratedResult = await decorateDataSingleResult(svc, result);
|
|
5667
|
-
return decoratedResult
|
|
6616
|
+
return unwrapServiceData(decoratedResult);
|
|
5668
6617
|
});
|
|
5669
6618
|
this.registerOpenApiRoute({
|
|
5670
6619
|
method: "get",
|
|
@@ -5683,7 +6632,7 @@ var DataRouter = class {
|
|
|
5683
6632
|
const result = await svc.findOne(filter2 ?? {}, { select }, {});
|
|
5684
6633
|
handleResultError(result);
|
|
5685
6634
|
const decoratedResult = await decorateDataSingleResult(svc, result);
|
|
5686
|
-
return decoratedResult
|
|
6635
|
+
return unwrapServiceData(decoratedResult);
|
|
5687
6636
|
});
|
|
5688
6637
|
this.registerOpenApiRoute({
|
|
5689
6638
|
method: "post",
|
|
@@ -5706,7 +6655,7 @@ var DataRouter = class {
|
|
|
5706
6655
|
const result = await svc.findById(id, { select }, {});
|
|
5707
6656
|
handleResultError(result);
|
|
5708
6657
|
const decoratedResult = await decorateDataSingleResult(svc, result);
|
|
5709
|
-
return decoratedResult
|
|
6658
|
+
return unwrapServiceData(decoratedResult);
|
|
5710
6659
|
});
|
|
5711
6660
|
this.registerOpenApiRoute({
|
|
5712
6661
|
method: "post",
|
|
@@ -5751,6 +6700,9 @@ var import_express_json_router8 = __toESM(require("@web-ts-toolkit/express-json-
|
|
|
5751
6700
|
var import_express = __toESM(require("express"));
|
|
5752
6701
|
var import_node_path = require("path");
|
|
5753
6702
|
var escapeHtml = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
|
|
6703
|
+
var escapeInlineScriptJson = (value) => {
|
|
6704
|
+
return value.replace(/<\/script/gi, "<\\/script").replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
6705
|
+
};
|
|
5754
6706
|
var renderDocsHtml = (specPath, title, cssUrl, bundleUrl) => `<!doctype html>
|
|
5755
6707
|
<html lang="en">
|
|
5756
6708
|
<head>
|
|
@@ -5764,7 +6716,7 @@ var renderDocsHtml = (specPath, title, cssUrl, bundleUrl) => `<!doctype html>
|
|
|
5764
6716
|
<script src="${escapeHtml(bundleUrl)}"></script>
|
|
5765
6717
|
<script>
|
|
5766
6718
|
window.ui = SwaggerUIBundle({
|
|
5767
|
-
url: ${JSON.stringify(specPath)},
|
|
6719
|
+
url: ${escapeInlineScriptJson(JSON.stringify(specPath))},
|
|
5768
6720
|
dom_id: '#swagger-ui',
|
|
5769
6721
|
deepLinking: true,
|
|
5770
6722
|
presets: [SwaggerUIBundle.presets.apis],
|
|
@@ -5819,10 +6771,10 @@ var accessRouterResponseHandler = import_express_json_router8.default.createHand
|
|
|
5819
6771
|
errorFormat: import_express_json_router8.default.ErrorFormats.rfc9457,
|
|
5820
6772
|
rfc9457ContentType: "application/problem+json"
|
|
5821
6773
|
});
|
|
5822
|
-
accessRouterResponseHandler.errorMessageProvider = function(
|
|
5823
|
-
const errorLike =
|
|
5824
|
-
|
|
5825
|
-
return errorLike.message || errorLike._message || String(
|
|
6774
|
+
accessRouterResponseHandler.errorMessageProvider = function(error2) {
|
|
6775
|
+
const errorLike = error2;
|
|
6776
|
+
error(error2);
|
|
6777
|
+
return errorLike.message || errorLike._message || String(error2);
|
|
5826
6778
|
};
|
|
5827
6779
|
|
|
5828
6780
|
// src/plugins.ts
|
|
@@ -5835,6 +6787,7 @@ function permissionsPlugin(schema, options) {
|
|
|
5835
6787
|
}
|
|
5836
6788
|
|
|
5837
6789
|
// src/index.ts
|
|
6790
|
+
var isMongooseModel = (value) => typeof value === "function" && value !== null && "modelName" in value && "schema" in value;
|
|
5838
6791
|
function createRuntimeApi(runtime) {
|
|
5839
6792
|
const runtimeMiddleware = function() {
|
|
5840
6793
|
return createSetCore(runtime);
|
|
@@ -5842,8 +6795,18 @@ function createRuntimeApi(runtime) {
|
|
|
5842
6795
|
const wtt2 = runtimeMiddleware;
|
|
5843
6796
|
wtt2.runtime = runtime;
|
|
5844
6797
|
wtt2.createRouter = function(modelName, options) {
|
|
5845
|
-
|
|
5846
|
-
|
|
6798
|
+
if ((0, import_utils28.isUndefined)(options)) {
|
|
6799
|
+
return new RootRouter(modelName, runtime);
|
|
6800
|
+
}
|
|
6801
|
+
if (typeof modelName === "string") {
|
|
6802
|
+
return new ModelRouter(modelName, options, runtime);
|
|
6803
|
+
}
|
|
6804
|
+
if (isMongooseModel(modelName)) {
|
|
6805
|
+
const name = String(modelName.modelName);
|
|
6806
|
+
runtime.registerModelInstance(name, modelName);
|
|
6807
|
+
return ModelRouter.fromModel(modelName, options, runtime);
|
|
6808
|
+
}
|
|
6809
|
+
throw new TypeError(`createRouter: unsupported first argument type: ${typeof modelName}`);
|
|
5847
6810
|
};
|
|
5848
6811
|
wtt2.createDataRouter = function(dataName, options) {
|
|
5849
6812
|
return new DataRouter(dataName, options, runtime);
|
|
@@ -5874,6 +6837,9 @@ function createRuntimeApi(runtime) {
|
|
|
5874
6837
|
wtt2.setDefaultModelOption = runtime.setDefaultModelOption.bind(runtime);
|
|
5875
6838
|
wtt2.getDefaultModelOptions = runtime.getDefaultModelOptions.bind(runtime);
|
|
5876
6839
|
wtt2.getDefaultModelOption = runtime.getDefaultModelOption.bind(runtime);
|
|
6840
|
+
wtt2.registerModelInstance = runtime.registerModelInstance.bind(runtime);
|
|
6841
|
+
wtt2.hasModelInstance = runtime.hasModelInstance.bind(runtime);
|
|
6842
|
+
wtt2.getModelInstance = runtime.getModelInstance.bind(runtime);
|
|
5877
6843
|
wtt2.RootRouter = RootRouter;
|
|
5878
6844
|
wtt2.ModelRouter = ModelRouter;
|
|
5879
6845
|
wtt2.DataRouter = DataRouter;
|
|
@@ -5890,11 +6856,14 @@ var index_default = acl;
|
|
|
5890
6856
|
AccessRuntime,
|
|
5891
6857
|
DataRouter,
|
|
5892
6858
|
ModelRouter,
|
|
6859
|
+
OpenApiCollisionError,
|
|
6860
|
+
OpenApiRegistry,
|
|
5893
6861
|
RootRouter,
|
|
5894
6862
|
acl,
|
|
5895
6863
|
combineRoutes,
|
|
5896
6864
|
createAccessRuntime,
|
|
5897
6865
|
createOpenApiRouter,
|
|
6866
|
+
defaultRuntime,
|
|
5898
6867
|
defineRequestSchema,
|
|
5899
6868
|
fromAjv,
|
|
5900
6869
|
fromArkType,
|
|
@@ -5910,12 +6879,19 @@ var index_default = acl;
|
|
|
5910
6879
|
getDefaultModelOptions,
|
|
5911
6880
|
getGlobalOption,
|
|
5912
6881
|
getGlobalOptions,
|
|
6882
|
+
getModelInstance,
|
|
5913
6883
|
getModelJsonSchema,
|
|
5914
6884
|
getModelNames,
|
|
5915
6885
|
getModelOption,
|
|
5916
6886
|
getModelOptions,
|
|
5917
6887
|
guard,
|
|
6888
|
+
hasModelInstance,
|
|
6889
|
+
isLevelEnabled,
|
|
5918
6890
|
permissionsPlugin,
|
|
6891
|
+
redactFilter,
|
|
6892
|
+
redactPayload,
|
|
6893
|
+
registerModelInstance,
|
|
6894
|
+
safeStringify,
|
|
5919
6895
|
setDefaultModelOption,
|
|
5920
6896
|
setDefaultModelOptions,
|
|
5921
6897
|
setGlobalOption,
|