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.cjs
CHANGED
|
@@ -284,7 +284,8 @@ function bootstrap(options) {
|
|
|
284
284
|
swaggerPath = "/docs",
|
|
285
285
|
swaggerJsonPath = "/docs/openapi.json",
|
|
286
286
|
middleware,
|
|
287
|
-
auth
|
|
287
|
+
auth,
|
|
288
|
+
coerce
|
|
288
289
|
} = options;
|
|
289
290
|
if (controllers.length === 0) {
|
|
290
291
|
reject(new Error("At least one controller must be provided to bootstrap()."));
|
|
@@ -304,7 +305,8 @@ function bootstrap(options) {
|
|
|
304
305
|
controllers,
|
|
305
306
|
artifactsDir: absoluteArtifactsDir,
|
|
306
307
|
middleware,
|
|
307
|
-
auth
|
|
308
|
+
auth,
|
|
309
|
+
coerce
|
|
308
310
|
});
|
|
309
311
|
app.use(router);
|
|
310
312
|
if (enableSwagger) {
|
|
@@ -359,6 +361,17 @@ function bootstrap(options) {
|
|
|
359
361
|
}
|
|
360
362
|
|
|
361
363
|
// src/adapter/express/index.ts
|
|
364
|
+
function normalizeCoerceOptions(coerce) {
|
|
365
|
+
return {
|
|
366
|
+
body: coerce?.body ?? false,
|
|
367
|
+
query: coerce?.query ?? false,
|
|
368
|
+
path: coerce?.path ?? false,
|
|
369
|
+
header: coerce?.header ?? false,
|
|
370
|
+
cookie: coerce?.cookie ?? false,
|
|
371
|
+
dateTime: coerce?.dateTime ?? false,
|
|
372
|
+
date: coerce?.date ?? false
|
|
373
|
+
};
|
|
374
|
+
}
|
|
362
375
|
async function createExpressRouter(options) {
|
|
363
376
|
const { controllers, artifactsDir = ".adorn", middleware = {} } = options;
|
|
364
377
|
let manifest;
|
|
@@ -385,6 +398,7 @@ async function createExpressRouter(options) {
|
|
|
385
398
|
const validator = precompiledValidators ? null : createValidator();
|
|
386
399
|
const router = (0, import_express2.Router)();
|
|
387
400
|
const instanceCache = /* @__PURE__ */ new Map();
|
|
401
|
+
const coerce = normalizeCoerceOptions(options.coerce);
|
|
388
402
|
function getInstance(Ctor) {
|
|
389
403
|
if (!instanceCache.has(Ctor)) {
|
|
390
404
|
instanceCache.set(Ctor, new Ctor());
|
|
@@ -447,6 +461,14 @@ async function createExpressRouter(options) {
|
|
|
447
461
|
}
|
|
448
462
|
for (const route of routes) {
|
|
449
463
|
const method = route.httpMethod.toLowerCase();
|
|
464
|
+
const openapiOperation = getOpenApiOperation(openapi, route);
|
|
465
|
+
const paramSchemaIndex = buildParamSchemaIndex(openapiOperation);
|
|
466
|
+
const bodySchema = getRequestBodySchema(openapiOperation, route.args.body?.contentType) ?? (route.args.body ? getSchemaByRef(route.args.body.schemaRef) : null);
|
|
467
|
+
const coerceBodyDates = getDateCoercionOptions(coerce, "body");
|
|
468
|
+
const coerceQueryDates = getDateCoercionOptions(coerce, "query");
|
|
469
|
+
const coercePathDates = getDateCoercionOptions(coerce, "path");
|
|
470
|
+
const coerceHeaderDates = getDateCoercionOptions(coerce, "header");
|
|
471
|
+
const coerceCookieDates = getDateCoercionOptions(coerce, "cookie");
|
|
450
472
|
const middlewareChain = [];
|
|
451
473
|
if (middleware.global) {
|
|
452
474
|
middlewareChain.push(...resolveMiddleware(middleware.global, middleware.named || {}));
|
|
@@ -474,29 +496,50 @@ async function createExpressRouter(options) {
|
|
|
474
496
|
}
|
|
475
497
|
const args = [];
|
|
476
498
|
if (route.args.body) {
|
|
477
|
-
|
|
499
|
+
const coercedBody = (coerceBodyDates.date || coerceBodyDates.dateTime) && bodySchema ? coerceDatesWithSchema(req.body, bodySchema, coerceBodyDates, openapi.components.schemas) : req.body;
|
|
500
|
+
args[route.args.body.index] = coercedBody;
|
|
478
501
|
}
|
|
479
502
|
for (const pathArg of route.args.path) {
|
|
480
|
-
const
|
|
503
|
+
const rawValue = req.params[pathArg.name];
|
|
504
|
+
const paramSchema = getParamSchemaFromIndex(paramSchemaIndex, "path", pathArg.name) ?? (pathArg.schemaRef ? getSchemaByRef(pathArg.schemaRef) : null) ?? schemaFromType(pathArg.schemaType);
|
|
505
|
+
const coerced = coerceParamValue(rawValue, paramSchema, coercePathDates, openapi.components.schemas);
|
|
481
506
|
args[pathArg.index] = coerced;
|
|
482
507
|
}
|
|
483
508
|
if (route.args.query.length > 0) {
|
|
484
|
-
const
|
|
485
|
-
const
|
|
486
|
-
if (
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
const parsed = parseQueryValue(req.query[q.name], q);
|
|
496
|
-
const coerced = coerceValue(parsed, q.schemaType);
|
|
509
|
+
const deepObjectArgs = route.args.query.filter((q) => q.serialization?.style === "deepObject");
|
|
510
|
+
const standardArgs = route.args.query.filter((q) => q.serialization?.style !== "deepObject");
|
|
511
|
+
if (deepObjectArgs.length > 0) {
|
|
512
|
+
const rawQuery = getRawQueryString(req);
|
|
513
|
+
const names = new Set(deepObjectArgs.map((q) => q.name));
|
|
514
|
+
const parsedDeep = parseDeepObjectParams(rawQuery, names);
|
|
515
|
+
for (const q of deepObjectArgs) {
|
|
516
|
+
const rawValue = parsedDeep[q.name];
|
|
517
|
+
const paramSchema = getParamSchemaFromIndex(paramSchemaIndex, "query", q.name) ?? (q.schemaRef ? getSchemaByRef(q.schemaRef) : null) ?? schemaFromType(q.schemaType);
|
|
518
|
+
const baseValue = rawValue === void 0 ? {} : rawValue;
|
|
519
|
+
const coerced = coerceParamValue(baseValue, paramSchema, coerceQueryDates, openapi.components.schemas);
|
|
497
520
|
args[q.index] = coerced;
|
|
498
521
|
}
|
|
499
522
|
}
|
|
523
|
+
if (standardArgs.length > 0) {
|
|
524
|
+
const firstQueryIndex = standardArgs[0].index;
|
|
525
|
+
const allSameIndex = standardArgs.every((q) => q.index === firstQueryIndex);
|
|
526
|
+
if (allSameIndex) {
|
|
527
|
+
args[firstQueryIndex] = {};
|
|
528
|
+
for (const q of standardArgs) {
|
|
529
|
+
const parsed = parseQueryValue(req.query[q.name], q);
|
|
530
|
+
const paramSchema = getParamSchemaFromIndex(paramSchemaIndex, "query", q.name) ?? (q.schemaRef ? getSchemaByRef(q.schemaRef) : null) ?? schemaFromType(q.schemaType);
|
|
531
|
+
const coerced = coerceParamValue(parsed, paramSchema, coerceQueryDates, openapi.components.schemas);
|
|
532
|
+
args[firstQueryIndex][q.name] = coerced;
|
|
533
|
+
}
|
|
534
|
+
} else {
|
|
535
|
+
for (const q of standardArgs) {
|
|
536
|
+
const parsed = parseQueryValue(req.query[q.name], q);
|
|
537
|
+
const paramSchema = getParamSchemaFromIndex(paramSchemaIndex, "query", q.name) ?? (q.schemaRef ? getSchemaByRef(q.schemaRef) : null) ?? schemaFromType(q.schemaType);
|
|
538
|
+
const coerced = coerceParamValue(parsed, paramSchema, coerceQueryDates, openapi.components.schemas);
|
|
539
|
+
args[q.index] = coerced;
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
500
543
|
}
|
|
501
544
|
if (route.args.headers.length > 0) {
|
|
502
545
|
const firstHeaderIndex = route.args.headers[0].index;
|
|
@@ -505,12 +548,16 @@ async function createExpressRouter(options) {
|
|
|
505
548
|
args[firstHeaderIndex] = {};
|
|
506
549
|
for (const h of route.args.headers) {
|
|
507
550
|
const headerValue = req.headers[h.name.toLowerCase()];
|
|
508
|
-
|
|
551
|
+
const paramSchema = getParamSchemaFromIndex(paramSchemaIndex, "header", h.name) ?? (h.schemaRef ? getSchemaByRef(h.schemaRef) : null) ?? schemaFromType(h.schemaType);
|
|
552
|
+
const coerced = coerceParamValue(headerValue, paramSchema, coerceHeaderDates, openapi.components.schemas);
|
|
553
|
+
args[firstHeaderIndex][h.name] = coerced ?? void 0;
|
|
509
554
|
}
|
|
510
555
|
} else {
|
|
511
556
|
for (const h of route.args.headers) {
|
|
512
557
|
const headerValue = req.headers[h.name.toLowerCase()];
|
|
513
|
-
|
|
558
|
+
const paramSchema = getParamSchemaFromIndex(paramSchemaIndex, "header", h.name) ?? (h.schemaRef ? getSchemaByRef(h.schemaRef) : null) ?? schemaFromType(h.schemaType);
|
|
559
|
+
const coerced = coerceParamValue(headerValue, paramSchema, coerceHeaderDates, openapi.components.schemas);
|
|
560
|
+
args[h.index] = coerced ?? void 0;
|
|
514
561
|
}
|
|
515
562
|
}
|
|
516
563
|
}
|
|
@@ -522,13 +569,15 @@ async function createExpressRouter(options) {
|
|
|
522
569
|
args[firstCookieIndex] = {};
|
|
523
570
|
for (const c of route.args.cookies) {
|
|
524
571
|
const cookieValue = cookies[c.name];
|
|
525
|
-
const
|
|
572
|
+
const paramSchema = getParamSchemaFromIndex(paramSchemaIndex, "cookie", c.name) ?? (c.schemaRef ? getSchemaByRef(c.schemaRef) : null) ?? schemaFromType(c.schemaType);
|
|
573
|
+
const coerced = coerceParamValue(cookieValue, paramSchema, coerceCookieDates, openapi.components.schemas);
|
|
526
574
|
args[firstCookieIndex][c.name] = coerced;
|
|
527
575
|
}
|
|
528
576
|
} else {
|
|
529
577
|
for (const c of route.args.cookies) {
|
|
530
578
|
const cookieValue = cookies[c.name];
|
|
531
|
-
const
|
|
579
|
+
const paramSchema = getParamSchemaFromIndex(paramSchemaIndex, "cookie", c.name) ?? (c.schemaRef ? getSchemaByRef(c.schemaRef) : null) ?? schemaFromType(c.schemaType);
|
|
580
|
+
const coerced = coerceParamValue(cookieValue, paramSchema, coerceCookieDates, openapi.components.schemas);
|
|
532
581
|
args[c.index] = coerced;
|
|
533
582
|
}
|
|
534
583
|
}
|
|
@@ -547,8 +596,53 @@ async function createExpressRouter(options) {
|
|
|
547
596
|
}
|
|
548
597
|
return router;
|
|
549
598
|
}
|
|
599
|
+
function getDateCoercionOptions(coerce, location) {
|
|
600
|
+
const enabled = coerce[location];
|
|
601
|
+
return {
|
|
602
|
+
dateTime: enabled && coerce.dateTime,
|
|
603
|
+
date: enabled && coerce.date
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
function toOpenApiPath(path3) {
|
|
607
|
+
return path3.replace(/:([^/]+)/g, "{$1}");
|
|
608
|
+
}
|
|
609
|
+
function getOpenApiOperation(openapi, route) {
|
|
610
|
+
const pathKey = toOpenApiPath(route.fullPath);
|
|
611
|
+
const pathItem = openapi.paths?.[pathKey];
|
|
612
|
+
if (!pathItem) return null;
|
|
613
|
+
return pathItem[route.httpMethod.toLowerCase()] ?? null;
|
|
614
|
+
}
|
|
615
|
+
function buildParamSchemaIndex(operation) {
|
|
616
|
+
const index = /* @__PURE__ */ new Map();
|
|
617
|
+
const params = operation?.parameters ?? [];
|
|
618
|
+
for (const param of params) {
|
|
619
|
+
if (!param?.name || !param?.in) continue;
|
|
620
|
+
if (param.schema) {
|
|
621
|
+
index.set(`${param.in}:${param.name}`, param.schema);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
return index;
|
|
625
|
+
}
|
|
626
|
+
function getParamSchemaFromIndex(index, location, name) {
|
|
627
|
+
return index.get(`${location}:${name}`) ?? null;
|
|
628
|
+
}
|
|
629
|
+
function getRequestBodySchema(operation, contentType) {
|
|
630
|
+
const content = operation?.requestBody?.content;
|
|
631
|
+
if (!content) return null;
|
|
632
|
+
if (contentType && content[contentType]?.schema) {
|
|
633
|
+
return content[contentType].schema;
|
|
634
|
+
}
|
|
635
|
+
const first = Object.values(content)[0];
|
|
636
|
+
return first?.schema ?? null;
|
|
637
|
+
}
|
|
638
|
+
function schemaFromType(schemaType) {
|
|
639
|
+
if (!schemaType) return null;
|
|
640
|
+
return { type: schemaType };
|
|
641
|
+
}
|
|
550
642
|
function validateRequestWithPrecompiled(route, req, validators) {
|
|
551
643
|
const errors = [];
|
|
644
|
+
const deepNames = new Set(route.args.query.filter((q) => q.serialization?.style === "deepObject").map((q) => q.name));
|
|
645
|
+
const deepValues = deepNames.size > 0 ? parseDeepObjectParams(getRawQueryString(req), deepNames) : {};
|
|
552
646
|
if (route.args.body) {
|
|
553
647
|
const validator = validators[route.operationId]?.body;
|
|
554
648
|
if (validator) {
|
|
@@ -567,14 +661,10 @@ function validateRequestWithPrecompiled(route, req, validators) {
|
|
|
567
661
|
}
|
|
568
662
|
}
|
|
569
663
|
for (const q of route.args.query) {
|
|
570
|
-
const value = req.query[q.name];
|
|
664
|
+
const value = q.serialization?.style === "deepObject" ? deepValues[q.name] : req.query[q.name];
|
|
571
665
|
if (value === void 0) continue;
|
|
572
|
-
const schema = {};
|
|
573
|
-
|
|
574
|
-
const type = Array.isArray(q.schemaType) ? q.schemaType[0] : q.schemaType;
|
|
575
|
-
schema.type = type;
|
|
576
|
-
}
|
|
577
|
-
const coerced = coerceValue(value, q.schemaType);
|
|
666
|
+
const schema = schemaFromType(q.schemaType) ?? {};
|
|
667
|
+
const coerced = coerceParamValue(value, schema, { dateTime: false, date: false }, {});
|
|
578
668
|
if (Object.keys(schema).length > 0 && coerced !== void 0) {
|
|
579
669
|
errors.push({
|
|
580
670
|
path: `#/query/${q.name}`,
|
|
@@ -587,12 +677,8 @@ function validateRequestWithPrecompiled(route, req, validators) {
|
|
|
587
677
|
for (const p of route.args.path) {
|
|
588
678
|
const value = req.params[p.name];
|
|
589
679
|
if (value === void 0) continue;
|
|
590
|
-
const schema = {};
|
|
591
|
-
|
|
592
|
-
const type = Array.isArray(p.schemaType) ? p.schemaType[0] : p.schemaType;
|
|
593
|
-
schema.type = type;
|
|
594
|
-
}
|
|
595
|
-
const coerced = coerceValue(value, p.schemaType);
|
|
680
|
+
const schema = schemaFromType(p.schemaType) ?? {};
|
|
681
|
+
const coerced = coerceParamValue(value, schema, { dateTime: false, date: false }, {});
|
|
596
682
|
if (Object.keys(schema).length > 0 && coerced !== void 0) {
|
|
597
683
|
errors.push({
|
|
598
684
|
path: `#/path/${p.name}`,
|
|
@@ -610,6 +696,10 @@ function validateRequest(route, req, openapi, validator) {
|
|
|
610
696
|
const schemaName = ref.replace("#/components/schemas/", "");
|
|
611
697
|
return openapi.components.schemas[schemaName] || null;
|
|
612
698
|
}
|
|
699
|
+
const openapiOperation = getOpenApiOperation(openapi, route);
|
|
700
|
+
const paramSchemaIndex = buildParamSchemaIndex(openapiOperation);
|
|
701
|
+
const deepNames = new Set(route.args.query.filter((q) => q.serialization?.style === "deepObject").map((q) => q.name));
|
|
702
|
+
const deepValues = deepNames.size > 0 ? parseDeepObjectParams(getRawQueryString(req), deepNames) : {};
|
|
613
703
|
const errors = [];
|
|
614
704
|
if (route.args.body) {
|
|
615
705
|
const bodySchema = getSchemaByRef(route.args.body.schemaRef);
|
|
@@ -629,19 +719,24 @@ function validateRequest(route, req, openapi, validator) {
|
|
|
629
719
|
}
|
|
630
720
|
}
|
|
631
721
|
for (const q of route.args.query) {
|
|
632
|
-
const value = req.query[q.name];
|
|
633
|
-
const
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
schema
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
722
|
+
const value = q.serialization?.style === "deepObject" ? deepValues[q.name] : req.query[q.name];
|
|
723
|
+
const openapiSchema = getParamSchemaFromIndex(paramSchemaIndex, "query", q.name);
|
|
724
|
+
let schema = {};
|
|
725
|
+
if (openapiSchema) {
|
|
726
|
+
schema = resolveSchema(openapiSchema, openapi.components.schemas);
|
|
727
|
+
} else {
|
|
728
|
+
if (q.schemaType) {
|
|
729
|
+
const type = Array.isArray(q.schemaType) ? q.schemaType[0] : q.schemaType;
|
|
730
|
+
schema.type = type;
|
|
731
|
+
}
|
|
732
|
+
if (q.schemaRef && q.schemaRef.includes("Inline")) {
|
|
733
|
+
const inlineSchema = getSchemaByRef(q.schemaRef);
|
|
734
|
+
if (inlineSchema) {
|
|
735
|
+
Object.assign(schema, inlineSchema);
|
|
736
|
+
}
|
|
642
737
|
}
|
|
643
738
|
}
|
|
644
|
-
const coerced =
|
|
739
|
+
const coerced = coerceParamValue(value, schema, { dateTime: false, date: false }, openapi.components.schemas);
|
|
645
740
|
if (Object.keys(schema).length > 0 && coerced !== void 0) {
|
|
646
741
|
const validate = validator.compile(schema);
|
|
647
742
|
const valid = validate(coerced);
|
|
@@ -659,18 +754,23 @@ function validateRequest(route, req, openapi, validator) {
|
|
|
659
754
|
}
|
|
660
755
|
for (const p of route.args.path) {
|
|
661
756
|
const value = req.params[p.name];
|
|
662
|
-
const
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
schema
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
757
|
+
const openapiSchema = getParamSchemaFromIndex(paramSchemaIndex, "path", p.name);
|
|
758
|
+
let schema = {};
|
|
759
|
+
if (openapiSchema) {
|
|
760
|
+
schema = resolveSchema(openapiSchema, openapi.components.schemas);
|
|
761
|
+
} else {
|
|
762
|
+
if (p.schemaType) {
|
|
763
|
+
const type = Array.isArray(p.schemaType) ? p.schemaType[0] : p.schemaType;
|
|
764
|
+
schema.type = type;
|
|
765
|
+
}
|
|
766
|
+
if (p.schemaRef && p.schemaRef.includes("Inline")) {
|
|
767
|
+
const inlineSchema = getSchemaByRef(p.schemaRef);
|
|
768
|
+
if (inlineSchema) {
|
|
769
|
+
Object.assign(schema, inlineSchema);
|
|
770
|
+
}
|
|
671
771
|
}
|
|
672
772
|
}
|
|
673
|
-
const coerced =
|
|
773
|
+
const coerced = coerceParamValue(value, schema, { dateTime: false, date: false }, openapi.components.schemas);
|
|
674
774
|
if (Object.keys(schema).length > 0 && coerced !== void 0) {
|
|
675
775
|
const validate = validator.compile(schema);
|
|
676
776
|
const valid = validate(coerced);
|
|
@@ -688,23 +788,127 @@ function validateRequest(route, req, openapi, validator) {
|
|
|
688
788
|
}
|
|
689
789
|
return errors.length > 0 ? errors : null;
|
|
690
790
|
}
|
|
691
|
-
function
|
|
791
|
+
function resolveSchema(schema, components, seen = /* @__PURE__ */ new Set()) {
|
|
792
|
+
const ref = schema.$ref;
|
|
793
|
+
if (typeof ref !== "string" || !ref.startsWith("#/components/schemas/")) {
|
|
794
|
+
return schema;
|
|
795
|
+
}
|
|
796
|
+
const name = ref.replace("#/components/schemas/", "");
|
|
797
|
+
if (seen.has(name)) return schema;
|
|
798
|
+
const next = components[name];
|
|
799
|
+
if (!next) return schema;
|
|
800
|
+
seen.add(name);
|
|
801
|
+
return resolveSchema(next, components, seen);
|
|
802
|
+
}
|
|
803
|
+
function coerceDatesWithSchema(value, schema, dateCoercion, components) {
|
|
804
|
+
if (!schema || !dateCoercion.date && !dateCoercion.dateTime) return value;
|
|
805
|
+
return coerceWithSchema(value, schema, dateCoercion, components, { coercePrimitives: false });
|
|
806
|
+
}
|
|
807
|
+
function coerceParamValue(value, schema, dateCoercion, components) {
|
|
808
|
+
if (!schema) return value;
|
|
809
|
+
return coerceWithSchema(value, schema, dateCoercion, components, { coercePrimitives: true });
|
|
810
|
+
}
|
|
811
|
+
function coerceWithSchema(value, schema, dateCoercion, components, options) {
|
|
692
812
|
if (value === void 0 || value === null) return value;
|
|
693
|
-
|
|
694
|
-
|
|
813
|
+
if (value instanceof Date) return value;
|
|
814
|
+
const resolved = resolveSchema(schema, components);
|
|
815
|
+
const override = resolved["x-adorn-coerce"];
|
|
816
|
+
const allowDateTime = override === true ? true : override === false ? false : dateCoercion.dateTime;
|
|
817
|
+
const allowDate = override === true ? true : override === false ? false : dateCoercion.date;
|
|
818
|
+
const byFormat = coerceDateString(value, resolved, allowDateTime, allowDate);
|
|
819
|
+
if (byFormat !== value) return byFormat;
|
|
820
|
+
const allOf = resolved.allOf;
|
|
821
|
+
if (Array.isArray(allOf)) {
|
|
822
|
+
let out = value;
|
|
823
|
+
for (const entry of allOf) {
|
|
824
|
+
out = coerceWithSchema(out, entry, { dateTime: allowDateTime, date: allowDate }, components, options);
|
|
825
|
+
}
|
|
826
|
+
return out;
|
|
827
|
+
}
|
|
828
|
+
const variants = resolved.oneOf ?? resolved.anyOf;
|
|
829
|
+
if (Array.isArray(variants)) {
|
|
830
|
+
for (const entry of variants) {
|
|
831
|
+
const out = coerceWithSchema(value, entry, { dateTime: allowDateTime, date: allowDate }, components, options);
|
|
832
|
+
if (out !== value) return out;
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
const schemaType = resolved.type;
|
|
836
|
+
const types = Array.isArray(schemaType) ? schemaType : schemaType ? [schemaType] : [];
|
|
837
|
+
if ((types.includes("array") || resolved.items) && Array.isArray(value)) {
|
|
838
|
+
const itemSchema = resolved.items ?? {};
|
|
839
|
+
return value.map((item) => coerceWithSchema(item, itemSchema, { dateTime: allowDateTime, date: allowDate }, components, options));
|
|
840
|
+
}
|
|
841
|
+
if ((types.includes("object") || resolved.properties || resolved.additionalProperties) && isPlainObject(value)) {
|
|
842
|
+
const props = resolved.properties;
|
|
843
|
+
const out = { ...value };
|
|
844
|
+
if (props) {
|
|
845
|
+
for (const [key, propSchema] of Object.entries(props)) {
|
|
846
|
+
if (Object.prototype.hasOwnProperty.call(value, key)) {
|
|
847
|
+
out[key] = coerceWithSchema(value[key], propSchema, { dateTime: allowDateTime, date: allowDate }, components, options);
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
const additional = resolved.additionalProperties;
|
|
852
|
+
if (additional && typeof additional === "object") {
|
|
853
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
854
|
+
if (props && Object.prototype.hasOwnProperty.call(props, key)) continue;
|
|
855
|
+
out[key] = coerceWithSchema(entry, additional, { dateTime: allowDateTime, date: allowDate }, components, options);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
return out;
|
|
859
|
+
}
|
|
860
|
+
if (options.coercePrimitives) {
|
|
861
|
+
return coercePrimitiveValue(value, types);
|
|
862
|
+
}
|
|
863
|
+
return value;
|
|
864
|
+
}
|
|
865
|
+
function coerceDateString(value, schema, allowDateTime, allowDate) {
|
|
866
|
+
if (typeof value !== "string") return value;
|
|
867
|
+
const format = schema.format;
|
|
868
|
+
const schemaType = schema.type;
|
|
869
|
+
const types = Array.isArray(schemaType) ? schemaType : schemaType ? [schemaType] : [];
|
|
870
|
+
const allowsString = types.length === 0 || types.includes("string");
|
|
871
|
+
if (format === "date-time" && allowDateTime && allowsString) {
|
|
872
|
+
const parsed = new Date(value);
|
|
873
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
874
|
+
throw new Error(`Invalid date-time: ${value}`);
|
|
875
|
+
}
|
|
876
|
+
return parsed;
|
|
877
|
+
}
|
|
878
|
+
if (format === "date" && allowDate && allowsString) {
|
|
879
|
+
if (!/^\d{4}-\d{2}-\d{2}$/.test(value)) {
|
|
880
|
+
throw new Error(`Invalid date: ${value}`);
|
|
881
|
+
}
|
|
882
|
+
const parsed = /* @__PURE__ */ new Date(`${value}T00:00:00.000Z`);
|
|
883
|
+
if (Number.isNaN(parsed.getTime())) {
|
|
884
|
+
throw new Error(`Invalid date: ${value}`);
|
|
885
|
+
}
|
|
886
|
+
return parsed;
|
|
887
|
+
}
|
|
888
|
+
return value;
|
|
889
|
+
}
|
|
890
|
+
function coercePrimitiveValue(value, types) {
|
|
891
|
+
if (value === void 0 || value === null) return value;
|
|
892
|
+
if (types.includes("number") || types.includes("integer")) {
|
|
695
893
|
const num = Number(value);
|
|
696
|
-
if (isNaN(num)) {
|
|
894
|
+
if (Number.isNaN(num)) {
|
|
697
895
|
throw new Error(`Invalid number: ${value}`);
|
|
698
896
|
}
|
|
699
897
|
return num;
|
|
700
898
|
}
|
|
701
|
-
if (
|
|
899
|
+
if (types.includes("boolean")) {
|
|
702
900
|
if (value === "true") return true;
|
|
703
901
|
if (value === "false") return false;
|
|
902
|
+
if (typeof value === "boolean") return value;
|
|
704
903
|
throw new Error(`Invalid boolean: ${value}`);
|
|
705
904
|
}
|
|
706
905
|
return value;
|
|
707
906
|
}
|
|
907
|
+
function isPlainObject(value) {
|
|
908
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
909
|
+
const proto = Object.getPrototypeOf(value);
|
|
910
|
+
return proto === Object.prototype || proto === null;
|
|
911
|
+
}
|
|
708
912
|
function parseQueryValue(value, param) {
|
|
709
913
|
if (value === void 0 || value === null) return value;
|
|
710
914
|
const isArray = Array.isArray(param.schemaType) ? param.schemaType.includes("array") : param.schemaType === "array";
|
|
@@ -728,6 +932,69 @@ function parseQueryValue(value, param) {
|
|
|
728
932
|
}
|
|
729
933
|
return value;
|
|
730
934
|
}
|
|
935
|
+
function getRawQueryString(req) {
|
|
936
|
+
const url = req.originalUrl ?? req.url ?? "";
|
|
937
|
+
const index = url.indexOf("?");
|
|
938
|
+
if (index === -1) return "";
|
|
939
|
+
return url.slice(index + 1);
|
|
940
|
+
}
|
|
941
|
+
function parseDeepObjectParams(rawQuery, names) {
|
|
942
|
+
const out = {};
|
|
943
|
+
if (!rawQuery || names.size === 0) return out;
|
|
944
|
+
const params = new URLSearchParams(rawQuery);
|
|
945
|
+
for (const [key, value] of params.entries()) {
|
|
946
|
+
const path3 = parseBracketPath(key);
|
|
947
|
+
if (path3.length === 0) continue;
|
|
948
|
+
const root = path3[0];
|
|
949
|
+
if (!names.has(root)) continue;
|
|
950
|
+
assignDeepValue(out, path3, value);
|
|
951
|
+
}
|
|
952
|
+
return out;
|
|
953
|
+
}
|
|
954
|
+
function parseBracketPath(key) {
|
|
955
|
+
const parts = [];
|
|
956
|
+
let current = "";
|
|
957
|
+
for (let i = 0; i < key.length; i++) {
|
|
958
|
+
const ch = key[i];
|
|
959
|
+
if (ch === "[") {
|
|
960
|
+
if (current) parts.push(current);
|
|
961
|
+
current = "";
|
|
962
|
+
continue;
|
|
963
|
+
}
|
|
964
|
+
if (ch === "]") {
|
|
965
|
+
if (current) parts.push(current);
|
|
966
|
+
current = "";
|
|
967
|
+
continue;
|
|
968
|
+
}
|
|
969
|
+
current += ch;
|
|
970
|
+
}
|
|
971
|
+
if (current) parts.push(current);
|
|
972
|
+
return parts;
|
|
973
|
+
}
|
|
974
|
+
function assignDeepValue(target, path3, value) {
|
|
975
|
+
let cursor = target;
|
|
976
|
+
for (let i = 0; i < path3.length; i++) {
|
|
977
|
+
const key = path3[i];
|
|
978
|
+
if (!key) continue;
|
|
979
|
+
const isLast = i === path3.length - 1;
|
|
980
|
+
if (isLast) {
|
|
981
|
+
const existing = cursor[key];
|
|
982
|
+
if (existing === void 0) {
|
|
983
|
+
cursor[key] = value;
|
|
984
|
+
} else if (Array.isArray(existing)) {
|
|
985
|
+
existing.push(value);
|
|
986
|
+
} else {
|
|
987
|
+
cursor[key] = [existing, value];
|
|
988
|
+
}
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
const next = cursor[key];
|
|
992
|
+
if (!isPlainObject(next)) {
|
|
993
|
+
cursor[key] = {};
|
|
994
|
+
}
|
|
995
|
+
cursor = cursor[key];
|
|
996
|
+
}
|
|
997
|
+
}
|
|
731
998
|
function parseCookies(cookieHeader) {
|
|
732
999
|
if (!cookieHeader) return {};
|
|
733
1000
|
const cookies = {};
|