adorn-api 1.0.6 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +66 -2
- package/dist/adapter/express/bootstrap.d.ts +1 -0
- package/dist/adapter/express/bootstrap.d.ts.map +1 -1
- package/dist/adapter/express/index.d.ts +11 -0
- package/dist/adapter/express/index.d.ts.map +1 -1
- package/dist/cli.cjs +141 -34
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +140 -33
- package/dist/cli.js.map +1 -1
- package/dist/compiler/analyze/extractQueryStyle.d.ts +8 -0
- package/dist/compiler/analyze/extractQueryStyle.d.ts.map +1 -0
- package/dist/compiler/manifest/emit.d.ts.map +1 -1
- package/dist/compiler/schema/openapi.d.ts.map +1 -1
- package/dist/compiler/schema/typeToJsonSchema.d.ts.map +1 -1
- package/dist/express.cjs +327 -60
- package/dist/express.cjs.map +1 -1
- package/dist/express.js +327 -60
- package/dist/express.js.map +1 -1
- package/dist/http.d.ts +4 -1
- package/dist/http.d.ts.map +1 -1
- package/dist/index.cjs +7 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -5
- package/dist/index.js.map +1 -1
- package/dist/metal/index.d.ts +1 -0
- package/dist/metal/index.d.ts.map +1 -1
- package/dist/metal/searchWhere.d.ts +42 -0
- package/dist/metal/searchWhere.d.ts.map +1 -0
- package/package.json +4 -4
package/dist/express.js
CHANGED
|
@@ -249,7 +249,8 @@ function bootstrap(options) {
|
|
|
249
249
|
swaggerPath = "/docs",
|
|
250
250
|
swaggerJsonPath = "/docs/openapi.json",
|
|
251
251
|
middleware,
|
|
252
|
-
auth
|
|
252
|
+
auth,
|
|
253
|
+
coerce
|
|
253
254
|
} = options;
|
|
254
255
|
if (controllers.length === 0) {
|
|
255
256
|
reject(new Error("At least one controller must be provided to bootstrap()."));
|
|
@@ -269,7 +270,8 @@ function bootstrap(options) {
|
|
|
269
270
|
controllers,
|
|
270
271
|
artifactsDir: absoluteArtifactsDir,
|
|
271
272
|
middleware,
|
|
272
|
-
auth
|
|
273
|
+
auth,
|
|
274
|
+
coerce
|
|
273
275
|
});
|
|
274
276
|
app.use(router);
|
|
275
277
|
if (enableSwagger) {
|
|
@@ -324,6 +326,17 @@ function bootstrap(options) {
|
|
|
324
326
|
}
|
|
325
327
|
|
|
326
328
|
// src/adapter/express/index.ts
|
|
329
|
+
function normalizeCoerceOptions(coerce) {
|
|
330
|
+
return {
|
|
331
|
+
body: coerce?.body ?? false,
|
|
332
|
+
query: coerce?.query ?? false,
|
|
333
|
+
path: coerce?.path ?? false,
|
|
334
|
+
header: coerce?.header ?? false,
|
|
335
|
+
cookie: coerce?.cookie ?? false,
|
|
336
|
+
dateTime: coerce?.dateTime ?? false,
|
|
337
|
+
date: coerce?.date ?? false
|
|
338
|
+
};
|
|
339
|
+
}
|
|
327
340
|
async function createExpressRouter(options) {
|
|
328
341
|
const { controllers, artifactsDir = ".adorn", middleware = {} } = options;
|
|
329
342
|
let manifest;
|
|
@@ -350,6 +363,7 @@ async function createExpressRouter(options) {
|
|
|
350
363
|
const validator = precompiledValidators ? null : createValidator();
|
|
351
364
|
const router = Router();
|
|
352
365
|
const instanceCache = /* @__PURE__ */ new Map();
|
|
366
|
+
const coerce = normalizeCoerceOptions(options.coerce);
|
|
353
367
|
function getInstance(Ctor) {
|
|
354
368
|
if (!instanceCache.has(Ctor)) {
|
|
355
369
|
instanceCache.set(Ctor, new Ctor());
|
|
@@ -412,6 +426,14 @@ async function createExpressRouter(options) {
|
|
|
412
426
|
}
|
|
413
427
|
for (const route of routes) {
|
|
414
428
|
const method = route.httpMethod.toLowerCase();
|
|
429
|
+
const openapiOperation = getOpenApiOperation(openapi, route);
|
|
430
|
+
const paramSchemaIndex = buildParamSchemaIndex(openapiOperation);
|
|
431
|
+
const bodySchema = getRequestBodySchema(openapiOperation, route.args.body?.contentType) ?? (route.args.body ? getSchemaByRef(route.args.body.schemaRef) : null);
|
|
432
|
+
const coerceBodyDates = getDateCoercionOptions(coerce, "body");
|
|
433
|
+
const coerceQueryDates = getDateCoercionOptions(coerce, "query");
|
|
434
|
+
const coercePathDates = getDateCoercionOptions(coerce, "path");
|
|
435
|
+
const coerceHeaderDates = getDateCoercionOptions(coerce, "header");
|
|
436
|
+
const coerceCookieDates = getDateCoercionOptions(coerce, "cookie");
|
|
415
437
|
const middlewareChain = [];
|
|
416
438
|
if (middleware.global) {
|
|
417
439
|
middlewareChain.push(...resolveMiddleware(middleware.global, middleware.named || {}));
|
|
@@ -439,29 +461,50 @@ async function createExpressRouter(options) {
|
|
|
439
461
|
}
|
|
440
462
|
const args = [];
|
|
441
463
|
if (route.args.body) {
|
|
442
|
-
|
|
464
|
+
const coercedBody = (coerceBodyDates.date || coerceBodyDates.dateTime) && bodySchema ? coerceDatesWithSchema(req.body, bodySchema, coerceBodyDates, openapi.components.schemas) : req.body;
|
|
465
|
+
args[route.args.body.index] = coercedBody;
|
|
443
466
|
}
|
|
444
467
|
for (const pathArg of route.args.path) {
|
|
445
|
-
const
|
|
468
|
+
const rawValue = req.params[pathArg.name];
|
|
469
|
+
const paramSchema = getParamSchemaFromIndex(paramSchemaIndex, "path", pathArg.name) ?? (pathArg.schemaRef ? getSchemaByRef(pathArg.schemaRef) : null) ?? schemaFromType(pathArg.schemaType);
|
|
470
|
+
const coerced = coerceParamValue(rawValue, paramSchema, coercePathDates, openapi.components.schemas);
|
|
446
471
|
args[pathArg.index] = coerced;
|
|
447
472
|
}
|
|
448
473
|
if (route.args.query.length > 0) {
|
|
449
|
-
const
|
|
450
|
-
const
|
|
451
|
-
if (
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
const parsed = parseQueryValue(req.query[q.name], q);
|
|
461
|
-
const coerced = coerceValue(parsed, q.schemaType);
|
|
474
|
+
const deepObjectArgs = route.args.query.filter((q) => q.serialization?.style === "deepObject");
|
|
475
|
+
const standardArgs = route.args.query.filter((q) => q.serialization?.style !== "deepObject");
|
|
476
|
+
if (deepObjectArgs.length > 0) {
|
|
477
|
+
const rawQuery = getRawQueryString(req);
|
|
478
|
+
const names = new Set(deepObjectArgs.map((q) => q.name));
|
|
479
|
+
const parsedDeep = parseDeepObjectParams(rawQuery, names);
|
|
480
|
+
for (const q of deepObjectArgs) {
|
|
481
|
+
const rawValue = parsedDeep[q.name];
|
|
482
|
+
const paramSchema = getParamSchemaFromIndex(paramSchemaIndex, "query", q.name) ?? (q.schemaRef ? getSchemaByRef(q.schemaRef) : null) ?? schemaFromType(q.schemaType);
|
|
483
|
+
const baseValue = rawValue === void 0 ? {} : rawValue;
|
|
484
|
+
const coerced = coerceParamValue(baseValue, paramSchema, coerceQueryDates, openapi.components.schemas);
|
|
462
485
|
args[q.index] = coerced;
|
|
463
486
|
}
|
|
464
487
|
}
|
|
488
|
+
if (standardArgs.length > 0) {
|
|
489
|
+
const firstQueryIndex = standardArgs[0].index;
|
|
490
|
+
const allSameIndex = standardArgs.every((q) => q.index === firstQueryIndex);
|
|
491
|
+
if (allSameIndex) {
|
|
492
|
+
args[firstQueryIndex] = {};
|
|
493
|
+
for (const q of standardArgs) {
|
|
494
|
+
const parsed = parseQueryValue(req.query[q.name], q);
|
|
495
|
+
const paramSchema = getParamSchemaFromIndex(paramSchemaIndex, "query", q.name) ?? (q.schemaRef ? getSchemaByRef(q.schemaRef) : null) ?? schemaFromType(q.schemaType);
|
|
496
|
+
const coerced = coerceParamValue(parsed, paramSchema, coerceQueryDates, openapi.components.schemas);
|
|
497
|
+
args[firstQueryIndex][q.name] = coerced;
|
|
498
|
+
}
|
|
499
|
+
} else {
|
|
500
|
+
for (const q of standardArgs) {
|
|
501
|
+
const parsed = parseQueryValue(req.query[q.name], q);
|
|
502
|
+
const paramSchema = getParamSchemaFromIndex(paramSchemaIndex, "query", q.name) ?? (q.schemaRef ? getSchemaByRef(q.schemaRef) : null) ?? schemaFromType(q.schemaType);
|
|
503
|
+
const coerced = coerceParamValue(parsed, paramSchema, coerceQueryDates, openapi.components.schemas);
|
|
504
|
+
args[q.index] = coerced;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
465
508
|
}
|
|
466
509
|
if (route.args.headers.length > 0) {
|
|
467
510
|
const firstHeaderIndex = route.args.headers[0].index;
|
|
@@ -470,12 +513,16 @@ async function createExpressRouter(options) {
|
|
|
470
513
|
args[firstHeaderIndex] = {};
|
|
471
514
|
for (const h of route.args.headers) {
|
|
472
515
|
const headerValue = req.headers[h.name.toLowerCase()];
|
|
473
|
-
|
|
516
|
+
const paramSchema = getParamSchemaFromIndex(paramSchemaIndex, "header", h.name) ?? (h.schemaRef ? getSchemaByRef(h.schemaRef) : null) ?? schemaFromType(h.schemaType);
|
|
517
|
+
const coerced = coerceParamValue(headerValue, paramSchema, coerceHeaderDates, openapi.components.schemas);
|
|
518
|
+
args[firstHeaderIndex][h.name] = coerced ?? void 0;
|
|
474
519
|
}
|
|
475
520
|
} else {
|
|
476
521
|
for (const h of route.args.headers) {
|
|
477
522
|
const headerValue = req.headers[h.name.toLowerCase()];
|
|
478
|
-
|
|
523
|
+
const paramSchema = getParamSchemaFromIndex(paramSchemaIndex, "header", h.name) ?? (h.schemaRef ? getSchemaByRef(h.schemaRef) : null) ?? schemaFromType(h.schemaType);
|
|
524
|
+
const coerced = coerceParamValue(headerValue, paramSchema, coerceHeaderDates, openapi.components.schemas);
|
|
525
|
+
args[h.index] = coerced ?? void 0;
|
|
479
526
|
}
|
|
480
527
|
}
|
|
481
528
|
}
|
|
@@ -487,13 +534,15 @@ async function createExpressRouter(options) {
|
|
|
487
534
|
args[firstCookieIndex] = {};
|
|
488
535
|
for (const c of route.args.cookies) {
|
|
489
536
|
const cookieValue = cookies[c.name];
|
|
490
|
-
const
|
|
537
|
+
const paramSchema = getParamSchemaFromIndex(paramSchemaIndex, "cookie", c.name) ?? (c.schemaRef ? getSchemaByRef(c.schemaRef) : null) ?? schemaFromType(c.schemaType);
|
|
538
|
+
const coerced = coerceParamValue(cookieValue, paramSchema, coerceCookieDates, openapi.components.schemas);
|
|
491
539
|
args[firstCookieIndex][c.name] = coerced;
|
|
492
540
|
}
|
|
493
541
|
} else {
|
|
494
542
|
for (const c of route.args.cookies) {
|
|
495
543
|
const cookieValue = cookies[c.name];
|
|
496
|
-
const
|
|
544
|
+
const paramSchema = getParamSchemaFromIndex(paramSchemaIndex, "cookie", c.name) ?? (c.schemaRef ? getSchemaByRef(c.schemaRef) : null) ?? schemaFromType(c.schemaType);
|
|
545
|
+
const coerced = coerceParamValue(cookieValue, paramSchema, coerceCookieDates, openapi.components.schemas);
|
|
497
546
|
args[c.index] = coerced;
|
|
498
547
|
}
|
|
499
548
|
}
|
|
@@ -512,8 +561,53 @@ async function createExpressRouter(options) {
|
|
|
512
561
|
}
|
|
513
562
|
return router;
|
|
514
563
|
}
|
|
564
|
+
function getDateCoercionOptions(coerce, location) {
|
|
565
|
+
const enabled = coerce[location];
|
|
566
|
+
return {
|
|
567
|
+
dateTime: enabled && coerce.dateTime,
|
|
568
|
+
date: enabled && coerce.date
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
function toOpenApiPath(path3) {
|
|
572
|
+
return path3.replace(/:([^/]+)/g, "{$1}");
|
|
573
|
+
}
|
|
574
|
+
function getOpenApiOperation(openapi, route) {
|
|
575
|
+
const pathKey = toOpenApiPath(route.fullPath);
|
|
576
|
+
const pathItem = openapi.paths?.[pathKey];
|
|
577
|
+
if (!pathItem) return null;
|
|
578
|
+
return pathItem[route.httpMethod.toLowerCase()] ?? null;
|
|
579
|
+
}
|
|
580
|
+
function buildParamSchemaIndex(operation) {
|
|
581
|
+
const index = /* @__PURE__ */ new Map();
|
|
582
|
+
const params = operation?.parameters ?? [];
|
|
583
|
+
for (const param of params) {
|
|
584
|
+
if (!param?.name || !param?.in) continue;
|
|
585
|
+
if (param.schema) {
|
|
586
|
+
index.set(`${param.in}:${param.name}`, param.schema);
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
return index;
|
|
590
|
+
}
|
|
591
|
+
function getParamSchemaFromIndex(index, location, name) {
|
|
592
|
+
return index.get(`${location}:${name}`) ?? null;
|
|
593
|
+
}
|
|
594
|
+
function getRequestBodySchema(operation, contentType) {
|
|
595
|
+
const content = operation?.requestBody?.content;
|
|
596
|
+
if (!content) return null;
|
|
597
|
+
if (contentType && content[contentType]?.schema) {
|
|
598
|
+
return content[contentType].schema;
|
|
599
|
+
}
|
|
600
|
+
const first = Object.values(content)[0];
|
|
601
|
+
return first?.schema ?? null;
|
|
602
|
+
}
|
|
603
|
+
function schemaFromType(schemaType) {
|
|
604
|
+
if (!schemaType) return null;
|
|
605
|
+
return { type: schemaType };
|
|
606
|
+
}
|
|
515
607
|
function validateRequestWithPrecompiled(route, req, validators) {
|
|
516
608
|
const errors = [];
|
|
609
|
+
const deepNames = new Set(route.args.query.filter((q) => q.serialization?.style === "deepObject").map((q) => q.name));
|
|
610
|
+
const deepValues = deepNames.size > 0 ? parseDeepObjectParams(getRawQueryString(req), deepNames) : {};
|
|
517
611
|
if (route.args.body) {
|
|
518
612
|
const validator = validators[route.operationId]?.body;
|
|
519
613
|
if (validator) {
|
|
@@ -532,14 +626,10 @@ function validateRequestWithPrecompiled(route, req, validators) {
|
|
|
532
626
|
}
|
|
533
627
|
}
|
|
534
628
|
for (const q of route.args.query) {
|
|
535
|
-
const value = req.query[q.name];
|
|
629
|
+
const value = q.serialization?.style === "deepObject" ? deepValues[q.name] : req.query[q.name];
|
|
536
630
|
if (value === void 0) continue;
|
|
537
|
-
const schema = {};
|
|
538
|
-
|
|
539
|
-
const type = Array.isArray(q.schemaType) ? q.schemaType[0] : q.schemaType;
|
|
540
|
-
schema.type = type;
|
|
541
|
-
}
|
|
542
|
-
const coerced = coerceValue(value, q.schemaType);
|
|
631
|
+
const schema = schemaFromType(q.schemaType) ?? {};
|
|
632
|
+
const coerced = coerceParamValue(value, schema, { dateTime: false, date: false }, {});
|
|
543
633
|
if (Object.keys(schema).length > 0 && coerced !== void 0) {
|
|
544
634
|
errors.push({
|
|
545
635
|
path: `#/query/${q.name}`,
|
|
@@ -552,12 +642,8 @@ function validateRequestWithPrecompiled(route, req, validators) {
|
|
|
552
642
|
for (const p of route.args.path) {
|
|
553
643
|
const value = req.params[p.name];
|
|
554
644
|
if (value === void 0) continue;
|
|
555
|
-
const schema = {};
|
|
556
|
-
|
|
557
|
-
const type = Array.isArray(p.schemaType) ? p.schemaType[0] : p.schemaType;
|
|
558
|
-
schema.type = type;
|
|
559
|
-
}
|
|
560
|
-
const coerced = coerceValue(value, p.schemaType);
|
|
645
|
+
const schema = schemaFromType(p.schemaType) ?? {};
|
|
646
|
+
const coerced = coerceParamValue(value, schema, { dateTime: false, date: false }, {});
|
|
561
647
|
if (Object.keys(schema).length > 0 && coerced !== void 0) {
|
|
562
648
|
errors.push({
|
|
563
649
|
path: `#/path/${p.name}`,
|
|
@@ -575,6 +661,10 @@ function validateRequest(route, req, openapi, validator) {
|
|
|
575
661
|
const schemaName = ref.replace("#/components/schemas/", "");
|
|
576
662
|
return openapi.components.schemas[schemaName] || null;
|
|
577
663
|
}
|
|
664
|
+
const openapiOperation = getOpenApiOperation(openapi, route);
|
|
665
|
+
const paramSchemaIndex = buildParamSchemaIndex(openapiOperation);
|
|
666
|
+
const deepNames = new Set(route.args.query.filter((q) => q.serialization?.style === "deepObject").map((q) => q.name));
|
|
667
|
+
const deepValues = deepNames.size > 0 ? parseDeepObjectParams(getRawQueryString(req), deepNames) : {};
|
|
578
668
|
const errors = [];
|
|
579
669
|
if (route.args.body) {
|
|
580
670
|
const bodySchema = getSchemaByRef(route.args.body.schemaRef);
|
|
@@ -594,19 +684,24 @@ function validateRequest(route, req, openapi, validator) {
|
|
|
594
684
|
}
|
|
595
685
|
}
|
|
596
686
|
for (const q of route.args.query) {
|
|
597
|
-
const value = req.query[q.name];
|
|
598
|
-
const
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
schema
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
687
|
+
const value = q.serialization?.style === "deepObject" ? deepValues[q.name] : req.query[q.name];
|
|
688
|
+
const openapiSchema = getParamSchemaFromIndex(paramSchemaIndex, "query", q.name);
|
|
689
|
+
let schema = {};
|
|
690
|
+
if (openapiSchema) {
|
|
691
|
+
schema = resolveSchema(openapiSchema, openapi.components.schemas);
|
|
692
|
+
} else {
|
|
693
|
+
if (q.schemaType) {
|
|
694
|
+
const type = Array.isArray(q.schemaType) ? q.schemaType[0] : q.schemaType;
|
|
695
|
+
schema.type = type;
|
|
696
|
+
}
|
|
697
|
+
if (q.schemaRef && q.schemaRef.includes("Inline")) {
|
|
698
|
+
const inlineSchema = getSchemaByRef(q.schemaRef);
|
|
699
|
+
if (inlineSchema) {
|
|
700
|
+
Object.assign(schema, inlineSchema);
|
|
701
|
+
}
|
|
607
702
|
}
|
|
608
703
|
}
|
|
609
|
-
const coerced =
|
|
704
|
+
const coerced = coerceParamValue(value, schema, { dateTime: false, date: false }, openapi.components.schemas);
|
|
610
705
|
if (Object.keys(schema).length > 0 && coerced !== void 0) {
|
|
611
706
|
const validate = validator.compile(schema);
|
|
612
707
|
const valid = validate(coerced);
|
|
@@ -624,18 +719,23 @@ function validateRequest(route, req, openapi, validator) {
|
|
|
624
719
|
}
|
|
625
720
|
for (const p of route.args.path) {
|
|
626
721
|
const value = req.params[p.name];
|
|
627
|
-
const
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
schema
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
722
|
+
const openapiSchema = getParamSchemaFromIndex(paramSchemaIndex, "path", p.name);
|
|
723
|
+
let schema = {};
|
|
724
|
+
if (openapiSchema) {
|
|
725
|
+
schema = resolveSchema(openapiSchema, openapi.components.schemas);
|
|
726
|
+
} else {
|
|
727
|
+
if (p.schemaType) {
|
|
728
|
+
const type = Array.isArray(p.schemaType) ? p.schemaType[0] : p.schemaType;
|
|
729
|
+
schema.type = type;
|
|
730
|
+
}
|
|
731
|
+
if (p.schemaRef && p.schemaRef.includes("Inline")) {
|
|
732
|
+
const inlineSchema = getSchemaByRef(p.schemaRef);
|
|
733
|
+
if (inlineSchema) {
|
|
734
|
+
Object.assign(schema, inlineSchema);
|
|
735
|
+
}
|
|
636
736
|
}
|
|
637
737
|
}
|
|
638
|
-
const coerced =
|
|
738
|
+
const coerced = coerceParamValue(value, schema, { dateTime: false, date: false }, openapi.components.schemas);
|
|
639
739
|
if (Object.keys(schema).length > 0 && coerced !== void 0) {
|
|
640
740
|
const validate = validator.compile(schema);
|
|
641
741
|
const valid = validate(coerced);
|
|
@@ -653,23 +753,127 @@ function validateRequest(route, req, openapi, validator) {
|
|
|
653
753
|
}
|
|
654
754
|
return errors.length > 0 ? errors : null;
|
|
655
755
|
}
|
|
656
|
-
function
|
|
756
|
+
function resolveSchema(schema, components, seen = /* @__PURE__ */ new Set()) {
|
|
757
|
+
const ref = schema.$ref;
|
|
758
|
+
if (typeof ref !== "string" || !ref.startsWith("#/components/schemas/")) {
|
|
759
|
+
return schema;
|
|
760
|
+
}
|
|
761
|
+
const name = ref.replace("#/components/schemas/", "");
|
|
762
|
+
if (seen.has(name)) return schema;
|
|
763
|
+
const next = components[name];
|
|
764
|
+
if (!next) return schema;
|
|
765
|
+
seen.add(name);
|
|
766
|
+
return resolveSchema(next, components, seen);
|
|
767
|
+
}
|
|
768
|
+
function coerceDatesWithSchema(value, schema, dateCoercion, components) {
|
|
769
|
+
if (!schema || !dateCoercion.date && !dateCoercion.dateTime) return value;
|
|
770
|
+
return coerceWithSchema(value, schema, dateCoercion, components, { coercePrimitives: false });
|
|
771
|
+
}
|
|
772
|
+
function coerceParamValue(value, schema, dateCoercion, components) {
|
|
773
|
+
if (!schema) return value;
|
|
774
|
+
return coerceWithSchema(value, schema, dateCoercion, components, { coercePrimitives: true });
|
|
775
|
+
}
|
|
776
|
+
function coerceWithSchema(value, schema, dateCoercion, components, options) {
|
|
657
777
|
if (value === void 0 || value === null) return value;
|
|
658
|
-
|
|
659
|
-
|
|
778
|
+
if (value instanceof Date) return value;
|
|
779
|
+
const resolved = resolveSchema(schema, components);
|
|
780
|
+
const override = resolved["x-adorn-coerce"];
|
|
781
|
+
const allowDateTime = override === true ? true : override === false ? false : dateCoercion.dateTime;
|
|
782
|
+
const allowDate = override === true ? true : override === false ? false : dateCoercion.date;
|
|
783
|
+
const byFormat = coerceDateString(value, resolved, allowDateTime, allowDate);
|
|
784
|
+
if (byFormat !== value) return byFormat;
|
|
785
|
+
const allOf = resolved.allOf;
|
|
786
|
+
if (Array.isArray(allOf)) {
|
|
787
|
+
let out = value;
|
|
788
|
+
for (const entry of allOf) {
|
|
789
|
+
out = coerceWithSchema(out, entry, { dateTime: allowDateTime, date: allowDate }, components, options);
|
|
790
|
+
}
|
|
791
|
+
return out;
|
|
792
|
+
}
|
|
793
|
+
const variants = resolved.oneOf ?? resolved.anyOf;
|
|
794
|
+
if (Array.isArray(variants)) {
|
|
795
|
+
for (const entry of variants) {
|
|
796
|
+
const out = coerceWithSchema(value, entry, { dateTime: allowDateTime, date: allowDate }, components, options);
|
|
797
|
+
if (out !== value) return out;
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
const schemaType = resolved.type;
|
|
801
|
+
const types = Array.isArray(schemaType) ? schemaType : schemaType ? [schemaType] : [];
|
|
802
|
+
if ((types.includes("array") || resolved.items) && Array.isArray(value)) {
|
|
803
|
+
const itemSchema = resolved.items ?? {};
|
|
804
|
+
return value.map((item) => coerceWithSchema(item, itemSchema, { dateTime: allowDateTime, date: allowDate }, components, options));
|
|
805
|
+
}
|
|
806
|
+
if ((types.includes("object") || resolved.properties || resolved.additionalProperties) && isPlainObject(value)) {
|
|
807
|
+
const props = resolved.properties;
|
|
808
|
+
const out = { ...value };
|
|
809
|
+
if (props) {
|
|
810
|
+
for (const [key, propSchema] of Object.entries(props)) {
|
|
811
|
+
if (Object.prototype.hasOwnProperty.call(value, key)) {
|
|
812
|
+
out[key] = coerceWithSchema(value[key], propSchema, { dateTime: allowDateTime, date: allowDate }, components, options);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
const additional = resolved.additionalProperties;
|
|
817
|
+
if (additional && typeof additional === "object") {
|
|
818
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
819
|
+
if (props && Object.prototype.hasOwnProperty.call(props, key)) continue;
|
|
820
|
+
out[key] = coerceWithSchema(entry, additional, { dateTime: allowDateTime, date: allowDate }, components, options);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
return out;
|
|
824
|
+
}
|
|
825
|
+
if (options.coercePrimitives) {
|
|
826
|
+
return coercePrimitiveValue(value, types);
|
|
827
|
+
}
|
|
828
|
+
return value;
|
|
829
|
+
}
|
|
830
|
+
function coerceDateString(value, schema, allowDateTime, allowDate) {
|
|
831
|
+
if (typeof value !== "string") return value;
|
|
832
|
+
const format = schema.format;
|
|
833
|
+
const schemaType = schema.type;
|
|
834
|
+
const types = Array.isArray(schemaType) ? schemaType : schemaType ? [schemaType] : [];
|
|
835
|
+
const allowsString = types.length === 0 || types.includes("string");
|
|
836
|
+
if (format === "date-time" && allowDateTime && allowsString) {
|
|
837
|
+
const parsed = new Date(value);
|
|
838
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
839
|
+
throw new Error(`Invalid date-time: ${value}`);
|
|
840
|
+
}
|
|
841
|
+
return parsed;
|
|
842
|
+
}
|
|
843
|
+
if (format === "date" && allowDate && allowsString) {
|
|
844
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
|
845
|
+
throw new Error(`Invalid date: ${value}`);
|
|
846
|
+
}
|
|
847
|
+
const parsed = /* @__PURE__ */ new Date(`${value}T00:00:00.000Z`);
|
|
848
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
849
|
+
throw new Error(`Invalid date: ${value}`);
|
|
850
|
+
}
|
|
851
|
+
return parsed;
|
|
852
|
+
}
|
|
853
|
+
return value;
|
|
854
|
+
}
|
|
855
|
+
function coercePrimitiveValue(value, types) {
|
|
856
|
+
if (value === void 0 || value === null) return value;
|
|
857
|
+
if (types.includes("number") || types.includes("integer")) {
|
|
660
858
|
const num = Number(value);
|
|
661
|
-
if (isNaN(num)) {
|
|
859
|
+
if (Number.isNaN(num)) {
|
|
662
860
|
throw new Error(`Invalid number: ${value}`);
|
|
663
861
|
}
|
|
664
862
|
return num;
|
|
665
863
|
}
|
|
666
|
-
if (
|
|
864
|
+
if (types.includes("boolean")) {
|
|
667
865
|
if (value === "true") return true;
|
|
668
866
|
if (value === "false") return false;
|
|
867
|
+
if (typeof value === "boolean") return value;
|
|
669
868
|
throw new Error(`Invalid boolean: ${value}`);
|
|
670
869
|
}
|
|
671
870
|
return value;
|
|
672
871
|
}
|
|
872
|
+
function isPlainObject(value) {
|
|
873
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
874
|
+
const proto = Object.getPrototypeOf(value);
|
|
875
|
+
return proto === Object.prototype || proto === null;
|
|
876
|
+
}
|
|
673
877
|
function parseQueryValue(value, param) {
|
|
674
878
|
if (value === void 0 || value === null) return value;
|
|
675
879
|
const isArray = Array.isArray(param.schemaType) ? param.schemaType.includes("array") : param.schemaType === "array";
|
|
@@ -693,6 +897,69 @@ function parseQueryValue(value, param) {
|
|
|
693
897
|
}
|
|
694
898
|
return value;
|
|
695
899
|
}
|
|
900
|
+
function getRawQueryString(req) {
|
|
901
|
+
const url = req.originalUrl ?? req.url ?? "";
|
|
902
|
+
const index = url.indexOf("?");
|
|
903
|
+
if (index === -1) return "";
|
|
904
|
+
return url.slice(index + 1);
|
|
905
|
+
}
|
|
906
|
+
function parseDeepObjectParams(rawQuery, names) {
|
|
907
|
+
const out = {};
|
|
908
|
+
if (!rawQuery || names.size === 0) return out;
|
|
909
|
+
const params = new URLSearchParams(rawQuery);
|
|
910
|
+
for (const [key, value] of params.entries()) {
|
|
911
|
+
const path3 = parseBracketPath(key);
|
|
912
|
+
if (path3.length === 0) continue;
|
|
913
|
+
const root = path3[0];
|
|
914
|
+
if (!names.has(root)) continue;
|
|
915
|
+
assignDeepValue(out, path3, value);
|
|
916
|
+
}
|
|
917
|
+
return out;
|
|
918
|
+
}
|
|
919
|
+
function parseBracketPath(key) {
|
|
920
|
+
const parts = [];
|
|
921
|
+
let current = "";
|
|
922
|
+
for (let i = 0; i < key.length; i++) {
|
|
923
|
+
const ch = key[i];
|
|
924
|
+
if (ch === "[") {
|
|
925
|
+
if (current) parts.push(current);
|
|
926
|
+
current = "";
|
|
927
|
+
continue;
|
|
928
|
+
}
|
|
929
|
+
if (ch === "]") {
|
|
930
|
+
if (current) parts.push(current);
|
|
931
|
+
current = "";
|
|
932
|
+
continue;
|
|
933
|
+
}
|
|
934
|
+
current += ch;
|
|
935
|
+
}
|
|
936
|
+
if (current) parts.push(current);
|
|
937
|
+
return parts;
|
|
938
|
+
}
|
|
939
|
+
function assignDeepValue(target, path3, value) {
|
|
940
|
+
let cursor = target;
|
|
941
|
+
for (let i = 0; i < path3.length; i++) {
|
|
942
|
+
const key = path3[i];
|
|
943
|
+
if (!key) continue;
|
|
944
|
+
const isLast = i === path3.length - 1;
|
|
945
|
+
if (isLast) {
|
|
946
|
+
const existing = cursor[key];
|
|
947
|
+
if (existing === void 0) {
|
|
948
|
+
cursor[key] = value;
|
|
949
|
+
} else if (Array.isArray(existing)) {
|
|
950
|
+
existing.push(value);
|
|
951
|
+
} else {
|
|
952
|
+
cursor[key] = [existing, value];
|
|
953
|
+
}
|
|
954
|
+
return;
|
|
955
|
+
}
|
|
956
|
+
const next = cursor[key];
|
|
957
|
+
if (!isPlainObject(next)) {
|
|
958
|
+
cursor[key] = {};
|
|
959
|
+
}
|
|
960
|
+
cursor = cursor[key];
|
|
961
|
+
}
|
|
962
|
+
}
|
|
696
963
|
function parseCookies(cookieHeader) {
|
|
697
964
|
if (!cookieHeader) return {};
|
|
698
965
|
const cookies = {};
|