@web-ts-toolkit/access-router 0.3.0 → 0.5.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/index.mjs CHANGED
@@ -1,16 +1,16 @@
1
1
  // src/index.ts
2
- import { isPlainObject as isPlainObject11, isString as isString12, isUndefined as isUndefined4 } from "@web-ts-toolkit/utils";
2
+ import { isPlainObject as isPlainObject14, isString as isString12, isUndefined as isUndefined4 } from "@web-ts-toolkit/utils";
3
3
 
4
4
  // src/middleware.ts
5
5
  import JsonRouter2 from "@web-ts-toolkit/express-json-router";
6
- import { isArray as isArray9, isFunction as isFunction6, isPlainObject as isPlainObject8, isString as isString7 } from "@web-ts-toolkit/utils";
6
+ import { isArray as isArray10, isFunction as isFunction6, isPlainObject as isPlainObject11, isString as isString7 } from "@web-ts-toolkit/utils";
7
7
 
8
8
  // src/core.ts
9
9
  import {
10
10
  castArray as castArray5,
11
11
  compact as compact3,
12
- forEach as forEach6,
13
- isArray as isArray8,
12
+ forEach as forEach7,
13
+ isArray as isArray9,
14
14
  isBoolean as isBoolean5,
15
15
  isFunction as isFunction5,
16
16
  isString as isString6,
@@ -25,11 +25,11 @@ import {
25
25
  addLeadingSlash,
26
26
  forEach as forEach2,
27
27
  get as get3,
28
- isArray as isArray3,
28
+ isArray as isArray4,
29
29
  isBoolean,
30
30
  isFunction,
31
31
  isNil as isNil2,
32
- isPlainObject as isPlainObject5,
32
+ isPlainObject as isPlainObject7,
33
33
  isString as isString4,
34
34
  keys
35
35
  } from "@web-ts-toolkit/utils";
@@ -51,20 +51,11 @@ var findElementById = (collection, id) => {
51
51
  };
52
52
 
53
53
  // src/helpers/document.ts
54
- import { get, isArray, isPlainObject as isPlainObject3, isPromise, isString as isString2, pick, set } from "@web-ts-toolkit/utils";
55
-
56
- // src/lib.ts
57
- import { Document, Schema } from "mongoose";
58
- import { isPlainObject } from "@web-ts-toolkit/utils";
59
- var isSchema = (val) => val instanceof Schema;
60
- var isObjectIdType = (val) => val === "ObjectId" || val === Schema.Types.ObjectId;
61
- var isReference = (val) => isPlainObject(val) && !!val.ref && isObjectIdType(val.type);
62
- var isDocument = function isDocument2(doc) {
63
- return doc instanceof Document;
64
- };
54
+ import { Document } from "mongoose";
55
+ import { get, isPlainObject as isPlainObject2, pick, set } from "@web-ts-toolkit/utils";
65
56
 
66
57
  // src/helpers/query.ts
67
- import { flattenDeep, isNaN, isNil, isPlainObject as isPlainObject2, isString, reduce } from "@web-ts-toolkit/utils";
58
+ import { flattenDeep, isNaN, isNil, isPlainObject, isString, reduce } from "@web-ts-toolkit/utils";
68
59
  function genPagination({
69
60
  skip,
70
61
  limit,
@@ -92,7 +83,7 @@ function parseSortString(sortString) {
92
83
  }
93
84
  function normalizeSelect(select) {
94
85
  if (Array.isArray(select)) return flattenDeep(select.map(normalizeSelect));
95
- if (isPlainObject2(select)) {
86
+ if (isPlainObject(select)) {
96
87
  return reduce(
97
88
  select,
98
89
  (ret, val, key) => {
@@ -108,17 +99,21 @@ function normalizeSelect(select) {
108
99
  }
109
100
 
110
101
  // src/helpers/document.ts
111
- function getDocValue(doc, path, defalutValue) {
102
+ import { isArray, isPromise, isString as isString2 } from "@web-ts-toolkit/utils";
103
+ function isDocument(doc) {
104
+ return doc instanceof Document;
105
+ }
106
+ function getDocValue(doc, path, defaultValue) {
112
107
  if (isDocument(doc)) {
113
- return get(doc._doc, path, defalutValue);
114
- } else if (isPlainObject3(doc)) {
115
- return get(doc, path, defalutValue);
108
+ return get(doc._doc, path, defaultValue);
109
+ } else if (isPlainObject2(doc)) {
110
+ return get(doc, path, defaultValue);
116
111
  }
117
112
  }
118
113
  function setDocValue(doc, path, value) {
119
114
  if (isDocument(doc)) {
120
115
  set(doc._doc, path, value);
121
- } else if (isPlainObject3(doc)) {
116
+ } else if (isPlainObject2(doc)) {
122
117
  set(doc, path, value);
123
118
  }
124
119
  }
@@ -207,16 +202,19 @@ function handleResultError(result) {
207
202
  }
208
203
 
209
204
  // src/helpers/index.ts
210
- import {
211
- forEach,
212
- isArray as isArray2,
213
- isEmpty,
214
- isObject,
215
- isPlainObject as isPlainObject4,
216
- isString as isString3,
217
- mapValuesAsync,
218
- noop
219
- } from "@web-ts-toolkit/utils";
205
+ import { forEach, isArray as isArray2, isEmpty, isObject, isPlainObject as isPlainObject4, isString as isString3, mapValuesAsync } from "@web-ts-toolkit/utils";
206
+
207
+ // src/lib.ts
208
+ import { Document as Document2, Schema } from "mongoose";
209
+ import { isPlainObject as isPlainObject3 } from "@web-ts-toolkit/utils";
210
+ var isSchema = (val) => val instanceof Schema;
211
+ var isObjectIdType = (val) => val === "ObjectId" || val === Schema.Types.ObjectId;
212
+ var isReference = (val) => isPlainObject3(val) && !!val.ref && isObjectIdType(val.type);
213
+ var isDocument2 = function isDocument3(doc) {
214
+ return doc instanceof Document2;
215
+ };
216
+
217
+ // src/helpers/index.ts
220
218
  function recurseObject(obj) {
221
219
  if (isSchema(obj)) {
222
220
  return buildRefs(obj.tree);
@@ -266,7 +264,7 @@ function buildSubPaths(schema) {
266
264
  }
267
265
  async function iterateQuery(query, handler) {
268
266
  if (!isPlainObject4(query)) return query;
269
- if (!handler) return noop;
267
+ if (!handler) return query;
270
268
  const queryObject = query;
271
269
  return mapValuesAsync(queryObject, async (val, key) => {
272
270
  if (isPlainObject4(val)) {
@@ -295,6 +293,252 @@ var createValidator = (fn) => {
295
293
  return { stringHandler, arrayHandler };
296
294
  };
297
295
 
296
+ // src/openapi/build-spec.ts
297
+ import { isArray as isArray3, isPlainObject as isPlainObject6 } from "@web-ts-toolkit/utils";
298
+
299
+ // src/openapi/responses.ts
300
+ var json = (schema) => ({
301
+ content: {
302
+ "application/json": {
303
+ schema
304
+ }
305
+ }
306
+ });
307
+ var problemJson = {
308
+ content: {
309
+ "application/problem+json": {
310
+ schema: {
311
+ type: "object",
312
+ properties: {
313
+ type: { type: "string" },
314
+ title: { type: "string" },
315
+ status: { type: "number" },
316
+ detail: { type: "string" },
317
+ errors: { type: "array", items: {} }
318
+ },
319
+ additionalProperties: true
320
+ }
321
+ }
322
+ }
323
+ };
324
+ var genericDataSchema = { type: "object", additionalProperties: true };
325
+ var genericListSchema = { type: "array", items: genericDataSchema };
326
+ var openApiResponses = {
327
+ single(description = "Success") {
328
+ return {
329
+ 200: { description, ...json(genericDataSchema) },
330
+ 400: { description: "Bad Request", ...problemJson },
331
+ 401: { description: "Unauthorized", ...problemJson },
332
+ 404: { description: "Not Found", ...problemJson }
333
+ };
334
+ },
335
+ list(description = "Success") {
336
+ return {
337
+ 200: { description, ...json(genericListSchema) },
338
+ 400: { description: "Bad Request", ...problemJson },
339
+ 401: { description: "Unauthorized", ...problemJson }
340
+ };
341
+ },
342
+ create(description = "Created") {
343
+ return {
344
+ 201: { description, ...json(genericDataSchema) },
345
+ 400: { description: "Bad Request", ...problemJson },
346
+ 401: { description: "Unauthorized", ...problemJson }
347
+ };
348
+ },
349
+ delete(description = "Deleted") {
350
+ return {
351
+ 200: { description, ...json(genericDataSchema) },
352
+ 400: { description: "Bad Request", ...problemJson },
353
+ 401: { description: "Unauthorized", ...problemJson },
354
+ 404: { description: "Not Found", ...problemJson }
355
+ };
356
+ },
357
+ batch(description = "Success") {
358
+ return {
359
+ 200: { description, ...json(genericListSchema) },
360
+ 400: { description: "Bad Request", ...problemJson },
361
+ 401: { description: "Unauthorized", ...problemJson }
362
+ };
363
+ }
364
+ };
365
+
366
+ // src/openapi/schemas.ts
367
+ import { z } from "zod";
368
+ import { isPlainObject as isPlainObject5 } from "@web-ts-toolkit/utils";
369
+ var fallbackObjectSchema = { type: "object", additionalProperties: true };
370
+ function patchOpenApiObjectSchema(source, properties) {
371
+ return {
372
+ kind: "objectProperties",
373
+ source,
374
+ properties
375
+ };
376
+ }
377
+ function defineOpenApiSchemaResolver(resolve) {
378
+ return {
379
+ kind: "schemaResolver",
380
+ resolve
381
+ };
382
+ }
383
+ function getNestedOpenApiSchemaDataSource(source) {
384
+ return isPlainObject5(source) && "data" in source ? source.data : void 0;
385
+ }
386
+ function unwrapNestedOpenApiSchemaSource(source, fallback) {
387
+ if (isPlainObject5(source) && ("default" in source || "data" in source)) {
388
+ return source.default ?? fallback;
389
+ }
390
+ return source ?? fallback;
391
+ }
392
+ function isOpenApiSchemaPatch(source) {
393
+ return isPlainObject5(source) && source.kind === "objectProperties";
394
+ }
395
+ function isOpenApiSchemaResolver(source) {
396
+ return isPlainObject5(source) && source.kind === "schemaResolver" && typeof source.resolve === "function";
397
+ }
398
+ function isJsonSchemaLike(source) {
399
+ return isPlainObject5(source) && ("type" in source || "properties" in source || "$ref" in source || "anyOf" in source);
400
+ }
401
+ function hasOpenApiSchema(source) {
402
+ return isPlainObject5(source) && isJsonSchemaLike(source.openapi);
403
+ }
404
+ function toOpenApiSchema(source) {
405
+ if (!source) return void 0;
406
+ const resolvedSource = isOpenApiSchemaResolver(source) ? source.resolve() : source;
407
+ if (!resolvedSource) return void 0;
408
+ if (isOpenApiSchemaPatch(resolvedSource)) {
409
+ const schema = toOpenApiSchema(resolvedSource.source) ?? { ...fallbackObjectSchema };
410
+ const properties = isPlainObject5(schema.properties) ? { ...schema.properties } : {};
411
+ for (const [key, value] of Object.entries(resolvedSource.properties)) {
412
+ properties[key] = toOpenApiSchema(value) ?? fallbackObjectSchema;
413
+ }
414
+ return {
415
+ ...schema,
416
+ type: schema.type ?? "object",
417
+ properties
418
+ };
419
+ }
420
+ if (hasOpenApiSchema(resolvedSource)) {
421
+ return resolvedSource.openapi;
422
+ }
423
+ if (isJsonSchemaLike(resolvedSource)) {
424
+ return resolvedSource;
425
+ }
426
+ try {
427
+ return z.toJSONSchema(resolvedSource);
428
+ } catch {
429
+ return fallbackObjectSchema;
430
+ }
431
+ }
432
+
433
+ // src/openapi/build-spec.ts
434
+ var openApiMediaType = "application/json";
435
+ var normalizePath = (path) => {
436
+ if (!path) return "/";
437
+ return path.startsWith("/") ? path : `/${path}`;
438
+ };
439
+ var toOpenApiPath = (path) => normalizePath(path).replace(/:([A-Za-z0-9_]+)/g, "{$1}");
440
+ var schemaToQueryParameters = (schema) => {
441
+ const jsonSchema = toOpenApiSchema(schema);
442
+ if (!jsonSchema || jsonSchema.type !== "object") return [];
443
+ const properties = isPlainObject6(jsonSchema.properties) ? jsonSchema.properties : {};
444
+ const required = new Set(
445
+ isArray3(jsonSchema.required) ? jsonSchema.required.filter((item) => typeof item === "string") : []
446
+ );
447
+ return Object.entries(properties).map(([name, value]) => ({
448
+ name,
449
+ in: "query",
450
+ required: required.has(name),
451
+ schema: value
452
+ }));
453
+ };
454
+ var schemaToPathParameters = (path, pathParams) => {
455
+ const names = [...toOpenApiPath(path).matchAll(/\{([^}]+)\}/g)].map((item) => item[1]);
456
+ return names.map((name) => ({
457
+ name,
458
+ in: "path",
459
+ required: true,
460
+ schema: (pathParams && name in pathParams ? toOpenApiSchema(pathParams[name]) : void 0) ?? { type: "string" }
461
+ }));
462
+ };
463
+ var getDefaultResponses = (route) => {
464
+ if (route.operationId === "root.query") return openApiResponses.batch();
465
+ if (route.method === "delete") return openApiResponses.delete();
466
+ if (route.operationId?.toLowerCase().includes("create")) return openApiResponses.create();
467
+ if (route.operationId?.toLowerCase().includes("list")) return openApiResponses.list();
468
+ if (route.operationId?.toLowerCase().includes("distinct")) return openApiResponses.list();
469
+ return openApiResponses.single();
470
+ };
471
+ var buildOperation = (route) => {
472
+ const queryParameters = schemaToQueryParameters(route.query);
473
+ const pathParameters = schemaToPathParameters(route.path, route.pathParams);
474
+ const bodySchema = toOpenApiSchema(route.body);
475
+ const responses = route.responses ?? getDefaultResponses(route);
476
+ const operation = {
477
+ operationId: route.operationId,
478
+ summary: route.summary,
479
+ description: route.description,
480
+ tags: route.tags,
481
+ deprecated: route.deprecated,
482
+ responses
483
+ };
484
+ const parameters = [...pathParameters, ...queryParameters];
485
+ if (parameters.length) {
486
+ operation.parameters = parameters;
487
+ }
488
+ if (bodySchema) {
489
+ operation.requestBody = {
490
+ required: true,
491
+ content: {
492
+ [openApiMediaType]: {
493
+ schema: bodySchema
494
+ }
495
+ }
496
+ };
497
+ }
498
+ if (route.acl) {
499
+ operation["x-access-router-acl"] = route.acl;
500
+ }
501
+ return operation;
502
+ };
503
+ function buildOpenApiSpec(routes, options) {
504
+ const paths = {};
505
+ const { servers, ...info } = options;
506
+ for (const route of routes) {
507
+ const path = toOpenApiPath(route.path);
508
+ if (!paths[path]) {
509
+ paths[path] = {};
510
+ }
511
+ paths[path][route.method] = buildOperation(route);
512
+ }
513
+ return {
514
+ openapi: "3.1.0",
515
+ info,
516
+ servers,
517
+ paths
518
+ };
519
+ }
520
+
521
+ // src/openapi/registry.ts
522
+ var OpenApiRegistry = class {
523
+ constructor() {
524
+ this.routes = [];
525
+ }
526
+ register(route) {
527
+ const existingIndex = this.routes.findIndex((item) => item.method === route.method && item.path === route.path);
528
+ if (existingIndex === -1) {
529
+ this.routes.push(route);
530
+ return;
531
+ }
532
+ this.routes[existingIndex] = route;
533
+ }
534
+ getRoutes() {
535
+ return [...this.routes];
536
+ }
537
+ getSpec(info) {
538
+ return buildOpenApiSpec(this.routes, info);
539
+ }
540
+ };
541
+
298
542
  // src/logger-default.ts
299
543
  import util from "util";
300
544
  import { createLogger, format, transports } from "winston";
@@ -401,9 +645,9 @@ var hasModelPermission = (value, modelPermissionPrefix) => {
401
645
  if (isString4(value)) {
402
646
  return value.trim().split(" ").some((item) => item.startsWith(modelPermissionPrefix));
403
647
  }
404
- if (isArray3(value)) {
648
+ if (isArray4(value)) {
405
649
  return value.some((item) => {
406
- if (isString4(item) || isArray3(item)) {
650
+ if (isString4(item) || isArray4(item)) {
407
651
  return hasModelPermission(item, modelPermissionPrefix);
408
652
  }
409
653
  return true;
@@ -417,13 +661,13 @@ var classifyPermissionSchema = (permissionSchema, modelPermissionPrefix) => {
417
661
  const modelPermissionKeys = {};
418
662
  forEach2(schemaKeys, (schemaKey) => {
419
663
  const schemaRule = permissionSchema[schemaKey];
420
- const ruleEntries = isPlainObject5(schemaRule) ? Object.entries(schemaRule) : FIELD_ACCESS_KEYS.map((accessKey) => [accessKey, schemaRule]);
664
+ const ruleEntries = isPlainObject7(schemaRule) ? Object.entries(schemaRule) : FIELD_ACCESS_KEYS.map((accessKey) => [accessKey, schemaRule]);
421
665
  for (let x = 0; x < ruleEntries.length; x++) {
422
666
  const [accessKey, value] = ruleEntries[x];
423
- if (!isArray3(globalPermissionKeys[accessKey])) {
667
+ if (!isArray4(globalPermissionKeys[accessKey])) {
424
668
  globalPermissionKeys[accessKey] = [];
425
669
  }
426
- if (!isArray3(modelPermissionKeys[accessKey])) {
670
+ if (!isArray4(modelPermissionKeys[accessKey])) {
427
671
  modelPermissionKeys[accessKey] = [];
428
672
  }
429
673
  if (isBoolean(value)) {
@@ -467,6 +711,16 @@ var AccessRuntime = class {
467
711
  this.modelRefs = {};
468
712
  this.modelSubs = {};
469
713
  this.modelAtts = {};
714
+ this.openApiRegistry = new OpenApiRegistry();
715
+ }
716
+ registerOpenApiRoute(route) {
717
+ this.openApiRegistry.register(route);
718
+ }
719
+ getOpenApiRoutes() {
720
+ return this.openApiRegistry.getRoutes();
721
+ }
722
+ getOpenApiSpec(info) {
723
+ return this.openApiRegistry.getSpec(info);
470
724
  }
471
725
  setGlobalOptions(options) {
472
726
  this.globalOptions.assign(options);
@@ -738,19 +992,19 @@ import {
738
992
  forEach as forEach3,
739
993
  get as get4,
740
994
  intersectionBy,
741
- isArray as isArray4,
742
- isPlainObject as isPlainObject6,
995
+ isArray as isArray5,
996
+ isPlainObject as isPlainObject8,
743
997
  uniq
744
998
  } from "@web-ts-toolkit/utils";
745
999
  function validateClientFilter(filter2) {
746
1000
  const errors = [];
747
1001
  const blockedOperators = /* @__PURE__ */ new Set(["$where", "$expr", "$function", "$accumulator"]);
748
1002
  const visit = (value, path) => {
749
- if (isArray4(value)) {
1003
+ if (isArray5(value)) {
750
1004
  value.forEach((item, index) => visit(item, `${path}[${index}]`));
751
1005
  return;
752
1006
  }
753
- if (!isPlainObject6(value)) return;
1007
+ if (!isPlainObject8(value)) return;
754
1008
  Object.entries(value).forEach(([key, child]) => {
755
1009
  const nextPath = path ? `${path}.${key}` : key;
756
1010
  if (blockedOperators.has(key)) {
@@ -866,7 +1120,7 @@ var Base = class {
866
1120
  if (!include) return docs;
867
1121
  const includes = compact(castArray(include));
868
1122
  if (includes.length === 0) return docs;
869
- const isSingle = !isArray4(docs);
1123
+ const isSingle = !isArray5(docs);
870
1124
  if (isSingle) docs = [docs];
871
1125
  for (let x = 0; x < includes.length; x++) {
872
1126
  const include2 = includes[x];
@@ -951,14 +1205,16 @@ var Base = class {
951
1205
  if (!result.success) return null;
952
1206
  let ret = result.data;
953
1207
  if (sqOptions.path) {
954
- ret = isArray4(ret) ? flatten(ret.map((v) => get4(v, sqOptions.path))) : get4(ret, sqOptions.path);
1208
+ ret = isArray5(ret) ? flatten(ret.map((v) => get4(v, sqOptions.path))) : get4(ret, sqOptions.path);
955
1209
  }
956
1210
  if (sqOptions.compact) {
957
1211
  ret = compact(castArray(ret));
958
1212
  }
959
1213
  return ret;
960
1214
  }
961
- async handleDate(val, key) {
1215
+ handleDate(val, key) {
1216
+ if (val instanceof Date) return val;
1217
+ if (typeof val === "string" || typeof val === "number") return new Date(val);
962
1218
  return /* @__PURE__ */ new Date();
963
1219
  }
964
1220
  };
@@ -966,7 +1222,7 @@ var Base = class {
966
1222
  // src/services/service.ts
967
1223
  import {
968
1224
  castArray as castArray2,
969
- isArray as isArray5,
1225
+ isArray as isArray6,
970
1226
  isBoolean as isBoolean3,
971
1227
  isFunction as isFunction3,
972
1228
  omit,
@@ -977,6 +1233,21 @@ import diff from "deep-diff";
977
1233
 
978
1234
  // src/model.ts
979
1235
  import mongoose3 from "mongoose";
1236
+
1237
+ // src/logger.ts
1238
+ var passthrough = (level) => (...args) => {
1239
+ const globalLogger = getGlobalOption("logger");
1240
+ const target = globalLogger?.[level] ?? defaultLogger[level];
1241
+ return target.apply(globalLogger ?? defaultLogger, args);
1242
+ };
1243
+ var logger = {
1244
+ debug: passthrough("debug"),
1245
+ info: passthrough("info"),
1246
+ warn: passthrough("warn"),
1247
+ error: passthrough("error")
1248
+ };
1249
+
1250
+ // src/model.ts
980
1251
  var Model = class {
981
1252
  constructor(modelName) {
982
1253
  this.modelName = modelName;
@@ -1006,12 +1277,15 @@ var Model = class {
1006
1277
  if (lean) builder.lean();
1007
1278
  return builder;
1008
1279
  }
1009
- // See https://github.com/Automattic/mongoose/blob/65b2d12a8f85f86136cfaf32947f338ba0c5f451/lib/query.js#L3011
1010
- validateSort(sort, logError = console.error) {
1280
+ validateSort(sort, logError = logger.error) {
1011
1281
  const validSortOrders = [1, -1, "asc", "ascending", "desc", "descending"];
1282
+ const fieldPathPattern = /^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*)*$/;
1283
+ const isValidFieldPath = (field) => fieldPathPattern.test(field);
1012
1284
  if (sort === null || sort === void 0) return true;
1013
1285
  if (typeof sort === "string") {
1014
- if (!/^[a-zA-Z0-9\s-]+/.test(sort)) {
1286
+ const fields = sort.trim().split(/\s+/).filter(Boolean);
1287
+ const isValid = fields.every((field) => isValidFieldPath(field.startsWith("-") ? field.slice(1) : field));
1288
+ if (!isValid) {
1015
1289
  logError("Invalid sort string:", sort);
1016
1290
  return false;
1017
1291
  }
@@ -1024,8 +1298,8 @@ var Model = class {
1024
1298
  return false;
1025
1299
  }
1026
1300
  const [key, order] = pair;
1027
- if (typeof key !== "string") {
1028
- logError("Invalid sort array key: must be string", key);
1301
+ if (typeof key !== "string" || !isValidFieldPath(key)) {
1302
+ logError("Invalid sort array key: must be a field path string", key);
1029
1303
  return false;
1030
1304
  }
1031
1305
  if (!validSortOrders.includes(order)) {
@@ -1037,7 +1311,11 @@ var Model = class {
1037
1311
  return isValid;
1038
1312
  }
1039
1313
  if (typeof sort === "object" && !(sort instanceof Map)) {
1040
- const isValid = Object.values(sort).every((order) => {
1314
+ const isValid = Object.entries(sort).every(([key, order]) => {
1315
+ if (!isValidFieldPath(key)) {
1316
+ logError("Invalid sort object key: must be a field path string", key);
1317
+ return false;
1318
+ }
1041
1319
  if (!validSortOrders.includes(order)) {
1042
1320
  logError('Invalid sort object value: must be 1, -1, "asc", or "desc"', order);
1043
1321
  return false;
@@ -1047,7 +1325,11 @@ var Model = class {
1047
1325
  return isValid;
1048
1326
  }
1049
1327
  if (sort instanceof Map) {
1050
- const isValid = Array.from(sort.values()).every((order) => {
1328
+ const isValid = Array.from(sort.entries()).every(([key, order]) => {
1329
+ if (!isValidFieldPath(key)) {
1330
+ logError("Invalid sort Map key: must be a field path string", key);
1331
+ return false;
1332
+ }
1051
1333
  if (!validSortOrders.includes(order)) {
1052
1334
  logError('Invalid sort Map value: must be 1, -1, "asc", or "desc"', order);
1053
1335
  return false;
@@ -1070,9 +1352,6 @@ var Model = class {
1070
1352
  if (lean) builder.lean();
1071
1353
  return builder;
1072
1354
  }
1073
- findOneAndDelete(filter2) {
1074
- return this.model.findOneAndDelete(filter2);
1075
- }
1076
1355
  exists(filter2) {
1077
1356
  if (!filter2) return null;
1078
1357
  return this.findOne(filter2).select("_id").lean();
@@ -1081,10 +1360,6 @@ var Model = class {
1081
1360
  countDocuments(filter2 = {}) {
1082
1361
  return this.model.countDocuments(filter2);
1083
1362
  }
1084
- // see https://mongoosejs.com/docs/api.html#model_Model.estimatedDocumentCount
1085
- estimatedDocumentCount() {
1086
- return this.model.estimatedDocumentCount();
1087
- }
1088
1363
  // see https://mongoosejs.com/docs/api.html#model_Model.distinct
1089
1364
  distinct(field, conditions = {}) {
1090
1365
  return this.model.distinct(field, conditions);
@@ -1092,19 +1367,6 @@ var Model = class {
1092
1367
  };
1093
1368
  var model_default = Model;
1094
1369
 
1095
- // src/logger.ts
1096
- var passthrough = (level) => (...args) => {
1097
- const globalLogger = getGlobalOption("logger");
1098
- const target = globalLogger?.[level] ?? defaultLogger[level];
1099
- return target.apply(globalLogger ?? defaultLogger, args);
1100
- };
1101
- var logger = {
1102
- debug: passthrough("debug"),
1103
- info: passthrough("info"),
1104
- warn: passthrough("warn"),
1105
- error: passthrough("error")
1106
- };
1107
-
1108
1370
  // src/services/model-subdocument-service.ts
1109
1371
  import { forEach as forEach4, get as get5, pick as pick3 } from "@web-ts-toolkit/utils";
1110
1372
  async function listSub(service, id, sub, options) {
@@ -1328,7 +1590,7 @@ function resolveExistsOptions(service, options = {}) {
1328
1590
 
1329
1591
  // src/services/service.ts
1330
1592
  var assertModelDocument = (value, modelName, hookName) => {
1331
- if (isDocument(value)) {
1593
+ if (isDocument2(value)) {
1332
1594
  return value;
1333
1595
  }
1334
1596
  throw new Error(`${hookName} hook for model=${modelName} must return a Mongoose document instance`);
@@ -1422,7 +1684,7 @@ var Service = class extends Base {
1422
1684
  genPagination({ skip, limit, page, pageSize }, this.options.listHardLimit)
1423
1685
  ]);
1424
1686
  const finalSelect = normalizeSelect(_select);
1425
- const filteredPopulate = isArray5(finalSelect) && isArray5(_populate) ? _populate.filter((p) => finalSelect.includes(p.path.split(".")[0])) : _populate;
1687
+ const filteredPopulate = isArray6(finalSelect) && isArray6(_populate) ? _populate.filter((p) => finalSelect.includes(p.path.split(".")[0])) : _populate;
1426
1688
  const { includes, includeLocalFields, includePaths } = this.processInclude(include);
1427
1689
  const query = {
1428
1690
  filter: _filter,
@@ -1505,7 +1767,7 @@ var Service = class extends Base {
1505
1767
  validationError = { success: false, code: "bad_request" /* BadRequest */ };
1506
1768
  return;
1507
1769
  }
1508
- } else if (isArray5(validated)) {
1770
+ } else if (isArray6(validated)) {
1509
1771
  if (validated.length > 0) {
1510
1772
  validationError = { success: false, code: "bad_request" /* BadRequest */, errors: validated };
1511
1773
  return;
@@ -1595,7 +1857,7 @@ var Service = class extends Base {
1595
1857
  const validated = await this.validate(allowedData, "update", context);
1596
1858
  if (isBoolean3(validated)) {
1597
1859
  if (!validated) return { success: false, code: "bad_request" /* BadRequest */ };
1598
- } else if (isArray5(validated)) {
1860
+ } else if (isArray6(validated)) {
1599
1861
  if (validated.length > 0) return { success: false, code: "bad_request" /* BadRequest */, errors: validated };
1600
1862
  }
1601
1863
  const prepared = await this.prepare(allowedData, "update", context);
@@ -2199,29 +2461,29 @@ var DataService = class {
2199
2461
  };
2200
2462
 
2201
2463
  // src/processors.ts
2202
- import { castArray as castArray3, cloneDeep, forEach as forEach5, get as get6, isArray as isArray6, map as map3, set as set5 } from "@web-ts-toolkit/utils";
2464
+ import { castArray as castArray3, cloneDeep, forEach as forEach6, get as get7, isArray as isArray7, map as map2, set as set5 } from "@web-ts-toolkit/utils";
2203
2465
  var copyAndDepopulate = (docObject, operations, options = { mutable: true, idField: "_id" }) => {
2204
- const obj = get6(options, "mutable", true) ? docObject : cloneDeep(docObject);
2205
- const idField = get6(options, "idField", "_id");
2206
- forEach5(castArray3(operations), (op) => {
2466
+ const obj = get7(options, "mutable", true) ? docObject : cloneDeep(docObject);
2467
+ const idField = get7(options, "idField", "_id");
2468
+ forEach6(castArray3(operations), (op) => {
2207
2469
  if (!op.src || !op.dest) return;
2208
2470
  let targets = [obj];
2209
2471
  const segs = op.src.split(".");
2210
- forEach5(segs, (seg, ind) => {
2472
+ forEach6(segs, (seg, ind) => {
2211
2473
  if (segs.length === ind + 1) {
2212
- forEach5(targets, (target) => {
2474
+ forEach6(targets, (target) => {
2213
2475
  const targetObject = target;
2214
- set5(targetObject, op.dest, get6(targetObject, seg));
2476
+ set5(targetObject, op.dest, get7(targetObject, seg));
2215
2477
  set5(
2216
2478
  targetObject,
2217
2479
  seg,
2218
- isArray6(targetObject[seg]) ? map3(targetObject[seg], idField) : get6(targetObject, `${seg}.${idField}`)
2480
+ isArray7(targetObject[seg]) ? map2(targetObject[seg], idField) : get7(targetObject, `${seg}.${idField}`)
2219
2481
  );
2220
2482
  });
2221
2483
  } else {
2222
2484
  targets = targets.reduce((ret, target) => {
2223
2485
  const targetObject = target;
2224
- if (isArray6(targetObject[seg])) ret.push(...targetObject[seg]);
2486
+ if (isArray7(targetObject[seg])) ret.push(...targetObject[seg]);
2225
2487
  else ret.push(targetObject[seg]);
2226
2488
  return ret;
2227
2489
  }, []);
@@ -2272,12 +2534,12 @@ var Cache = class {
2272
2534
  import {
2273
2535
  castArray as castArray4,
2274
2536
  arrayToRecord,
2275
- isArray as isArray7,
2537
+ isArray as isArray8,
2276
2538
  isBoolean as isBoolean4,
2277
2539
  isEmpty as isEmpty2,
2278
2540
  isEqual,
2279
2541
  isFunction as isFunction4,
2280
- isPlainObject as isPlainObject7,
2542
+ isPlainObject as isPlainObject10,
2281
2543
  isString as isString5
2282
2544
  } from "@web-ts-toolkit/utils";
2283
2545
 
@@ -2317,10 +2579,10 @@ var permission_default = Permission;
2317
2579
 
2318
2580
  // src/core-shared.ts
2319
2581
  function isAndFilter(filter2) {
2320
- return !!filter2 && isPlainObject7(filter2) && Object.keys(filter2).length === 1 && isArray7(filter2.$and);
2582
+ return !!filter2 && isPlainObject10(filter2) && Object.keys(filter2).length === 1 && isArray8(filter2.$and);
2321
2583
  }
2322
2584
  function isMergeableClause(filter2) {
2323
- return !!filter2 && isPlainObject7(filter2) && Object.keys(filter2).every((key) => !key.startsWith("$"));
2585
+ return !!filter2 && isPlainObject10(filter2) && Object.keys(filter2).every((key) => !key.startsWith("$"));
2324
2586
  }
2325
2587
  function optimizeAndFilter(clausesInput) {
2326
2588
  const clauses = [];
@@ -2365,7 +2627,7 @@ function optimizeAndFilter(clausesInput) {
2365
2627
  }
2366
2628
  function normalizeFilter(filter2) {
2367
2629
  if (filter2 === false) return false;
2368
- if (!filter2 || !isPlainObject7(filter2) || isEmpty2(filter2)) return null;
2630
+ if (!filter2 || !isPlainObject10(filter2) || isEmpty2(filter2)) return null;
2369
2631
  if (!isAndFilter(filter2)) {
2370
2632
  return filter2;
2371
2633
  }
@@ -2417,8 +2679,8 @@ async function setRequestPermissions(req) {
2417
2679
  const globalPermissions = getGlobalOption("globalPermissions");
2418
2680
  if (!isFunction4(globalPermissions)) return;
2419
2681
  const permissions = await globalPermissions.call(req, req);
2420
- if (isPlainObject7(permissions)) req[requestPermissionField] = permissions;
2421
- else if (isArray7(permissions)) req[requestPermissionField] = arrayToRecord(permissions);
2682
+ if (isPlainObject10(permissions)) req[requestPermissionField] = permissions;
2683
+ else if (isArray8(permissions)) req[requestPermissionField] = arrayToRecord(permissions);
2422
2684
  else if (isString5(permissions)) req[requestPermissionField] = { [permissions]: true };
2423
2685
  }
2424
2686
  async function evaluateRouteGuard(req, permissions, routeGuard) {
@@ -2430,7 +2692,7 @@ async function evaluateRouteGuard(req, permissions, routeGuard) {
2430
2692
  if (isString5(routeGuard)) {
2431
2693
  return stringHandler(routeGuard);
2432
2694
  }
2433
- if (isArray7(routeGuard)) {
2695
+ if (isArray8(routeGuard)) {
2434
2696
  return arrayHandler(routeGuard);
2435
2697
  }
2436
2698
  if (isFunction4(routeGuard)) {
@@ -2468,7 +2730,7 @@ async function collectSchemaFields({
2468
2730
  if (value) fields.push(key);
2469
2731
  } else if (isString5(value)) {
2470
2732
  if (stringHandler(value)) fields.push(key);
2471
- } else if (isArray7(value)) {
2733
+ } else if (isArray8(value)) {
2472
2734
  if (arrayHandler(value)) fields.push(key);
2473
2735
  } else if (isFunction4(value)) {
2474
2736
  if (await value.call(req, ...functionArgs)) fields.push(key);
@@ -2757,7 +3019,7 @@ var Core = class {
2757
3019
  if (isFunction5(validate)) {
2758
3020
  const permissions = this.getGlobalPermissions();
2759
3021
  return validate.call(this.req, allowedData, permissions, context);
2760
- } else if (isBoolean5(validate) || isArray8(validate)) {
3022
+ } else if (isBoolean5(validate) || isArray9(validate)) {
2761
3023
  return validate;
2762
3024
  } else {
2763
3025
  return true;
@@ -2888,7 +3150,7 @@ var Core = class {
2888
3150
  runTasks(modelName, docObject, task) {
2889
3151
  const tasks = compact3(castArray5(task));
2890
3152
  if (tasks.length === 0) return docObject;
2891
- forEach6(tasks, (task2) => {
3153
+ forEach7(tasks, (task2) => {
2892
3154
  const { type, args, options } = task2;
2893
3155
  switch (type) {
2894
3156
  case "COPY_AND_DEPOPULATE":
@@ -2971,19 +3233,19 @@ function guard(condition) {
2971
3233
  const permissions = req[PERMISSIONS];
2972
3234
  let cond = condition;
2973
3235
  let phas = (key) => permissions.has(key);
2974
- if (isPlainObject8(condition)) {
3236
+ if (isPlainObject11(condition)) {
2975
3237
  const { modelName, id, condition: _cond } = condition;
2976
3238
  const svc = req.macl.getPublicService(modelName);
2977
3239
  const select = getModelOption(modelName, `alwaysSelectFields.read`, void 0);
2978
3240
  let _id = isString7(id) ? id : null;
2979
- if (isPlainObject8(id)) {
3241
+ if (isPlainObject11(id)) {
2980
3242
  const { type = "param", key } = id;
2981
3243
  if (type === "param") {
2982
3244
  const paramValue = req.params[key];
2983
- _id = isArray9(paramValue) ? paramValue[0] ?? null : paramValue ?? null;
3245
+ _id = isArray10(paramValue) ? paramValue[0] ?? null : paramValue ?? null;
2984
3246
  } else if (type === "query") {
2985
3247
  const _qval = req.query[key];
2986
- if (isArray9(_qval)) _id = _qval.length > 0 ? _qval[0] : null;
3248
+ if (isArray10(_qval)) _id = _qval.length > 0 ? _qval[0] : null;
2987
3249
  else _id = _qval;
2988
3250
  } else {
2989
3251
  _id = null;
@@ -3003,7 +3265,7 @@ function guard(condition) {
3003
3265
  const { stringHandler, arrayHandler } = createValidator(phas);
3004
3266
  if (isString7(cond)) {
3005
3267
  if (stringHandler(cond)) return next();
3006
- } else if (isArray9(cond)) {
3268
+ } else if (isArray10(cond)) {
3007
3269
  if (arrayHandler(cond)) return next();
3008
3270
  } else if (isFunction6(cond)) {
3009
3271
  const result = await cond.call(req, permissions);
@@ -3014,11 +3276,11 @@ function guard(condition) {
3014
3276
  }
3015
3277
 
3016
3278
  // src/routers/index.ts
3017
- import express from "express";
3279
+ import express2 from "express";
3018
3280
 
3019
3281
  // src/routers/model-router.ts
3020
3282
  import JsonRouter5 from "@web-ts-toolkit/express-json-router";
3021
- import { forEach as forEach7, isPlainObject as isPlainObject9, isString as isString9, isUndefined as isUndefined2, normalizeUrlPath, padEnd } from "@web-ts-toolkit/utils";
3283
+ import { forEach as forEach8, isPlainObject as isPlainObject12, isString as isString9, isUndefined as isUndefined2, normalizeUrlPath as normalizeUrlPath2, padEnd } from "@web-ts-toolkit/utils";
3022
3284
 
3023
3285
  // src/routers/router-mutation.ts
3024
3286
  var MODEL_BUILD_TIME_OPTION_KEYS = /* @__PURE__ */ new Set([
@@ -3110,88 +3372,88 @@ function formatModelUpsertResponse(result) {
3110
3372
  import JsonRouter4 from "@web-ts-toolkit/express-json-router";
3111
3373
 
3112
3374
  // src/validation/common.ts
3113
- import { z } from "zod";
3114
- var stringOrStringArray = z.union([z.string(), z.array(z.string())]);
3115
- var queryBooleanString = z.enum(["true", "false"]);
3116
- var positiveIntegerString = z.string().regex(/^\d+$/, "Expected a non-negative integer");
3117
- var nonNegativeIntegerSchema = z.number().int().min(0);
3118
- var unknownRecord = z.record(z.string(), z.unknown());
3119
- var projectionObjectSchema = z.record(z.string(), z.union([z.literal(1), z.literal(-1)]));
3120
- var projectionSchema = z.union([z.string(), z.array(z.string()), projectionObjectSchema]);
3121
- var sortOrderSchema = z.union([
3122
- z.literal(-1),
3123
- z.literal(1),
3124
- z.literal("asc"),
3125
- z.literal("ascending"),
3126
- z.literal("desc"),
3127
- z.literal("descending")
3375
+ import { z as z2 } from "zod";
3376
+ var stringOrStringArray = z2.union([z2.string(), z2.array(z2.string())]);
3377
+ var queryBooleanString = z2.enum(["true", "false"]);
3378
+ var positiveIntegerString = z2.string().regex(/^\d+$/, "Expected a non-negative integer");
3379
+ var nonNegativeIntegerSchema = z2.number().int().min(0);
3380
+ var unknownRecord = z2.record(z2.string(), z2.unknown());
3381
+ var projectionObjectSchema = z2.record(z2.string(), z2.union([z2.literal(1), z2.literal(-1)]));
3382
+ var projectionSchema = z2.union([z2.string(), z2.array(z2.string()), projectionObjectSchema]);
3383
+ var sortOrderSchema = z2.union([
3384
+ z2.literal(-1),
3385
+ z2.literal(1),
3386
+ z2.literal("asc"),
3387
+ z2.literal("ascending"),
3388
+ z2.literal("desc"),
3389
+ z2.literal("descending")
3128
3390
  ]);
3129
- var sortSchema = z.union([
3130
- z.string(),
3131
- z.record(z.string(), sortOrderSchema),
3132
- z.array(z.tuple([z.string(), sortOrderSchema])),
3133
- z.null()
3391
+ var sortSchema = z2.union([
3392
+ z2.string(),
3393
+ z2.record(z2.string(), sortOrderSchema),
3394
+ z2.array(z2.tuple([z2.string(), sortOrderSchema])),
3395
+ z2.null()
3134
3396
  ]);
3135
- var populateSchema = z.union([
3136
- z.string(),
3137
- z.array(
3138
- z.union([
3139
- z.string(),
3140
- z.object({
3141
- path: z.string().min(1),
3397
+ var populateSchema = z2.union([
3398
+ z2.string(),
3399
+ z2.array(
3400
+ z2.union([
3401
+ z2.string(),
3402
+ z2.object({
3403
+ path: z2.string().min(1),
3142
3404
  select: projectionSchema.optional(),
3143
- match: z.unknown().optional(),
3144
- access: z.enum(["list", "read"]).optional()
3405
+ match: z2.unknown().optional(),
3406
+ access: z2.enum(["list", "read"]).optional()
3145
3407
  }).passthrough()
3146
3408
  ])
3147
3409
  ),
3148
- z.object({
3149
- path: z.string().min(1),
3410
+ z2.object({
3411
+ path: z2.string().min(1),
3150
3412
  select: projectionSchema.optional(),
3151
- match: z.unknown().optional(),
3152
- access: z.enum(["list", "read"]).optional()
3413
+ match: z2.unknown().optional(),
3414
+ access: z2.enum(["list", "read"]).optional()
3153
3415
  }).passthrough()
3154
3416
  ]);
3155
- var subPopulateSchema = z.union([
3156
- z.string(),
3157
- z.array(
3158
- z.union([
3159
- z.string(),
3160
- z.object({
3161
- path: z.string().min(1),
3417
+ var subPopulateSchema = z2.union([
3418
+ z2.string(),
3419
+ z2.array(
3420
+ z2.union([
3421
+ z2.string(),
3422
+ z2.object({
3423
+ path: z2.string().min(1),
3162
3424
  select: projectionSchema.optional()
3163
3425
  }).passthrough()
3164
3426
  ])
3165
3427
  ),
3166
- z.object({
3167
- path: z.string().min(1),
3428
+ z2.object({
3429
+ path: z2.string().min(1),
3168
3430
  select: projectionSchema.optional()
3169
3431
  }).passthrough()
3170
3432
  ]);
3171
- var includeItemSchema = z.object({
3172
- model: z.string().min(1),
3173
- op: z.enum(["list", "read", "count"]),
3174
- path: z.string().min(1),
3175
- filter: z.record(z.string(), z.unknown()).optional(),
3176
- localField: z.string().min(1),
3177
- foreignField: z.string().min(1),
3178
- args: z.unknown().optional(),
3179
- options: z.unknown().optional()
3433
+ var includeItemSchema = z2.object({
3434
+ model: z2.string().min(1),
3435
+ op: z2.enum(["list", "read", "count"]),
3436
+ path: z2.string().min(1),
3437
+ filter: z2.record(z2.string(), z2.unknown()).optional(),
3438
+ localField: z2.string().min(1),
3439
+ foreignField: z2.string().min(1),
3440
+ args: z2.unknown().optional(),
3441
+ options: z2.unknown().optional()
3180
3442
  }).passthrough();
3181
- var includeSchema = z.union([includeItemSchema, z.array(includeItemSchema)]);
3182
- var fieldsSchema = z.array(z.string().min(1));
3183
- var taskSchema = z.object({
3184
- type: z.string().min(1),
3185
- args: z.unknown(),
3443
+ var includeSchema = z2.union([includeItemSchema, z2.array(includeItemSchema)]);
3444
+ var fieldsSchema = z2.array(z2.string().min(1));
3445
+ var taskSchema = z2.object({
3446
+ type: z2.string().min(1),
3447
+ args: z2.unknown(),
3186
3448
  options: unknownRecord.optional()
3187
3449
  });
3188
- var tasksSchema = z.union([taskSchema, z.array(taskSchema)]);
3189
- var objectOrArraySchema = z.union([z.record(z.string(), z.unknown()), z.array(z.unknown())]);
3450
+ var tasksSchema = z2.union([taskSchema, z2.array(taskSchema)]);
3451
+ var objectOrArraySchema = z2.union([z2.record(z2.string(), z2.unknown()), z2.array(z2.unknown())]);
3190
3452
  function rejectKeys(body, ctx, keys2) {
3191
3453
  for (const key of keys2) {
3192
3454
  if (key in body) {
3193
3455
  ctx.addIssue({
3194
- code: z.ZodIssueCode.custom,
3456
+ code: z2.ZodIssueCode.custom,
3195
3457
  message: `Unsupported field: ${key}`,
3196
3458
  path: [key]
3197
3459
  });
@@ -3204,7 +3466,7 @@ var clientErrors = JsonRouter4.clientErrors;
3204
3466
  function parsePathParam(value, parameter) {
3205
3467
  const result = stringOrStringArray.safeParse(value);
3206
3468
  if (!result.success) {
3207
- throwValidationError(result.error.issues, parameter, "parameter");
3469
+ throwValidationError(normalizeIssues(result.error.issues), parameter, "parameter");
3208
3470
  }
3209
3471
  const param = Array.isArray(result.data) ? result.data[0] : result.data;
3210
3472
  if (!param) {
@@ -3217,49 +3479,336 @@ function parsePathParam(value, parameter) {
3217
3479
  function parseQuery(schema, value) {
3218
3480
  const result = schema.safeParse(value);
3219
3481
  if (!result.success) {
3220
- throwValidationError(result.error.issues, void 0, "parameter");
3482
+ throwValidationError(normalizeIssues(result.error.issues), void 0, "parameter");
3221
3483
  }
3222
3484
  return result.data;
3223
3485
  }
3224
3486
  function parseBody(schema, value) {
3225
3487
  const result = schema.safeParse(value ?? {});
3226
3488
  if (!result.success) {
3227
- throwValidationError(result.error.issues, void 0, "pointer");
3489
+ throwValidationError(normalizeIssues(result.error.issues), void 0, "pointer");
3228
3490
  }
3229
3491
  return result.data;
3230
3492
  }
3231
3493
  function parseBodyWithSchema(schema, value, userSchema) {
3232
3494
  const body = parseBody(schema, value);
3233
- return isZodSchema(userSchema) ? parseUserSchema(userSchema, body) : body;
3495
+ return isRequestSchema(userSchema) ? parseUserSchema(userSchema, body) : Promise.resolve(body);
3234
3496
  }
3235
- function parseNestedBodyWithSchema(schema, value, nestedKey, userSchema) {
3497
+ async function parseNestedBodyWithSchema(schema, value, nestedKey, userSchema) {
3236
3498
  const body = parseBody(schema, value);
3237
- if (!isZodSchema(userSchema)) return body;
3499
+ if (!isRequestSchema(userSchema)) return body;
3238
3500
  return {
3239
3501
  ...body,
3240
- [nestedKey]: parseUserSchema(userSchema, body?.[nestedKey], [nestedKey])
3502
+ [nestedKey]: await parseUserSchema(userSchema, body?.[nestedKey], [nestedKey])
3241
3503
  };
3242
3504
  }
3243
3505
  function throwValidationError(issues, key, location = "pointer") {
3244
3506
  const errors = issues.map((issue) => formatIssue(issue, key, location));
3245
3507
  throw new clientErrors.BadRequestError("Bad Request", { errors });
3246
3508
  }
3247
- function parseUserSchema(schema, value, prefix = []) {
3248
- const result = schema.safeParse(value);
3249
- if (!result.success) {
3250
- const issues = result.error.issues.map((issue) => ({
3251
- ...issue,
3252
- path: prefix.concat(issue.path.map(String))
3253
- }));
3254
- throwValidationError(issues, void 0, "pointer");
3255
- }
3256
- return result.data;
3509
+ async function parseUserSchema(schema, value, prefix = []) {
3510
+ const validator = toRequestSchemaValidator(schema);
3511
+ const result = await validator(value);
3512
+ if (!isRequestSchemaFailure(result)) {
3513
+ return result.data;
3514
+ }
3515
+ const issues = result.issues.map((issue) => ({
3516
+ ...issue,
3517
+ path: prefix.concat((issue.path ?? []).map(String))
3518
+ }));
3519
+ throwValidationError(issues, void 0, "pointer");
3257
3520
  }
3258
3521
  function isZodSchema(schema) {
3259
3522
  return typeof schema === "object" && schema !== null && "safeParse" in schema && typeof schema.safeParse === "function";
3260
3523
  }
3524
+ function isRequestSchema(schema) {
3525
+ return isZodSchema(schema) || isStandardSchema(schema) || isRequestSchemaValidator(schema) || isRequestSchemaAdapter(schema);
3526
+ }
3527
+ function isRequestSchemaValidator(schema) {
3528
+ return typeof schema === "function";
3529
+ }
3530
+ function isRequestSchemaAdapter(schema) {
3531
+ return typeof schema === "object" && schema !== null && "validate" in schema && typeof schema.validate === "function";
3532
+ }
3533
+ function isStandardSchema(schema) {
3534
+ return typeof schema === "object" && schema !== null && "~standard" in schema && typeof schema["~standard"] === "object" && schema["~standard"] !== null && "validate" in schema["~standard"] && typeof schema["~standard"].validate === "function";
3535
+ }
3536
+ function isRequestSchemaFailure(result) {
3537
+ return result.success === false;
3538
+ }
3539
+ function toRequestSchemaValidator(schema) {
3540
+ if (isZodSchema(schema)) return fromZod(schema);
3541
+ if (isStandardSchema(schema)) return fromStandardSchema(schema);
3542
+ if (isRequestSchemaValidator(schema)) return schema;
3543
+ return schema.validate;
3544
+ }
3545
+ function defineRequestSchema(validator, options = {}) {
3546
+ return {
3547
+ validate: validator,
3548
+ openapi: options.openapi
3549
+ };
3550
+ }
3551
+ function fromZod(schema) {
3552
+ return (value) => {
3553
+ const result = schema.safeParse(value);
3554
+ if (result.success) {
3555
+ return {
3556
+ success: true,
3557
+ data: result.data
3558
+ };
3559
+ }
3560
+ return {
3561
+ success: false,
3562
+ issues: normalizeIssues(result.error.issues)
3563
+ };
3564
+ };
3565
+ }
3566
+ function fromStandardSchema(schema) {
3567
+ return async (value) => {
3568
+ const result = await schema["~standard"].validate(value);
3569
+ if (!isStandardSchemaFailure(result)) {
3570
+ return {
3571
+ success: true,
3572
+ data: result.value
3573
+ };
3574
+ }
3575
+ return {
3576
+ success: false,
3577
+ issues: normalizeIssues(result.issues)
3578
+ };
3579
+ };
3580
+ }
3581
+ function fromYup(schema) {
3582
+ return async (value) => {
3583
+ try {
3584
+ const data = await schema.validate(value, { abortEarly: false });
3585
+ return {
3586
+ success: true,
3587
+ data
3588
+ };
3589
+ } catch (error) {
3590
+ return {
3591
+ success: false,
3592
+ issues: normalizeYupIssues(error)
3593
+ };
3594
+ }
3595
+ };
3596
+ }
3597
+ function fromJoi(schema) {
3598
+ return async (value) => {
3599
+ const result = await schema.validate(value, { abortEarly: false });
3600
+ if (!result.error?.details?.length) {
3601
+ return {
3602
+ success: true,
3603
+ data: result.value
3604
+ };
3605
+ }
3606
+ return {
3607
+ success: false,
3608
+ issues: normalizeJoiIssues(result.error.details)
3609
+ };
3610
+ };
3611
+ }
3612
+ function fromAjv(validator) {
3613
+ return async (value) => {
3614
+ const valid = await validator(value);
3615
+ if (valid) {
3616
+ return {
3617
+ success: true,
3618
+ data: value
3619
+ };
3620
+ }
3621
+ return {
3622
+ success: false,
3623
+ issues: normalizeAjvIssues(validator.errors ?? [])
3624
+ };
3625
+ };
3626
+ }
3627
+ function fromValibot(schema, safeParse) {
3628
+ return async (value) => {
3629
+ const result = await safeParse(schema, value, { abortEarly: false });
3630
+ if (result.success) {
3631
+ return {
3632
+ success: true,
3633
+ data: result.output
3634
+ };
3635
+ }
3636
+ return {
3637
+ success: false,
3638
+ issues: normalizeValibotIssues(result.issues)
3639
+ };
3640
+ };
3641
+ }
3642
+ function fromArkType(type) {
3643
+ return async (value) => {
3644
+ const result = await type(value);
3645
+ if (!isArkTypeErrors(result)) {
3646
+ return {
3647
+ success: true,
3648
+ data: result
3649
+ };
3650
+ }
3651
+ return {
3652
+ success: false,
3653
+ issues: normalizeArkTypeIssues(result)
3654
+ };
3655
+ };
3656
+ }
3657
+ function fromIoTs(decoder) {
3658
+ return async (value) => {
3659
+ const result = decoder.decode(value);
3660
+ if (result._tag === "Right") {
3661
+ return {
3662
+ success: true,
3663
+ data: result.right
3664
+ };
3665
+ }
3666
+ return {
3667
+ success: false,
3668
+ issues: normalizeIoTsIssues(result.left)
3669
+ };
3670
+ };
3671
+ }
3672
+ function fromSuperstruct(struct, validate) {
3673
+ return async (value) => {
3674
+ const [failure, output] = await validate(value, struct);
3675
+ if (!failure) {
3676
+ return {
3677
+ success: true,
3678
+ data: output
3679
+ };
3680
+ }
3681
+ return {
3682
+ success: false,
3683
+ issues: normalizeSuperstructFailure(failure)
3684
+ };
3685
+ };
3686
+ }
3687
+ function fromVine(validator) {
3688
+ return async (value) => {
3689
+ try {
3690
+ const output = await validator.validate(value);
3691
+ return {
3692
+ success: true,
3693
+ data: output
3694
+ };
3695
+ } catch (error) {
3696
+ return {
3697
+ success: false,
3698
+ issues: normalizeVineError(error)
3699
+ };
3700
+ }
3701
+ };
3702
+ }
3703
+ function isStandardSchemaFailure(result) {
3704
+ return Array.isArray(result.issues);
3705
+ }
3706
+ function normalizeIssues(issues) {
3707
+ return issues.map((issue) => ({
3708
+ message: issue.message,
3709
+ path: issue.path?.flatMap((segment) => normalizePathSegment(segment))
3710
+ }));
3711
+ }
3712
+ function normalizePathSegment(segment) {
3713
+ const key = isStandardSchemaPathSegment(segment) ? segment.key : segment;
3714
+ return typeof key === "string" || typeof key === "number" ? [key] : [];
3715
+ }
3716
+ function isStandardSchemaPathSegment(segment) {
3717
+ return typeof segment === "object" && segment !== null && "key" in segment;
3718
+ }
3719
+ function normalizeYupIssues(error) {
3720
+ if (!isYupValidationError(error)) {
3721
+ return [{ message: "Validation failed" }];
3722
+ }
3723
+ const issues = error.inner?.length ? error.inner : [error];
3724
+ return issues.map((issue) => ({
3725
+ message: issue.message,
3726
+ path: parsePathString(issue.path)
3727
+ }));
3728
+ }
3729
+ function normalizeJoiIssues(issues) {
3730
+ return issues.map((issue) => ({
3731
+ message: issue.message,
3732
+ path: issue.path ? [...issue.path] : void 0
3733
+ }));
3734
+ }
3735
+ function normalizeAjvIssues(issues) {
3736
+ return issues.map((issue) => ({
3737
+ message: issue.message ?? "Validation failed",
3738
+ path: parseAjvPath(issue)
3739
+ }));
3740
+ }
3741
+ function normalizeValibotIssues(issues) {
3742
+ return issues.map((issue) => ({
3743
+ message: issue.message,
3744
+ path: issue.path?.flatMap(
3745
+ (item) => typeof item.key === "string" || typeof item.key === "number" ? [item.key] : []
3746
+ )
3747
+ }));
3748
+ }
3749
+ function normalizeArkTypeIssues(issues) {
3750
+ const normalized = issues.map((issue) => ({
3751
+ message: issue.message ?? issue.problem ?? issues.summary ?? "Validation failed",
3752
+ path: issue.path ? [...issue.path] : void 0
3753
+ }));
3754
+ return normalized.length ? normalized : [{ message: issues.summary ?? "Validation failed" }];
3755
+ }
3756
+ function normalizeIoTsIssues(issues) {
3757
+ return issues.map((issue) => ({
3758
+ message: issue.message ?? "Validation failed",
3759
+ path: issue.context.map((entry) => entry.key).filter(Boolean)
3760
+ }));
3761
+ }
3762
+ function normalizeSuperstructFailure(failure) {
3763
+ const failures = failure.failures?.() ?? [failure];
3764
+ return failures.map((entry) => ({
3765
+ message: entry.message ?? "Validation failed",
3766
+ path: normalizeSuperstructPath(entry)
3767
+ }));
3768
+ }
3769
+ function normalizeSuperstructPath(failure) {
3770
+ if (failure.path?.length) return [...failure.path];
3771
+ if (typeof failure.key === "string" || typeof failure.key === "number") return [failure.key];
3772
+ return void 0;
3773
+ }
3774
+ function normalizeVineError(error) {
3775
+ if (!isVineValidationError(error)) {
3776
+ return [{ message: "Validation failed" }];
3777
+ }
3778
+ return error.messages.map((message) => ({
3779
+ message: message.message,
3780
+ path: normalizeVineField(message)
3781
+ }));
3782
+ }
3783
+ function normalizeVineField(message) {
3784
+ const path = message.field.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean).map((segment) => /^\d+$/.test(segment) ? Number(segment) : segment);
3785
+ if (typeof message.index === "number" && path.length === 0) {
3786
+ return [message.index];
3787
+ }
3788
+ return path.length ? path : void 0;
3789
+ }
3790
+ function parseAjvPath(issue) {
3791
+ const path = issue.instancePath?.split("/").filter(Boolean).map((segment) => /^\d+$/.test(segment) ? Number(segment) : segment);
3792
+ if (issue.params?.missingProperty) {
3793
+ return (path ?? []).concat(issue.params.missingProperty);
3794
+ }
3795
+ return path;
3796
+ }
3797
+ function parsePathString(path) {
3798
+ if (!path) return void 0;
3799
+ return path.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean).map((segment) => /^\d+$/.test(segment) ? Number(segment) : segment);
3800
+ }
3801
+ function isYupValidationError(error) {
3802
+ return typeof error === "object" && error !== null && "message" in error;
3803
+ }
3804
+ function isArkTypeErrors(value) {
3805
+ return Array.isArray(value);
3806
+ }
3807
+ function isVineValidationError(error) {
3808
+ return typeof error === "object" && error !== null && "messages" in error && Array.isArray(error.messages);
3809
+ }
3261
3810
  function formatIssue(issue, key, location = "pointer") {
3262
- const path = issue.path.map(String);
3811
+ const path = (issue.path ?? []).map(String);
3263
3812
  const joinedPath = path.join(".");
3264
3813
  if (location === "parameter") {
3265
3814
  return {
@@ -3277,8 +3826,8 @@ function buildPointer(path) {
3277
3826
  }
3278
3827
 
3279
3828
  // src/validation/model-router.ts
3280
- import { z as z2 } from "zod";
3281
- var listQuerySchema = z2.object({
3829
+ import { z as z3 } from "zod";
3830
+ var listQuerySchema = z3.object({
3282
3831
  skip: positiveIntegerString.optional(),
3283
3832
  limit: positiveIntegerString.optional(),
3284
3833
  page: positiveIntegerString.optional(),
@@ -3288,122 +3837,122 @@ var listQuerySchema = z2.object({
3288
3837
  include_count: queryBooleanString.optional(),
3289
3838
  include_extra_headers: queryBooleanString.optional()
3290
3839
  }).passthrough();
3291
- var createQuerySchema = z2.object({
3840
+ var createQuerySchema = z3.object({
3292
3841
  include_permissions: queryBooleanString.optional()
3293
3842
  }).passthrough();
3294
- var readQuerySchema = z2.object({
3843
+ var readQuerySchema = z3.object({
3295
3844
  include_permissions: queryBooleanString.optional(),
3296
3845
  try_list: queryBooleanString.optional()
3297
3846
  }).passthrough();
3298
- var updateQuerySchema = z2.object({
3847
+ var updateQuerySchema = z3.object({
3299
3848
  returning_all: queryBooleanString.optional(),
3300
3849
  include_permissions: queryBooleanString.optional()
3301
3850
  }).passthrough();
3302
- var upsertQuerySchema = z2.object({
3851
+ var upsertQuerySchema = z3.object({
3303
3852
  returning_all: queryBooleanString.optional(),
3304
3853
  include_permissions: queryBooleanString.optional()
3305
3854
  }).passthrough();
3306
- var listBodySchema = z2.object({
3855
+ var listBodySchema = z3.object({
3307
3856
  filter: objectOrArraySchema.optional(),
3308
3857
  select: projectionSchema.optional(),
3309
3858
  sort: sortSchema.optional(),
3310
3859
  populate: populateSchema.optional(),
3311
3860
  include: includeSchema.optional(),
3312
3861
  tasks: tasksSchema.optional(),
3313
- skip: z2.union([nonNegativeIntegerSchema, positiveIntegerString]).optional(),
3314
- limit: z2.union([nonNegativeIntegerSchema, positiveIntegerString]).optional(),
3315
- page: z2.union([nonNegativeIntegerSchema, positiveIntegerString]).optional(),
3316
- pageSize: z2.union([nonNegativeIntegerSchema, positiveIntegerString]).optional(),
3317
- options: z2.object({
3318
- skim: z2.boolean().optional(),
3319
- includePermissions: z2.boolean().optional(),
3320
- includeCount: z2.boolean().optional(),
3321
- includeExtraHeaders: z2.boolean().optional(),
3322
- populateAccess: z2.unknown().optional()
3862
+ skip: z3.union([nonNegativeIntegerSchema, positiveIntegerString]).optional(),
3863
+ limit: z3.union([nonNegativeIntegerSchema, positiveIntegerString]).optional(),
3864
+ page: z3.union([nonNegativeIntegerSchema, positiveIntegerString]).optional(),
3865
+ pageSize: z3.union([nonNegativeIntegerSchema, positiveIntegerString]).optional(),
3866
+ options: z3.object({
3867
+ skim: z3.boolean().optional(),
3868
+ includePermissions: z3.boolean().optional(),
3869
+ includeCount: z3.boolean().optional(),
3870
+ includeExtraHeaders: z3.boolean().optional(),
3871
+ populateAccess: z3.unknown().optional()
3323
3872
  }).passthrough().optional()
3324
3873
  }).passthrough().superRefine((body, ctx) => rejectKeys(body, ctx, ["query"]));
3325
- var countBodySchema = z2.object({
3874
+ var countBodySchema = z3.object({
3326
3875
  filter: objectOrArraySchema.optional(),
3327
- options: z2.object({
3328
- access: z2.unknown().optional()
3876
+ options: z3.object({
3877
+ access: z3.unknown().optional()
3329
3878
  }).passthrough().optional()
3330
3879
  }).passthrough().superRefine((body, ctx) => rejectKeys(body, ctx, ["query", "access"]));
3331
- var readFilterBodySchema = z2.object({
3880
+ var readFilterBodySchema = z3.object({
3332
3881
  filter: objectOrArraySchema.optional(),
3333
3882
  select: projectionSchema.optional(),
3334
3883
  sort: sortSchema.optional(),
3335
3884
  populate: populateSchema.optional(),
3336
3885
  include: includeSchema.optional(),
3337
3886
  tasks: tasksSchema.optional(),
3338
- options: z2.object({
3339
- skim: z2.boolean().optional(),
3340
- includePermissions: z2.boolean().optional(),
3341
- tryList: z2.boolean().optional(),
3342
- populateAccess: z2.unknown().optional()
3887
+ options: z3.object({
3888
+ skim: z3.boolean().optional(),
3889
+ includePermissions: z3.boolean().optional(),
3890
+ tryList: z3.boolean().optional(),
3891
+ populateAccess: z3.unknown().optional()
3343
3892
  }).passthrough().optional()
3344
3893
  }).passthrough();
3345
- var readByIdBodySchema = z2.object({
3894
+ var readByIdBodySchema = z3.object({
3346
3895
  select: projectionSchema.optional(),
3347
3896
  populate: populateSchema.optional(),
3348
3897
  include: includeSchema.optional(),
3349
3898
  tasks: tasksSchema.optional(),
3350
- options: z2.object({
3351
- skim: z2.boolean().optional(),
3352
- includePermissions: z2.boolean().optional(),
3353
- tryList: z2.boolean().optional(),
3354
- populateAccess: z2.unknown().optional()
3899
+ options: z3.object({
3900
+ skim: z3.boolean().optional(),
3901
+ includePermissions: z3.boolean().optional(),
3902
+ tryList: z3.boolean().optional(),
3903
+ populateAccess: z3.unknown().optional()
3355
3904
  }).passthrough().optional()
3356
3905
  }).passthrough();
3357
- var advancedCreateBodySchema = z2.object({
3358
- data: z2.unknown(),
3906
+ var advancedCreateBodySchema = z3.object({
3907
+ data: z3.unknown(),
3359
3908
  select: projectionSchema.optional(),
3360
3909
  populate: populateSchema.optional(),
3361
3910
  tasks: tasksSchema.optional(),
3362
- options: z2.object({
3363
- includePermissions: z2.boolean().optional(),
3364
- populateAccess: z2.unknown().optional()
3911
+ options: z3.object({
3912
+ includePermissions: z3.boolean().optional(),
3913
+ populateAccess: z3.unknown().optional()
3365
3914
  }).passthrough().optional()
3366
3915
  }).passthrough();
3367
- var createBodySchema = z2.union([
3368
- z2.record(z2.string(), z2.unknown()),
3369
- z2.array(z2.record(z2.string(), z2.unknown()))
3916
+ var createBodySchema = z3.union([
3917
+ z3.record(z3.string(), z3.unknown()),
3918
+ z3.array(z3.record(z3.string(), z3.unknown()))
3370
3919
  ]);
3371
- var updateBodySchema = z2.record(z2.string(), z2.unknown());
3372
- var advancedUpdateBodySchema = z2.object({
3373
- data: z2.unknown(),
3920
+ var updateBodySchema = z3.record(z3.string(), z3.unknown());
3921
+ var advancedUpdateBodySchema = z3.object({
3922
+ data: z3.unknown(),
3374
3923
  select: projectionSchema.optional(),
3375
3924
  populate: populateSchema.optional(),
3376
3925
  tasks: tasksSchema.optional(),
3377
- options: z2.object({
3378
- returningAll: z2.boolean().optional(),
3379
- includePermissions: z2.boolean().optional(),
3380
- populateAccess: z2.unknown().optional()
3926
+ options: z3.object({
3927
+ returningAll: z3.boolean().optional(),
3928
+ includePermissions: z3.boolean().optional(),
3929
+ populateAccess: z3.unknown().optional()
3381
3930
  }).passthrough().optional()
3382
3931
  }).passthrough();
3383
- var upsertBodySchema = z2.record(z2.string(), z2.unknown());
3384
- var advancedUpsertBodySchema = z2.object({
3385
- data: z2.record(z2.string(), z2.unknown()),
3932
+ var upsertBodySchema = z3.record(z3.string(), z3.unknown());
3933
+ var advancedUpsertBodySchema = z3.object({
3934
+ data: z3.record(z3.string(), z3.unknown()),
3386
3935
  select: projectionSchema.optional(),
3387
3936
  populate: populateSchema.optional(),
3388
3937
  tasks: tasksSchema.optional(),
3389
- options: z2.object({
3390
- returningAll: z2.boolean().optional(),
3391
- includePermissions: z2.boolean().optional(),
3392
- populateAccess: z2.unknown().optional()
3938
+ options: z3.object({
3939
+ returningAll: z3.boolean().optional(),
3940
+ includePermissions: z3.boolean().optional(),
3941
+ populateAccess: z3.unknown().optional()
3393
3942
  }).passthrough().optional()
3394
3943
  }).passthrough();
3395
- var distinctBodySchema = z2.object({
3944
+ var distinctBodySchema = z3.object({
3396
3945
  filter: objectOrArraySchema.optional()
3397
3946
  }).passthrough().superRefine((body, ctx) => rejectKeys(body, ctx, ["query"]));
3398
- var subListBodySchema = z2.object({
3399
- filter: z2.record(z2.string(), z2.unknown()).optional(),
3947
+ var subListBodySchema = z3.object({
3948
+ filter: z3.record(z3.string(), z3.unknown()).optional(),
3400
3949
  select: fieldsSchema.optional()
3401
3950
  }).passthrough().superRefine((body, ctx) => rejectKeys(body, ctx, ["fields"]));
3402
- var subMutationBodySchema = z2.union([
3403
- z2.record(z2.string(), z2.unknown()),
3404
- z2.array(z2.record(z2.string(), z2.unknown()))
3951
+ var subMutationBodySchema = z3.union([
3952
+ z3.record(z3.string(), z3.unknown()),
3953
+ z3.array(z3.record(z3.string(), z3.unknown()))
3405
3954
  ]);
3406
- var subReadBodySchema = z2.object({
3955
+ var subReadBodySchema = z3.object({
3407
3956
  select: fieldsSchema.optional(),
3408
3957
  populate: subPopulateSchema.optional()
3409
3958
  }).passthrough().superRefine((body, ctx) => rejectKeys(body, ctx, ["fields"]));
@@ -3416,54 +3965,54 @@ var requestSchemas = {
3416
3965
  };
3417
3966
 
3418
3967
  // src/validation/data-router.ts
3419
- import { z as z3 } from "zod";
3420
- var dataListBodySchema = z3.object({
3968
+ import { z as z4 } from "zod";
3969
+ var dataListBodySchema = z4.object({
3421
3970
  filter: objectOrArraySchema.optional(),
3422
3971
  select: projectionSchema.optional(),
3423
- sort: z3.string().optional(),
3424
- skip: z3.union([nonNegativeIntegerSchema, positiveIntegerString]).optional(),
3425
- limit: z3.union([nonNegativeIntegerSchema, positiveIntegerString]).optional(),
3426
- page: z3.union([nonNegativeIntegerSchema, positiveIntegerString]).optional(),
3427
- pageSize: z3.union([nonNegativeIntegerSchema, positiveIntegerString]).optional(),
3428
- options: z3.object({
3429
- includeCount: z3.boolean().optional(),
3430
- includeExtraHeaders: z3.boolean().optional()
3972
+ sort: z4.string().optional(),
3973
+ skip: z4.union([nonNegativeIntegerSchema, positiveIntegerString]).optional(),
3974
+ limit: z4.union([nonNegativeIntegerSchema, positiveIntegerString]).optional(),
3975
+ page: z4.union([nonNegativeIntegerSchema, positiveIntegerString]).optional(),
3976
+ pageSize: z4.union([nonNegativeIntegerSchema, positiveIntegerString]).optional(),
3977
+ options: z4.object({
3978
+ includeCount: z4.boolean().optional(),
3979
+ includeExtraHeaders: z4.boolean().optional()
3431
3980
  }).passthrough().optional()
3432
3981
  }).passthrough();
3433
- var dataReadFilterBodySchema = z3.object({
3982
+ var dataReadFilterBodySchema = z4.object({
3434
3983
  filter: objectOrArraySchema.optional(),
3435
3984
  select: projectionSchema.optional()
3436
3985
  }).passthrough().superRefine((body, ctx) => rejectKeys(body, ctx, ["options"]));
3437
- var dataReadByIdBodySchema = z3.object({
3986
+ var dataReadByIdBodySchema = z4.object({
3438
3987
  select: projectionSchema.optional()
3439
3988
  }).passthrough().superRefine((body, ctx) => rejectKeys(body, ctx, ["options"]));
3440
3989
 
3441
3990
  // src/validation/root-router.ts
3442
- import { z as z4 } from "zod";
3991
+ import { z as z5 } from "zod";
3443
3992
  var rootEntryBaseSchema = {
3444
- target: z4.enum(["model", "data"]),
3445
- name: z4.string().min(1),
3446
- order: z4.number().int().optional()
3993
+ target: z5.enum(["model", "data"]),
3994
+ name: z5.string().min(1),
3995
+ order: z5.number().int().optional()
3447
3996
  };
3448
- var rootModelListArgsSchema = z4.object({
3997
+ var rootModelListArgsSchema = z5.object({
3449
3998
  select: projectionSchema.optional(),
3450
3999
  populate: populateSchema.optional(),
3451
4000
  include: includeSchema.optional(),
3452
4001
  sort: sortSchema.optional(),
3453
- skip: z4.union([nonNegativeIntegerSchema, positiveIntegerString]).optional(),
3454
- limit: z4.union([nonNegativeIntegerSchema, positiveIntegerString]).optional(),
3455
- page: z4.union([nonNegativeIntegerSchema, positiveIntegerString]).optional(),
3456
- pageSize: z4.union([nonNegativeIntegerSchema, positiveIntegerString]).optional(),
4002
+ skip: z5.union([nonNegativeIntegerSchema, positiveIntegerString]).optional(),
4003
+ limit: z5.union([nonNegativeIntegerSchema, positiveIntegerString]).optional(),
4004
+ page: z5.union([nonNegativeIntegerSchema, positiveIntegerString]).optional(),
4005
+ pageSize: z5.union([nonNegativeIntegerSchema, positiveIntegerString]).optional(),
3457
4006
  tasks: tasksSchema.optional()
3458
4007
  }).passthrough();
3459
- var rootModelListOptionsSchema = z4.object({
3460
- skim: z4.boolean().optional(),
3461
- includePermissions: z4.boolean().optional(),
3462
- includeCount: z4.boolean().optional(),
3463
- populateAccess: z4.unknown().optional(),
3464
- lean: z4.boolean().optional()
4008
+ var rootModelListOptionsSchema = z5.object({
4009
+ skim: z5.boolean().optional(),
4010
+ includePermissions: z5.boolean().optional(),
4011
+ includeCount: z5.boolean().optional(),
4012
+ populateAccess: z5.unknown().optional(),
4013
+ lean: z5.boolean().optional()
3465
4014
  }).passthrough();
3466
- var rootModelReadArgsSchema = z4.object({
4015
+ var rootModelReadArgsSchema = z5.object({
3467
4016
  select: projectionSchema.optional(),
3468
4017
  populate: populateSchema.optional(),
3469
4018
  include: includeSchema.optional(),
@@ -3472,210 +4021,219 @@ var rootModelReadArgsSchema = z4.object({
3472
4021
  var rootModelReadFilterArgsSchema = rootModelReadArgsSchema.extend({
3473
4022
  sort: sortSchema.optional()
3474
4023
  });
3475
- var rootModelReadOptionsSchema = z4.object({
3476
- skim: z4.boolean().optional(),
3477
- includePermissions: z4.boolean().optional(),
3478
- tryList: z4.boolean().optional(),
3479
- populateAccess: z4.unknown().optional(),
3480
- lean: z4.boolean().optional()
4024
+ var rootModelReadOptionsSchema = z5.object({
4025
+ skim: z5.boolean().optional(),
4026
+ includePermissions: z5.boolean().optional(),
4027
+ tryList: z5.boolean().optional(),
4028
+ populateAccess: z5.unknown().optional(),
4029
+ lean: z5.boolean().optional()
3481
4030
  }).passthrough();
3482
- var rootModelCreateArgsSchema = z4.object({
4031
+ var rootModelCreateArgsSchema = z5.object({
3483
4032
  select: projectionSchema.optional(),
3484
4033
  populate: populateSchema.optional(),
3485
4034
  tasks: tasksSchema.optional()
3486
4035
  }).passthrough();
3487
- var rootModelCreateOptionsSchema = z4.object({
3488
- skim: z4.boolean().optional(),
3489
- includePermissions: z4.boolean().optional(),
3490
- populateAccess: z4.unknown().optional()
4036
+ var rootModelCreateOptionsSchema = z5.object({
4037
+ skim: z5.boolean().optional(),
4038
+ includePermissions: z5.boolean().optional(),
4039
+ populateAccess: z5.unknown().optional()
3491
4040
  }).passthrough();
3492
- var rootModelUpdateArgsSchema = z4.object({
4041
+ var rootModelUpdateArgsSchema = z5.object({
3493
4042
  select: projectionSchema.optional(),
3494
4043
  populate: populateSchema.optional(),
3495
4044
  tasks: tasksSchema.optional()
3496
4045
  }).passthrough();
3497
- var rootModelUpdateOptionsSchema = z4.object({
3498
- skim: z4.boolean().optional(),
3499
- returningAll: z4.boolean().optional(),
3500
- includePermissions: z4.boolean().optional(),
3501
- populateAccess: z4.unknown().optional()
4046
+ var rootModelUpdateOptionsSchema = z5.object({
4047
+ skim: z5.boolean().optional(),
4048
+ returningAll: z5.boolean().optional(),
4049
+ includePermissions: z5.boolean().optional(),
4050
+ populateAccess: z5.unknown().optional()
3502
4051
  }).passthrough();
3503
4052
  var rootModelUpsertArgsSchema = rootModelUpdateArgsSchema;
3504
4053
  var rootModelUpsertOptionsSchema = rootModelUpdateOptionsSchema;
3505
- var rootModelSubListArgsSchema = z4.object({
4054
+ var rootModelSubListArgsSchema = z5.object({
3506
4055
  select: fieldsSchema.optional()
3507
4056
  }).passthrough();
3508
- var rootModelSubReadArgsSchema = z4.object({
4057
+ var rootModelSubReadArgsSchema = z5.object({
3509
4058
  select: fieldsSchema.optional(),
3510
4059
  populate: subPopulateSchema.optional()
3511
4060
  }).passthrough();
3512
- var rootModelCountOptionsSchema = z4.object({
3513
- access: z4.unknown().optional()
4061
+ var rootModelCountOptionsSchema = z5.object({
4062
+ access: z5.unknown().optional()
3514
4063
  }).passthrough();
3515
- var rootDataListArgsSchema = z4.object({
4064
+ var rootDataListArgsSchema = z5.object({
3516
4065
  select: projectionSchema.optional(),
3517
- sort: z4.string().optional(),
3518
- skip: z4.union([nonNegativeIntegerSchema, positiveIntegerString]).optional(),
3519
- limit: z4.union([nonNegativeIntegerSchema, positiveIntegerString]).optional(),
3520
- page: z4.union([nonNegativeIntegerSchema, positiveIntegerString]).optional(),
3521
- pageSize: z4.union([nonNegativeIntegerSchema, positiveIntegerString]).optional()
4066
+ sort: z5.string().optional(),
4067
+ skip: z5.union([nonNegativeIntegerSchema, positiveIntegerString]).optional(),
4068
+ limit: z5.union([nonNegativeIntegerSchema, positiveIntegerString]).optional(),
4069
+ page: z5.union([nonNegativeIntegerSchema, positiveIntegerString]).optional(),
4070
+ pageSize: z5.union([nonNegativeIntegerSchema, positiveIntegerString]).optional()
3522
4071
  }).passthrough();
3523
- var rootDataListOptionsSchema = z4.object({
3524
- includeCount: z4.boolean().optional()
4072
+ var rootDataListOptionsSchema = z5.object({
4073
+ includeCount: z5.boolean().optional()
3525
4074
  }).passthrough();
3526
- var rootDataReadArgsSchema = z4.object({
4075
+ var rootDataReadArgsSchema = z5.object({
3527
4076
  select: projectionSchema.optional()
3528
4077
  }).passthrough();
3529
- var rootModelQueryEntrySchema = z4.union([
3530
- z4.object({ ...rootEntryBaseSchema, target: z4.literal("model"), op: z4.literal("new") }).passthrough(),
3531
- z4.object({
4078
+ var rootModelQueryEntrySchema = z5.union([
4079
+ z5.object({ ...rootEntryBaseSchema, target: z5.literal("model"), op: z5.literal("new") }).passthrough(),
4080
+ z5.object({
3532
4081
  ...rootEntryBaseSchema,
3533
- target: z4.literal("model"),
3534
- op: z4.literal("list"),
4082
+ target: z5.literal("model"),
4083
+ op: z5.literal("list"),
3535
4084
  filter: objectOrArraySchema.optional(),
3536
4085
  args: rootModelListArgsSchema.optional(),
3537
4086
  options: rootModelListOptionsSchema.optional()
3538
4087
  }).passthrough(),
3539
- z4.object({
4088
+ z5.object({
3540
4089
  ...rootEntryBaseSchema,
3541
- target: z4.literal("model"),
3542
- op: z4.literal("read"),
3543
- id: z4.string().min(1),
4090
+ target: z5.literal("model"),
4091
+ op: z5.literal("read"),
4092
+ id: z5.string().min(1),
3544
4093
  args: rootModelReadArgsSchema.optional(),
3545
4094
  options: rootModelReadOptionsSchema.optional()
3546
4095
  }).passthrough(),
3547
- z4.object({
4096
+ z5.object({
3548
4097
  ...rootEntryBaseSchema,
3549
- target: z4.literal("model"),
3550
- op: z4.literal("read"),
4098
+ target: z5.literal("model"),
4099
+ op: z5.literal("read"),
3551
4100
  filter: objectOrArraySchema,
3552
4101
  args: rootModelReadFilterArgsSchema.optional(),
3553
4102
  options: rootModelReadOptionsSchema.optional()
3554
4103
  }).passthrough(),
3555
- z4.object({
4104
+ z5.object({
3556
4105
  ...rootEntryBaseSchema,
3557
- target: z4.literal("model"),
3558
- op: z4.literal("create"),
3559
- data: z4.unknown(),
4106
+ target: z5.literal("model"),
4107
+ op: z5.literal("create"),
4108
+ data: z5.unknown(),
3560
4109
  args: rootModelCreateArgsSchema.optional(),
3561
4110
  options: rootModelCreateOptionsSchema.optional()
3562
4111
  }).passthrough(),
3563
- z4.object({
4112
+ z5.object({
3564
4113
  ...rootEntryBaseSchema,
3565
- target: z4.literal("model"),
3566
- op: z4.literal("update"),
3567
- id: z4.string().min(1),
3568
- data: z4.unknown(),
4114
+ target: z5.literal("model"),
4115
+ op: z5.literal("update"),
4116
+ id: z5.string().min(1),
4117
+ data: z5.unknown(),
3569
4118
  args: rootModelUpdateArgsSchema.optional(),
3570
4119
  options: rootModelUpdateOptionsSchema.optional()
3571
4120
  }).passthrough(),
3572
- z4.object({
4121
+ z5.object({
3573
4122
  ...rootEntryBaseSchema,
3574
- target: z4.literal("model"),
3575
- op: z4.literal("upsert"),
3576
- data: z4.record(z4.string(), z4.unknown()),
4123
+ target: z5.literal("model"),
4124
+ op: z5.literal("upsert"),
4125
+ data: z5.record(z5.string(), z5.unknown()),
3577
4126
  args: rootModelUpsertArgsSchema.optional(),
3578
4127
  options: rootModelUpsertOptionsSchema.optional()
3579
4128
  }).passthrough(),
3580
- z4.object({ ...rootEntryBaseSchema, target: z4.literal("model"), op: z4.literal("delete"), id: z4.string().min(1) }).passthrough(),
3581
- z4.object({
4129
+ z5.object({ ...rootEntryBaseSchema, target: z5.literal("model"), op: z5.literal("delete"), id: z5.string().min(1) }).passthrough(),
4130
+ z5.object({
3582
4131
  ...rootEntryBaseSchema,
3583
- target: z4.literal("model"),
3584
- op: z4.literal("subList"),
3585
- id: z4.string().min(1),
3586
- sub: z4.string().min(1),
3587
- filter: z4.record(z4.string(), z4.unknown()).optional(),
4132
+ target: z5.literal("model"),
4133
+ op: z5.literal("subList"),
4134
+ id: z5.string().min(1),
4135
+ sub: z5.string().min(1),
4136
+ filter: z5.record(z5.string(), z5.unknown()).optional(),
3588
4137
  args: rootModelSubListArgsSchema.optional()
3589
4138
  }).passthrough(),
3590
- z4.object({
4139
+ z5.object({
3591
4140
  ...rootEntryBaseSchema,
3592
- target: z4.literal("model"),
3593
- op: z4.literal("subRead"),
3594
- id: z4.string().min(1),
3595
- sub: z4.string().min(1),
3596
- subId: z4.string().min(1),
4141
+ target: z5.literal("model"),
4142
+ op: z5.literal("subRead"),
4143
+ id: z5.string().min(1),
4144
+ sub: z5.string().min(1),
4145
+ subId: z5.string().min(1),
3597
4146
  args: rootModelSubReadArgsSchema.optional()
3598
4147
  }).passthrough(),
3599
- z4.object({
4148
+ z5.object({
3600
4149
  ...rootEntryBaseSchema,
3601
- target: z4.literal("model"),
3602
- op: z4.literal("subCreate"),
3603
- id: z4.string().min(1),
3604
- sub: z4.string().min(1),
3605
- data: z4.union([z4.record(z4.string(), z4.unknown()), z4.array(z4.record(z4.string(), z4.unknown()))])
4150
+ target: z5.literal("model"),
4151
+ op: z5.literal("subCreate"),
4152
+ id: z5.string().min(1),
4153
+ sub: z5.string().min(1),
4154
+ data: z5.union([z5.record(z5.string(), z5.unknown()), z5.array(z5.record(z5.string(), z5.unknown()))])
3606
4155
  }).passthrough(),
3607
- z4.object({
4156
+ z5.object({
3608
4157
  ...rootEntryBaseSchema,
3609
- target: z4.literal("model"),
3610
- op: z4.literal("subUpdate"),
3611
- id: z4.string().min(1),
3612
- sub: z4.string().min(1),
3613
- subId: z4.string().min(1),
3614
- data: z4.union([z4.record(z4.string(), z4.unknown()), z4.array(z4.record(z4.string(), z4.unknown()))])
4158
+ target: z5.literal("model"),
4159
+ op: z5.literal("subUpdate"),
4160
+ id: z5.string().min(1),
4161
+ sub: z5.string().min(1),
4162
+ subId: z5.string().min(1),
4163
+ data: z5.union([z5.record(z5.string(), z5.unknown()), z5.array(z5.record(z5.string(), z5.unknown()))])
3615
4164
  }).passthrough(),
3616
- z4.object({
4165
+ z5.object({
3617
4166
  ...rootEntryBaseSchema,
3618
- target: z4.literal("model"),
3619
- op: z4.literal("subBulkUpdate"),
3620
- id: z4.string().min(1),
3621
- sub: z4.string().min(1),
3622
- data: z4.union([z4.record(z4.string(), z4.unknown()), z4.array(z4.record(z4.string(), z4.unknown()))])
4167
+ target: z5.literal("model"),
4168
+ op: z5.literal("subBulkUpdate"),
4169
+ id: z5.string().min(1),
4170
+ sub: z5.string().min(1),
4171
+ data: z5.union([z5.record(z5.string(), z5.unknown()), z5.array(z5.record(z5.string(), z5.unknown()))])
3623
4172
  }).passthrough(),
3624
- z4.object({
4173
+ z5.object({
3625
4174
  ...rootEntryBaseSchema,
3626
- target: z4.literal("model"),
3627
- op: z4.literal("subDelete"),
3628
- id: z4.string().min(1),
3629
- sub: z4.string().min(1),
3630
- subId: z4.string().min(1)
4175
+ target: z5.literal("model"),
4176
+ op: z5.literal("subDelete"),
4177
+ id: z5.string().min(1),
4178
+ sub: z5.string().min(1),
4179
+ subId: z5.string().min(1)
3631
4180
  }).passthrough(),
3632
- z4.object({
4181
+ z5.object({
3633
4182
  ...rootEntryBaseSchema,
3634
- target: z4.literal("model"),
3635
- op: z4.literal("distinct"),
3636
- field: z4.string().min(1),
4183
+ target: z5.literal("model"),
4184
+ op: z5.literal("distinct"),
4185
+ field: z5.string().min(1),
3637
4186
  filter: objectOrArraySchema.optional()
3638
4187
  }).passthrough(),
3639
- z4.object({
4188
+ z5.object({
3640
4189
  ...rootEntryBaseSchema,
3641
- target: z4.literal("model"),
3642
- op: z4.literal("count"),
4190
+ target: z5.literal("model"),
4191
+ op: z5.literal("count"),
3643
4192
  filter: objectOrArraySchema.optional(),
3644
4193
  options: rootModelCountOptionsSchema.optional()
3645
4194
  }).passthrough()
3646
4195
  ]);
3647
- var rootDataQueryEntrySchema = z4.union([
3648
- z4.object({
4196
+ var rootDataQueryEntrySchema = z5.union([
4197
+ z5.object({
3649
4198
  ...rootEntryBaseSchema,
3650
- target: z4.literal("data"),
3651
- op: z4.literal("list"),
4199
+ target: z5.literal("data"),
4200
+ op: z5.literal("list"),
3652
4201
  filter: objectOrArraySchema.optional(),
3653
4202
  args: rootDataListArgsSchema.optional(),
3654
4203
  options: rootDataListOptionsSchema.optional()
3655
4204
  }).passthrough(),
3656
- z4.object({
4205
+ z5.object({
3657
4206
  ...rootEntryBaseSchema,
3658
- target: z4.literal("data"),
3659
- op: z4.literal("read"),
3660
- id: z4.string().min(1),
4207
+ target: z5.literal("data"),
4208
+ op: z5.literal("read"),
4209
+ id: z5.string().min(1),
3661
4210
  args: rootDataReadArgsSchema.optional()
3662
4211
  }).passthrough(),
3663
- z4.object({
4212
+ z5.object({
3664
4213
  ...rootEntryBaseSchema,
3665
- target: z4.literal("data"),
3666
- op: z4.literal("read"),
4214
+ target: z5.literal("data"),
4215
+ op: z5.literal("read"),
3667
4216
  filter: objectOrArraySchema,
3668
4217
  args: rootDataReadArgsSchema.optional()
3669
4218
  }).passthrough()
3670
4219
  ]);
3671
- var rootQuerySchema = z4.array(z4.union([rootModelQueryEntrySchema, rootDataQueryEntrySchema]));
4220
+ var rootQuerySchema = z5.array(z5.union([rootModelQueryEntrySchema, rootDataQueryEntrySchema]));
3672
4221
 
3673
4222
  // src/routers/shared.ts
3674
- var parseBooleanString = (str, defaultValue) => str ? str === "true" : defaultValue;
4223
+ import { parseBooleanString } from "@web-ts-toolkit/utils";
3675
4224
 
3676
4225
  // src/routers/model-router-collection-routes.ts
3677
4226
  function setModelCollectionRoutes(context) {
3678
4227
  const { router, options } = context;
4228
+ const getAdvancedCreateOpenApiBody = () => {
4229
+ const advancedCreateSchema = context.getRequestSchema("requestSchemas.advancedCreate");
4230
+ const dataSchema = context.getRequestSchema("requestSchemas.advancedCreate.data") ?? getNestedOpenApiSchemaDataSource(advancedCreateSchema);
4231
+ const bodySchema = unwrapNestedOpenApiSchemaSource(
4232
+ context.getRequestSchema("requestSchemas.advancedCreate.default") ?? advancedCreateSchema,
4233
+ advancedCreateBodySchema
4234
+ );
4235
+ return dataSchema ? patchOpenApiObjectSchema(bodySchema, { data: dataSchema }) : bodySchema;
4236
+ };
3679
4237
  router.get("", async (req) => {
3680
4238
  await context.assertAllowed(req, "list");
3681
4239
  const { skip, limit, page, page_size, skim, include_permissions, include_count, include_extra_headers } = parseQuery(requestSchemas.listQuery, req.query);
@@ -3694,9 +4252,16 @@ function setModelCollectionRoutes(context) {
3694
4252
  handleResultError(result);
3695
4253
  return formatModelListResponse(req, result, includeCount, includeExtraHeaders);
3696
4254
  });
4255
+ context.registerOpenApiRoute({
4256
+ method: "get",
4257
+ path: "",
4258
+ operationId: `${context.modelName}.list`,
4259
+ summary: `List ${context.modelName} documents`,
4260
+ query: requestSchemas.listQuery
4261
+ });
3697
4262
  router.post(`/${options.queryRouteSegment}`, async (req) => {
3698
4263
  await context.assertAllowed(req, "list");
3699
- const body = parseBodyWithSchema(
4264
+ const body = await parseBodyWithSchema(
3700
4265
  listBodySchema,
3701
4266
  req.body,
3702
4267
  context.getRequestSchema("requestSchemas.advancedList")
@@ -3719,19 +4284,38 @@ function setModelCollectionRoutes(context) {
3719
4284
  handleResultError(result);
3720
4285
  return formatModelListResponse(req, result, includeCount, includeExtraHeaders);
3721
4286
  });
4287
+ context.registerOpenApiRoute({
4288
+ method: "post",
4289
+ path: `/${options.queryRouteSegment}`,
4290
+ operationId: `${context.modelName}.listAdvanced`,
4291
+ summary: `Advanced list ${context.modelName} documents`,
4292
+ body: defineOpenApiSchemaResolver(() => context.getRequestSchema("requestSchemas.advancedList") ?? listBodySchema)
4293
+ });
3722
4294
  router.post("", async (req) => {
3723
4295
  await context.assertAllowed(req, "create");
3724
4296
  const { include_permissions } = parseQuery(requestSchemas.createQuery, req.query);
3725
- const data = parseBodyWithSchema(createBodySchema, req.body, context.getRequestSchema("requestSchemas.create"));
4297
+ const data = await parseBodyWithSchema(
4298
+ createBodySchema,
4299
+ req.body,
4300
+ context.getRequestSchema("requestSchemas.create")
4301
+ );
3726
4302
  const svc = context.getPublicService(req);
3727
4303
  const result = await svc._create(data, {}, { includePermissions: parseBooleanString(include_permissions) });
3728
4304
  handleResultError(result);
3729
4305
  return formatModelCreatedResponse(result);
3730
4306
  });
4307
+ context.registerOpenApiRoute({
4308
+ method: "post",
4309
+ path: "",
4310
+ operationId: `${context.modelName}.create`,
4311
+ summary: `Create a ${context.modelName} document`,
4312
+ query: requestSchemas.createQuery,
4313
+ body: defineOpenApiSchemaResolver(() => context.getRequestSchema("requestSchemas.create") ?? createBodySchema)
4314
+ });
3731
4315
  router.post(`/${options.mutationRouteSegment}`, async (req) => {
3732
4316
  await context.assertAllowed(req, "create");
3733
4317
  const { include_permissions } = parseQuery(requestSchemas.createQuery, req.query);
3734
- const body = parseNestedBodyWithSchema(
4318
+ const body = await parseNestedBodyWithSchema(
3735
4319
  advancedCreateBodySchema,
3736
4320
  req.body,
3737
4321
  "data",
@@ -3739,7 +4323,7 @@ function setModelCollectionRoutes(context) {
3739
4323
  );
3740
4324
  const { data, select, populate, tasks } = body;
3741
4325
  const advancedOptions = body.options ?? {};
3742
- parseBodyWithSchema(
4326
+ await parseBodyWithSchema(
3743
4327
  advancedCreateBodySchema,
3744
4328
  { data, select, populate, tasks, options: advancedOptions },
3745
4329
  context.getRequestSchema("requestSchemas.advancedCreate.default") ?? context.getRequestSchema("requestSchemas.advancedCreate")
@@ -3757,17 +4341,49 @@ function setModelCollectionRoutes(context) {
3757
4341
  handleResultError(result);
3758
4342
  return formatModelCreatedResponse(result);
3759
4343
  });
4344
+ context.registerOpenApiRoute({
4345
+ method: "post",
4346
+ path: `/${options.mutationRouteSegment}`,
4347
+ operationId: `${context.modelName}.createAdvanced`,
4348
+ summary: `Advanced create a ${context.modelName} document`,
4349
+ query: requestSchemas.createQuery,
4350
+ body: defineOpenApiSchemaResolver(getAdvancedCreateOpenApiBody)
4351
+ });
3760
4352
  router.get("/new", async (req) => {
3761
4353
  const svc = context.getPublicService(req);
3762
4354
  const result = await svc._new();
3763
4355
  handleResultError(result);
3764
4356
  return unwrapServiceData(result);
3765
4357
  });
4358
+ context.registerOpenApiRoute({
4359
+ method: "get",
4360
+ path: "/new",
4361
+ operationId: `${context.modelName}.new`,
4362
+ summary: `Get a new ${context.modelName} document template`
4363
+ });
3766
4364
  }
3767
4365
 
3768
4366
  // src/routers/model-router-document-routes.ts
3769
4367
  function setModelDocumentRoutes(context) {
3770
4368
  const { router, options, modelName } = context;
4369
+ const getAdvancedUpdateOpenApiBody = () => {
4370
+ const advancedUpdateSchema = context.getRequestSchema("requestSchemas.advancedUpdate");
4371
+ const dataSchema = context.getRequestSchema("requestSchemas.advancedUpdate.data") ?? getNestedOpenApiSchemaDataSource(advancedUpdateSchema);
4372
+ const bodySchema = unwrapNestedOpenApiSchemaSource(
4373
+ context.getRequestSchema("requestSchemas.advancedUpdate.default") ?? advancedUpdateSchema,
4374
+ advancedUpdateBodySchema
4375
+ );
4376
+ return dataSchema ? patchOpenApiObjectSchema(bodySchema, { data: dataSchema }) : bodySchema;
4377
+ };
4378
+ const getAdvancedUpsertOpenApiBody = () => {
4379
+ const advancedUpsertSchema = context.getRequestSchema("requestSchemas.advancedUpsert");
4380
+ const dataSchema = context.getRequestSchema("requestSchemas.advancedUpsert.data") ?? getNestedOpenApiSchemaDataSource(advancedUpsertSchema);
4381
+ const bodySchema = unwrapNestedOpenApiSchemaSource(
4382
+ context.getRequestSchema("requestSchemas.advancedUpsert.default") ?? advancedUpsertSchema,
4383
+ advancedUpsertBodySchema
4384
+ );
4385
+ return dataSchema ? patchOpenApiObjectSchema(bodySchema, { data: dataSchema }) : bodySchema;
4386
+ };
3771
4387
  router.get("/count", async (req) => {
3772
4388
  await context.assertAllowed(req, "count");
3773
4389
  const svc = context.getPublicService(req);
@@ -3775,9 +4391,15 @@ function setModelDocumentRoutes(context) {
3775
4391
  handleResultError(result);
3776
4392
  return unwrapServiceData(result);
3777
4393
  });
4394
+ context.registerOpenApiRoute({
4395
+ method: "get",
4396
+ path: "/count",
4397
+ operationId: `${modelName}.count`,
4398
+ summary: `Count ${modelName} documents`
4399
+ });
3778
4400
  router.post("/count", async (req) => {
3779
4401
  await context.assertAllowed(req, "count");
3780
- const { filter: filter2, options: countOptions = {} } = parseBodyWithSchema(
4402
+ const { filter: filter2, options: countOptions = {} } = await parseBodyWithSchema(
3781
4403
  countBodySchema,
3782
4404
  req.body,
3783
4405
  context.getRequestSchema("requestSchemas.count")
@@ -3790,6 +4412,13 @@ function setModelDocumentRoutes(context) {
3790
4412
  handleResultError(result);
3791
4413
  return unwrapServiceData(result);
3792
4414
  });
4415
+ context.registerOpenApiRoute({
4416
+ method: "post",
4417
+ path: "/count",
4418
+ operationId: `${modelName}.countWithFilter`,
4419
+ summary: `Count ${modelName} documents with a filter`,
4420
+ body: defineOpenApiSchemaResolver(() => context.getRequestSchema("requestSchemas.count") ?? countBodySchema)
4421
+ });
3793
4422
  router.get(`/:${options.idParam}`, async (req) => {
3794
4423
  await context.assertAllowed(req, "read");
3795
4424
  const id = parsePathParam(req.params[options.idParam], options.idParam);
@@ -3806,9 +4435,16 @@ function setModelDocumentRoutes(context) {
3806
4435
  handleResultError(result);
3807
4436
  return unwrapServiceData(result);
3808
4437
  });
4438
+ context.registerOpenApiRoute({
4439
+ method: "get",
4440
+ path: `/:${options.idParam}`,
4441
+ operationId: `${modelName}.read`,
4442
+ summary: `Read a ${modelName} document`,
4443
+ query: requestSchemas.readQuery
4444
+ });
3809
4445
  router.post(`/${options.queryRouteSegment}/__filter`, async (req) => {
3810
4446
  await context.assertAllowed(req, "read");
3811
- const body = parseBodyWithSchema(
4447
+ const body = await parseBodyWithSchema(
3812
4448
  readFilterBodySchema,
3813
4449
  req.body,
3814
4450
  context.getRequestSchema("requestSchemas.advancedReadFilter")
@@ -3825,10 +4461,19 @@ function setModelDocumentRoutes(context) {
3825
4461
  handleResultError(result);
3826
4462
  return unwrapServiceData(result);
3827
4463
  });
4464
+ context.registerOpenApiRoute({
4465
+ method: "post",
4466
+ path: `/${options.queryRouteSegment}/__filter`,
4467
+ operationId: `${modelName}.readByFilterAdvanced`,
4468
+ summary: `Read a ${modelName} document by filter`,
4469
+ body: defineOpenApiSchemaResolver(
4470
+ () => context.getRequestSchema("requestSchemas.advancedReadFilter") ?? readFilterBodySchema
4471
+ )
4472
+ });
3828
4473
  router.post(`/${options.queryRouteSegment}/:${options.idParam}`, async (req) => {
3829
4474
  await context.assertAllowed(req, "read");
3830
4475
  const id = parsePathParam(req.params[options.idParam], options.idParam);
3831
- const body = parseBodyWithSchema(
4476
+ const body = await parseBodyWithSchema(
3832
4477
  readByIdBodySchema,
3833
4478
  req.body,
3834
4479
  context.getRequestSchema("requestSchemas.advancedRead")
@@ -3845,11 +4490,24 @@ function setModelDocumentRoutes(context) {
3845
4490
  handleResultError(result);
3846
4491
  return unwrapServiceData(result);
3847
4492
  });
4493
+ context.registerOpenApiRoute({
4494
+ method: "post",
4495
+ path: `/${options.queryRouteSegment}/:${options.idParam}`,
4496
+ operationId: `${modelName}.readAdvanced`,
4497
+ summary: `Read a ${modelName} document with advanced options`,
4498
+ body: defineOpenApiSchemaResolver(
4499
+ () => context.getRequestSchema("requestSchemas.advancedRead") ?? readByIdBodySchema
4500
+ )
4501
+ });
3848
4502
  router.patch(`/:${options.idParam}`, async (req) => {
3849
4503
  await context.assertAllowed(req, "update");
3850
4504
  const id = parsePathParam(req.params[options.idParam], options.idParam);
3851
4505
  const { returning_all, include_permissions } = parseQuery(requestSchemas.updateQuery, req.query);
3852
- const data = parseBodyWithSchema(updateBodySchema, req.body, context.getRequestSchema("requestSchemas.update"));
4506
+ const data = await parseBodyWithSchema(
4507
+ updateBodySchema,
4508
+ req.body,
4509
+ context.getRequestSchema("requestSchemas.update")
4510
+ );
3853
4511
  const svc = context.getPublicService(req);
3854
4512
  const result = await svc._update(
3855
4513
  id,
@@ -3863,11 +4521,19 @@ function setModelDocumentRoutes(context) {
3863
4521
  handleResultError(result);
3864
4522
  return unwrapServiceData(result);
3865
4523
  });
4524
+ context.registerOpenApiRoute({
4525
+ method: "patch",
4526
+ path: `/:${options.idParam}`,
4527
+ operationId: `${modelName}.update`,
4528
+ summary: `Update a ${modelName} document`,
4529
+ query: requestSchemas.updateQuery,
4530
+ body: defineOpenApiSchemaResolver(() => context.getRequestSchema("requestSchemas.update") ?? updateBodySchema)
4531
+ });
3866
4532
  router.patch(`/${options.mutationRouteSegment}/:${options.idParam}`, async (req) => {
3867
4533
  await context.assertAllowed(req, "update");
3868
4534
  const id = parsePathParam(req.params[options.idParam], options.idParam);
3869
4535
  const { returning_all, include_permissions } = parseQuery(requestSchemas.updateQuery, req.query);
3870
- const body = parseNestedBodyWithSchema(
4536
+ const body = await parseNestedBodyWithSchema(
3871
4537
  advancedUpdateBodySchema,
3872
4538
  req.body,
3873
4539
  "data",
@@ -3875,7 +4541,7 @@ function setModelDocumentRoutes(context) {
3875
4541
  );
3876
4542
  const { data, select, populate, tasks } = body;
3877
4543
  const advancedOptions = body.options ?? {};
3878
- parseBodyWithSchema(
4544
+ await parseBodyWithSchema(
3879
4545
  advancedUpdateBodySchema,
3880
4546
  { data, select, populate, tasks, options: advancedOptions },
3881
4547
  context.getRequestSchema("requestSchemas.advancedUpdate.default") ?? context.getRequestSchema("requestSchemas.advancedUpdate")
@@ -3895,11 +4561,23 @@ function setModelDocumentRoutes(context) {
3895
4561
  handleResultError(result);
3896
4562
  return unwrapServiceData(result);
3897
4563
  });
4564
+ context.registerOpenApiRoute({
4565
+ method: "patch",
4566
+ path: `/${options.mutationRouteSegment}/:${options.idParam}`,
4567
+ operationId: `${modelName}.updateAdvanced`,
4568
+ summary: `Advanced update a ${modelName} document`,
4569
+ query: requestSchemas.updateQuery,
4570
+ body: defineOpenApiSchemaResolver(getAdvancedUpdateOpenApiBody)
4571
+ });
3898
4572
  router.put(`/`, async (req) => {
3899
4573
  await context.assertAllowed(req, "upsert");
3900
4574
  const svc = context.getPublicService(req);
3901
4575
  const { returning_all, include_permissions } = parseQuery(requestSchemas.upsertQuery, req.query);
3902
- const body = parseBodyWithSchema(upsertBodySchema, req.body, context.getRequestSchema("requestSchemas.upsert"));
4576
+ const body = await parseBodyWithSchema(
4577
+ upsertBodySchema,
4578
+ req.body,
4579
+ context.getRequestSchema("requestSchemas.upsert")
4580
+ );
3903
4581
  const result = await svc._upsert(
3904
4582
  body,
3905
4583
  {},
@@ -3911,11 +4589,19 @@ function setModelDocumentRoutes(context) {
3911
4589
  handleResultError(result);
3912
4590
  return formatModelUpsertResponse(result);
3913
4591
  });
4592
+ context.registerOpenApiRoute({
4593
+ method: "put",
4594
+ path: "/",
4595
+ operationId: `${modelName}.upsert`,
4596
+ summary: `Upsert a ${modelName} document`,
4597
+ query: requestSchemas.upsertQuery,
4598
+ body: defineOpenApiSchemaResolver(() => context.getRequestSchema("requestSchemas.upsert") ?? upsertBodySchema)
4599
+ });
3914
4600
  router.put(`/${options.mutationRouteSegment}`, async (req) => {
3915
4601
  await context.assertAllowed(req, "upsert");
3916
4602
  const svc = context.getPublicService(req);
3917
4603
  const { returning_all, include_permissions } = parseQuery(requestSchemas.upsertQuery, req.query);
3918
- const body = parseNestedBodyWithSchema(
4604
+ const body = await parseNestedBodyWithSchema(
3919
4605
  advancedUpsertBodySchema,
3920
4606
  req.body,
3921
4607
  "data",
@@ -3923,7 +4609,7 @@ function setModelDocumentRoutes(context) {
3923
4609
  );
3924
4610
  const { data, select, populate, tasks } = body;
3925
4611
  const advancedOptions = body.options ?? {};
3926
- parseBodyWithSchema(
4612
+ await parseBodyWithSchema(
3927
4613
  advancedUpsertBodySchema,
3928
4614
  { data, select, populate, tasks, options: advancedOptions },
3929
4615
  context.getRequestSchema("requestSchemas.advancedUpsert.default") ?? context.getRequestSchema("requestSchemas.advancedUpsert")
@@ -3941,6 +4627,14 @@ function setModelDocumentRoutes(context) {
3941
4627
  handleResultError(result);
3942
4628
  return formatModelUpsertResponse(result);
3943
4629
  });
4630
+ context.registerOpenApiRoute({
4631
+ method: "put",
4632
+ path: `/${options.mutationRouteSegment}`,
4633
+ operationId: `${modelName}.upsertAdvanced`,
4634
+ summary: `Advanced upsert a ${modelName} document`,
4635
+ query: requestSchemas.upsertQuery,
4636
+ body: defineOpenApiSchemaResolver(getAdvancedUpsertOpenApiBody)
4637
+ });
3944
4638
  router.delete(`/:${options.idParam}`, async (req) => {
3945
4639
  await context.assertAllowed(req, "delete");
3946
4640
  const id = parsePathParam(req.params[options.idParam], options.idParam);
@@ -3949,6 +4643,12 @@ function setModelDocumentRoutes(context) {
3949
4643
  handleResultError(result);
3950
4644
  return unwrapServiceData(result);
3951
4645
  });
4646
+ context.registerOpenApiRoute({
4647
+ method: "delete",
4648
+ path: `/:${options.idParam}`,
4649
+ operationId: `${modelName}.delete`,
4650
+ summary: `Delete a ${modelName} document`
4651
+ });
3952
4652
  router.get("/distinct/:field", async (req) => {
3953
4653
  await context.assertAllowed(req, "distinct");
3954
4654
  const field = parsePathParam(req.params.field, "field");
@@ -3957,10 +4657,16 @@ function setModelDocumentRoutes(context) {
3957
4657
  handleResultError(result);
3958
4658
  return unwrapServiceData(result);
3959
4659
  });
4660
+ context.registerOpenApiRoute({
4661
+ method: "get",
4662
+ path: "/distinct/:field",
4663
+ operationId: `${modelName}.distinct`,
4664
+ summary: `Get distinct values for a ${modelName} field`
4665
+ });
3960
4666
  router.post("/distinct/:field", async (req) => {
3961
4667
  await context.assertAllowed(req, "distinct");
3962
4668
  const field = parsePathParam(req.params.field, "field");
3963
- const { filter: filter2 } = parseBodyWithSchema(
4669
+ const { filter: filter2 } = await parseBodyWithSchema(
3964
4670
  distinctBodySchema,
3965
4671
  req.body,
3966
4672
  context.getRequestSchema("requestSchemas.distinct")
@@ -3970,6 +4676,13 @@ function setModelDocumentRoutes(context) {
3970
4676
  handleResultError(result);
3971
4677
  return unwrapServiceData(result);
3972
4678
  });
4679
+ context.registerOpenApiRoute({
4680
+ method: "post",
4681
+ path: "/distinct/:field",
4682
+ operationId: `${modelName}.distinctWithFilter`,
4683
+ summary: `Get distinct values for a ${modelName} field with a filter`,
4684
+ body: defineOpenApiSchemaResolver(() => context.getRequestSchema("requestSchemas.distinct") ?? distinctBodySchema)
4685
+ });
3973
4686
  }
3974
4687
 
3975
4688
  // src/routers/model-router-subdocument-routes.ts
@@ -3986,12 +4699,18 @@ function setModelSubDocumentRoutes(context) {
3986
4699
  handleResultError(result);
3987
4700
  return unwrapServiceData(result);
3988
4701
  });
4702
+ context.registerOpenApiRoute({
4703
+ method: "get",
4704
+ path: `/:${context.options.idParam}/${sub}`,
4705
+ operationId: `${context.modelName}.${sub}.list`,
4706
+ summary: `List ${sub} subdocuments`
4707
+ });
3989
4708
  context.router.post(
3990
4709
  `/:${context.options.idParam}/${sub}/${context.options.queryRouteSegment}`,
3991
4710
  async (req) => {
3992
4711
  await context.assertAllowed(req, `subs.${sub}.list`);
3993
4712
  const id = parsePathParam(req.params[context.options.idParam], context.options.idParam);
3994
- const body = parseBodyWithSchema(
4713
+ const body = await parseBodyWithSchema(
3995
4714
  subListBodySchema,
3996
4715
  req.body,
3997
4716
  context.getRequestSchema("requestSchemas.subList")
@@ -4002,10 +4721,17 @@ function setModelSubDocumentRoutes(context) {
4002
4721
  return unwrapServiceData(result);
4003
4722
  }
4004
4723
  );
4724
+ context.registerOpenApiRoute({
4725
+ method: "post",
4726
+ path: `/:${context.options.idParam}/${sub}/${context.options.queryRouteSegment}`,
4727
+ operationId: `${context.modelName}.${sub}.listAdvanced`,
4728
+ summary: `Advanced list ${sub} subdocuments`,
4729
+ body: defineOpenApiSchemaResolver(() => context.getRequestSchema("requestSchemas.subList") ?? subListBodySchema)
4730
+ });
4005
4731
  context.router.patch(`/:${context.options.idParam}/${sub}`, async (req) => {
4006
4732
  await context.assertAllowed(req, `subs.${sub}.update`);
4007
4733
  const id = parsePathParam(req.params[context.options.idParam], context.options.idParam);
4008
- const data = parseBodyWithSchema(
4734
+ const data = await parseBodyWithSchema(
4009
4735
  subMutationBodySchema,
4010
4736
  req.body,
4011
4737
  context.getRequestSchema("requestSchemas.subBulkUpdate")
@@ -4015,6 +4741,15 @@ function setModelSubDocumentRoutes(context) {
4015
4741
  handleResultError(result);
4016
4742
  return unwrapServiceData(result);
4017
4743
  });
4744
+ context.registerOpenApiRoute({
4745
+ method: "patch",
4746
+ path: `/:${context.options.idParam}/${sub}`,
4747
+ operationId: `${context.modelName}.${sub}.bulkUpdate`,
4748
+ summary: `Bulk update ${sub} subdocuments`,
4749
+ body: defineOpenApiSchemaResolver(
4750
+ () => context.getRequestSchema("requestSchemas.subBulkUpdate") ?? subMutationBodySchema
4751
+ )
4752
+ });
4018
4753
  context.router.get(`/:${context.options.idParam}/${sub}/:subId`, async (req) => {
4019
4754
  await context.assertAllowed(req, `subs.${sub}.read`);
4020
4755
  const id = parsePathParam(req.params[context.options.idParam], context.options.idParam);
@@ -4024,13 +4759,19 @@ function setModelSubDocumentRoutes(context) {
4024
4759
  handleResultError(result);
4025
4760
  return unwrapServiceData(result);
4026
4761
  });
4762
+ context.registerOpenApiRoute({
4763
+ method: "get",
4764
+ path: `/:${context.options.idParam}/${sub}/:subId`,
4765
+ operationId: `${context.modelName}.${sub}.read`,
4766
+ summary: `Read a ${sub} subdocument`
4767
+ });
4027
4768
  context.router.post(
4028
4769
  `/:${context.options.idParam}/${sub}/:subId/${context.options.queryRouteSegment}`,
4029
4770
  async (req) => {
4030
4771
  await context.assertAllowed(req, `subs.${sub}.read`);
4031
4772
  const id = parsePathParam(req.params[context.options.idParam], context.options.idParam);
4032
4773
  const subId = parsePathParam(req.params.subId, "subId");
4033
- const body = parseBodyWithSchema(
4774
+ const body = await parseBodyWithSchema(
4034
4775
  subReadBodySchema,
4035
4776
  req.body,
4036
4777
  context.getRequestSchema("requestSchemas.subRead")
@@ -4043,11 +4784,18 @@ function setModelSubDocumentRoutes(context) {
4043
4784
  return unwrapServiceData(result);
4044
4785
  }
4045
4786
  );
4787
+ context.registerOpenApiRoute({
4788
+ method: "post",
4789
+ path: `/:${context.options.idParam}/${sub}/:subId/${context.options.queryRouteSegment}`,
4790
+ operationId: `${context.modelName}.${sub}.readAdvanced`,
4791
+ summary: `Advanced read a ${sub} subdocument`,
4792
+ body: defineOpenApiSchemaResolver(() => context.getRequestSchema("requestSchemas.subRead") ?? subReadBodySchema)
4793
+ });
4046
4794
  context.router.patch(`/:${context.options.idParam}/${sub}/:subId`, async (req) => {
4047
4795
  await context.assertAllowed(req, `subs.${sub}.update`);
4048
4796
  const id = parsePathParam(req.params[context.options.idParam], context.options.idParam);
4049
4797
  const subId = parsePathParam(req.params.subId, "subId");
4050
- const data = parseBodyWithSchema(
4798
+ const data = await parseBodyWithSchema(
4051
4799
  subMutationBodySchema,
4052
4800
  req.body,
4053
4801
  context.getRequestSchema("requestSchemas.subUpdate")
@@ -4057,10 +4805,19 @@ function setModelSubDocumentRoutes(context) {
4057
4805
  handleResultError(result);
4058
4806
  return unwrapServiceData(result);
4059
4807
  });
4808
+ context.registerOpenApiRoute({
4809
+ method: "patch",
4810
+ path: `/:${context.options.idParam}/${sub}/:subId`,
4811
+ operationId: `${context.modelName}.${sub}.update`,
4812
+ summary: `Update a ${sub} subdocument`,
4813
+ body: defineOpenApiSchemaResolver(
4814
+ () => context.getRequestSchema("requestSchemas.subUpdate") ?? subMutationBodySchema
4815
+ )
4816
+ });
4060
4817
  context.router.post(`/:${context.options.idParam}/${sub}`, async (req) => {
4061
4818
  await context.assertAllowed(req, `subs.${sub}.create`);
4062
4819
  const id = parsePathParam(req.params[context.options.idParam], context.options.idParam);
4063
- const data = parseBodyWithSchema(
4820
+ const data = await parseBodyWithSchema(
4064
4821
  subMutationBodySchema,
4065
4822
  req.body,
4066
4823
  context.getRequestSchema("requestSchemas.subCreate")
@@ -4070,6 +4827,15 @@ function setModelSubDocumentRoutes(context) {
4070
4827
  handleResultError(result);
4071
4828
  return formatModelCreatedResponse(result);
4072
4829
  });
4830
+ context.registerOpenApiRoute({
4831
+ method: "post",
4832
+ path: `/:${context.options.idParam}/${sub}`,
4833
+ operationId: `${context.modelName}.${sub}.create`,
4834
+ summary: `Create a ${sub} subdocument`,
4835
+ body: defineOpenApiSchemaResolver(
4836
+ () => context.getRequestSchema("requestSchemas.subCreate") ?? subMutationBodySchema
4837
+ )
4838
+ });
4073
4839
  context.router.delete(`/:${context.options.idParam}/${sub}/:subId`, async (req) => {
4074
4840
  await context.assertAllowed(req, `subs.${sub}.delete`);
4075
4841
  const id = parsePathParam(req.params[context.options.idParam], context.options.idParam);
@@ -4079,9 +4845,25 @@ function setModelSubDocumentRoutes(context) {
4079
4845
  handleResultError(result);
4080
4846
  return unwrapServiceData(result);
4081
4847
  });
4848
+ context.registerOpenApiRoute({
4849
+ method: "delete",
4850
+ path: `/:${context.options.idParam}/${sub}/:subId`,
4851
+ operationId: `${context.modelName}.${sub}.delete`,
4852
+ summary: `Delete a ${sub} subdocument`
4853
+ });
4082
4854
  }
4083
4855
  }
4084
4856
 
4857
+ // src/openapi/route-registration.ts
4858
+ import { normalizeUrlPath } from "@web-ts-toolkit/utils";
4859
+ function registerOpenApiRoute(runtime, basePath, defaultTag, route) {
4860
+ runtime.registerOpenApiRoute({
4861
+ ...route,
4862
+ path: normalizeUrlPath(basePath + route.path),
4863
+ tags: route.tags ?? [defaultTag]
4864
+ });
4865
+ }
4866
+
4085
4867
  // src/routers/model-router.ts
4086
4868
  var clientErrors2 = JsonRouter5.clientErrors;
4087
4869
  function setOption(parentKey, optionKey, option) {
@@ -4247,7 +5029,7 @@ var ModelRouter = class {
4247
5029
  this.runtime.setModelOptions(modelName, initialOptions);
4248
5030
  attachRuntimeToModel(modelName, this.runtime);
4249
5031
  this.options = this.runtime.getModelOptions(modelName);
4250
- this.fullBasePath = normalizeUrlPath(this.options.parentPath + this.options.basePath);
5032
+ this.fullBasePath = normalizeUrlPath2(this.options.parentPath + this.options.basePath);
4251
5033
  this.modelName = modelName;
4252
5034
  this.router = new JsonRouter5(this.options.basePath, createSetCore(this.runtime), accessRouterResponseHandler);
4253
5035
  this.model = new model_default(modelName);
@@ -4272,6 +5054,9 @@ var ModelRouter = class {
4272
5054
  const allowed = await req.macl.isAllowed(this.modelName, access);
4273
5055
  if (!allowed) throw new clientErrors2.UnauthorizedError();
4274
5056
  }
5057
+ registerOpenApiRoute(route) {
5058
+ registerOpenApiRoute(this.runtime, this.fullBasePath, this.modelName, route);
5059
+ }
4275
5060
  ///////////////////////
4276
5061
  // Collection Routes //
4277
5062
  ///////////////////////
@@ -4282,7 +5067,8 @@ var ModelRouter = class {
4282
5067
  options: this.options,
4283
5068
  getRequestSchema: this.getRequestSchema.bind(this),
4284
5069
  getPublicService: this.getPublicService.bind(this),
4285
- assertAllowed: this.assertAllowed.bind(this)
5070
+ assertAllowed: this.assertAllowed.bind(this),
5071
+ registerOpenApiRoute: this.registerOpenApiRoute.bind(this)
4286
5072
  });
4287
5073
  }
4288
5074
  /////////////////////
@@ -4295,7 +5081,8 @@ var ModelRouter = class {
4295
5081
  options: this.options,
4296
5082
  getRequestSchema: this.getRequestSchema.bind(this),
4297
5083
  getPublicService: this.getPublicService.bind(this),
4298
- assertAllowed: this.assertAllowed.bind(this)
5084
+ assertAllowed: this.assertAllowed.bind(this),
5085
+ registerOpenApiRoute: this.registerOpenApiRoute.bind(this)
4299
5086
  });
4300
5087
  }
4301
5088
  /////////////////////////
@@ -4308,13 +5095,14 @@ var ModelRouter = class {
4308
5095
  options: this.options,
4309
5096
  getRequestSchema: this.getRequestSchema.bind(this),
4310
5097
  getPublicService: this.getPublicService.bind(this),
4311
- assertAllowed: this.assertAllowed.bind(this)
5098
+ assertAllowed: this.assertAllowed.bind(this),
5099
+ registerOpenApiRoute: this.registerOpenApiRoute.bind(this)
4312
5100
  });
4313
5101
  }
4314
5102
  logEndpoints() {
4315
5103
  runWithRuntime(this.runtime, () => {
4316
- forEach7(this.router.endpoints, ({ method, path }) => {
4317
- logger.info(`${padEnd(method, 6)} ${normalizeUrlPath(this.options.parentPath + path)}`);
5104
+ forEach8(this.router.endpoints, ({ method, path }) => {
5105
+ logger.info(`${padEnd(method, 6)} ${normalizeUrlPath2(this.options.parentPath + path)}`);
4318
5106
  });
4319
5107
  });
4320
5108
  }
@@ -4327,7 +5115,7 @@ var ModelRouter = class {
4327
5115
  value
4328
5116
  );
4329
5117
  }
4330
- if (arguments.length === 1 && isPlainObject9(keyOrOptions)) {
5118
+ if (arguments.length === 1 && isPlainObject12(keyOrOptions)) {
4331
5119
  assertMutableRouterOptions("model", keyOrOptions);
4332
5120
  this.runtime.setModelOptions(this.modelName, keyOrOptions);
4333
5121
  }
@@ -4350,7 +5138,7 @@ var ModelRouter = class {
4350
5138
 
4351
5139
  // src/routers/root-router.ts
4352
5140
  import JsonRouter6 from "@web-ts-toolkit/express-json-router";
4353
- import { isNumber as _isNumber, isString as isString10, orderBy as _orderBy } from "@web-ts-toolkit/utils";
5141
+ import { isNumber as _isNumber, isString as isString10, normalizeUrlPath as normalizeUrlPath3, orderBy as _orderBy } from "@web-ts-toolkit/utils";
4354
5142
 
4355
5143
  // src/core-data.ts
4356
5144
  var DataCore = class {
@@ -4596,6 +5384,9 @@ var RootRouter = class {
4596
5384
  );
4597
5385
  this.setRoutes();
4598
5386
  }
5387
+ registerOpenApiRoute(route) {
5388
+ registerOpenApiRoute(this.runtime, normalizeUrlPath3(this.basename), "root", route);
5389
+ }
4599
5390
  wrapResult(index, item, result) {
4600
5391
  return {
4601
5392
  index,
@@ -4681,6 +5472,13 @@ var RootRouter = class {
4681
5472
  }
4682
5473
  return _orderBy(results, ["index"], ["asc"]);
4683
5474
  });
5475
+ this.registerOpenApiRoute({
5476
+ method: "post",
5477
+ path: "",
5478
+ operationId: "root.query",
5479
+ summary: "Execute batched model and data operations",
5480
+ body: rootQuerySchema
5481
+ });
4684
5482
  }
4685
5483
  get routes() {
4686
5484
  return this.router.original;
@@ -4689,7 +5487,7 @@ var RootRouter = class {
4689
5487
 
4690
5488
  // src/routers/data-router.ts
4691
5489
  import JsonRouter7 from "@web-ts-toolkit/express-json-router";
4692
- import { isPlainObject as isPlainObject10, isString as isString11, isUndefined as isUndefined3, normalizeUrlPath as normalizeUrlPath2 } from "@web-ts-toolkit/utils";
5490
+ import { isPlainObject as isPlainObject13, isString as isString11, isUndefined as isUndefined3, normalizeUrlPath as normalizeUrlPath4 } from "@web-ts-toolkit/utils";
4693
5491
  var clientErrors4 = JsonRouter7.clientErrors;
4694
5492
  function setOption2(parentKey, optionKey, option) {
4695
5493
  const key = isUndefined3(option) ? parentKey : `${parentKey}.${optionKey}`;
@@ -4729,7 +5527,7 @@ var DataRouter = class {
4729
5527
  this.runtime = runtime;
4730
5528
  this.runtime.setDataOptions(dataName, initialOptions);
4731
5529
  this.options = this.runtime.getDataOptions(dataName);
4732
- this.fullBasePath = normalizeUrlPath2(this.options.parentPath + this.options.basePath);
5530
+ this.fullBasePath = normalizeUrlPath4(this.options.parentPath + this.options.basePath);
4733
5531
  this.dataName = dataName;
4734
5532
  this.router = new JsonRouter7(this.options.basePath, createSetDataCore(this.runtime), accessRouterResponseHandler);
4735
5533
  this.setCollectionRoutes();
@@ -4748,6 +5546,9 @@ var DataRouter = class {
4748
5546
  const allowed = await req.dacl.isAllowed(this.dataName, access);
4749
5547
  if (!allowed) throw new clientErrors4.UnauthorizedError();
4750
5548
  }
5549
+ registerOpenApiRoute(route) {
5550
+ registerOpenApiRoute(this.runtime, this.fullBasePath, this.dataName, route);
5551
+ }
4751
5552
  ///////////////////////
4752
5553
  // Collection Routes //
4753
5554
  ///////////////////////
@@ -4772,6 +5573,13 @@ var DataRouter = class {
4772
5573
  const decoratedResult = await decorateDataListResult(svc, result);
4773
5574
  return formatListResponse(req, decoratedResult, includeCount, includeExtraHeaders);
4774
5575
  });
5576
+ this.registerOpenApiRoute({
5577
+ method: "get",
5578
+ path: "",
5579
+ operationId: `${this.dataName}.list`,
5580
+ summary: `List ${this.dataName} records`,
5581
+ query: requestSchemas.listQuery
5582
+ });
4775
5583
  this.router.post(`/${this.options.queryRouteSegment}`, async (req) => {
4776
5584
  await this.assertAllowed(req, "list");
4777
5585
  let {
@@ -4783,7 +5591,7 @@ var DataRouter = class {
4783
5591
  page,
4784
5592
  pageSize,
4785
5593
  options = {}
4786
- } = parseBodyWithSchema(dataListBodySchema, req.body, this.getRequestSchema("requestSchemas.advancedList"));
5594
+ } = await parseBodyWithSchema(dataListBodySchema, req.body, this.getRequestSchema("requestSchemas.advancedList"));
4787
5595
  const { includeCount, includeExtraHeaders } = options;
4788
5596
  const svc = this.getService(req);
4789
5597
  const result = await svc.find(
@@ -4795,6 +5603,15 @@ var DataRouter = class {
4795
5603
  const decoratedResult = await decorateDataListResult(svc, result);
4796
5604
  return formatListResponse(req, decoratedResult, includeCount, includeExtraHeaders);
4797
5605
  });
5606
+ this.registerOpenApiRoute({
5607
+ method: "post",
5608
+ path: `/${this.options.queryRouteSegment}`,
5609
+ operationId: `${this.dataName}.listAdvanced`,
5610
+ summary: `Advanced list ${this.dataName} records`,
5611
+ body: defineOpenApiSchemaResolver(
5612
+ () => this.getRequestSchema("requestSchemas.advancedList") ?? dataListBodySchema
5613
+ )
5614
+ });
4798
5615
  }
4799
5616
  /////////////////////
4800
5617
  // Document Routes //
@@ -4809,9 +5626,15 @@ var DataRouter = class {
4809
5626
  const decoratedResult = await decorateDataSingleResult(svc, result);
4810
5627
  return decoratedResult.data;
4811
5628
  });
5629
+ this.registerOpenApiRoute({
5630
+ method: "get",
5631
+ path: `/:${this.options.idParam}`,
5632
+ operationId: `${this.dataName}.read`,
5633
+ summary: `Read a ${this.dataName} record`
5634
+ });
4812
5635
  this.router.post(`/${this.options.queryRouteSegment}/__filter`, async (req) => {
4813
5636
  await this.assertAllowed(req, "read");
4814
- let { filter: filter2, select } = parseBodyWithSchema(
5637
+ let { filter: filter2, select } = await parseBodyWithSchema(
4815
5638
  dataReadFilterBodySchema,
4816
5639
  req.body,
4817
5640
  this.getRequestSchema("requestSchemas.advancedReadFilter")
@@ -4822,10 +5645,19 @@ var DataRouter = class {
4822
5645
  const decoratedResult = await decorateDataSingleResult(svc, result);
4823
5646
  return decoratedResult.data;
4824
5647
  });
5648
+ this.registerOpenApiRoute({
5649
+ method: "post",
5650
+ path: `/${this.options.queryRouteSegment}/__filter`,
5651
+ operationId: `${this.dataName}.readByFilter`,
5652
+ summary: `Read a ${this.dataName} record by filter`,
5653
+ body: defineOpenApiSchemaResolver(
5654
+ () => this.getRequestSchema("requestSchemas.advancedReadFilter") ?? dataReadFilterBodySchema
5655
+ )
5656
+ });
4825
5657
  this.router.post(`/${this.options.queryRouteSegment}/:${this.options.idParam}`, async (req) => {
4826
5658
  await this.assertAllowed(req, "read");
4827
5659
  const id = parsePathParam(req.params[this.options.idParam], this.options.idParam);
4828
- let { select } = parseBodyWithSchema(
5660
+ let { select } = await parseBodyWithSchema(
4829
5661
  dataReadByIdBodySchema,
4830
5662
  req.body,
4831
5663
  this.getRequestSchema("requestSchemas.advancedRead")
@@ -4836,13 +5668,22 @@ var DataRouter = class {
4836
5668
  const decoratedResult = await decorateDataSingleResult(svc, result);
4837
5669
  return decoratedResult.data;
4838
5670
  });
5671
+ this.registerOpenApiRoute({
5672
+ method: "post",
5673
+ path: `/${this.options.queryRouteSegment}/:${this.options.idParam}`,
5674
+ operationId: `${this.dataName}.readAdvanced`,
5675
+ summary: `Advanced read a ${this.dataName} record`,
5676
+ body: defineOpenApiSchemaResolver(
5677
+ () => this.getRequestSchema("requestSchemas.advancedRead") ?? dataReadByIdBodySchema
5678
+ )
5679
+ });
4839
5680
  }
4840
5681
  set(keyOrOptions, value) {
4841
5682
  if (arguments.length === 2 && isString11(keyOrOptions)) {
4842
5683
  assertMutableRouterOption("data", keyOrOptions);
4843
5684
  this.runtime.setDataOption(this.dataName, keyOrOptions, value);
4844
5685
  }
4845
- if (arguments.length === 1 && isPlainObject10(keyOrOptions)) {
5686
+ if (arguments.length === 1 && isPlainObject13(keyOrOptions)) {
4846
5687
  assertMutableRouterOptions("data", keyOrOptions);
4847
5688
  this.runtime.setDataOptions(this.dataName, keyOrOptions);
4848
5689
  }
@@ -4865,6 +5706,62 @@ var DataRouter = class {
4865
5706
 
4866
5707
  // src/routers/index.ts
4867
5708
  import JsonRouter8 from "@web-ts-toolkit/express-json-router";
5709
+
5710
+ // src/openapi/router.ts
5711
+ import express from "express";
5712
+ import { posix } from "path";
5713
+ var escapeHtml = (value) => value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
5714
+ var renderDocsHtml = (specPath, title, cssUrl, bundleUrl) => `<!doctype html>
5715
+ <html lang="en">
5716
+ <head>
5717
+ <meta charset="utf-8" />
5718
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
5719
+ <title>${escapeHtml(title)}</title>
5720
+ <link rel="stylesheet" href="${escapeHtml(cssUrl)}" />
5721
+ </head>
5722
+ <body>
5723
+ <div id="swagger-ui"></div>
5724
+ <script src="${escapeHtml(bundleUrl)}"></script>
5725
+ <script>
5726
+ window.ui = SwaggerUIBundle({
5727
+ url: ${JSON.stringify(specPath)},
5728
+ dom_id: '#swagger-ui',
5729
+ deepLinking: true,
5730
+ presets: [SwaggerUIBundle.presets.apis],
5731
+ layout: 'BaseLayout',
5732
+ });
5733
+ </script>
5734
+ </body>
5735
+ </html>`;
5736
+ var getRelativeSpecPath = (docsPath, jsonPath) => {
5737
+ const docsDir = posix.dirname(docsPath.startsWith("/") ? docsPath : `/${docsPath}`);
5738
+ const normalizedJsonPath = jsonPath.startsWith("/") ? jsonPath : `/${jsonPath}`;
5739
+ return posix.relative(docsDir, normalizedJsonPath) || posix.basename(normalizedJsonPath);
5740
+ };
5741
+ function createOpenApiRouter(runtime, options = {}) {
5742
+ const router = express.Router();
5743
+ const jsonPath = options.jsonPath ?? "/openapi.json";
5744
+ const docsPath = options.docsPath ?? "/docs";
5745
+ const swaggerUiCssUrl = options.swaggerUiCssUrl ?? "https://unpkg.com/swagger-ui-dist@5/swagger-ui.css";
5746
+ const swaggerUiBundleUrl = options.swaggerUiBundleUrl ?? "https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js";
5747
+ const info = {
5748
+ title: options.title ?? "Access Router API",
5749
+ version: options.version ?? "0.0.0",
5750
+ description: options.description,
5751
+ servers: options.servers
5752
+ };
5753
+ router.get(jsonPath, (_req, res) => {
5754
+ res.json(runtime.getOpenApiSpec(info));
5755
+ });
5756
+ if (docsPath !== false) {
5757
+ router.get(docsPath, (_req, res) => {
5758
+ res.type("html").send(renderDocsHtml(getRelativeSpecPath(docsPath, jsonPath), info.title, swaggerUiCssUrl, swaggerUiBundleUrl));
5759
+ });
5760
+ }
5761
+ return router;
5762
+ }
5763
+
5764
+ // src/routers/index.ts
4868
5765
  var hasRoutes = (input) => {
4869
5766
  return typeof input === "object" && input !== null && "routes" in input;
4870
5767
  };
@@ -4872,7 +5769,7 @@ var resolveRoutes = (input) => {
4872
5769
  return hasRoutes(input) ? input.routes : input;
4873
5770
  };
4874
5771
  function combineRoutes(...inputs) {
4875
- const router = express.Router();
5772
+ const router = express2.Router();
4876
5773
  inputs.forEach((input) => {
4877
5774
  router.use(resolveRoutes(input));
4878
5775
  });
@@ -4884,7 +5781,7 @@ var accessRouterResponseHandler = JsonRouter8.createHandler({
4884
5781
  });
4885
5782
  accessRouterResponseHandler.errorMessageProvider = function(error) {
4886
5783
  const errorLike = error;
4887
- console.error(error);
5784
+ logger.error(error);
4888
5785
  return errorLike.message || errorLike._message || String(error);
4889
5786
  };
4890
5787
 
@@ -4911,12 +5808,15 @@ function createRuntimeApi(runtime) {
4911
5808
  wtt2.createDataRouter = function(dataName, options) {
4912
5809
  return new DataRouter(dataName, options, runtime);
4913
5810
  };
5811
+ wtt2.createOpenApiRouter = function(options) {
5812
+ return createOpenApiRouter(runtime, options);
5813
+ };
4914
5814
  wtt2.combineRoutes = combineRoutes;
4915
5815
  wtt2.set = function(keyOrOptions, value) {
4916
5816
  if (arguments.length === 2 && isString12(keyOrOptions)) {
4917
5817
  return runtime.setGlobalOption(keyOrOptions, value);
4918
5818
  }
4919
- if (arguments.length === 1 && isPlainObject11(keyOrOptions)) {
5819
+ if (arguments.length === 1 && isPlainObject14(keyOrOptions)) {
4920
5820
  return runtime.setGlobalOptions(keyOrOptions);
4921
5821
  }
4922
5822
  };
@@ -4953,7 +5853,19 @@ export {
4953
5853
  acl,
4954
5854
  combineRoutes,
4955
5855
  createAccessRuntime,
5856
+ createOpenApiRouter,
4956
5857
  index_default as default,
5858
+ defineRequestSchema,
5859
+ fromAjv,
5860
+ fromArkType,
5861
+ fromIoTs,
5862
+ fromJoi,
5863
+ fromStandardSchema,
5864
+ fromSuperstruct,
5865
+ fromValibot,
5866
+ fromVine,
5867
+ fromYup,
5868
+ fromZod,
4957
5869
  getDefaultModelOption,
4958
5870
  getDefaultModelOptions,
4959
5871
  getGlobalOption,