@web-ts-toolkit/access-router 0.45.0 → 0.47.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 +255 -9
- package/advanced.d.mts +1 -1
- package/advanced.d.ts +1 -1
- package/advanced.js +40 -17
- package/advanced.mjs +42 -18
- package/index.d.mts +5 -186
- package/index.d.ts +5 -186
- package/index.js +622 -291
- package/index.mjs +485 -152
- package/llms.txt +68 -3
- package/package.json +3 -3
- package/{parsers-DppKW-Fd.d.mts → parsers-BidvJUu7.d.mts} +353 -9
- package/{parsers-DppKW-Fd.d.ts → parsers-BidvJUu7.d.ts} +353 -9
package/index.js
CHANGED
|
@@ -77,14 +77,14 @@ __export(index_exports, {
|
|
|
77
77
|
setModelOptions: () => setModelOptions
|
|
78
78
|
});
|
|
79
79
|
module.exports = __toCommonJS(index_exports);
|
|
80
|
-
var
|
|
80
|
+
var import_utils32 = require("@web-ts-toolkit/utils");
|
|
81
81
|
|
|
82
82
|
// src/middleware.ts
|
|
83
83
|
var import_express_json_router2 = __toESM(require("@web-ts-toolkit/express-json-router"));
|
|
84
|
-
var
|
|
84
|
+
var import_utils25 = require("@web-ts-toolkit/utils");
|
|
85
85
|
|
|
86
86
|
// src/core.ts
|
|
87
|
-
var
|
|
87
|
+
var import_utils24 = require("@web-ts-toolkit/utils");
|
|
88
88
|
|
|
89
89
|
// src/runtime.ts
|
|
90
90
|
var import_mongoose3 = __toESM(require("mongoose"));
|
|
@@ -122,8 +122,12 @@ var mapWithConcurrencyLimit = async (items, limit, iteratee) => {
|
|
|
122
122
|
};
|
|
123
123
|
var RequestConcurrencyScheduler = class {
|
|
124
124
|
constructor(limit) {
|
|
125
|
+
this.activeWork = 0;
|
|
126
|
+
this.waiters = [];
|
|
125
127
|
this.limit = Math.max(1, limit);
|
|
126
128
|
}
|
|
129
|
+
// Each map bounds its own orchestration workers. Recursive maps never hold
|
|
130
|
+
// shared work permits while awaiting descendants (including at limit 1).
|
|
127
131
|
async map(items, iteratee) {
|
|
128
132
|
return mapWithConcurrencyLimit(items, this.limit, (item, index) => iteratee(item, index, true));
|
|
129
133
|
}
|
|
@@ -131,6 +135,23 @@ var RequestConcurrencyScheduler = class {
|
|
|
131
135
|
const [result] = await this.map([void 0], () => iteratee(true));
|
|
132
136
|
return result;
|
|
133
137
|
}
|
|
138
|
+
// Only leaf persistence operations belong here, not service/hook callbacks.
|
|
139
|
+
// All work calls on this scheduler share a FIFO admission ceiling.
|
|
140
|
+
async work(operation) {
|
|
141
|
+
if (this.activeWork >= this.limit) {
|
|
142
|
+
await new Promise((resolve) => this.waiters.push(resolve));
|
|
143
|
+
} else {
|
|
144
|
+
this.activeWork += 1;
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
return await operation();
|
|
148
|
+
} finally {
|
|
149
|
+
const next = this.waiters.shift();
|
|
150
|
+
if (next)
|
|
151
|
+
next();
|
|
152
|
+
else this.activeWork -= 1;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
134
155
|
};
|
|
135
156
|
|
|
136
157
|
// src/helpers/document.ts
|
|
@@ -355,6 +376,41 @@ function normalizeSortForOrderBy(sort) {
|
|
|
355
376
|
orders: normalized.fields.map(({ direction }) => direction)
|
|
356
377
|
};
|
|
357
378
|
}
|
|
379
|
+
function sanitizeSortFields(sort, allowedFields) {
|
|
380
|
+
const { fields, errors } = normalizeSort(sort);
|
|
381
|
+
if (errors.length > 0) return { sort: void 0, stripped: [], errors };
|
|
382
|
+
const allowedSet = new Set(allowedFields.concat(["id", "_id"]));
|
|
383
|
+
const kept = fields.filter(({ field }) => allowedSet.has(field));
|
|
384
|
+
const stripped = fields.filter(({ field }) => !allowedSet.has(field)).map(({ field }) => field);
|
|
385
|
+
if (stripped.length === 0) return { sort, stripped, errors: [] };
|
|
386
|
+
if (kept.length === 0) return { sort: void 0, stripped, errors: [] };
|
|
387
|
+
if (typeof sort === "string") {
|
|
388
|
+
return {
|
|
389
|
+
sort: kept.map(({ field, direction }) => direction === "desc" ? `-${field}` : field).join(" "),
|
|
390
|
+
stripped,
|
|
391
|
+
errors: []
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
if (sort instanceof Map) {
|
|
395
|
+
return {
|
|
396
|
+
sort: new Map(kept.map(({ field, direction }) => [field, direction])),
|
|
397
|
+
stripped,
|
|
398
|
+
errors: []
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
if (Array.isArray(sort)) {
|
|
402
|
+
return {
|
|
403
|
+
sort: kept.map(({ field, direction }) => [field, direction]),
|
|
404
|
+
stripped,
|
|
405
|
+
errors: []
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
return {
|
|
409
|
+
sort: Object.fromEntries(kept.map(({ field, direction }) => [field, direction])),
|
|
410
|
+
stripped,
|
|
411
|
+
errors: []
|
|
412
|
+
};
|
|
413
|
+
}
|
|
358
414
|
|
|
359
415
|
// src/helpers/index.ts
|
|
360
416
|
var import_utils6 = require("@web-ts-toolkit/utils");
|
|
@@ -1222,6 +1278,7 @@ var AccessRuntime = class {
|
|
|
1222
1278
|
this.modelAtts = {};
|
|
1223
1279
|
this.modelInstances = {};
|
|
1224
1280
|
this.openApiRegistry = new OpenApiRegistry();
|
|
1281
|
+
this.openApiPathPrefix = "";
|
|
1225
1282
|
this.allowGlobalModelLookup = options.allowGlobalModelLookup ?? false;
|
|
1226
1283
|
ensureMongooseJsonSchemaInitialized();
|
|
1227
1284
|
}
|
|
@@ -1250,7 +1307,24 @@ var AccessRuntime = class {
|
|
|
1250
1307
|
return global;
|
|
1251
1308
|
}
|
|
1252
1309
|
registerOpenApiRoute(route) {
|
|
1253
|
-
this.openApiRegistry.register(
|
|
1310
|
+
this.openApiRegistry.register(
|
|
1311
|
+
this.openApiPathPrefix ? { ...route, path: (0, import_utils11.normalizeUrlPath)(`${this.openApiPathPrefix}/${route.path}`) } : route
|
|
1312
|
+
);
|
|
1313
|
+
}
|
|
1314
|
+
/**
|
|
1315
|
+
* Compose OpenAPI paths while synchronously constructing mounted routers.
|
|
1316
|
+
* Prefixes apply before collision checks; prior descriptors and Express paths
|
|
1317
|
+
* are unchanged. Nested calls compose and the prior scope is restored even on
|
|
1318
|
+
* failure. The callback must finish registration synchronously (no async work).
|
|
1319
|
+
*/
|
|
1320
|
+
withOpenApiPathPrefix(prefix, build) {
|
|
1321
|
+
const previous = this.openApiPathPrefix;
|
|
1322
|
+
this.openApiPathPrefix = (0, import_utils11.normalizeUrlPath)(`${previous}/${prefix}`);
|
|
1323
|
+
try {
|
|
1324
|
+
return build();
|
|
1325
|
+
} finally {
|
|
1326
|
+
this.openApiPathPrefix = previous;
|
|
1327
|
+
}
|
|
1254
1328
|
}
|
|
1255
1329
|
/**
|
|
1256
1330
|
* Enable strict OpenAPI collision detection for the remainder of this
|
|
@@ -1925,7 +1999,8 @@ function resolveValueNode(template, parent, bare) {
|
|
|
1925
1999
|
if (!isParentRefMarker(inner) || typeof inner.$parent !== "string" || (inner.$parent?.length ?? 0) === 0) {
|
|
1926
2000
|
throw new CorrelatedReferenceError('Invalid $escape payload: expected { "$escape": { "$parent": "<field>" } }');
|
|
1927
2001
|
}
|
|
1928
|
-
|
|
2002
|
+
const literal = literalParentRefObject(inner.$parent);
|
|
2003
|
+
return bare ? { $eq: literal } : literal;
|
|
1929
2004
|
}
|
|
1930
2005
|
if (isParentRefMarker(template)) {
|
|
1931
2006
|
const path = template.$parent;
|
|
@@ -2054,8 +2129,28 @@ function isCorrelatedInclude(value) {
|
|
|
2054
2129
|
return (0, import_utils12.isPlainObject)(value) && value.mode === "correlated";
|
|
2055
2130
|
}
|
|
2056
2131
|
|
|
2132
|
+
// src/helpers/request-work.ts
|
|
2133
|
+
var states = /* @__PURE__ */ new WeakMap();
|
|
2134
|
+
function getRequestWorkState(req, runtime) {
|
|
2135
|
+
let owners = states.get(req);
|
|
2136
|
+
if (!owners) {
|
|
2137
|
+
owners = /* @__PURE__ */ new WeakMap();
|
|
2138
|
+
states.set(req, owners);
|
|
2139
|
+
}
|
|
2140
|
+
let state = owners.get(runtime);
|
|
2141
|
+
if (!state) {
|
|
2142
|
+
state = {
|
|
2143
|
+
totalQueries: 0,
|
|
2144
|
+
scheduler: new RequestConcurrencyScheduler(
|
|
2145
|
+
resolveRequestComplexity(runtime.getGlobalOption("requestComplexity")).maxBulkConcurrency
|
|
2146
|
+
)
|
|
2147
|
+
};
|
|
2148
|
+
owners.set(runtime, state);
|
|
2149
|
+
}
|
|
2150
|
+
return state;
|
|
2151
|
+
}
|
|
2152
|
+
|
|
2057
2153
|
// src/services/base.ts
|
|
2058
|
-
var correlatedExecStates = /* @__PURE__ */ new WeakMap();
|
|
2059
2154
|
function getCorrelatedTemplateDepth(includes) {
|
|
2060
2155
|
let max = 0;
|
|
2061
2156
|
const visit = (entries, depth) => {
|
|
@@ -2105,6 +2200,7 @@ function validateClientFilter(filter2) {
|
|
|
2105
2200
|
}
|
|
2106
2201
|
var Base = class {
|
|
2107
2202
|
constructor(req, modelName) {
|
|
2203
|
+
this.requestRuntime = getActiveRuntime() ?? defaultRuntime;
|
|
2108
2204
|
this.req = req;
|
|
2109
2205
|
this.modelName = modelName;
|
|
2110
2206
|
}
|
|
@@ -2242,6 +2338,7 @@ var Base = class {
|
|
|
2242
2338
|
}
|
|
2243
2339
|
seenCorrelatedPaths.add(entry.path);
|
|
2244
2340
|
}
|
|
2341
|
+
this.assertIncludeOutputPaths([...legacy, ...correlated]);
|
|
2245
2342
|
let includeLocalFields = [];
|
|
2246
2343
|
let includePaths = [];
|
|
2247
2344
|
(0, import_utils13.forEach)(legacy, (inc) => {
|
|
@@ -2258,6 +2355,39 @@ var Base = class {
|
|
|
2258
2355
|
correlatedReferenceFields: (0, import_utils13.uniq)((0, import_utils13.compact)(correlatedReferenceFields))
|
|
2259
2356
|
};
|
|
2260
2357
|
}
|
|
2358
|
+
validateIncludeOutputPath(path, modelName) {
|
|
2359
|
+
const permissionField = getModelOption(modelName, "documentPermissionField");
|
|
2360
|
+
if ((0, import_utils13.get)((0, import_utils13.set)({}, path, true), permissionField) !== void 0 || (0, import_utils13.get)((0, import_utils13.set)({}, permissionField, true), path) !== void 0) {
|
|
2361
|
+
this.throwClientRequestError(
|
|
2362
|
+
"bad_request" /* BadRequest */,
|
|
2363
|
+
`Include output path ${path} overlaps document permission field ${permissionField} on model ${modelName}`
|
|
2364
|
+
);
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
2367
|
+
assertIncludeOutputPaths(include) {
|
|
2368
|
+
const pending = [{ entries: include, modelName: this.modelName }];
|
|
2369
|
+
const visited = /* @__PURE__ */ new WeakMap();
|
|
2370
|
+
while (pending.length > 0) {
|
|
2371
|
+
const { entries, modelName } = pending.pop();
|
|
2372
|
+
if (modelName !== this.modelName && !getModelNames().includes(modelName)) {
|
|
2373
|
+
this.throwClientRequestError("bad_request" /* BadRequest */, `Model ${modelName} not found`);
|
|
2374
|
+
}
|
|
2375
|
+
for (const entry of entries) {
|
|
2376
|
+
if (!(0, import_utils13.isPlainObject)(entry)) continue;
|
|
2377
|
+
const models = visited.get(entry) ?? /* @__PURE__ */ new Set();
|
|
2378
|
+
if (models.has(modelName)) continue;
|
|
2379
|
+
models.add(modelName);
|
|
2380
|
+
visited.set(entry, models);
|
|
2381
|
+
if (typeof entry.path === "string" && entry.path) {
|
|
2382
|
+
this.validateIncludeOutputPath(entry.path, modelName);
|
|
2383
|
+
}
|
|
2384
|
+
if ((entry.op === "read" || entry.op === "list") && typeof entry.model === "string") {
|
|
2385
|
+
const nested = entry.args?.include;
|
|
2386
|
+
if (nested) pending.push({ entries: (0, import_utils13.compact)((0, import_utils13.castArray)(nested)), modelName: entry.model });
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
}
|
|
2390
|
+
}
|
|
2261
2391
|
getForeignKeyValues(value) {
|
|
2262
2392
|
return (0, import_utils13.flatten)((0, import_utils13.castArray)(value));
|
|
2263
2393
|
}
|
|
@@ -2392,6 +2522,7 @@ var Base = class {
|
|
|
2392
2522
|
const svc = await this.getAuthorizedTargetService(model, "count");
|
|
2393
2523
|
const localValues = docs.flatMap((doc) => this.getForeignKeyValues((0, import_utils13.get)(doc, localField)));
|
|
2394
2524
|
const result = await svc.countByFieldValues(foreignField, localValues, _filters ?? {}, "count");
|
|
2525
|
+
if (!result.success && result.code === "bad_request" /* BadRequest */) throw new ClientRequestError(result);
|
|
2395
2526
|
if (!result.success) return docs;
|
|
2396
2527
|
for (let y = 0; y < docs.length; y++) {
|
|
2397
2528
|
const doc = docs[y];
|
|
@@ -2400,16 +2531,7 @@ var Base = class {
|
|
|
2400
2531
|
return docs;
|
|
2401
2532
|
}
|
|
2402
2533
|
getCorrelatedExecState() {
|
|
2403
|
-
|
|
2404
|
-
let state = correlatedExecStates.get(key);
|
|
2405
|
-
if (!state) {
|
|
2406
|
-
state = {
|
|
2407
|
-
totalQueries: 0,
|
|
2408
|
-
scheduler: new RequestConcurrencyScheduler(this.getRequestComplexity().maxBulkConcurrency)
|
|
2409
|
-
};
|
|
2410
|
-
correlatedExecStates.set(key, state);
|
|
2411
|
-
}
|
|
2412
|
-
return state;
|
|
2534
|
+
return getRequestWorkState(this.req, this.requestRuntime);
|
|
2413
2535
|
}
|
|
2414
2536
|
claimCorrelatedQuerySlot() {
|
|
2415
2537
|
const limits = this.getRequestComplexity();
|
|
@@ -2806,10 +2928,30 @@ var Base = class {
|
|
|
2806
2928
|
};
|
|
2807
2929
|
|
|
2808
2930
|
// src/services/service.ts
|
|
2809
|
-
var
|
|
2931
|
+
var import_utils17 = require("@web-ts-toolkit/utils");
|
|
2810
2932
|
var import_just_diff = require("just-diff");
|
|
2811
2933
|
|
|
2812
2934
|
// src/model.ts
|
|
2935
|
+
var import_utils14 = require("@web-ts-toolkit/utils");
|
|
2936
|
+
function admitModelPersistence(adapter, scheduler) {
|
|
2937
|
+
return {
|
|
2938
|
+
modelName: adapter.modelName,
|
|
2939
|
+
mongooseModel: adapter.mongooseModel,
|
|
2940
|
+
new: () => adapter.new(),
|
|
2941
|
+
// Mongoose create(array) otherwise starts all document saves at once. Keep
|
|
2942
|
+
// the adapter's array input/output contract, with one permit per item; all
|
|
2943
|
+
// items are submitted even if one rejects, as with unordered create(array).
|
|
2944
|
+
create: (data) => Array.isArray(data) ? Promise.all(data.map((item) => scheduler.work(async () => (await adapter.create([item]))[0]))) : scheduler.work(() => adapter.create(data)),
|
|
2945
|
+
find: (props) => scheduler.work(() => adapter.find(props)),
|
|
2946
|
+
findOne: (props) => scheduler.work(() => adapter.findOne(props)),
|
|
2947
|
+
exists: (filter2) => scheduler.work(() => adapter.exists(filter2)),
|
|
2948
|
+
countDocuments: (filter2) => scheduler.work(() => adapter.countDocuments(filter2)),
|
|
2949
|
+
distinct: (field, conditions) => scheduler.work(() => adapter.distinct(field, conditions)),
|
|
2950
|
+
aggregate: (pipeline) => scheduler.work(() => adapter.aggregate(pipeline)),
|
|
2951
|
+
// Casting builds no persistence work and must not acquire a recursive permit.
|
|
2952
|
+
...adapter.castFilter ? { castFilter: (filter2) => adapter.castFilter(filter2) } : {}
|
|
2953
|
+
};
|
|
2954
|
+
}
|
|
2813
2955
|
var Model = class {
|
|
2814
2956
|
constructor(modelName, runtime) {
|
|
2815
2957
|
this.modelName = modelName;
|
|
@@ -2862,6 +3004,11 @@ var Model = class {
|
|
|
2862
3004
|
countDocuments(filter2 = {}) {
|
|
2863
3005
|
return this.mongooseModel.countDocuments(filter2);
|
|
2864
3006
|
}
|
|
3007
|
+
/** Cast a copy using this model's query schema/setters, without executing query middleware or persistence. */
|
|
3008
|
+
castFilter(filter2) {
|
|
3009
|
+
const query = this.mongooseModel.countDocuments((0, import_utils14.cloneDeep)(filter2));
|
|
3010
|
+
return query.cast(this.mongooseModel);
|
|
3011
|
+
}
|
|
2865
3012
|
// see https://mongoosejs.com/docs/api.html#model_Model.distinct
|
|
2866
3013
|
distinct(field, conditions = {}) {
|
|
2867
3014
|
return this.mongooseModel.distinct(field, conditions);
|
|
@@ -2872,6 +3019,39 @@ var Model = class {
|
|
|
2872
3019
|
};
|
|
2873
3020
|
var model_default = Model;
|
|
2874
3021
|
|
|
3022
|
+
// src/helpers/apply-update.ts
|
|
3023
|
+
var import_utils15 = require("@web-ts-toolkit/utils");
|
|
3024
|
+
function applyUpdate(target, data, allowedFields) {
|
|
3025
|
+
const wholeFields = /* @__PURE__ */ new Set();
|
|
3026
|
+
const partialContainers = /* @__PURE__ */ new Set();
|
|
3027
|
+
const collectBoundary = (shape, prefix = "") => {
|
|
3028
|
+
for (const [key, value] of Object.entries(shape)) {
|
|
3029
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
3030
|
+
if (value === true) wholeFields.add(path);
|
|
3031
|
+
else {
|
|
3032
|
+
partialContainers.add(path);
|
|
3033
|
+
collectBoundary(value, path);
|
|
3034
|
+
}
|
|
3035
|
+
}
|
|
3036
|
+
};
|
|
3037
|
+
for (const field of allowedFields) {
|
|
3038
|
+
collectBoundary((0, import_utils15.set)({}, field, true));
|
|
3039
|
+
}
|
|
3040
|
+
const apply = (values, prefix = "") => {
|
|
3041
|
+
for (const [key, value] of Object.entries(values)) {
|
|
3042
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
3043
|
+
if (partialContainers.has(path) && !wholeFields.has(path) && ((0, import_utils15.isPlainObject)(value) || Array.isArray(value))) {
|
|
3044
|
+
apply(value, path);
|
|
3045
|
+
} else if (isDocument2(target)) {
|
|
3046
|
+
target.set(path, value);
|
|
3047
|
+
} else {
|
|
3048
|
+
(0, import_utils15.set)(target, path, value);
|
|
3049
|
+
}
|
|
3050
|
+
}
|
|
3051
|
+
};
|
|
3052
|
+
apply(data ?? {});
|
|
3053
|
+
}
|
|
3054
|
+
|
|
2875
3055
|
// src/logger-helpers.ts
|
|
2876
3056
|
var SENSITIVE_FILTER_KEYS = /* @__PURE__ */ new Set([
|
|
2877
3057
|
"password",
|
|
@@ -3050,28 +3230,52 @@ var error = (error2) => {
|
|
|
3050
3230
|
};
|
|
3051
3231
|
|
|
3052
3232
|
// src/services/model-subdocument-service.ts
|
|
3053
|
-
var
|
|
3233
|
+
var import_utils16 = require("@web-ts-toolkit/utils");
|
|
3234
|
+
async function visibleMutationRows(service, isAllowed, parentId, sub, rows, response) {
|
|
3235
|
+
const accesses = ["read", `subs.${sub}.read`];
|
|
3236
|
+
if (response === "create") accesses.push(`subs.${sub}.list`);
|
|
3237
|
+
const allowed = await Promise.all(accesses.map(isAllowed));
|
|
3238
|
+
if (allowed.some((value) => !value)) return [];
|
|
3239
|
+
const parentFilter = await service.genFilter("read", { _id: parentId });
|
|
3240
|
+
if (parentFilter === false) return [];
|
|
3241
|
+
const parent = await service.findRawParentDoc({
|
|
3242
|
+
filter: { $and: [{ _id: parentId }, parentFilter] },
|
|
3243
|
+
select: "_id",
|
|
3244
|
+
populate: [],
|
|
3245
|
+
lean: true
|
|
3246
|
+
});
|
|
3247
|
+
if (!parent) return [];
|
|
3248
|
+
const targetFilter = response === "create" ? {} : response === "single" ? { _id: rows[0]?._id } : { _id: { $in: rows.map((row) => row._id) } };
|
|
3249
|
+
const [readFilter, listFilter, select] = await Promise.all([
|
|
3250
|
+
service.genFilter(`subs.${sub}.read`, targetFilter),
|
|
3251
|
+
response === "create" ? service.genFilter(`subs.${sub}.list`) : Promise.resolve({}),
|
|
3252
|
+
service.genQuerySelect("read", null, false, [sub, "sub"])
|
|
3253
|
+
]);
|
|
3254
|
+
if (readFilter === false || listFilter === false) return [];
|
|
3255
|
+
const visible = filterCollection(filterCollection(rows, listFilter), readFilter);
|
|
3256
|
+
return select ? visible.map((row) => (0, import_utils16.pick)(toObject(row), select.concat("_id"))) : visible;
|
|
3257
|
+
}
|
|
3054
3258
|
async function listSub(service, id, sub, options) {
|
|
3055
3259
|
const { filter: ft, select } = options ?? {};
|
|
3056
3260
|
const filterErrors = validateClientFilter(ft);
|
|
3057
3261
|
if (filterErrors.length > 0) return { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors: filterErrors };
|
|
3058
3262
|
const parentDoc = await getParentDoc(service, id, sub, null, { access: "read" });
|
|
3059
3263
|
if (!parentDoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
3060
|
-
let result = (0,
|
|
3264
|
+
let result = (0, import_utils16.get)(parentDoc, sub);
|
|
3061
3265
|
const [subFilter, subSelect] = await Promise.all([
|
|
3062
3266
|
service.genFilter(`subs.${sub}.list`, ft),
|
|
3063
3267
|
service.genQuerySelect("list", select, false, [sub, "sub"])
|
|
3064
3268
|
]);
|
|
3065
3269
|
if (subFilter === false) return { success: false, kind: "error", code: "forbidden" /* Forbidden */ };
|
|
3066
3270
|
result = filterCollection(result, subFilter);
|
|
3067
|
-
if (subSelect) result = result.map((v) => (0,
|
|
3271
|
+
if (subSelect) result = result.map((v) => (0, import_utils16.pick)(toObject(v), subSelect.concat("_id")));
|
|
3068
3272
|
return { success: true, kind: "list", code: "success" /* Success */, data: result, count: result.length };
|
|
3069
3273
|
}
|
|
3070
3274
|
async function readSub(service, id, sub, subId, options) {
|
|
3071
3275
|
const { select, populate } = options ?? {};
|
|
3072
3276
|
const parentDoc = await getParentDoc(service, id, sub, { populate }, { access: "read" });
|
|
3073
3277
|
if (!parentDoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
3074
|
-
const result = (0,
|
|
3278
|
+
const result = (0, import_utils16.get)(parentDoc, sub);
|
|
3075
3279
|
const [subFilter, subSelect] = await Promise.all([
|
|
3076
3280
|
service.genFilter(`subs.${sub}.read`, { _id: subId }),
|
|
3077
3281
|
service.genQuerySelect("read", select, false, [sub, "sub"])
|
|
@@ -3079,28 +3283,27 @@ async function readSub(service, id, sub, subId, options) {
|
|
|
3079
3283
|
if (subFilter === false) return { success: false, kind: "error", code: "forbidden" /* Forbidden */ };
|
|
3080
3284
|
let subdoc = findElement(result, subFilter);
|
|
3081
3285
|
if (!subdoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
3082
|
-
if (subSelect) subdoc = (0,
|
|
3286
|
+
if (subSelect) subdoc = (0, import_utils16.pick)(toObject(subdoc), subSelect.concat(["_id"]));
|
|
3083
3287
|
return { success: true, kind: "single", code: "success" /* Success */, data: subdoc };
|
|
3084
3288
|
}
|
|
3085
|
-
async function updateSub(service, id, sub, subId, data) {
|
|
3289
|
+
async function updateSub(service, persist, isAllowed, id, sub, subId, data) {
|
|
3086
3290
|
const parentDoc = await getParentDoc(service, id, sub, null, { access: "update" });
|
|
3087
3291
|
if (!parentDoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
3088
|
-
const result = (0,
|
|
3089
|
-
const [subFilter,
|
|
3292
|
+
const result = (0, import_utils16.get)(parentDoc, sub);
|
|
3293
|
+
const [subFilter, subUpdateSelect] = await Promise.all([
|
|
3090
3294
|
service.genFilter(`subs.${sub}.update`, { _id: subId }),
|
|
3091
|
-
service.genQuerySelect("read", null, false, [sub, "sub"]),
|
|
3092
3295
|
service.genQuerySelect("update", null, false, [sub, "sub"])
|
|
3093
3296
|
]);
|
|
3094
3297
|
if (subFilter === false) return { success: false, kind: "error", code: "forbidden" /* Forbidden */ };
|
|
3095
|
-
|
|
3298
|
+
const subdoc = findElement(result, subFilter);
|
|
3096
3299
|
if (!subdoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
3097
|
-
const allowedData = (0,
|
|
3098
|
-
|
|
3099
|
-
await parentDoc.save();
|
|
3100
|
-
|
|
3101
|
-
return { success: true, kind: "single", code: "success" /* Success */, data:
|
|
3300
|
+
const allowedData = (0, import_utils16.pick)(data, subUpdateSelect);
|
|
3301
|
+
applyUpdate(subdoc, allowedData, subUpdateSelect);
|
|
3302
|
+
await persist(() => parentDoc.save());
|
|
3303
|
+
const visible = await visibleMutationRows(service, isAllowed, parentDoc._id, sub, [subdoc], "single");
|
|
3304
|
+
return { success: true, kind: "single", code: "success" /* Success */, data: visible[0] ?? null };
|
|
3102
3305
|
}
|
|
3103
|
-
async function bulkUpdateSub(service, id, sub, data) {
|
|
3306
|
+
async function bulkUpdateSub(service, persist, isAllowed, id, sub, data) {
|
|
3104
3307
|
const { maxBulkItems } = service.getRequestComplexity();
|
|
3105
3308
|
if (data.length > maxBulkItems) {
|
|
3106
3309
|
return {
|
|
@@ -3112,25 +3315,24 @@ async function bulkUpdateSub(service, id, sub, data) {
|
|
|
3112
3315
|
}
|
|
3113
3316
|
const parentDoc = await getParentDoc(service, id, sub, null, { access: "update" });
|
|
3114
3317
|
if (!parentDoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
3115
|
-
let result = (0,
|
|
3116
|
-
const [subFilter,
|
|
3318
|
+
let result = (0, import_utils16.get)(parentDoc, sub);
|
|
3319
|
+
const [subFilter, subUpdateSelect] = await Promise.all([
|
|
3117
3320
|
service.genFilter(`subs.${sub}.update`, { _id: { $in: data.map((v) => v._id) } }),
|
|
3118
|
-
service.genQuerySelect("read", null, false, [sub, "sub"]),
|
|
3119
3321
|
service.genQuerySelect("update", null, false, [sub, "sub"])
|
|
3120
3322
|
]);
|
|
3121
3323
|
if (subFilter === false) return { success: false, kind: "error", code: "forbidden" /* Forbidden */ };
|
|
3122
|
-
result = filterCollection(result, subFilter);
|
|
3123
|
-
(0,
|
|
3324
|
+
result = filterCollection(result, subFilter).filter((subdoc) => findElementById(data, subdoc._id));
|
|
3325
|
+
(0, import_utils16.forEach)(result, (subdoc) => {
|
|
3124
3326
|
const tdata = findElementById(data, subdoc._id);
|
|
3125
3327
|
if (!tdata) return;
|
|
3126
|
-
const allowedData = (0,
|
|
3127
|
-
|
|
3328
|
+
const allowedData = (0, import_utils16.pick)(tdata, subUpdateSelect);
|
|
3329
|
+
applyUpdate(subdoc, allowedData, subUpdateSelect);
|
|
3128
3330
|
});
|
|
3129
|
-
await parentDoc.save();
|
|
3130
|
-
|
|
3331
|
+
await persist(() => parentDoc.save());
|
|
3332
|
+
result = await visibleMutationRows(service, isAllowed, parentDoc._id, sub, result, "bulk");
|
|
3131
3333
|
return { success: true, kind: "list", code: "success" /* Success */, data: result, count: result.length };
|
|
3132
3334
|
}
|
|
3133
|
-
async function createSub(service, id, sub, data, options) {
|
|
3335
|
+
async function createSub(service, persist, isAllowed, id, sub, data, options) {
|
|
3134
3336
|
const { addFirst } = options ?? {};
|
|
3135
3337
|
const { maxBulkItems } = service.getRequestComplexity();
|
|
3136
3338
|
if (Array.isArray(data) && data.length > maxBulkItems) {
|
|
@@ -3143,31 +3345,28 @@ async function createSub(service, id, sub, data, options) {
|
|
|
3143
3345
|
}
|
|
3144
3346
|
const parentDoc = await getParentDoc(service, id, sub, null, { access: "update" });
|
|
3145
3347
|
if (!parentDoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
3146
|
-
let result = (0,
|
|
3147
|
-
const
|
|
3148
|
-
|
|
3149
|
-
service.genQuerySelect("read", null, false, [sub, "sub"])
|
|
3150
|
-
]);
|
|
3151
|
-
const allowedData = Array.isArray(data) ? data.map((row) => (0, import_utils14.pick)(row, subCreateSelect)) : (0, import_utils14.pick)(data, subCreateSelect);
|
|
3348
|
+
let result = (0, import_utils16.get)(parentDoc, sub);
|
|
3349
|
+
const subCreateSelect = await service.genQuerySelect("create", null, false, [sub, "sub"]);
|
|
3350
|
+
const allowedData = Array.isArray(data) ? data.map((row) => (0, import_utils16.pick)(row, subCreateSelect)) : (0, import_utils16.pick)(data, subCreateSelect);
|
|
3152
3351
|
if (Array.isArray(allowedData)) {
|
|
3153
3352
|
addFirst === true ? result.unshift(...allowedData) : result.push(...allowedData);
|
|
3154
3353
|
} else {
|
|
3155
3354
|
addFirst === true ? result.unshift(allowedData) : result.push(allowedData);
|
|
3156
3355
|
}
|
|
3157
|
-
await parentDoc.save();
|
|
3158
|
-
|
|
3356
|
+
await persist(() => parentDoc.save());
|
|
3357
|
+
result = await visibleMutationRows(service, isAllowed, parentDoc._id, sub, result, "create");
|
|
3159
3358
|
return { success: true, kind: "list", code: "created" /* Created */, data: result, count: result.length };
|
|
3160
3359
|
}
|
|
3161
|
-
async function deleteSub(service, id, sub, subId) {
|
|
3360
|
+
async function deleteSub(service, persist, id, sub, subId) {
|
|
3162
3361
|
const parentDoc = await getParentDoc(service, id, sub, null, { access: "update" });
|
|
3163
3362
|
if (!parentDoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
3164
|
-
const result = (0,
|
|
3363
|
+
const result = (0, import_utils16.get)(parentDoc, sub);
|
|
3165
3364
|
const subFilter = await service.genFilter(`subs.${sub}.delete`, { _id: subId });
|
|
3166
3365
|
if (subFilter === false) return { success: false, kind: "error", code: "forbidden" /* Forbidden */ };
|
|
3167
3366
|
const subdoc = findElement(result, subFilter);
|
|
3168
3367
|
if (!subdoc) return { success: false, kind: "error", code: "not_found" /* NotFound */ };
|
|
3169
3368
|
await ("deleteOne" in subdoc ? subdoc.deleteOne?.() : subdoc.remove?.());
|
|
3170
|
-
await parentDoc.save();
|
|
3369
|
+
await persist(() => parentDoc.save());
|
|
3171
3370
|
return { success: true, kind: "single", code: "success" /* Success */, data: subdoc._id };
|
|
3172
3371
|
}
|
|
3173
3372
|
async function getParentDoc(service, id, sub, args, options) {
|
|
@@ -3326,6 +3525,15 @@ var shouldKeepCorrelatedRef = (ref, keep) => {
|
|
|
3326
3525
|
return false;
|
|
3327
3526
|
};
|
|
3328
3527
|
var Service = class extends Base {
|
|
3528
|
+
constructor(req, modelName) {
|
|
3529
|
+
super(req, modelName);
|
|
3530
|
+
this.persist = (operation) => this.getCorrelatedExecState().scheduler.work(operation);
|
|
3531
|
+
this.model = admitModelPersistence(this.createModelAdapter(modelName), this.getCorrelatedExecState().scheduler);
|
|
3532
|
+
this.options = this.getModelRouterOptions(modelName);
|
|
3533
|
+
this.defaults = this.options.defaults || {};
|
|
3534
|
+
this.baseFields = ["_id"];
|
|
3535
|
+
this.baseFieldsExt = this.baseFields.concat(this.options.documentPermissionField);
|
|
3536
|
+
}
|
|
3329
3537
|
findRawParentDoc(args) {
|
|
3330
3538
|
return this.model.findOne({
|
|
3331
3539
|
...args,
|
|
@@ -3342,6 +3550,10 @@ var Service = class extends Base {
|
|
|
3342
3550
|
asServiceHookContext(context) {
|
|
3343
3551
|
return context;
|
|
3344
3552
|
}
|
|
3553
|
+
getEffectiveAllowedSortFields(allowedSortFields) {
|
|
3554
|
+
const extra = this.options?.sortableFields ?? [];
|
|
3555
|
+
return extra.length > 0 ? [...allowedSortFields, ...extra] : allowedSortFields;
|
|
3556
|
+
}
|
|
3345
3557
|
beginOp(op, filter2, extra) {
|
|
3346
3558
|
const startedAt = Date.now();
|
|
3347
3559
|
debug({
|
|
@@ -3363,14 +3575,6 @@ var Service = class extends Base {
|
|
|
3363
3575
|
...extra ?? {}
|
|
3364
3576
|
});
|
|
3365
3577
|
}
|
|
3366
|
-
constructor(req, modelName) {
|
|
3367
|
-
super(req, modelName);
|
|
3368
|
-
this.model = this.createModelAdapter(modelName);
|
|
3369
|
-
this.options = this.getModelRouterOptions(modelName);
|
|
3370
|
-
this.defaults = this.options.defaults || {};
|
|
3371
|
-
this.baseFields = ["_id"];
|
|
3372
|
-
this.baseFieldsExt = this.baseFields.concat(this.options.documentPermissionField);
|
|
3373
|
-
}
|
|
3374
3578
|
async findOne(filter2, args, options) {
|
|
3375
3579
|
const filterErrors = this.validateClientFilter(filter2);
|
|
3376
3580
|
if (filterErrors.length > 0) return { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors: filterErrors };
|
|
@@ -3378,30 +3582,36 @@ var Service = class extends Base {
|
|
|
3378
3582
|
const { skim, includePermissions, access, populateAccess, lean } = this.resolveFindOneOptions(options);
|
|
3379
3583
|
const { filter: overrideFilter, select: overrideSelect, populate: overridePopulate } = overrides ?? {};
|
|
3380
3584
|
let parsedFilter;
|
|
3585
|
+
let processedInclude;
|
|
3381
3586
|
try {
|
|
3382
|
-
|
|
3587
|
+
processedInclude = this.processInclude(include);
|
|
3588
|
+
parsedFilter = overrideFilter === false ? false : await this.parseClientData(filter2);
|
|
3383
3589
|
} catch (error2) {
|
|
3384
3590
|
const result = this.getClientRequestErrorResult(error2);
|
|
3385
3591
|
if (result) return result;
|
|
3386
3592
|
throw error2;
|
|
3387
3593
|
}
|
|
3388
3594
|
let [_filter, _select, _populate, allowedSortFields] = await Promise.all([
|
|
3389
|
-
overrideFilter
|
|
3595
|
+
overrideFilter ?? this.genFilter(access, parsedFilter),
|
|
3390
3596
|
overrideSelect || this.genQuerySelect(access, select),
|
|
3391
3597
|
overridePopulate || this.genPopulate(populateAccess || access, populate),
|
|
3392
3598
|
this.genAllowedFields({}, access, this.baseFieldsExt)
|
|
3393
3599
|
]);
|
|
3394
|
-
const { includes, correlatedIncludes, includeLocalFields, includePaths, correlatedReferenceFields } =
|
|
3600
|
+
const { includes, correlatedIncludes, includeLocalFields, includePaths, correlatedReferenceFields } = processedInclude;
|
|
3395
3601
|
const correlatedOutputPaths = correlatedIncludes.map((entry) => entry.path);
|
|
3396
3602
|
const finalSelect = normalizeSelect(_select).concat(includeLocalFields, correlatedReferenceFields);
|
|
3603
|
+
const effectiveAllowedSortFields = this.getEffectiveAllowedSortFields(allowedSortFields);
|
|
3604
|
+
const stripDisallowedSort = this.options?.stripDisallowedSort ?? false;
|
|
3605
|
+
const sanitizedSort = stripDisallowedSort ? sanitizeSortFields(sort, effectiveAllowedSortFields) : null;
|
|
3606
|
+
const effectiveSort = stripDisallowedSort ? sanitizedSort?.sort : sort;
|
|
3397
3607
|
const query = {
|
|
3398
3608
|
filter: _filter,
|
|
3399
3609
|
select: finalSelect,
|
|
3400
|
-
sort,
|
|
3610
|
+
sort: effectiveSort,
|
|
3401
3611
|
populate: _populate
|
|
3402
3612
|
};
|
|
3403
3613
|
const startedAt = this.beginOp("findOne", _filter, {
|
|
3404
|
-
sort,
|
|
3614
|
+
sort: effectiveSort,
|
|
3405
3615
|
selectCount: finalSelect.length,
|
|
3406
3616
|
populateCount: Array.isArray(_populate) ? _populate.length : _populate ? 1 : 0
|
|
3407
3617
|
});
|
|
@@ -3409,10 +3619,17 @@ var Service = class extends Base {
|
|
|
3409
3619
|
this.completeOp("findOne", startedAt, "forbidden" /* Forbidden */, _filter);
|
|
3410
3620
|
return { success: false, kind: "error", code: "forbidden" /* Forbidden */, query };
|
|
3411
3621
|
}
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3622
|
+
if (stripDisallowedSort) {
|
|
3623
|
+
if (sanitizedSort && sanitizedSort.errors.length > 0) {
|
|
3624
|
+
this.completeOp("findOne", startedAt, "bad_request" /* BadRequest */, _filter);
|
|
3625
|
+
return { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors: sanitizedSort.errors, query };
|
|
3626
|
+
}
|
|
3627
|
+
} else {
|
|
3628
|
+
const sortErrors = validateSortFields(sort, effectiveAllowedSortFields);
|
|
3629
|
+
if (sortErrors.length > 0) {
|
|
3630
|
+
this.completeOp("findOne", startedAt, "bad_request" /* BadRequest */, _filter);
|
|
3631
|
+
return { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors: sortErrors, query };
|
|
3632
|
+
}
|
|
3416
3633
|
}
|
|
3417
3634
|
let doc = await this.model.findOne({ ...query, lean });
|
|
3418
3635
|
if (!doc) {
|
|
@@ -3426,7 +3643,7 @@ var Service = class extends Base {
|
|
|
3426
3643
|
originalDocumentSnapshot: toObject(doc),
|
|
3427
3644
|
resolvedQuery: query
|
|
3428
3645
|
};
|
|
3429
|
-
const correlatedSnapshots = [(0,
|
|
3646
|
+
const correlatedSnapshots = [(0, import_utils17.cloneDeep)(toObject(doc))];
|
|
3430
3647
|
try {
|
|
3431
3648
|
doc = await this.includeDocs(doc, includes);
|
|
3432
3649
|
if (correlatedIncludes.length > 0) {
|
|
@@ -3473,7 +3690,8 @@ var Service = class extends Base {
|
|
|
3473
3690
|
const { select, populate, include, overrides } = this.resolveFindByIdArgs(args);
|
|
3474
3691
|
const { skim, includePermissions, access, populateAccess, lean } = this.resolveFindByIdOptions(options);
|
|
3475
3692
|
const { select: overrideSelect, populate: overridePopulate, idFilter: overrideIdFilter } = overrides ?? {};
|
|
3476
|
-
const filter2 = overrideIdFilter
|
|
3693
|
+
const filter2 = overrideIdFilter ?? await this.genIDFilter(id);
|
|
3694
|
+
if (filter2 === false) return { success: false, kind: "error", code: "forbidden" /* Forbidden */, query: { filter: filter2 } };
|
|
3477
3695
|
return this.findOne(
|
|
3478
3696
|
filter2,
|
|
3479
3697
|
{
|
|
@@ -3495,33 +3713,39 @@ var Service = class extends Base {
|
|
|
3495
3713
|
const { skim, includePermissions, includeCount, populateAccess, lean } = this.resolveFindOptions(options);
|
|
3496
3714
|
const { filter: overrideFilter, select: overrideSelect, populate: overridePopulate } = overrides ?? {};
|
|
3497
3715
|
let parsedFilter;
|
|
3716
|
+
let processedInclude;
|
|
3498
3717
|
try {
|
|
3499
|
-
|
|
3718
|
+
processedInclude = this.processInclude(include);
|
|
3719
|
+
parsedFilter = overrideFilter === false ? false : await this.parseClientData(filter2);
|
|
3500
3720
|
} catch (error2) {
|
|
3501
3721
|
const result = this.getClientRequestErrorResult(error2);
|
|
3502
3722
|
if (result) return result;
|
|
3503
3723
|
throw error2;
|
|
3504
3724
|
}
|
|
3505
3725
|
const [_filter, _select, _populate, pagination, allowedSortFields] = await Promise.all([
|
|
3506
|
-
overrideFilter
|
|
3726
|
+
overrideFilter ?? this.genFilter("list", parsedFilter),
|
|
3507
3727
|
overrideSelect || this.genQuerySelect("list", select),
|
|
3508
3728
|
overridePopulate || this.genPopulate(populateAccess, populate),
|
|
3509
3729
|
genPagination({ skip, limit, page, pageSize }, this.options.listHardLimit),
|
|
3510
3730
|
this.genAllowedFields({}, "list", this.baseFieldsExt)
|
|
3511
3731
|
]);
|
|
3512
3732
|
const finalSelect = normalizeSelect(_select);
|
|
3513
|
-
const filteredPopulate = (0,
|
|
3514
|
-
const { includes, correlatedIncludes, includeLocalFields, includePaths, correlatedReferenceFields } =
|
|
3733
|
+
const filteredPopulate = (0, import_utils17.isArray)(finalSelect) && (0, import_utils17.isArray)(_populate) ? _populate.filter((p) => finalSelect.includes(p.path.split(".")[0])) : _populate;
|
|
3734
|
+
const { includes, correlatedIncludes, includeLocalFields, includePaths, correlatedReferenceFields } = processedInclude;
|
|
3515
3735
|
const correlatedOutputPaths = correlatedIncludes.map((entry) => entry.path);
|
|
3736
|
+
const effectiveAllowedSortFields = this.getEffectiveAllowedSortFields(allowedSortFields);
|
|
3737
|
+
const stripDisallowedSort = this.options?.stripDisallowedSort ?? false;
|
|
3738
|
+
const sanitizedSort = stripDisallowedSort ? sanitizeSortFields(sort, effectiveAllowedSortFields) : null;
|
|
3739
|
+
const effectiveSort = stripDisallowedSort ? sanitizedSort?.sort : sort;
|
|
3516
3740
|
const query = {
|
|
3517
3741
|
filter: _filter,
|
|
3518
3742
|
select: finalSelect.concat(includeLocalFields, correlatedReferenceFields),
|
|
3519
3743
|
populate: filteredPopulate,
|
|
3520
|
-
sort,
|
|
3744
|
+
sort: effectiveSort,
|
|
3521
3745
|
...pagination
|
|
3522
3746
|
};
|
|
3523
3747
|
const startedAt = this.beginOp("find", _filter, {
|
|
3524
|
-
sort,
|
|
3748
|
+
sort: effectiveSort,
|
|
3525
3749
|
skip: pagination.skip,
|
|
3526
3750
|
limit: pagination.limit,
|
|
3527
3751
|
selectCount: finalSelect.concat(includeLocalFields, correlatedReferenceFields).length,
|
|
@@ -3531,10 +3755,17 @@ var Service = class extends Base {
|
|
|
3531
3755
|
this.completeOp("find", startedAt, "forbidden" /* Forbidden */, _filter);
|
|
3532
3756
|
return { success: false, kind: "error", code: "forbidden" /* Forbidden */, query };
|
|
3533
3757
|
}
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3758
|
+
if (stripDisallowedSort) {
|
|
3759
|
+
if (sanitizedSort && sanitizedSort.errors.length > 0) {
|
|
3760
|
+
this.completeOp("find", startedAt, "bad_request" /* BadRequest */, _filter);
|
|
3761
|
+
return { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors: sanitizedSort.errors, query };
|
|
3762
|
+
}
|
|
3763
|
+
} else {
|
|
3764
|
+
const sortErrors = validateSortFields(sort, effectiveAllowedSortFields);
|
|
3765
|
+
if (sortErrors.length > 0) {
|
|
3766
|
+
this.completeOp("find", startedAt, "bad_request" /* BadRequest */, _filter);
|
|
3767
|
+
return { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors: sortErrors, query };
|
|
3768
|
+
}
|
|
3538
3769
|
}
|
|
3539
3770
|
let docs = await this.model.find({
|
|
3540
3771
|
...query,
|
|
@@ -3548,8 +3779,8 @@ var Service = class extends Base {
|
|
|
3548
3779
|
originalDocumentSnapshot: toObject(doc),
|
|
3549
3780
|
resolvedQuery: query
|
|
3550
3781
|
}));
|
|
3551
|
-
const _decorate = (0,
|
|
3552
|
-
const correlatedSnapshots = docs.map((doc) => (0,
|
|
3782
|
+
const _decorate = (0, import_utils17.isFunction)(decorate) ? decorate : (v) => v;
|
|
3783
|
+
const correlatedSnapshots = docs.map((doc) => (0, import_utils17.cloneDeep)(toObject(doc)));
|
|
3553
3784
|
try {
|
|
3554
3785
|
docs = await this.includeDocs(docs, includes);
|
|
3555
3786
|
if (correlatedIncludes.length > 0) {
|
|
@@ -3665,16 +3896,16 @@ var Service = class extends Base {
|
|
|
3665
3896
|
resolvedQuery: resolvedPopulate.length > 0 ? { populate: resolvedPopulate } : {}
|
|
3666
3897
|
};
|
|
3667
3898
|
const allowedFields = await this.genAllowedFields(item, "create");
|
|
3668
|
-
const allowedData = (0,
|
|
3899
|
+
const allowedData = (0, import_utils17.pick)(item, allowedFields);
|
|
3669
3900
|
context.allowedFields = allowedFields;
|
|
3670
3901
|
context.allowedData = allowedData;
|
|
3671
3902
|
const validated = await this.validate(allowedData, "create", context);
|
|
3672
|
-
if ((0,
|
|
3903
|
+
if ((0, import_utils17.isBoolean)(validated)) {
|
|
3673
3904
|
if (!validated) {
|
|
3674
3905
|
validationErrors.push({ index, errors: [] });
|
|
3675
3906
|
return void 0;
|
|
3676
3907
|
}
|
|
3677
|
-
} else if ((0,
|
|
3908
|
+
} else if ((0, import_utils17.isArray)(validated)) {
|
|
3678
3909
|
if (validated.length > 0) {
|
|
3679
3910
|
validationErrors.push({
|
|
3680
3911
|
index,
|
|
@@ -3704,7 +3935,7 @@ var Service = class extends Base {
|
|
|
3704
3935
|
contexts[index].preparedData = preparedData;
|
|
3705
3936
|
return preparedData;
|
|
3706
3937
|
});
|
|
3707
|
-
const _decorate = (0,
|
|
3938
|
+
const _decorate = (0, import_utils17.isFunction)(decorate) ? decorate : (v) => v;
|
|
3708
3939
|
const createdDocs = await this.model.create(items);
|
|
3709
3940
|
const docs = await mapWithConcurrencyLimit(createdDocs, maxBulkConcurrency, async (doc, index) => {
|
|
3710
3941
|
contexts[index].currentDocument = doc;
|
|
@@ -3721,7 +3952,7 @@ var Service = class extends Base {
|
|
|
3721
3952
|
}
|
|
3722
3953
|
if (includeDocPermissions) doc = await this.addDocPermissions(doc, "create", contexts[index]);
|
|
3723
3954
|
if (includePermissions) doc = await this.addFieldPermissions(doc, "read", contexts[index]);
|
|
3724
|
-
if (resolvedPopulate.length > 0) await populateDoc(doc, resolvedPopulate);
|
|
3955
|
+
if (resolvedPopulate.length > 0) await this.persist(() => populateDoc(doc, resolvedPopulate));
|
|
3725
3956
|
doc = await this.trimOutputFields(doc, "read", this.baseFieldsExt);
|
|
3726
3957
|
let outputDoc = await _decorate(doc, contexts[index]);
|
|
3727
3958
|
if (!includePermissions) outputDoc = this.addEmptyPermissions(outputDoc);
|
|
@@ -3777,6 +4008,13 @@ var Service = class extends Base {
|
|
|
3777
4008
|
data: doc
|
|
3778
4009
|
};
|
|
3779
4010
|
}
|
|
4011
|
+
/**
|
|
4012
|
+
* Update at authorized policy paths: admitted leaves preserve omitted siblings;
|
|
4013
|
+
* whole-authorized objects/arrays replace. Shared by updateById and existing-row
|
|
4014
|
+
* upsert. Validation precedes trusted prepare output (not re-filtered); partial
|
|
4015
|
+
* prepare containers preserve omissions. Transform retains explicit document-setter
|
|
4016
|
+
* replacement authority. Send nested client JSON, not literal dotted update keys.
|
|
4017
|
+
*/
|
|
3780
4018
|
async updateOne(filter2, data, args, options, decorate) {
|
|
3781
4019
|
const filterErrors = this.validateClientFilter(filter2);
|
|
3782
4020
|
if (filterErrors.length > 0) return { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors: filterErrors };
|
|
@@ -3784,7 +4022,7 @@ var Service = class extends Base {
|
|
|
3784
4022
|
const { skim, includePermissions, populateAccess } = this.resolveUpdateOneOptions(options);
|
|
3785
4023
|
const { filter: overrideFilter, populate: overridePopulate } = overrides ?? {};
|
|
3786
4024
|
const [_filter, _populate] = await Promise.all([
|
|
3787
|
-
overrideFilter
|
|
4025
|
+
overrideFilter ?? this.genFilter("update", filter2),
|
|
3788
4026
|
overridePopulate || this.genPopulate(populateAccess, populate)
|
|
3789
4027
|
]);
|
|
3790
4028
|
const query = { filter: _filter, populate: _populate };
|
|
@@ -3822,16 +4060,16 @@ var Service = class extends Base {
|
|
|
3822
4060
|
context.docPermissions = this.getDocPermissions(doc);
|
|
3823
4061
|
context.currentDocument = doc;
|
|
3824
4062
|
const allowedFields = await this.genAllowedFields(doc, "update");
|
|
3825
|
-
const allowedData = (0,
|
|
4063
|
+
const allowedData = (0, import_utils17.pick)(data, allowedFields);
|
|
3826
4064
|
context.allowedFields = allowedFields;
|
|
3827
4065
|
context.allowedData = allowedData;
|
|
3828
4066
|
const validated = await this.validate(allowedData, "update", context);
|
|
3829
|
-
if ((0,
|
|
4067
|
+
if ((0, import_utils17.isBoolean)(validated)) {
|
|
3830
4068
|
if (!validated) {
|
|
3831
4069
|
this.completeOp("updateOne", startedAt, "bad_request" /* BadRequest */, _filter);
|
|
3832
4070
|
return { success: false, kind: "error", code: "bad_request" /* BadRequest */ };
|
|
3833
4071
|
}
|
|
3834
|
-
} else if ((0,
|
|
4072
|
+
} else if ((0, import_utils17.isArray)(validated)) {
|
|
3835
4073
|
if (validated.length > 0) {
|
|
3836
4074
|
this.completeOp("updateOne", startedAt, "bad_request" /* BadRequest */, _filter);
|
|
3837
4075
|
return { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors: validated };
|
|
@@ -3839,18 +4077,18 @@ var Service = class extends Base {
|
|
|
3839
4077
|
}
|
|
3840
4078
|
const prepared = await this.prepare(allowedData, "update", context);
|
|
3841
4079
|
context.preparedData = prepared;
|
|
3842
|
-
|
|
4080
|
+
applyUpdate(doc, prepared, allowedFields);
|
|
3843
4081
|
context.modifiedPaths = doc.modifiedPaths();
|
|
3844
4082
|
doc = assertModelDocument(await this.transform(doc, "update", context), this.modelName, "transform");
|
|
3845
4083
|
context.currentDocument = doc;
|
|
3846
|
-
doc = await doc.save();
|
|
4084
|
+
doc = await this.persist(() => doc.save());
|
|
3847
4085
|
const diffExcludeFields = [this.options.documentPermissionField, "__v"];
|
|
3848
4086
|
this.asServiceHookContext(context).diff = (d) => {
|
|
3849
4087
|
context.changes = (0, import_just_diff.diff)(
|
|
3850
|
-
(0,
|
|
3851
|
-
(0,
|
|
4088
|
+
(0, import_utils17.omit)(context.originalDocumentSnapshot, diffExcludeFields),
|
|
4089
|
+
(0, import_utils17.omit)(d.toObject({ virtuals: false }), diffExcludeFields)
|
|
3852
4090
|
) || [];
|
|
3853
|
-
context.modifiedPaths = (0,
|
|
4091
|
+
context.modifiedPaths = (0, import_utils17.uniq)(context.changes.map((di) => di.path.length > 0 ? String(di.path[0]) : ""));
|
|
3854
4092
|
};
|
|
3855
4093
|
doc = assertModelDocument(await this.afterPersist(doc, "update", context), this.modelName, "afterPersist");
|
|
3856
4094
|
context.currentDocument = doc;
|
|
@@ -3863,10 +4101,10 @@ var Service = class extends Base {
|
|
|
3863
4101
|
}
|
|
3864
4102
|
if (includeDocPermissions) doc = await this.addDocPermissions(doc, "update", context);
|
|
3865
4103
|
if (includePermissions) doc = await this.addFieldPermissions(doc, "update", context);
|
|
3866
|
-
if (_populate) await populateDoc(doc, _populate);
|
|
4104
|
+
if (_populate) await this.persist(() => populateDoc(doc, _populate));
|
|
3867
4105
|
doc = await this.trimOutputFields(doc, "read", this.baseFieldsExt);
|
|
3868
4106
|
let outputDoc = doc;
|
|
3869
|
-
if ((0,
|
|
4107
|
+
if ((0, import_utils17.isFunction)(decorate)) outputDoc = await decorate(outputDoc, context);
|
|
3870
4108
|
if (!includePermissions) outputDoc = this.addEmptyPermissions(outputDoc);
|
|
3871
4109
|
this.completeOp("updateOne", startedAt, "success" /* Success */, _filter);
|
|
3872
4110
|
return { success: true, kind: "single", code: "success" /* Success */, data: outputDoc, input: prepared };
|
|
@@ -3875,7 +4113,8 @@ var Service = class extends Base {
|
|
|
3875
4113
|
const { populate, overrides } = this.resolveUpdateByIdArgs(args);
|
|
3876
4114
|
const { skim, includePermissions, populateAccess } = this.resolveUpdateByIdOptions(options);
|
|
3877
4115
|
const { populate: overridePopulate, idFilter: overrideIdFilter } = overrides;
|
|
3878
|
-
const filter2 = overrideIdFilter
|
|
4116
|
+
const filter2 = overrideIdFilter ?? await this.genIDFilter(id);
|
|
4117
|
+
if (filter2 === false) return { success: false, kind: "error", code: "forbidden" /* Forbidden */, query: { filter: filter2 } };
|
|
3879
4118
|
return this.updateOne(
|
|
3880
4119
|
filter2,
|
|
3881
4120
|
data,
|
|
@@ -3895,7 +4134,7 @@ var Service = class extends Base {
|
|
|
3895
4134
|
const { populate, overrides } = this.resolveUpsertArgs(args);
|
|
3896
4135
|
const { skim, includePermissions, populateAccess } = this.resolveUpsertOptions(options);
|
|
3897
4136
|
const { filter: overrideFilter, populate: overridePopulate } = overrides ?? {};
|
|
3898
|
-
const _filter = await (overrideFilter
|
|
4137
|
+
const _filter = await (overrideFilter ?? this.genFilter("update", filter2));
|
|
3899
4138
|
const query = { filter: _filter };
|
|
3900
4139
|
const startedAt = this.beginOp("upsert", _filter);
|
|
3901
4140
|
if (_filter === false) {
|
|
@@ -3955,7 +4194,9 @@ var Service = class extends Base {
|
|
|
3955
4194
|
resolvedQuery: query
|
|
3956
4195
|
};
|
|
3957
4196
|
await this.beforeDelete(doc, context);
|
|
3958
|
-
await
|
|
4197
|
+
await this.persist(
|
|
4198
|
+
() => "deleteOne" in doc ? doc.deleteOne() : doc.remove()
|
|
4199
|
+
);
|
|
3959
4200
|
context.finalDocumentSnapshot = toObject(doc);
|
|
3960
4201
|
await this.afterDelete(doc, context);
|
|
3961
4202
|
this.completeOp("delete", startedAt, "success" /* Success */, filter2);
|
|
@@ -3966,6 +4207,7 @@ var Service = class extends Base {
|
|
|
3966
4207
|
if (filterErrors.length > 0) return { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors: filterErrors };
|
|
3967
4208
|
const { access, includeId } = this.resolveExistsOptions(options);
|
|
3968
4209
|
filter2 = await this.genFilter(access, filter2);
|
|
4210
|
+
if (filter2 === false) return { success: false, kind: "error", code: "forbidden" /* Forbidden */, query: { filter: filter2 } };
|
|
3969
4211
|
const result = await this.model.exists(filter2);
|
|
3970
4212
|
return {
|
|
3971
4213
|
success: true,
|
|
@@ -4035,6 +4277,13 @@ var Service = class extends Base {
|
|
|
4035
4277
|
query
|
|
4036
4278
|
};
|
|
4037
4279
|
}
|
|
4280
|
+
/**
|
|
4281
|
+
* Group target document IDs by original parent-key aliases using one aggregate.
|
|
4282
|
+
* Resolves the requested row policy (legacy count includes pass `count`), casts
|
|
4283
|
+
* authorized filters/foreign operands with the target schema, and deduplicates IDs
|
|
4284
|
+
* independently of pagination. Cast failures return BadRequest; false policy returns
|
|
4285
|
+
* Forbidden. Casting does not execute countDocuments middleware/plugins.
|
|
4286
|
+
*/
|
|
4038
4287
|
async countByFieldValues(foreignField, values, filter2 = {}, access = "list") {
|
|
4039
4288
|
if (!isValidFieldPath(foreignField)) {
|
|
4040
4289
|
return {
|
|
@@ -4046,14 +4295,32 @@ var Service = class extends Base {
|
|
|
4046
4295
|
}
|
|
4047
4296
|
const filterErrors = this.validateClientFilter(filter2);
|
|
4048
4297
|
if (filterErrors.length > 0) return { success: false, kind: "error", code: "bad_request" /* BadRequest */, errors: filterErrors };
|
|
4049
|
-
const uniqueValues = (0,
|
|
4298
|
+
const uniqueValues = (0, import_utils17.uniqBy)(values, (value) => String(value));
|
|
4050
4299
|
filter2 = await this.genFilter(access, {
|
|
4051
4300
|
...filter2,
|
|
4052
4301
|
[foreignField]: { $in: uniqueValues }
|
|
4053
4302
|
});
|
|
4054
4303
|
const query = { filter: filter2 };
|
|
4055
4304
|
if (filter2 === false) return { success: false, kind: "error", code: "forbidden" /* Forbidden */, query };
|
|
4056
|
-
|
|
4305
|
+
let matchFilter = filter2;
|
|
4306
|
+
let castValues = uniqueValues;
|
|
4307
|
+
try {
|
|
4308
|
+
if (this.model.castFilter) {
|
|
4309
|
+
matchFilter = this.model.castFilter(matchFilter);
|
|
4310
|
+
const foreignFilter = this.model.castFilter({ [foreignField]: { $in: uniqueValues } });
|
|
4311
|
+
castValues = foreignFilter[foreignField]?.$in;
|
|
4312
|
+
if (!Array.isArray(castValues) || castValues.length !== uniqueValues.length) {
|
|
4313
|
+
throw new Error("Foreign field query casting must retain the join operands");
|
|
4314
|
+
}
|
|
4315
|
+
}
|
|
4316
|
+
} catch {
|
|
4317
|
+
return {
|
|
4318
|
+
success: false,
|
|
4319
|
+
kind: "error",
|
|
4320
|
+
code: "bad_request" /* BadRequest */,
|
|
4321
|
+
errors: [{ detail: "Invalid grouped include count filter or foreign-key value" }]
|
|
4322
|
+
};
|
|
4323
|
+
}
|
|
4057
4324
|
const rows = await this.model.aggregate([
|
|
4058
4325
|
{ $match: matchFilter },
|
|
4059
4326
|
{
|
|
@@ -4064,12 +4331,20 @@ var Service = class extends Base {
|
|
|
4064
4331
|
}
|
|
4065
4332
|
},
|
|
4066
4333
|
{ $unwind: "$foreignValues" },
|
|
4067
|
-
{ $match: { foreignValues: { $in:
|
|
4334
|
+
{ $match: { foreignValues: { $in: castValues } } },
|
|
4068
4335
|
{ $group: { _id: "$foreignValues", documentIds: { $addToSet: "$_id" } } }
|
|
4069
4336
|
]);
|
|
4070
|
-
const
|
|
4337
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
4071
4338
|
for (const row of rows) {
|
|
4072
|
-
|
|
4339
|
+
const key = String(row._id);
|
|
4340
|
+
const ids = grouped.get(key) ?? /* @__PURE__ */ new Set();
|
|
4341
|
+
for (const id of row.documentIds) ids.add(String(id));
|
|
4342
|
+
grouped.set(key, ids);
|
|
4343
|
+
}
|
|
4344
|
+
const counts = /* @__PURE__ */ new Map();
|
|
4345
|
+
for (let i = 0; i < uniqueValues.length; i++) {
|
|
4346
|
+
const ids = grouped.get(String(castValues[i]));
|
|
4347
|
+
if (ids) counts.set(String(uniqueValues[i]), ids);
|
|
4073
4348
|
}
|
|
4074
4349
|
return { success: true, kind: "single", code: "success" /* Success */, data: counts, query };
|
|
4075
4350
|
}
|
|
@@ -4122,7 +4397,7 @@ var Service = class extends Base {
|
|
|
4122
4397
|
return resolveExistsOptions(this, options);
|
|
4123
4398
|
}
|
|
4124
4399
|
async getFieldPermissionAccess(ids) {
|
|
4125
|
-
const uniqueIds = (0,
|
|
4400
|
+
const uniqueIds = (0, import_utils17.compact)((0, import_utils17.uniqBy)(ids, (id) => String(id)).map((id) => String(id)));
|
|
4126
4401
|
if (uniqueIds.length === 0) {
|
|
4127
4402
|
return {
|
|
4128
4403
|
readIds: /* @__PURE__ */ new Set(),
|
|
@@ -4148,17 +4423,60 @@ var Service = class extends Base {
|
|
|
4148
4423
|
async readSub(id, sub, subId, options) {
|
|
4149
4424
|
return readSub(this, id, sub, subId, options);
|
|
4150
4425
|
}
|
|
4426
|
+
/**
|
|
4427
|
+
* Apply authorized policy-path updates, preserving protected siblings, then save.
|
|
4428
|
+
* Output requires post-save parent read and subdocument read operation/row access;
|
|
4429
|
+
* hidden output is successful `data: null` (direct/root-entry 200), not a failed write.
|
|
4430
|
+
* Visible output uses read fields plus `_id`. Do not retry solely for null output.
|
|
4431
|
+
*/
|
|
4151
4432
|
async updateSub(id, sub, subId, data) {
|
|
4152
|
-
return updateSub(
|
|
4433
|
+
return updateSub(
|
|
4434
|
+
this,
|
|
4435
|
+
this.persist,
|
|
4436
|
+
(access) => this.req.macl.isAllowed(this.modelName, access),
|
|
4437
|
+
id,
|
|
4438
|
+
sub,
|
|
4439
|
+
subId,
|
|
4440
|
+
data
|
|
4441
|
+
);
|
|
4153
4442
|
}
|
|
4443
|
+
/**
|
|
4444
|
+
* Save authorized targeted updates, then return readable targets in stored order.
|
|
4445
|
+
* Requires parent/subdocument read access for output, not subdocument list access;
|
|
4446
|
+
* uses read fields plus `_id`. Hidden output succeeds with `data: [], count: 0`
|
|
4447
|
+
* (direct/root-entry 200); count measures visible rows, not all updated rows.
|
|
4448
|
+
*/
|
|
4154
4449
|
async bulkUpdateSub(id, sub, data) {
|
|
4155
|
-
return bulkUpdateSub(
|
|
4450
|
+
return bulkUpdateSub(
|
|
4451
|
+
this,
|
|
4452
|
+
this.persist,
|
|
4453
|
+
(access) => this.req.macl.isAllowed(this.modelName, access),
|
|
4454
|
+
id,
|
|
4455
|
+
sub,
|
|
4456
|
+
(0, import_utils17.castArray)(data)
|
|
4457
|
+
);
|
|
4156
4458
|
}
|
|
4459
|
+
/**
|
|
4460
|
+
* Append/prepend admitted rows, then return the visible full array (including when
|
|
4461
|
+
* input is empty). Output requires post-save parent read and subdocument list AND
|
|
4462
|
+
* read guards/row filters; fields use read projection plus `_id`.
|
|
4463
|
+
* Hidden output succeeds with code `created`, `data: [], count: 0`
|
|
4464
|
+
* (direct/root-entry 201). Visible count/order follow filtered stored rows.
|
|
4465
|
+
* Empty output does not mean the write failed; addFirst is a service option.
|
|
4466
|
+
*/
|
|
4157
4467
|
async createSub(id, sub, data, options) {
|
|
4158
|
-
return createSub(
|
|
4468
|
+
return createSub(
|
|
4469
|
+
this,
|
|
4470
|
+
this.persist,
|
|
4471
|
+
(access) => this.req.macl.isAllowed(this.modelName, access),
|
|
4472
|
+
id,
|
|
4473
|
+
sub,
|
|
4474
|
+
data,
|
|
4475
|
+
options
|
|
4476
|
+
);
|
|
4159
4477
|
}
|
|
4160
4478
|
async deleteSub(id, sub, subId) {
|
|
4161
|
-
return deleteSub(this, id, sub, subId);
|
|
4479
|
+
return deleteSub(this, this.persist, id, sub, subId);
|
|
4162
4480
|
}
|
|
4163
4481
|
async getParentDoc(id, sub, args, options) {
|
|
4164
4482
|
return getParentDoc(this, id, sub, args, options);
|
|
@@ -4166,7 +4484,7 @@ var Service = class extends Base {
|
|
|
4166
4484
|
};
|
|
4167
4485
|
|
|
4168
4486
|
// src/services/public-service.ts
|
|
4169
|
-
var
|
|
4487
|
+
var import_utils18 = require("@web-ts-toolkit/utils");
|
|
4170
4488
|
var PublicService = class extends Service {
|
|
4171
4489
|
async _list(filter2, args, options) {
|
|
4172
4490
|
const { select, populate, include, sort, skip, limit, page, pageSize, tasks } = this.resolvePublicListArgs(args);
|
|
@@ -4205,7 +4523,7 @@ var PublicService = class extends Service {
|
|
|
4205
4523
|
let d = toObject(doc);
|
|
4206
4524
|
d = await this.decorate(d, "create", context);
|
|
4207
4525
|
d = this.runTasks(d, tasks);
|
|
4208
|
-
if (select) d = (0,
|
|
4526
|
+
if (select) d = (0, import_utils18.pick)(d, [...normalizeSelect(select), ...this.baseFieldsExt]);
|
|
4209
4527
|
return d;
|
|
4210
4528
|
}
|
|
4211
4529
|
);
|
|
@@ -4232,6 +4550,7 @@ var PublicService = class extends Service {
|
|
|
4232
4550
|
},
|
|
4233
4551
|
{ skim, includePermissions, access, populateAccess, lean }
|
|
4234
4552
|
);
|
|
4553
|
+
if (!result.success && (result.code === "bad_request" /* BadRequest */ || result.code === "forbidden" /* Forbidden */)) return result;
|
|
4235
4554
|
if (tryList && (!result.success || !result.data)) {
|
|
4236
4555
|
const listAllowed = await this.req.macl.isAllowed(this.modelName, "list");
|
|
4237
4556
|
if (!listAllowed) {
|
|
@@ -4272,6 +4591,7 @@ var PublicService = class extends Service {
|
|
|
4272
4591
|
},
|
|
4273
4592
|
{ skim, includePermissions, access, populateAccess, lean }
|
|
4274
4593
|
);
|
|
4594
|
+
if (!result.success && (result.code === "bad_request" /* BadRequest */ || result.code === "forbidden" /* Forbidden */)) return result;
|
|
4275
4595
|
if (tryList && (!result.success || !result.data)) {
|
|
4276
4596
|
const listAllowed = await this.req.macl.isAllowed(this.modelName, "list");
|
|
4277
4597
|
if (!listAllowed) {
|
|
@@ -4310,8 +4630,8 @@ var PublicService = class extends Service {
|
|
|
4310
4630
|
let d = toObject(doc);
|
|
4311
4631
|
d = await this.decorate(d, "update", context);
|
|
4312
4632
|
d = this.runTasks(d, tasks);
|
|
4313
|
-
if (select) d = (0,
|
|
4314
|
-
else if (!returningAll) d = (0,
|
|
4633
|
+
if (select) d = (0, import_utils18.pick)(d, [...normalizeSelect(select), ...this.baseFieldsExt]);
|
|
4634
|
+
else if (!returningAll) d = (0, import_utils18.pick)(d, [...Object.keys(data), "_id"]);
|
|
4315
4635
|
return d;
|
|
4316
4636
|
}
|
|
4317
4637
|
);
|
|
@@ -4320,6 +4640,11 @@ var PublicService = class extends Service {
|
|
|
4320
4640
|
}
|
|
4321
4641
|
return result;
|
|
4322
4642
|
}
|
|
4643
|
+
/**
|
|
4644
|
+
* With an _id, check update-policy existence then update: terminal denial returns
|
|
4645
|
+
* Forbidden, an allowed miss remains Unauthorized, and neither creates a row.
|
|
4646
|
+
* Without an _id, use the create path.
|
|
4647
|
+
*/
|
|
4323
4648
|
async _upsert(data, args, options) {
|
|
4324
4649
|
const idKey = this.getIdentifier();
|
|
4325
4650
|
if (idKey !== "_id") {
|
|
@@ -4433,7 +4758,7 @@ var PublicService = class extends Service {
|
|
|
4433
4758
|
};
|
|
4434
4759
|
|
|
4435
4760
|
// src/services/data-service.ts
|
|
4436
|
-
var
|
|
4761
|
+
var import_utils19 = require("@web-ts-toolkit/utils");
|
|
4437
4762
|
var DataService = class {
|
|
4438
4763
|
constructor(req, dataName) {
|
|
4439
4764
|
this.req = req;
|
|
@@ -4455,7 +4780,7 @@ var DataService = class {
|
|
|
4455
4780
|
let doc = await findElement(this.data, _filter);
|
|
4456
4781
|
if (!doc) return { success: false, kind: "error", code: "not_found" /* NotFound */, query };
|
|
4457
4782
|
doc = await this.trimOutputFields(doc, access);
|
|
4458
|
-
if (_select.length > 0) doc = (0,
|
|
4783
|
+
if (_select.length > 0) doc = (0, import_utils19.pick)(doc, _select);
|
|
4459
4784
|
return { success: true, kind: "single", code: "success" /* Success */, data: doc, query };
|
|
4460
4785
|
}
|
|
4461
4786
|
async findById(id, args, options) {
|
|
@@ -4495,13 +4820,13 @@ var DataService = class {
|
|
|
4495
4820
|
const totalCount = docs.length;
|
|
4496
4821
|
if (sort) {
|
|
4497
4822
|
const normalizedSort = normalizeSortForOrderBy(sort);
|
|
4498
|
-
docs = (0,
|
|
4823
|
+
docs = (0, import_utils19.orderBy)(docs, normalizedSort.fields, normalizedSort.orders);
|
|
4499
4824
|
}
|
|
4500
4825
|
const pagedDocs = docs.slice(query.skip, query.limit && query.skip + query.limit);
|
|
4501
4826
|
const { maxHookConcurrency } = this.getRequestComplexity();
|
|
4502
4827
|
const trimmed = await mapWithConcurrencyLimit(pagedDocs, maxHookConcurrency, async (doc) => {
|
|
4503
4828
|
doc = await this.trimOutputFields(doc, "list");
|
|
4504
|
-
if (_select.length > 0) doc = (0,
|
|
4829
|
+
if (_select.length > 0) doc = (0, import_utils19.pick)(doc, _select);
|
|
4505
4830
|
return doc;
|
|
4506
4831
|
});
|
|
4507
4832
|
if (!hasExplicitLimit && trimmed.length === totalCount) {
|
|
@@ -4550,7 +4875,7 @@ var DataService = class {
|
|
|
4550
4875
|
};
|
|
4551
4876
|
|
|
4552
4877
|
// src/processors.ts
|
|
4553
|
-
var
|
|
4878
|
+
var import_utils20 = require("@web-ts-toolkit/utils");
|
|
4554
4879
|
var UNSAFE_PATH_KEYS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
|
|
4555
4880
|
var DEFAULT_MAX_SLICE = 1e3;
|
|
4556
4881
|
var DEFAULT_MASK_REPLACEMENT = "***";
|
|
@@ -4585,10 +4910,10 @@ function resolveLeafParents(obj, src) {
|
|
|
4585
4910
|
for (let ind = 0; ind < segs.length - 1; ind += 1) {
|
|
4586
4911
|
const seg = segs[ind];
|
|
4587
4912
|
targets = targets.reduce((ret, target) => {
|
|
4588
|
-
if (!(0,
|
|
4913
|
+
if (!(0, import_utils20.isPlainObject)(target)) return ret;
|
|
4589
4914
|
const targetObject = target;
|
|
4590
4915
|
const next = targetObject[seg];
|
|
4591
|
-
if ((0,
|
|
4916
|
+
if ((0, import_utils20.isArray)(next)) {
|
|
4592
4917
|
for (const item of next) {
|
|
4593
4918
|
ret.push(item);
|
|
4594
4919
|
}
|
|
@@ -4598,8 +4923,8 @@ function resolveLeafParents(obj, src) {
|
|
|
4598
4923
|
}
|
|
4599
4924
|
const leaf = segs[segs.length - 1];
|
|
4600
4925
|
const pairs = [];
|
|
4601
|
-
(0,
|
|
4602
|
-
if (!(0,
|
|
4926
|
+
(0, import_utils20.forEach)(targets, (target) => {
|
|
4927
|
+
if (!(0, import_utils20.isPlainObject)(target)) return;
|
|
4603
4928
|
pairs.push({ parent: target, leaf });
|
|
4604
4929
|
});
|
|
4605
4930
|
return pairs;
|
|
@@ -4607,8 +4932,8 @@ function resolveLeafParents(obj, src) {
|
|
|
4607
4932
|
var copyAndDepopulate = (docObject, operations, options = { mutable: true, idField: "_id" }) => {
|
|
4608
4933
|
const mutable = options.mutable !== false;
|
|
4609
4934
|
const idField = options.idField ?? "_id";
|
|
4610
|
-
const obj = mutable ? docObject : (0,
|
|
4611
|
-
(0,
|
|
4935
|
+
const obj = mutable ? docObject : (0, import_utils20.cloneDeep)(docObject);
|
|
4936
|
+
(0, import_utils20.forEach)(Array.isArray(operations) ? operations : [], (op) => {
|
|
4612
4937
|
if (!op || typeof op.src !== "string" || typeof op.dest !== "string") return;
|
|
4613
4938
|
const src = op.src;
|
|
4614
4939
|
const dest = op.dest;
|
|
@@ -4617,19 +4942,19 @@ var copyAndDepopulate = (docObject, operations, options = { mutable: true, idFie
|
|
|
4617
4942
|
assertSafePath(dest);
|
|
4618
4943
|
let targets = [obj];
|
|
4619
4944
|
const segs = src.split(".");
|
|
4620
|
-
(0,
|
|
4945
|
+
(0, import_utils20.forEach)(segs, (seg, ind) => {
|
|
4621
4946
|
if (segs.length === ind + 1) {
|
|
4622
|
-
(0,
|
|
4623
|
-
if (!(0,
|
|
4947
|
+
(0, import_utils20.forEach)(targets, (target) => {
|
|
4948
|
+
if (!(0, import_utils20.isPlainObject)(target)) return;
|
|
4624
4949
|
const targetObject = target;
|
|
4625
4950
|
const leaf = targetObject[seg];
|
|
4626
4951
|
const sourceForDest = leaf;
|
|
4627
4952
|
let depopulated;
|
|
4628
|
-
if ((0,
|
|
4953
|
+
if ((0, import_utils20.isArray)(leaf)) {
|
|
4629
4954
|
if (leaf.length === 0) {
|
|
4630
4955
|
depopulated = [];
|
|
4631
|
-
} else if (leaf.every((item) => (0,
|
|
4632
|
-
depopulated = (0,
|
|
4956
|
+
} else if (leaf.every((item) => (0, import_utils20.isPlainObject)(item))) {
|
|
4957
|
+
depopulated = (0, import_utils20.map)(leaf, idField).map((id, i) => {
|
|
4633
4958
|
if (id === void 0) {
|
|
4634
4959
|
failMissingId(`${src}[${i}]`);
|
|
4635
4960
|
}
|
|
@@ -4638,8 +4963,8 @@ var copyAndDepopulate = (docObject, operations, options = { mutable: true, idFie
|
|
|
4638
4963
|
} else {
|
|
4639
4964
|
return;
|
|
4640
4965
|
}
|
|
4641
|
-
} else if ((0,
|
|
4642
|
-
const id = (0,
|
|
4966
|
+
} else if ((0, import_utils20.isPlainObject)(leaf)) {
|
|
4967
|
+
const id = (0, import_utils20.get)(leaf, idField);
|
|
4643
4968
|
if (id === void 0) {
|
|
4644
4969
|
failMissingId(src);
|
|
4645
4970
|
}
|
|
@@ -4647,15 +4972,15 @@ var copyAndDepopulate = (docObject, operations, options = { mutable: true, idFie
|
|
|
4647
4972
|
} else {
|
|
4648
4973
|
return;
|
|
4649
4974
|
}
|
|
4650
|
-
(0,
|
|
4651
|
-
(0,
|
|
4975
|
+
(0, import_utils20.set)(targetObject, dest, sourceForDest);
|
|
4976
|
+
(0, import_utils20.set)(targetObject, seg, depopulated);
|
|
4652
4977
|
});
|
|
4653
4978
|
} else {
|
|
4654
4979
|
targets = targets.reduce((ret, target) => {
|
|
4655
|
-
if (!(0,
|
|
4980
|
+
if (!(0, import_utils20.isPlainObject)(target)) return ret;
|
|
4656
4981
|
const targetObject = target;
|
|
4657
4982
|
const next = targetObject[seg];
|
|
4658
|
-
if ((0,
|
|
4983
|
+
if ((0, import_utils20.isArray)(next)) {
|
|
4659
4984
|
for (const item of next) {
|
|
4660
4985
|
ret.push(item);
|
|
4661
4986
|
}
|
|
@@ -4669,8 +4994,8 @@ var copyAndDepopulate = (docObject, operations, options = { mutable: true, idFie
|
|
|
4669
4994
|
};
|
|
4670
4995
|
var copyPaths = (docObject, operations, options = { mutable: true }) => {
|
|
4671
4996
|
const mutable = options.mutable !== false;
|
|
4672
|
-
const obj = mutable ? docObject : (0,
|
|
4673
|
-
(0,
|
|
4997
|
+
const obj = mutable ? docObject : (0, import_utils20.cloneDeep)(docObject);
|
|
4998
|
+
(0, import_utils20.forEach)(Array.isArray(operations) ? operations : [], (op) => {
|
|
4674
4999
|
if (!op || typeof op.src !== "string" || typeof op.dest !== "string") return;
|
|
4675
5000
|
const src = op.src;
|
|
4676
5001
|
const dest = op.dest;
|
|
@@ -4678,17 +5003,17 @@ var copyPaths = (docObject, operations, options = { mutable: true }) => {
|
|
|
4678
5003
|
assertSafePath(src, "copyPaths");
|
|
4679
5004
|
assertSafePath(dest, "copyPaths");
|
|
4680
5005
|
if (src === dest) return;
|
|
4681
|
-
(0,
|
|
4682
|
-
if (!(0,
|
|
4683
|
-
(0,
|
|
5006
|
+
(0, import_utils20.forEach)(resolveLeafParents(obj, src), ({ parent, leaf }) => {
|
|
5007
|
+
if (!(0, import_utils20.hasOwn)(parent, leaf)) return;
|
|
5008
|
+
(0, import_utils20.set)(parent, dest, (0, import_utils20.cloneDeep)(parent[leaf]));
|
|
4684
5009
|
});
|
|
4685
5010
|
});
|
|
4686
5011
|
return obj;
|
|
4687
5012
|
};
|
|
4688
5013
|
var movePaths = (docObject, operations, options = { mutable: true }) => {
|
|
4689
5014
|
const mutable = options.mutable !== false;
|
|
4690
|
-
const obj = mutable ? docObject : (0,
|
|
4691
|
-
(0,
|
|
5015
|
+
const obj = mutable ? docObject : (0, import_utils20.cloneDeep)(docObject);
|
|
5016
|
+
(0, import_utils20.forEach)(Array.isArray(operations) ? operations : [], (op) => {
|
|
4692
5017
|
if (!op || typeof op.src !== "string" || typeof op.dest !== "string") return;
|
|
4693
5018
|
const src = op.src;
|
|
4694
5019
|
const dest = op.dest;
|
|
@@ -4696,10 +5021,10 @@ var movePaths = (docObject, operations, options = { mutable: true }) => {
|
|
|
4696
5021
|
assertSafePath(src, "movePaths");
|
|
4697
5022
|
assertSafePath(dest, "movePaths");
|
|
4698
5023
|
if (src === dest) return;
|
|
4699
|
-
(0,
|
|
4700
|
-
if (!(0,
|
|
4701
|
-
const value = (0,
|
|
4702
|
-
(0,
|
|
5024
|
+
(0, import_utils20.forEach)(resolveLeafParents(obj, src), ({ parent, leaf }) => {
|
|
5025
|
+
if (!(0, import_utils20.hasOwn)(parent, leaf)) return;
|
|
5026
|
+
const value = (0, import_utils20.cloneDeep)(parent[leaf]);
|
|
5027
|
+
(0, import_utils20.set)(parent, dest, value);
|
|
4703
5028
|
if (dest === leaf || dest.startsWith(`${leaf}.`)) return;
|
|
4704
5029
|
delete parent[leaf];
|
|
4705
5030
|
});
|
|
@@ -4710,8 +5035,8 @@ var sliceArrays = (docObject, operations, options = { mutable: true, maxSlice: D
|
|
|
4710
5035
|
const mutable = options.mutable !== false;
|
|
4711
5036
|
const maxSlice = options.maxSlice ?? DEFAULT_MAX_SLICE;
|
|
4712
5037
|
assertNonNegativeSafeInteger(maxSlice, "maxSlice", "sliceArrays");
|
|
4713
|
-
const obj = mutable ? docObject : (0,
|
|
4714
|
-
(0,
|
|
5038
|
+
const obj = mutable ? docObject : (0, import_utils20.cloneDeep)(docObject);
|
|
5039
|
+
(0, import_utils20.forEach)(Array.isArray(operations) ? operations : [], (op) => {
|
|
4715
5040
|
if (!op || typeof op.src !== "string") return;
|
|
4716
5041
|
const src = op.src;
|
|
4717
5042
|
if (src.length === 0) return;
|
|
@@ -4728,15 +5053,15 @@ var sliceArrays = (docObject, operations, options = { mutable: true, maxSlice: D
|
|
|
4728
5053
|
assertNonNegativeSafeInteger(op.limit, "limit", "sliceArrays");
|
|
4729
5054
|
limit = Math.min(op.limit, maxSlice);
|
|
4730
5055
|
}
|
|
4731
|
-
(0,
|
|
5056
|
+
(0, import_utils20.forEach)(resolveLeafParents(obj, src), ({ parent, leaf }) => {
|
|
4732
5057
|
const leafValue = parent[leaf];
|
|
4733
|
-
if (!(0,
|
|
5058
|
+
if (!(0, import_utils20.isArray)(leafValue)) return;
|
|
4734
5059
|
const end = typeof limit === "undefined" ? void 0 : skip + limit;
|
|
4735
5060
|
const window = leafValue.slice(skip, end);
|
|
4736
5061
|
if (typeof rawDest === "undefined") {
|
|
4737
|
-
(0,
|
|
5062
|
+
(0, import_utils20.set)(parent, leaf, window);
|
|
4738
5063
|
} else {
|
|
4739
|
-
(0,
|
|
5064
|
+
(0, import_utils20.set)(parent, rawDest, window);
|
|
4740
5065
|
}
|
|
4741
5066
|
});
|
|
4742
5067
|
});
|
|
@@ -4744,44 +5069,44 @@ var sliceArrays = (docObject, operations, options = { mutable: true, maxSlice: D
|
|
|
4744
5069
|
};
|
|
4745
5070
|
var countPaths = (docObject, operations, options = { mutable: true }) => {
|
|
4746
5071
|
const mutable = options.mutable !== false;
|
|
4747
|
-
const obj = mutable ? docObject : (0,
|
|
4748
|
-
(0,
|
|
5072
|
+
const obj = mutable ? docObject : (0, import_utils20.cloneDeep)(docObject);
|
|
5073
|
+
(0, import_utils20.forEach)(Array.isArray(operations) ? operations : [], (op) => {
|
|
4749
5074
|
if (!op || typeof op.src !== "string" || typeof op.dest !== "string") return;
|
|
4750
5075
|
const src = op.src;
|
|
4751
5076
|
const dest = op.dest;
|
|
4752
5077
|
if (src.length === 0 || dest.length === 0) return;
|
|
4753
5078
|
assertSafePath(src, "countPaths");
|
|
4754
5079
|
assertSafePath(dest, "countPaths");
|
|
4755
|
-
(0,
|
|
4756
|
-
if (!(0,
|
|
5080
|
+
(0, import_utils20.forEach)(resolveLeafParents(obj, src), ({ parent, leaf }) => {
|
|
5081
|
+
if (!(0, import_utils20.hasOwn)(parent, leaf)) return;
|
|
4757
5082
|
const leafValue = parent[leaf];
|
|
4758
5083
|
let count;
|
|
4759
|
-
if ((0,
|
|
5084
|
+
if ((0, import_utils20.isArray)(leafValue)) {
|
|
4760
5085
|
count = leafValue.length;
|
|
4761
|
-
} else if ((0,
|
|
5086
|
+
} else if ((0, import_utils20.isPlainObject)(leafValue)) {
|
|
4762
5087
|
count = Object.keys(leafValue).length;
|
|
4763
5088
|
} else if (leafValue === null || typeof leafValue === "undefined") {
|
|
4764
5089
|
count = 0;
|
|
4765
5090
|
} else {
|
|
4766
5091
|
count = 1;
|
|
4767
5092
|
}
|
|
4768
|
-
(0,
|
|
5093
|
+
(0, import_utils20.set)(parent, dest, count);
|
|
4769
5094
|
});
|
|
4770
5095
|
});
|
|
4771
5096
|
return obj;
|
|
4772
5097
|
};
|
|
4773
5098
|
var maskPaths = (docObject, operations, options = { mutable: true }) => {
|
|
4774
5099
|
const mutable = options.mutable !== false;
|
|
4775
|
-
const obj = mutable ? docObject : (0,
|
|
4776
|
-
(0,
|
|
5100
|
+
const obj = mutable ? docObject : (0, import_utils20.cloneDeep)(docObject);
|
|
5101
|
+
(0, import_utils20.forEach)(Array.isArray(operations) ? operations : [], (op) => {
|
|
4777
5102
|
if (!op || typeof op.src !== "string") return;
|
|
4778
5103
|
const src = op.src;
|
|
4779
5104
|
if (src.length === 0) return;
|
|
4780
5105
|
assertSafePath(src, "maskPaths");
|
|
4781
5106
|
const replacement = Object.prototype.hasOwnProperty.call(op, "replacement") ? op.replacement : DEFAULT_MASK_REPLACEMENT;
|
|
4782
|
-
(0,
|
|
4783
|
-
if (!(0,
|
|
4784
|
-
(0,
|
|
5107
|
+
(0, import_utils20.forEach)(resolveLeafParents(obj, src), ({ parent, leaf }) => {
|
|
5108
|
+
if (!(0, import_utils20.hasOwn)(parent, leaf)) return;
|
|
5109
|
+
(0, import_utils20.set)(parent, leaf, replacement);
|
|
4785
5110
|
});
|
|
4786
5111
|
});
|
|
4787
5112
|
return obj;
|
|
@@ -4825,10 +5150,10 @@ var Cache = class {
|
|
|
4825
5150
|
};
|
|
4826
5151
|
|
|
4827
5152
|
// src/core-shared.ts
|
|
4828
|
-
var
|
|
5153
|
+
var import_utils22 = require("@web-ts-toolkit/utils");
|
|
4829
5154
|
|
|
4830
5155
|
// src/permission.ts
|
|
4831
|
-
var
|
|
5156
|
+
var import_utils21 = require("@web-ts-toolkit/utils");
|
|
4832
5157
|
var Permission = class {
|
|
4833
5158
|
constructor(permissions) {
|
|
4834
5159
|
this.permissions = { ...permissions };
|
|
@@ -4844,7 +5169,7 @@ var Permission = class {
|
|
|
4844
5169
|
return [permissionOrPermissions].concat(permissions);
|
|
4845
5170
|
}
|
|
4846
5171
|
hasKey(permission) {
|
|
4847
|
-
return (0,
|
|
5172
|
+
return (0, import_utils21.hasOwn)(this.permissions, permission);
|
|
4848
5173
|
}
|
|
4849
5174
|
has(permission) {
|
|
4850
5175
|
return this.permissions[permission] === true;
|
|
@@ -4864,10 +5189,10 @@ var permission_default = Permission;
|
|
|
4864
5189
|
|
|
4865
5190
|
// src/core-shared.ts
|
|
4866
5191
|
function isAndFilter(filter2) {
|
|
4867
|
-
return !!filter2 && (0,
|
|
5192
|
+
return !!filter2 && (0, import_utils22.isPlainObject)(filter2) && Object.keys(filter2).length === 1 && (0, import_utils22.isArray)(filter2.$and);
|
|
4868
5193
|
}
|
|
4869
5194
|
function isMergeableClause(filter2) {
|
|
4870
|
-
return !!filter2 && (0,
|
|
5195
|
+
return !!filter2 && (0, import_utils22.isPlainObject)(filter2) && Object.keys(filter2).every((key) => !key.startsWith("$"));
|
|
4871
5196
|
}
|
|
4872
5197
|
function optimizeAndFilter(clausesInput) {
|
|
4873
5198
|
const clauses = [];
|
|
@@ -4882,10 +5207,10 @@ function optimizeAndFilter(clausesInput) {
|
|
|
4882
5207
|
}
|
|
4883
5208
|
}
|
|
4884
5209
|
const dedupeKey = (value) => {
|
|
4885
|
-
if ((0,
|
|
5210
|
+
if ((0, import_utils22.isArray)(value)) {
|
|
4886
5211
|
return `[${value.map(dedupeKey).join(",")}]`;
|
|
4887
5212
|
}
|
|
4888
|
-
if ((0,
|
|
5213
|
+
if ((0, import_utils22.isPlainObject)(value)) {
|
|
4889
5214
|
return `{${Object.keys(value).sort().map((key) => `${key}:${dedupeKey(value[key])}`).join(",")}}`;
|
|
4890
5215
|
}
|
|
4891
5216
|
return JSON.stringify(value);
|
|
@@ -4909,7 +5234,7 @@ function optimizeAndFilter(clausesInput) {
|
|
|
4909
5234
|
}
|
|
4910
5235
|
let canMerge = true;
|
|
4911
5236
|
for (const [key, value] of Object.entries(clause)) {
|
|
4912
|
-
if ((0,
|
|
5237
|
+
if ((0, import_utils22.hasOwn)(mergedClause, key) && !(0, import_utils22.isEqual)(mergedClause[key], value)) {
|
|
4913
5238
|
canMerge = false;
|
|
4914
5239
|
break;
|
|
4915
5240
|
}
|
|
@@ -4920,24 +5245,24 @@ function optimizeAndFilter(clausesInput) {
|
|
|
4920
5245
|
}
|
|
4921
5246
|
Object.assign(mergedClause, clause);
|
|
4922
5247
|
}
|
|
4923
|
-
const finalClauses = (0,
|
|
5248
|
+
const finalClauses = (0, import_utils22.isEmpty)(mergedClause) ? remainingClauses : [mergedClause, ...remainingClauses];
|
|
4924
5249
|
if (finalClauses.length === 0) return null;
|
|
4925
5250
|
if (finalClauses.length === 1) return finalClauses[0];
|
|
4926
5251
|
return { $and: finalClauses };
|
|
4927
5252
|
}
|
|
4928
5253
|
function normalizeFilter(filter2) {
|
|
4929
5254
|
if (filter2 === false) return false;
|
|
4930
|
-
if (!filter2 || !(0,
|
|
5255
|
+
if (!filter2 || !(0, import_utils22.isPlainObject)(filter2) || (0, import_utils22.isEmpty)(filter2)) return null;
|
|
4931
5256
|
if (!isAndFilter(filter2)) {
|
|
4932
5257
|
return filter2;
|
|
4933
5258
|
}
|
|
4934
5259
|
return optimizeAndFilter(filter2.$and);
|
|
4935
5260
|
}
|
|
4936
5261
|
async function resolveIdentifierFilter(req, idField, resolveIdFilter, id) {
|
|
4937
|
-
if ((0,
|
|
5262
|
+
if ((0, import_utils22.isFunction)(resolveIdFilter)) {
|
|
4938
5263
|
return await resolveIdFilter.call(req, id);
|
|
4939
5264
|
}
|
|
4940
|
-
if ((0,
|
|
5265
|
+
if ((0, import_utils22.isString)(idField)) {
|
|
4941
5266
|
return { [idField]: id };
|
|
4942
5267
|
}
|
|
4943
5268
|
return { _id: id };
|
|
@@ -4953,12 +5278,12 @@ async function resolveAccessFilter({
|
|
|
4953
5278
|
}) {
|
|
4954
5279
|
let nextFilter = normalizeFilter(filter2);
|
|
4955
5280
|
const overrideFilterFn = getOption(`overrideFilter.${access}`, null);
|
|
4956
|
-
if (nextFilter !== false && (0,
|
|
5281
|
+
if (nextFilter !== false && (0, import_utils22.isFunction)(overrideFilterFn)) {
|
|
4957
5282
|
nextFilter = normalizeFilter(await overrideFilterFn.call(req, nextFilter, permissions));
|
|
4958
5283
|
}
|
|
4959
5284
|
if (nextFilter === false) return false;
|
|
4960
5285
|
const baseFilterFn = getOption(`baseFilter.${access}`, null);
|
|
4961
|
-
if (!(0,
|
|
5286
|
+
if (!(0, import_utils22.isFunction)(baseFilterFn)) return nextFilter ?? {};
|
|
4962
5287
|
const baseFilter = normalizeFilter(
|
|
4963
5288
|
cache.has(cacheKey) ? cache.get(cacheKey) : await baseFilterFn.call(req, permissions)
|
|
4964
5289
|
);
|
|
@@ -4992,34 +5317,34 @@ async function setRequestPermissions(req) {
|
|
|
4992
5317
|
if (owner === activeRuntime) return;
|
|
4993
5318
|
if (hasFieldValue && owner === void 0) return;
|
|
4994
5319
|
const globalPermissions = getGlobalOption("globalPermissions");
|
|
4995
|
-
if (!(0,
|
|
5320
|
+
if (!(0, import_utils22.isFunction)(globalPermissions)) return;
|
|
4996
5321
|
const permissions = await globalPermissions.call(req, req);
|
|
4997
|
-
if ((0,
|
|
4998
|
-
else if ((0,
|
|
4999
|
-
else if ((0,
|
|
5322
|
+
if ((0, import_utils22.isPlainObject)(permissions)) req[requestPermissionField] = permissions;
|
|
5323
|
+
else if ((0, import_utils22.isArray)(permissions)) req[requestPermissionField] = (0, import_utils22.arrayToRecord)(permissions);
|
|
5324
|
+
else if ((0, import_utils22.isString)(permissions)) req[requestPermissionField] = { [permissions]: true };
|
|
5000
5325
|
owners.set(requestPermissionField, activeRuntime);
|
|
5001
5326
|
}
|
|
5002
5327
|
async function evaluateRouteGuard(req, permissions, routeGuard) {
|
|
5003
5328
|
const phas = (key) => permissions.has(key);
|
|
5004
5329
|
const { stringHandler, arrayHandler } = createValidator(phas);
|
|
5005
|
-
if ((0,
|
|
5330
|
+
if ((0, import_utils22.isBoolean)(routeGuard)) {
|
|
5006
5331
|
return routeGuard === true;
|
|
5007
5332
|
}
|
|
5008
|
-
if ((0,
|
|
5333
|
+
if ((0, import_utils22.isString)(routeGuard)) {
|
|
5009
5334
|
return stringHandler(routeGuard);
|
|
5010
5335
|
}
|
|
5011
|
-
if ((0,
|
|
5336
|
+
if ((0, import_utils22.isArray)(routeGuard)) {
|
|
5012
5337
|
return arrayHandler(routeGuard);
|
|
5013
5338
|
}
|
|
5014
|
-
if ((0,
|
|
5339
|
+
if ((0, import_utils22.isFunction)(routeGuard)) {
|
|
5015
5340
|
return routeGuard.call(req, permissions);
|
|
5016
5341
|
}
|
|
5017
5342
|
return false;
|
|
5018
5343
|
}
|
|
5019
5344
|
async function callHookChain(req, hook, doc, permissions, context) {
|
|
5020
|
-
const hooks = (0,
|
|
5345
|
+
const hooks = (0, import_utils22.castArray)(hook);
|
|
5021
5346
|
for (let x = 0; x < hooks.length; x++) {
|
|
5022
|
-
if ((0,
|
|
5347
|
+
if ((0, import_utils22.isFunction)(hooks[x])) {
|
|
5023
5348
|
doc = await hooks[x].call(req, doc, permissions, context);
|
|
5024
5349
|
}
|
|
5025
5350
|
}
|
|
@@ -5042,13 +5367,13 @@ async function collectSchemaFields({
|
|
|
5042
5367
|
if (baseFields.includes(key)) continue;
|
|
5043
5368
|
const val = permissionSchema[key];
|
|
5044
5369
|
const value = val && val[access] || val;
|
|
5045
|
-
if ((0,
|
|
5370
|
+
if ((0, import_utils22.isBoolean)(value)) {
|
|
5046
5371
|
if (value) fields.push(key);
|
|
5047
|
-
} else if ((0,
|
|
5372
|
+
} else if ((0, import_utils22.isString)(value)) {
|
|
5048
5373
|
if (stringHandler(value)) fields.push(key);
|
|
5049
|
-
} else if ((0,
|
|
5374
|
+
} else if ((0, import_utils22.isArray)(value)) {
|
|
5050
5375
|
if (arrayHandler(value)) fields.push(key);
|
|
5051
|
-
} else if ((0,
|
|
5376
|
+
} else if ((0, import_utils22.isFunction)(value)) {
|
|
5052
5377
|
if (await value.call(req, ...functionArgs)) fields.push(key);
|
|
5053
5378
|
}
|
|
5054
5379
|
}
|
|
@@ -5134,7 +5459,7 @@ async function runDecorateAllHook({
|
|
|
5134
5459
|
}
|
|
5135
5460
|
|
|
5136
5461
|
// src/acl/select-resolution.ts
|
|
5137
|
-
var
|
|
5462
|
+
var import_utils23 = require("@web-ts-toolkit/utils");
|
|
5138
5463
|
async function collectAllowedFieldsForRequest({
|
|
5139
5464
|
req,
|
|
5140
5465
|
permissionSchema,
|
|
@@ -5202,18 +5527,19 @@ async function resolveSelectForRequest({
|
|
|
5202
5527
|
const excludeall = normalizedSelect.every((v) => v.startsWith("-"));
|
|
5203
5528
|
if (excludeall) {
|
|
5204
5529
|
normalizedSelect = normalizedSelect.map((v) => v.substring(1));
|
|
5205
|
-
fields = (0,
|
|
5530
|
+
fields = (0, import_utils23.difference)(fields, normalizedSelect);
|
|
5206
5531
|
if (excludeid) fields.push("-_id");
|
|
5207
5532
|
} else {
|
|
5208
|
-
fields = (0,
|
|
5533
|
+
fields = (0, import_utils23.intersection)(normalizedSelect, fields.concat(excludeid ? "-_id" : "_id"));
|
|
5209
5534
|
}
|
|
5210
5535
|
}
|
|
5211
5536
|
return fields.concat(alwaysSelectFields);
|
|
5212
5537
|
}
|
|
5213
|
-
return (0,
|
|
5538
|
+
return (0, import_utils23.intersection)(normalizedSelect, fields);
|
|
5214
5539
|
}
|
|
5215
5540
|
|
|
5216
5541
|
// src/core.ts
|
|
5542
|
+
var emptyPermissionLeaf = () => ({ $: "_" });
|
|
5217
5543
|
var Core = class {
|
|
5218
5544
|
constructor(req) {
|
|
5219
5545
|
this.cachedPermissions = null;
|
|
@@ -5225,10 +5551,10 @@ var Core = class {
|
|
|
5225
5551
|
getIdentifier(modelName) {
|
|
5226
5552
|
const resolveIdFilter = getModelOption(modelName, "resolveIdFilter");
|
|
5227
5553
|
const idField = getModelOption(modelName, "idField");
|
|
5228
|
-
if ((0,
|
|
5554
|
+
if ((0, import_utils24.isFunction)(resolveIdFilter)) {
|
|
5229
5555
|
return null;
|
|
5230
5556
|
}
|
|
5231
|
-
if ((0,
|
|
5557
|
+
if ((0, import_utils24.isString)(idField)) {
|
|
5232
5558
|
return idField;
|
|
5233
5559
|
}
|
|
5234
5560
|
return "_id";
|
|
@@ -5306,11 +5632,11 @@ var Core = class {
|
|
|
5306
5632
|
async genPopulate(modelName, access = "read", _populate = null, subPaths = []) {
|
|
5307
5633
|
if (!_populate) return [];
|
|
5308
5634
|
let populate = Array.isArray(_populate) ? _populate : [_populate];
|
|
5309
|
-
populate = (0,
|
|
5635
|
+
populate = (0, import_utils24.compact)(
|
|
5310
5636
|
await Promise.all(
|
|
5311
5637
|
populate.map(async (p) => {
|
|
5312
|
-
const populateAccess = !(0,
|
|
5313
|
-
const ret = (0,
|
|
5638
|
+
const populateAccess = !(0, import_utils24.isString)(p) && p.access ? p.access : access;
|
|
5639
|
+
const ret = (0, import_utils24.isString)(p) ? { path: p } : {
|
|
5314
5640
|
path: p.path,
|
|
5315
5641
|
select: normalizeSelect(p.select)
|
|
5316
5642
|
};
|
|
@@ -5348,10 +5674,10 @@ var Core = class {
|
|
|
5348
5674
|
}
|
|
5349
5675
|
async validate(modelName, allowedData, access, context) {
|
|
5350
5676
|
const validate = getModelOption(modelName, `validate.${access}`, null);
|
|
5351
|
-
if ((0,
|
|
5677
|
+
if ((0, import_utils24.isFunction)(validate)) {
|
|
5352
5678
|
const permissions = this.getGlobalPermissions();
|
|
5353
5679
|
return validate.call(this.req, allowedData, permissions, context);
|
|
5354
|
-
} else if ((0,
|
|
5680
|
+
} else if ((0, import_utils24.isBoolean)(validate) || (0, import_utils24.isArray)(validate)) {
|
|
5355
5681
|
return validate;
|
|
5356
5682
|
} else {
|
|
5357
5683
|
return true;
|
|
@@ -5376,7 +5702,7 @@ var Core = class {
|
|
|
5376
5702
|
const changeOptions = getModelOption(modelName, `onChange`, {});
|
|
5377
5703
|
for (let x = 0; x < context.modifiedPaths.length; x++) {
|
|
5378
5704
|
const mpath = context.modifiedPaths[x];
|
|
5379
|
-
if ((0,
|
|
5705
|
+
if ((0, import_utils24.isFunction)(changeOptions[mpath])) {
|
|
5380
5706
|
await changeOptions[mpath].call(
|
|
5381
5707
|
this.req,
|
|
5382
5708
|
context.originalDocumentSnapshot[mpath],
|
|
@@ -5400,7 +5726,7 @@ var Core = class {
|
|
|
5400
5726
|
async genDocPermissions(modelName, doc, access, context) {
|
|
5401
5727
|
const docPermissionsFn = getModelOption(modelName, `docPermissions.${access}`, null);
|
|
5402
5728
|
let docPermissions = {};
|
|
5403
|
-
if ((0,
|
|
5729
|
+
if ((0, import_utils24.isFunction)(docPermissionsFn)) {
|
|
5404
5730
|
const permissions = this.getGlobalPermissions();
|
|
5405
5731
|
try {
|
|
5406
5732
|
docPermissions = await docPermissionsFn.call(this.req, doc, permissions, context);
|
|
@@ -5417,7 +5743,7 @@ var Core = class {
|
|
|
5417
5743
|
}
|
|
5418
5744
|
addEmptyPermissions(modelName, doc) {
|
|
5419
5745
|
const docPermissionField = getModelOption(modelName, "documentPermissionField");
|
|
5420
|
-
setDocValue(doc, docPermissionField, { _view:
|
|
5746
|
+
setDocValue(doc, docPermissionField, { _view: emptyPermissionLeaf(), _edit: emptyPermissionLeaf() });
|
|
5421
5747
|
return doc;
|
|
5422
5748
|
}
|
|
5423
5749
|
async addDocPermissions(modelName, doc, access, context) {
|
|
@@ -5451,7 +5777,7 @@ var Core = class {
|
|
|
5451
5777
|
readExists ? this.genAllowedFields(modelName, doc, "read") : [],
|
|
5452
5778
|
updateExists ? this.genAllowedFields(modelName, doc, "update") : []
|
|
5453
5779
|
]);
|
|
5454
|
-
const viewObj = (0,
|
|
5780
|
+
const viewObj = (0, import_utils24.reduce)(
|
|
5455
5781
|
views,
|
|
5456
5782
|
(ret, view) => {
|
|
5457
5783
|
ret[view] = true;
|
|
@@ -5459,7 +5785,7 @@ var Core = class {
|
|
|
5459
5785
|
},
|
|
5460
5786
|
{}
|
|
5461
5787
|
);
|
|
5462
|
-
const editObj = (0,
|
|
5788
|
+
const editObj = (0, import_utils24.reduce)(
|
|
5463
5789
|
edits,
|
|
5464
5790
|
(ret, view) => {
|
|
5465
5791
|
ret[view] = true;
|
|
@@ -5467,8 +5793,8 @@ var Core = class {
|
|
|
5467
5793
|
},
|
|
5468
5794
|
{}
|
|
5469
5795
|
);
|
|
5470
|
-
setDocValue(doc, `${docPermissionField}._view`, viewObj);
|
|
5471
|
-
setDocValue(doc, `${docPermissionField}._edit`, editObj);
|
|
5796
|
+
setDocValue(doc, `${docPermissionField}._view`, Object.keys(viewObj).length > 0 ? viewObj : emptyPermissionLeaf());
|
|
5797
|
+
setDocValue(doc, `${docPermissionField}._edit`, Object.keys(editObj).length > 0 ? editObj : emptyPermissionLeaf());
|
|
5472
5798
|
return doc;
|
|
5473
5799
|
}
|
|
5474
5800
|
async decorate(modelName, doc, access, context) {
|
|
@@ -5483,9 +5809,9 @@ var Core = class {
|
|
|
5483
5809
|
return runDecorateAllHook({ req: this.req, hook: decorateAll, docs, permissions, context });
|
|
5484
5810
|
}
|
|
5485
5811
|
runTasks(modelName, docObject, task) {
|
|
5486
|
-
const tasks = (0,
|
|
5812
|
+
const tasks = (0, import_utils24.compact)((0, import_utils24.castArray)(task));
|
|
5487
5813
|
if (tasks.length === 0) return docObject;
|
|
5488
|
-
(0,
|
|
5814
|
+
(0, import_utils24.forEach)(tasks, (task2) => {
|
|
5489
5815
|
const { type, args, options } = task2;
|
|
5490
5816
|
switch (type) {
|
|
5491
5817
|
case "COPY_AND_DEPOPULATE":
|
|
@@ -5553,9 +5879,9 @@ var Core = class {
|
|
|
5553
5879
|
}
|
|
5554
5880
|
const [, field, op] = keys2;
|
|
5555
5881
|
const subOption = getExactModelOption(modelName, `operationAccess.${access}`);
|
|
5556
|
-
if ((0,
|
|
5882
|
+
if ((0, import_utils24.isUndefined)(subOption)) {
|
|
5557
5883
|
const subFieldOption = getExactModelOption(modelName, `operationAccess.subs.${field}`);
|
|
5558
|
-
if ((0,
|
|
5884
|
+
if ((0, import_utils24.isUndefined)(subFieldOption)) {
|
|
5559
5885
|
const opOption = getModelOption(modelName, `operationAccess.${op}`);
|
|
5560
5886
|
return this.canActivate(opOption);
|
|
5561
5887
|
}
|
|
@@ -5607,25 +5933,25 @@ function guard(condition) {
|
|
|
5607
5933
|
const permissions = req[PERMISSIONS];
|
|
5608
5934
|
let cond = condition;
|
|
5609
5935
|
let phas = (key) => permissions.has(key);
|
|
5610
|
-
if ((0,
|
|
5936
|
+
if ((0, import_utils25.isPlainObject)(condition)) {
|
|
5611
5937
|
const { modelName, id, condition: _cond } = condition;
|
|
5612
5938
|
const svc = req.macl.getPublicService(modelName);
|
|
5613
5939
|
const select = getModelOption(modelName, `alwaysSelectFields.read`, void 0);
|
|
5614
|
-
let _id = (0,
|
|
5615
|
-
if ((0,
|
|
5940
|
+
let _id = (0, import_utils25.isString)(id) ? id : null;
|
|
5941
|
+
if ((0, import_utils25.isPlainObject)(id)) {
|
|
5616
5942
|
const { type = "param", key } = id;
|
|
5617
5943
|
if (type === "param") {
|
|
5618
5944
|
const paramValue = req.params[key];
|
|
5619
|
-
_id = (0,
|
|
5945
|
+
_id = (0, import_utils25.isArray)(paramValue) ? paramValue[0] ?? null : paramValue ?? null;
|
|
5620
5946
|
} else if (type === "query") {
|
|
5621
5947
|
const _qval = req.query[key];
|
|
5622
|
-
if ((0,
|
|
5948
|
+
if ((0, import_utils25.isArray)(_qval)) _id = _qval.length > 0 ? _qval[0] : null;
|
|
5623
5949
|
else _id = _qval;
|
|
5624
5950
|
} else {
|
|
5625
5951
|
_id = null;
|
|
5626
5952
|
}
|
|
5627
5953
|
}
|
|
5628
|
-
if (!(0,
|
|
5954
|
+
if (!(0, import_utils25.isString)(_id) || !_id) {
|
|
5629
5955
|
return next(new import_express_json_router2.default.clientErrors.BadRequestError());
|
|
5630
5956
|
}
|
|
5631
5957
|
const result = await svc._read(_id, { select });
|
|
@@ -5637,11 +5963,11 @@ function guard(condition) {
|
|
|
5637
5963
|
cond = _cond;
|
|
5638
5964
|
}
|
|
5639
5965
|
const { stringHandler, arrayHandler } = createValidator(phas);
|
|
5640
|
-
if ((0,
|
|
5966
|
+
if ((0, import_utils25.isString)(cond)) {
|
|
5641
5967
|
if (stringHandler(cond)) return next();
|
|
5642
|
-
} else if ((0,
|
|
5968
|
+
} else if ((0, import_utils25.isArray)(cond)) {
|
|
5643
5969
|
if (arrayHandler(cond)) return next();
|
|
5644
|
-
} else if ((0,
|
|
5970
|
+
} else if ((0, import_utils25.isFunction)(cond)) {
|
|
5645
5971
|
const result = await cond.call(req, permissions);
|
|
5646
5972
|
if (!!result) return next();
|
|
5647
5973
|
}
|
|
@@ -5654,7 +5980,7 @@ var import_express2 = __toESM(require("express"));
|
|
|
5654
5980
|
|
|
5655
5981
|
// src/routers/model-router.ts
|
|
5656
5982
|
var import_express_json_router5 = __toESM(require("@web-ts-toolkit/express-json-router"));
|
|
5657
|
-
var
|
|
5983
|
+
var import_utils29 = require("@web-ts-toolkit/utils");
|
|
5658
5984
|
|
|
5659
5985
|
// src/routers/router-mutation.ts
|
|
5660
5986
|
var MODEL_BUILD_TIME_OPTION_KEYS = /* @__PURE__ */ new Set([
|
|
@@ -5783,7 +6109,7 @@ var import_express_json_router4 = __toESM(require("@web-ts-toolkit/express-json-
|
|
|
5783
6109
|
|
|
5784
6110
|
// src/validation/common.ts
|
|
5785
6111
|
var import_zod2 = require("zod");
|
|
5786
|
-
var
|
|
6112
|
+
var import_utils26 = require("@web-ts-toolkit/utils");
|
|
5787
6113
|
var stringOrStringArray = import_zod2.z.union([import_zod2.z.string(), import_zod2.z.array(import_zod2.z.string())]);
|
|
5788
6114
|
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);
|
|
5789
6115
|
var queryBooleanString = import_zod2.z.enum(["true", "false"]);
|
|
@@ -5883,7 +6209,7 @@ var correlatedFilterSchema = import_zod2.z.record(import_zod2.z.string(), import
|
|
|
5883
6209
|
}
|
|
5884
6210
|
});
|
|
5885
6211
|
var correlatedEmptyOptionsSchema = import_zod2.z.unknown().refine(
|
|
5886
|
-
(value) => value === void 0 || (0,
|
|
6212
|
+
(value) => value === void 0 || (0, import_utils26.isPlainObject)(value) && Object.keys(value).length === 0,
|
|
5887
6213
|
"Correlated include options must be absent or empty"
|
|
5888
6214
|
).optional();
|
|
5889
6215
|
var correlatedPathSchema = import_zod2.z.string().min(1).superRefine((path, ctx) => {
|
|
@@ -5920,7 +6246,7 @@ var correlatedListArgsSchema = import_zod2.z.object({
|
|
|
5920
6246
|
include: import_zod2.z.lazy(() => correlatedNestedIncludeSchema).optional()
|
|
5921
6247
|
}).strict().superRefine((args, ctx) => checkArgsMarkers(args.select, args.sort, ctx));
|
|
5922
6248
|
var correlatedCountArgsSchema = import_zod2.z.unknown().refine(
|
|
5923
|
-
(value) => value === void 0 || (0,
|
|
6249
|
+
(value) => value === void 0 || (0, import_utils26.isPlainObject)(value) && Object.keys(value).length === 0,
|
|
5924
6250
|
"Count includes accept no args"
|
|
5925
6251
|
).optional();
|
|
5926
6252
|
var correlatedReadByIdSchema = import_zod2.z.object({
|
|
@@ -5970,7 +6296,7 @@ var correlatedNestedIncludeSchema = import_zod2.z.union([includeItemSchema, impo
|
|
|
5970
6296
|
var rejectDuplicateCorrelatedPaths = (items, ctx) => {
|
|
5971
6297
|
const seen = /* @__PURE__ */ new Set();
|
|
5972
6298
|
items.forEach((item, index) => {
|
|
5973
|
-
if ((0,
|
|
6299
|
+
if ((0, import_utils26.isPlainObject)(item) && item.mode === "correlated" && typeof item.path === "string") {
|
|
5974
6300
|
if (seen.has(item.path)) {
|
|
5975
6301
|
ctx.addIssue({
|
|
5976
6302
|
code: import_zod2.z.ZodIssueCode.custom,
|
|
@@ -6169,19 +6495,12 @@ function fromJoi(schema) {
|
|
|
6169
6495
|
}
|
|
6170
6496
|
function fromAjv(validator) {
|
|
6171
6497
|
return async (value) => {
|
|
6172
|
-
|
|
6173
|
-
if (isThenable(outcome)) {
|
|
6498
|
+
if (validator.$async === true) {
|
|
6174
6499
|
try {
|
|
6175
|
-
const data = await
|
|
6176
|
-
if (data === false) {
|
|
6177
|
-
return {
|
|
6178
|
-
success: false,
|
|
6179
|
-
issues: normalizeAjvIssues(validator.errors ?? [])
|
|
6180
|
-
};
|
|
6181
|
-
}
|
|
6500
|
+
const data = await validator(value);
|
|
6182
6501
|
return {
|
|
6183
6502
|
success: true,
|
|
6184
|
-
data
|
|
6503
|
+
data
|
|
6185
6504
|
};
|
|
6186
6505
|
} catch (error2) {
|
|
6187
6506
|
if (!isAjvValidationError(error2)) {
|
|
@@ -6189,11 +6508,21 @@ function fromAjv(validator) {
|
|
|
6189
6508
|
}
|
|
6190
6509
|
return {
|
|
6191
6510
|
success: false,
|
|
6192
|
-
issues: normalizeAjvIssues(error2.errors
|
|
6511
|
+
issues: normalizeAjvIssues(error2.errors)
|
|
6193
6512
|
};
|
|
6194
6513
|
}
|
|
6195
6514
|
}
|
|
6196
|
-
const
|
|
6515
|
+
const outcome = validator(value);
|
|
6516
|
+
if (isThenable(outcome)) {
|
|
6517
|
+
void Promise.resolve(outcome).catch(() => {
|
|
6518
|
+
});
|
|
6519
|
+
throw new TypeError(
|
|
6520
|
+
"fromAjv: promise/thenable validators must declare $async: true and resolve with validated data"
|
|
6521
|
+
);
|
|
6522
|
+
}
|
|
6523
|
+
if (typeof outcome !== "boolean") {
|
|
6524
|
+
throw new TypeError("fromAjv: synchronous validators must return a boolean verdict");
|
|
6525
|
+
}
|
|
6197
6526
|
if (outcome) {
|
|
6198
6527
|
return {
|
|
6199
6528
|
success: true,
|
|
@@ -6202,7 +6531,9 @@ function fromAjv(validator) {
|
|
|
6202
6531
|
}
|
|
6203
6532
|
return {
|
|
6204
6533
|
success: false,
|
|
6205
|
-
|
|
6534
|
+
// Normalize immediately, copying message/path before another call can
|
|
6535
|
+
// replace the errors array or mutate its entries.
|
|
6536
|
+
issues: normalizeAjvIssues(validator.errors ?? [])
|
|
6206
6537
|
};
|
|
6207
6538
|
};
|
|
6208
6539
|
}
|
|
@@ -6432,14 +6763,14 @@ function isArkTypeErrors(value) {
|
|
|
6432
6763
|
return record.arkKind === "errors" || record[" arkKind"] === "errors";
|
|
6433
6764
|
}
|
|
6434
6765
|
function isThenable(value) {
|
|
6435
|
-
return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function";
|
|
6766
|
+
return (typeof value === "object" || typeof value === "function") && value !== null && "then" in value && typeof value.then === "function";
|
|
6436
6767
|
}
|
|
6437
6768
|
function isAjvValidationError(error2) {
|
|
6438
6769
|
if (typeof error2 !== "object" || error2 === null || !("errors" in error2)) {
|
|
6439
6770
|
return false;
|
|
6440
6771
|
}
|
|
6441
|
-
const
|
|
6442
|
-
return
|
|
6772
|
+
const candidate = error2;
|
|
6773
|
+
return candidate.ajv === true && candidate.validation === true && Array.isArray(candidate.errors);
|
|
6443
6774
|
}
|
|
6444
6775
|
function isVineValidationError(error2) {
|
|
6445
6776
|
if (typeof error2 !== "object" || error2 === null || !("messages" in error2)) {
|
|
@@ -6878,7 +7209,7 @@ var rootDataQueryEntrySchema = import_zod5.z.union([
|
|
|
6878
7209
|
var rootQuerySchema = import_zod5.z.array(import_zod5.z.union([rootModelQueryEntrySchema, rootDataQueryEntrySchema]));
|
|
6879
7210
|
|
|
6880
7211
|
// src/routers/shared.ts
|
|
6881
|
-
var
|
|
7212
|
+
var import_utils27 = require("@web-ts-toolkit/utils");
|
|
6882
7213
|
|
|
6883
7214
|
// src/routers/model-router-collection-routes.ts
|
|
6884
7215
|
function setModelCollectionRoutes(context) {
|
|
@@ -6896,14 +7227,14 @@ function setModelCollectionRoutes(context) {
|
|
|
6896
7227
|
await context.assertAllowed(req, "list");
|
|
6897
7228
|
const { skip, limit, page, page_size, skim, include_permissions, include_count, include_extra_headers } = parseQuery(requestSchemas.listQuery, req.query);
|
|
6898
7229
|
const svc = context.getPublicService(req);
|
|
6899
|
-
const includeCount = (0,
|
|
6900
|
-
const includeExtraHeaders = (0,
|
|
7230
|
+
const includeCount = (0, import_utils27.parseBooleanString)(include_count);
|
|
7231
|
+
const includeExtraHeaders = (0, import_utils27.parseBooleanString)(include_extra_headers);
|
|
6901
7232
|
const result = await svc._list(
|
|
6902
7233
|
{},
|
|
6903
7234
|
{ skip, limit, page, pageSize: page_size },
|
|
6904
7235
|
{
|
|
6905
|
-
skim: (0,
|
|
6906
|
-
includePermissions: (0,
|
|
7236
|
+
skim: (0, import_utils27.parseBooleanString)(skim),
|
|
7237
|
+
includePermissions: (0, import_utils27.parseBooleanString)(include_permissions),
|
|
6907
7238
|
includeCount
|
|
6908
7239
|
}
|
|
6909
7240
|
);
|
|
@@ -6958,7 +7289,7 @@ function setModelCollectionRoutes(context) {
|
|
|
6958
7289
|
context.getRequestSchema("requestSchemas.create")
|
|
6959
7290
|
);
|
|
6960
7291
|
const svc = context.getPublicService(req);
|
|
6961
|
-
const result = await svc._create(data, {}, { includePermissions: (0,
|
|
7292
|
+
const result = await svc._create(data, {}, { includePermissions: (0, import_utils27.parseBooleanString)(include_permissions) });
|
|
6962
7293
|
handleResultError(result);
|
|
6963
7294
|
return formatModelCreatedResponse(result);
|
|
6964
7295
|
});
|
|
@@ -6994,7 +7325,7 @@ function setModelCollectionRoutes(context) {
|
|
|
6994
7325
|
finalData,
|
|
6995
7326
|
{ select: finalSelect, populate: finalPopulate, tasks: finalTasks },
|
|
6996
7327
|
{
|
|
6997
|
-
includePermissions: includePermissions ?? (0,
|
|
7328
|
+
includePermissions: includePermissions ?? (0, import_utils27.parseBooleanString)(include_permissions),
|
|
6998
7329
|
populateAccess
|
|
6999
7330
|
}
|
|
7000
7331
|
);
|
|
@@ -7086,8 +7417,8 @@ function setModelDocumentRoutes(context) {
|
|
|
7086
7417
|
id,
|
|
7087
7418
|
{},
|
|
7088
7419
|
{
|
|
7089
|
-
includePermissions: (0,
|
|
7090
|
-
tryList: (0,
|
|
7420
|
+
includePermissions: (0, import_utils27.parseBooleanString)(include_permissions),
|
|
7421
|
+
tryList: (0, import_utils27.parseBooleanString)(try_list)
|
|
7091
7422
|
}
|
|
7092
7423
|
);
|
|
7093
7424
|
handleResultError(result);
|
|
@@ -7172,8 +7503,8 @@ function setModelDocumentRoutes(context) {
|
|
|
7172
7503
|
data,
|
|
7173
7504
|
{},
|
|
7174
7505
|
{
|
|
7175
|
-
returningAll: (0,
|
|
7176
|
-
includePermissions: (0,
|
|
7506
|
+
returningAll: (0, import_utils27.parseBooleanString)(returning_all),
|
|
7507
|
+
includePermissions: (0, import_utils27.parseBooleanString)(include_permissions)
|
|
7177
7508
|
}
|
|
7178
7509
|
);
|
|
7179
7510
|
handleResultError(result);
|
|
@@ -7219,8 +7550,8 @@ function setModelDocumentRoutes(context) {
|
|
|
7219
7550
|
finalData,
|
|
7220
7551
|
{ select: finalSelect, populate: finalPopulate, tasks: finalTasks },
|
|
7221
7552
|
{
|
|
7222
|
-
returningAll: returningAll ?? (0,
|
|
7223
|
-
includePermissions: includePermissions ?? (0,
|
|
7553
|
+
returningAll: returningAll ?? (0, import_utils27.parseBooleanString)(returning_all),
|
|
7554
|
+
includePermissions: includePermissions ?? (0, import_utils27.parseBooleanString)(include_permissions),
|
|
7224
7555
|
populateAccess
|
|
7225
7556
|
}
|
|
7226
7557
|
);
|
|
@@ -7248,8 +7579,8 @@ function setModelDocumentRoutes(context) {
|
|
|
7248
7579
|
body,
|
|
7249
7580
|
{},
|
|
7250
7581
|
{
|
|
7251
|
-
returningAll: (0,
|
|
7252
|
-
includePermissions: (0,
|
|
7582
|
+
returningAll: (0, import_utils27.parseBooleanString)(returning_all),
|
|
7583
|
+
includePermissions: (0, import_utils27.parseBooleanString)(include_permissions)
|
|
7253
7584
|
}
|
|
7254
7585
|
);
|
|
7255
7586
|
handleResultError(result);
|
|
@@ -7293,8 +7624,8 @@ function setModelDocumentRoutes(context) {
|
|
|
7293
7624
|
finalData,
|
|
7294
7625
|
{ select: finalSelect, populate: finalPopulate, tasks: finalTasks },
|
|
7295
7626
|
{
|
|
7296
|
-
returningAll: returningAll ?? (0,
|
|
7297
|
-
includePermissions: includePermissions ?? (0,
|
|
7627
|
+
returningAll: returningAll ?? (0, import_utils27.parseBooleanString)(returning_all),
|
|
7628
|
+
includePermissions: includePermissions ?? (0, import_utils27.parseBooleanString)(include_permissions),
|
|
7298
7629
|
populateAccess
|
|
7299
7630
|
}
|
|
7300
7631
|
);
|
|
@@ -7532,11 +7863,11 @@ function setModelSubDocumentRoutes(context) {
|
|
|
7532
7863
|
}
|
|
7533
7864
|
|
|
7534
7865
|
// src/openapi/route-registration.ts
|
|
7535
|
-
var
|
|
7866
|
+
var import_utils28 = require("@web-ts-toolkit/utils");
|
|
7536
7867
|
function registerOpenApiRoute(runtime, basePath, defaultTag, route) {
|
|
7537
7868
|
runtime.registerOpenApiRoute({
|
|
7538
7869
|
...route,
|
|
7539
|
-
path: (0,
|
|
7870
|
+
path: (0, import_utils28.normalizeUrlPath)(basePath + route.path),
|
|
7540
7871
|
tags: route.tags ?? [defaultTag]
|
|
7541
7872
|
});
|
|
7542
7873
|
}
|
|
@@ -7544,8 +7875,8 @@ function registerOpenApiRoute(runtime, basePath, defaultTag, route) {
|
|
|
7544
7875
|
// src/routers/model-router.ts
|
|
7545
7876
|
var clientErrors2 = import_express_json_router5.default.clientErrors;
|
|
7546
7877
|
function setOption(parentKey, optionKey, option) {
|
|
7547
|
-
const key = (0,
|
|
7548
|
-
const value = (0,
|
|
7878
|
+
const key = (0, import_utils29.isUndefined)(option) ? parentKey : `${parentKey}.${optionKey}`;
|
|
7879
|
+
const value = (0, import_utils29.isUndefined)(option) ? optionKey : option;
|
|
7549
7880
|
assertMutableRouterOption("model", key);
|
|
7550
7881
|
this.runtime.setModelOption(this.modelName, key, value);
|
|
7551
7882
|
return this;
|
|
@@ -7707,7 +8038,7 @@ var ModelRouter = class _ModelRouter {
|
|
|
7707
8038
|
this.runtime = runtime;
|
|
7708
8039
|
this.runtime.setModelOptions(modelName, initialOptions);
|
|
7709
8040
|
this.options = this.runtime.getModelOptions(modelName);
|
|
7710
|
-
this.fullBasePath = (0,
|
|
8041
|
+
this.fullBasePath = (0, import_utils29.normalizeUrlPath)(this.options.parentPath + this.options.basePath);
|
|
7711
8042
|
this.modelName = modelName;
|
|
7712
8043
|
this.router = new import_express_json_router5.default(this.options.basePath, createSetCore(this.runtime), accessRouterResponseHandler);
|
|
7713
8044
|
this.model = new model_default(modelName, this.runtime);
|
|
@@ -7787,13 +8118,13 @@ var ModelRouter = class _ModelRouter {
|
|
|
7787
8118
|
}
|
|
7788
8119
|
logEndpoints() {
|
|
7789
8120
|
runWithRuntime(this.runtime, () => {
|
|
7790
|
-
(0,
|
|
7791
|
-
logInfoMessage(`${(0,
|
|
8121
|
+
(0, import_utils29.forEach)(this.router.getEndpoints(), ({ method, path }) => {
|
|
8122
|
+
logInfoMessage(`${(0, import_utils29.padEnd)(method, 6)} ${(0, import_utils29.normalizeUrlPath)(this.options.parentPath + path)}`);
|
|
7792
8123
|
});
|
|
7793
8124
|
});
|
|
7794
8125
|
}
|
|
7795
8126
|
set(keyOrOptions, value) {
|
|
7796
|
-
if (arguments.length === 2 && (0,
|
|
8127
|
+
if (arguments.length === 2 && (0, import_utils29.isString)(keyOrOptions)) {
|
|
7797
8128
|
assertMutableRouterOption("model", keyOrOptions);
|
|
7798
8129
|
this.runtime.setModelOption(
|
|
7799
8130
|
this.modelName,
|
|
@@ -7801,7 +8132,7 @@ var ModelRouter = class _ModelRouter {
|
|
|
7801
8132
|
value
|
|
7802
8133
|
);
|
|
7803
8134
|
}
|
|
7804
|
-
if (arguments.length === 1 && (0,
|
|
8135
|
+
if (arguments.length === 1 && (0, import_utils29.isPlainObject)(keyOrOptions)) {
|
|
7805
8136
|
assertMutableRouterOptions("model", keyOrOptions);
|
|
7806
8137
|
this.runtime.setModelOptions(this.modelName, keyOrOptions);
|
|
7807
8138
|
}
|
|
@@ -7824,7 +8155,7 @@ var ModelRouter = class _ModelRouter {
|
|
|
7824
8155
|
|
|
7825
8156
|
// src/routers/root-router.ts
|
|
7826
8157
|
var import_express_json_router6 = __toESM(require("@web-ts-toolkit/express-json-router"));
|
|
7827
|
-
var
|
|
8158
|
+
var import_utils30 = require("@web-ts-toolkit/utils");
|
|
7828
8159
|
|
|
7829
8160
|
// src/core-data.ts
|
|
7830
8161
|
var DataCore = class {
|
|
@@ -8090,7 +8421,7 @@ var RootRouter = class {
|
|
|
8090
8421
|
this.setRoutes();
|
|
8091
8422
|
}
|
|
8092
8423
|
registerOpenApiRoute(route) {
|
|
8093
|
-
registerOpenApiRoute(this.runtime, (0,
|
|
8424
|
+
registerOpenApiRoute(this.runtime, (0, import_utils30.normalizeUrlPath)(this.basename), "root", route);
|
|
8094
8425
|
}
|
|
8095
8426
|
wrapResult(index, item, result) {
|
|
8096
8427
|
return {
|
|
@@ -8151,7 +8482,7 @@ var RootRouter = class {
|
|
|
8151
8482
|
groupItemsByOrder(items) {
|
|
8152
8483
|
const groups = /* @__PURE__ */ new Map();
|
|
8153
8484
|
items.forEach((item, index) => {
|
|
8154
|
-
const order = (0,
|
|
8485
|
+
const order = (0, import_utils30.isNumber)(item.order) ? item.order : 0;
|
|
8155
8486
|
const group = groups.get(order);
|
|
8156
8487
|
if (group) {
|
|
8157
8488
|
group.push({ index, item });
|
|
@@ -8222,7 +8553,7 @@ var RootRouter = class {
|
|
|
8222
8553
|
const arrResult = await this.runGroup(req, groupedItems[x]);
|
|
8223
8554
|
results.push(...arrResult);
|
|
8224
8555
|
}
|
|
8225
|
-
return (0,
|
|
8556
|
+
return (0, import_utils30.orderBy)(results, ["index"], ["asc"]);
|
|
8226
8557
|
});
|
|
8227
8558
|
this.registerOpenApiRoute({
|
|
8228
8559
|
method: "post",
|
|
@@ -8240,11 +8571,11 @@ var RootRouter = class {
|
|
|
8240
8571
|
|
|
8241
8572
|
// src/routers/data-router.ts
|
|
8242
8573
|
var import_express_json_router7 = __toESM(require("@web-ts-toolkit/express-json-router"));
|
|
8243
|
-
var
|
|
8574
|
+
var import_utils31 = require("@web-ts-toolkit/utils");
|
|
8244
8575
|
var clientErrors4 = import_express_json_router7.default.clientErrors;
|
|
8245
8576
|
function setOption2(parentKey, optionKey, option) {
|
|
8246
|
-
const key = (0,
|
|
8247
|
-
const value = (0,
|
|
8577
|
+
const key = (0, import_utils31.isUndefined)(option) ? parentKey : `${parentKey}.${optionKey}`;
|
|
8578
|
+
const value = (0, import_utils31.isUndefined)(option) ? optionKey : option;
|
|
8248
8579
|
assertMutableRouterOption("data", key);
|
|
8249
8580
|
this.runtime.setDataOption(this.dataName, key, value);
|
|
8250
8581
|
return this;
|
|
@@ -8285,7 +8616,7 @@ var DataRouter = class {
|
|
|
8285
8616
|
this.runtime = runtime;
|
|
8286
8617
|
this.runtime.setDataOptions(dataName, initialOptions);
|
|
8287
8618
|
this.options = this.runtime.getDataOptions(dataName);
|
|
8288
|
-
this.fullBasePath = (0,
|
|
8619
|
+
this.fullBasePath = (0, import_utils31.normalizeUrlPath)(this.options.parentPath + this.options.basePath);
|
|
8289
8620
|
this.dataName = dataName;
|
|
8290
8621
|
this.router = new import_express_json_router7.default(this.options.basePath, createSetDataCore(this.runtime), accessRouterResponseHandler);
|
|
8291
8622
|
this.setCollectionRoutes();
|
|
@@ -8318,8 +8649,8 @@ var DataRouter = class {
|
|
|
8318
8649
|
req.query
|
|
8319
8650
|
);
|
|
8320
8651
|
const svc = this.getService(req);
|
|
8321
|
-
const includeCount = (0,
|
|
8322
|
-
const includeExtraHeaders = (0,
|
|
8652
|
+
const includeCount = (0, import_utils27.parseBooleanString)(include_count);
|
|
8653
|
+
const includeExtraHeaders = (0, import_utils27.parseBooleanString)(include_extra_headers);
|
|
8323
8654
|
const result = await svc.find(
|
|
8324
8655
|
{},
|
|
8325
8656
|
{ skip, limit, page, pageSize: page_size },
|
|
@@ -8437,11 +8768,11 @@ var DataRouter = class {
|
|
|
8437
8768
|
});
|
|
8438
8769
|
}
|
|
8439
8770
|
set(keyOrOptions, value) {
|
|
8440
|
-
if (arguments.length === 2 && (0,
|
|
8771
|
+
if (arguments.length === 2 && (0, import_utils31.isString)(keyOrOptions)) {
|
|
8441
8772
|
assertMutableRouterOption("data", keyOrOptions);
|
|
8442
8773
|
this.runtime.setDataOption(this.dataName, keyOrOptions, value);
|
|
8443
8774
|
}
|
|
8444
|
-
if (arguments.length === 1 && (0,
|
|
8775
|
+
if (arguments.length === 1 && (0, import_utils31.isPlainObject)(keyOrOptions)) {
|
|
8445
8776
|
assertMutableRouterOptions("data", keyOrOptions);
|
|
8446
8777
|
this.runtime.setDataOptions(this.dataName, keyOrOptions);
|
|
8447
8778
|
}
|
|
@@ -8564,7 +8895,7 @@ function createRuntimeApi(runtime) {
|
|
|
8564
8895
|
const wtt2 = runtimeMiddleware;
|
|
8565
8896
|
wtt2.runtime = runtime;
|
|
8566
8897
|
wtt2.createRouter = function(modelName, options) {
|
|
8567
|
-
if ((0,
|
|
8898
|
+
if ((0, import_utils32.isUndefined)(options)) {
|
|
8568
8899
|
return new RootRouter(modelName, runtime);
|
|
8569
8900
|
}
|
|
8570
8901
|
if (typeof modelName === "string") {
|
|
@@ -8585,10 +8916,10 @@ function createRuntimeApi(runtime) {
|
|
|
8585
8916
|
};
|
|
8586
8917
|
wtt2.combineRoutes = combineRoutes;
|
|
8587
8918
|
wtt2.set = function(keyOrOptions, value) {
|
|
8588
|
-
if (arguments.length === 2 && (0,
|
|
8919
|
+
if (arguments.length === 2 && (0, import_utils32.isString)(keyOrOptions)) {
|
|
8589
8920
|
return runtime.setGlobalOption(keyOrOptions, value);
|
|
8590
8921
|
}
|
|
8591
|
-
if (arguments.length === 1 && (0,
|
|
8922
|
+
if (arguments.length === 1 && (0, import_utils32.isPlainObject)(keyOrOptions)) {
|
|
8592
8923
|
return runtime.setGlobalOptions(keyOrOptions);
|
|
8593
8924
|
}
|
|
8594
8925
|
};
|