typespec-hono 0.19.0 → 0.20.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/dist/src/app.js +43 -1
- package/dist/src/runtime.d.ts +9 -1
- package/package.json +2 -2
- package/src/runtime.ts +9 -1
package/dist/src/app.js
CHANGED
|
@@ -512,7 +512,49 @@ type Fields<T> = string extends keyof T ? ([T[string]] extends [never] ? unknown
|
|
|
512
512
|
* here on its own, because this signature is derived from `z.infer` rather than from those
|
|
513
513
|
* types - which is exactly the half-fix `test/openmodel/` exists to catch.
|
|
514
514
|
*/
|
|
515
|
-
|
|
515
|
+
/**
|
|
516
|
+
* **The ENVELOPE a handler has to be able to say, beside the body it returns.**
|
|
517
|
+
*
|
|
518
|
+
* `@statusCode` and `@header` properties are stripped from the body schema - correctly, they
|
|
519
|
+
* are not body - so a return type derived from that schema alone could not carry them. The arms
|
|
520
|
+
* name them anyway: `{ headers: [{ property: "correlationId" }] }` tells `respond` to read a
|
|
521
|
+
* property off the returned value, and `when: { property: "statusCode" }` tells it which arm
|
|
522
|
+
* the handler meant. Measured before this existed, on `payload__head`:
|
|
523
|
+
* `Awaitable<Result<void>>` against an arm naming two header properties, so the emitter
|
|
524
|
+
* published an envelope contract nothing could satisfy.
|
|
525
|
+
*
|
|
526
|
+
* Only the SUCCESS statuses count. `responseHeaders` covers error responses too, and a header
|
|
527
|
+
* declared on a 404 is the error body's business rather than something a handler returns.
|
|
528
|
+
*/
|
|
529
|
+
const successStatuses = route.statusSelector?.statuses ?? [route.statusCode];
|
|
530
|
+
const envelope = [];
|
|
531
|
+
if (route.statusSelector !== undefined) {
|
|
532
|
+
envelope.push(`${objectKey(route.statusSelector.property)}: ${route.statusSelector.statuses.join(" | ")}`);
|
|
533
|
+
}
|
|
534
|
+
const headerEntries = route.responseHeaders.filter((entry) => successStatuses.includes(entry.status));
|
|
535
|
+
const declaredOn = new Map();
|
|
536
|
+
for (const entry of headerEntries) {
|
|
537
|
+
for (const header of entry.headers) {
|
|
538
|
+
const seen = declaredOn.get(header.property);
|
|
539
|
+
declaredOn.set(header.property, {
|
|
540
|
+
count: (seen?.count ?? 0) + 1,
|
|
541
|
+
type: header.type,
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
for (const [property, { count, type }] of declaredOn) {
|
|
546
|
+
// Required only where EVERY success status declares it; otherwise the handler cannot know
|
|
547
|
+
// which status it is answering with until it has chosen one.
|
|
548
|
+
const optional = count === headerEntries.length && headerEntries.length === successStatuses.length;
|
|
549
|
+
envelope.push(`${objectKey(property)}${optional ? "" : "?"}: ${type}`);
|
|
550
|
+
}
|
|
551
|
+
const envelopeType = envelope.length === 0 ? undefined : `{ ${envelope.join("; ")} }`;
|
|
552
|
+
const body = names.response === undefined ? undefined : `Produced<z.infer<typeof ${names.response}>>`;
|
|
553
|
+
const output = body === undefined
|
|
554
|
+
? (envelopeType ?? "void")
|
|
555
|
+
: envelopeType === undefined
|
|
556
|
+
? body
|
|
557
|
+
: `${body} & ${envelopeType}`;
|
|
516
558
|
const signature = `ctx: Ctx, input: ${input ?? EMPTY_INPUT}`;
|
|
517
559
|
const doc = route.summary === undefined ? "" : `\t/** ${route.summary} */\n`;
|
|
518
560
|
return `${doc}\t${route.operationId}(${signature}): Awaitable<Result<${output}>>;`;
|
package/dist/src/runtime.d.ts
CHANGED
|
@@ -36,7 +36,15 @@ export interface ResponseArm {
|
|
|
36
36
|
}[];
|
|
37
37
|
readonly when?: {
|
|
38
38
|
readonly property: string;
|
|
39
|
-
|
|
39
|
+
/**
|
|
40
|
+
* **A number too, because a `@statusCode` union selects by the status itself.**
|
|
41
|
+
*
|
|
42
|
+
* `model Created { @statusCode statusCode: 200 | 201 }` names the property that chooses, and
|
|
43
|
+
* its values are the statuses. The discriminator case carries a boolean or string literal off
|
|
44
|
+
* the body instead; both are the same question - which arm did the handler mean - so both use
|
|
45
|
+
* this one field.
|
|
46
|
+
*/
|
|
47
|
+
readonly value: boolean | number | string;
|
|
40
48
|
};
|
|
41
49
|
}
|
|
42
50
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "typespec-hono",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "TypeSpec emitter: generate a Hono server, and the Zod validators it enforces, from an HTTP service definition, agreeing with the OpenAPI document @typespec/openapi3 publishes from the same source.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare-workers",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"provenance": true
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"typespec-http-zod": "^0.
|
|
47
|
+
"typespec-http-zod": "^0.22.0"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@hono/zod-openapi": "^1.4.0",
|
package/src/runtime.ts
CHANGED
|
@@ -34,7 +34,15 @@ export interface ResponseArm {
|
|
|
34
34
|
readonly headers?: readonly { readonly name: string; readonly property: string }[];
|
|
35
35
|
readonly when?: {
|
|
36
36
|
readonly property: string;
|
|
37
|
-
|
|
37
|
+
/**
|
|
38
|
+
* **A number too, because a `@statusCode` union selects by the status itself.**
|
|
39
|
+
*
|
|
40
|
+
* `model Created { @statusCode statusCode: 200 | 201 }` names the property that chooses, and
|
|
41
|
+
* its values are the statuses. The discriminator case carries a boolean or string literal off
|
|
42
|
+
* the body instead; both are the same question - which arm did the handler mean - so both use
|
|
43
|
+
* this one field.
|
|
44
|
+
*/
|
|
45
|
+
readonly value: boolean | number | string;
|
|
38
46
|
};
|
|
39
47
|
}
|
|
40
48
|
|