typespec-hono 0.7.0 → 0.9.1
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.d.ts +14 -1
- package/dist/src/app.js +26 -6
- package/dist/src/runtime.d.ts +22 -0
- package/package.json +2 -2
- package/src/runtime.ts +19 -0
package/dist/src/app.d.ts
CHANGED
|
@@ -23,7 +23,20 @@ import { type EmittedRoute, type EmittedService } from "typespec-http-zod";
|
|
|
23
23
|
* operation (validators included) over a template no router could mount. What a request body must
|
|
24
24
|
* look like does not depend on that.
|
|
25
25
|
*/
|
|
26
|
-
export declare function toHonoPath(template: string, refuse: (template: string, name: string) => void
|
|
26
|
+
export declare function toHonoPath(template: string, refuse: (template: string, name: string) => void,
|
|
27
|
+
/**
|
|
28
|
+
* Wire names the document says carry RFC 6570 reserved expansion, so their value may contain `/`.
|
|
29
|
+
*
|
|
30
|
+
* **A hierarchical identifier is ONE value, not several segments.** An Obsidian note is
|
|
31
|
+
* `areas/health.md`; an S3 key and a GitHub file path are the same shape. A router that stops at
|
|
32
|
+
* the first `/` binds `areas` and 404s the rest. Hono spells the greedy form `:name{.+}`.
|
|
33
|
+
*
|
|
34
|
+
* Read from `EmittedRoute.reservedPathParameters`, which the library resolves from `allowReserved`
|
|
35
|
+
* on the parameter. **Never from the template**: the operator does not survive to `route.path`,
|
|
36
|
+
* `@typespec/http` strips it, and it can also be set with no operator in the template at all, so
|
|
37
|
+
* the template is a derived artefact rather than the source of truth.
|
|
38
|
+
*/
|
|
39
|
+
reserved?: ReadonlySet<string>): string;
|
|
27
40
|
/** The one thing a Hono server cannot express, handed back rather than thrown. */
|
|
28
41
|
export interface RenderRefusals {
|
|
29
42
|
readonly unsupportedPathTemplate: (route: EmittedRoute, template: string, name: string) => void;
|
package/dist/src/app.js
CHANGED
|
@@ -29,12 +29,32 @@ const PLAIN_PATH_PARAMETER = /^[A-Za-z0-9_.~-]+$/;
|
|
|
29
29
|
* operation (validators included) over a template no router could mount. What a request body must
|
|
30
30
|
* look like does not depend on that.
|
|
31
31
|
*/
|
|
32
|
-
export function toHonoPath(template, refuse
|
|
32
|
+
export function toHonoPath(template, refuse,
|
|
33
|
+
/**
|
|
34
|
+
* Wire names the document says carry RFC 6570 reserved expansion, so their value may contain `/`.
|
|
35
|
+
*
|
|
36
|
+
* **A hierarchical identifier is ONE value, not several segments.** An Obsidian note is
|
|
37
|
+
* `areas/health.md`; an S3 key and a GitHub file path are the same shape. A router that stops at
|
|
38
|
+
* the first `/` binds `areas` and 404s the rest. Hono spells the greedy form `:name{.+}`.
|
|
39
|
+
*
|
|
40
|
+
* Read from `EmittedRoute.reservedPathParameters`, which the library resolves from `allowReserved`
|
|
41
|
+
* on the parameter. **Never from the template**: the operator does not survive to `route.path`,
|
|
42
|
+
* `@typespec/http` strips it, and it can also be set with no operator in the template at all, so
|
|
43
|
+
* the template is a derived artefact rather than the source of truth.
|
|
44
|
+
*/
|
|
45
|
+
reserved = new Set()) {
|
|
33
46
|
return template.replace(/\{([^}]+)\}/g, (match, name) => {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
47
|
+
/**
|
|
48
|
+
* **The name check comes FIRST and is unconditional.** A name Hono cannot carry is refused
|
|
49
|
+
* whether or not it is reserved: greedy matching does not make a space or a `+` in a parameter
|
|
50
|
+
* name expressible, and letting one through because another flag was set would mount a route
|
|
51
|
+
* matching the wrong requests rather than one that fails.
|
|
52
|
+
*/
|
|
53
|
+
if (!PLAIN_PATH_PARAMETER.test(name)) {
|
|
54
|
+
refuse(template, name);
|
|
55
|
+
return match;
|
|
56
|
+
}
|
|
57
|
+
return reserved.has(name) ? `:${name}{.+}` : `:${name}`;
|
|
38
58
|
});
|
|
39
59
|
}
|
|
40
60
|
/**
|
|
@@ -386,7 +406,7 @@ securityFor) {
|
|
|
386
406
|
/** A HEAD with no GET beside it: registered under GET, and guarded so only a HEAD reaches it. */
|
|
387
407
|
const headOnly = plainGroups.length === 0 && headGroups.length > 0;
|
|
388
408
|
const method = HONO_METHOD[registrationVerbOf(route.verb)] ?? "on";
|
|
389
|
-
const path = toHonoPath(route.path, (template, name) => refuse.unsupportedPathTemplate(route, template, name));
|
|
409
|
+
const path = toHonoPath(route.path, (template, name) => refuse.unsupportedPathTemplate(route, template, name), new Set(route.reservedPathParameters));
|
|
390
410
|
/**
|
|
391
411
|
* A route inside a sub-app is registered RELATIVE to the prefix it is mounted at.
|
|
392
412
|
*
|
package/dist/src/runtime.d.ts
CHANGED
|
@@ -12,6 +12,28 @@ import type { input, output, ZodType } from "zod";
|
|
|
12
12
|
export interface ResponseArm {
|
|
13
13
|
readonly status: number | "default" | `${1 | 2 | 3 | 4 | 5}XX`;
|
|
14
14
|
readonly schema: ZodType | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* The media types this response offers, where the document names MORE than one.
|
|
17
|
+
*
|
|
18
|
+
* Absent where it names one, which is what an application already assumes, so "one type" and
|
|
19
|
+
* "not carried" are the same state rather than two to tell apart. Present, it is the set to
|
|
20
|
+
* negotiate against: `selectContentType` takes the caller's `Accept` and these.
|
|
21
|
+
*/
|
|
22
|
+
readonly contentTypes?: readonly string[];
|
|
23
|
+
/**
|
|
24
|
+
* The headers this response declares, as the document publishes them.
|
|
25
|
+
*
|
|
26
|
+
* **Two names, because two different things need them.** `name` is the WIRE name, which is what
|
|
27
|
+
* the response sets; `property` is the name on the value the handler returned, which is where the
|
|
28
|
+
* value is read from. `@header("x-correlation-id") correlationId: string` is `x-correlation-id`
|
|
29
|
+
* on the wire and `correlationId` in the result, and they differ for any header with a hyphen.
|
|
30
|
+
*
|
|
31
|
+
* Absent where the response declares none.
|
|
32
|
+
*/
|
|
33
|
+
readonly headers?: readonly {
|
|
34
|
+
readonly name: string;
|
|
35
|
+
readonly property: string;
|
|
36
|
+
}[];
|
|
15
37
|
readonly when?: {
|
|
16
38
|
readonly property: string;
|
|
17
39
|
readonly value: boolean | string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "typespec-hono",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
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.11.0"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@hono/zod-openapi": "^1.4.0",
|
package/src/runtime.ts
CHANGED
|
@@ -14,6 +14,25 @@ import type { input, output, ZodType } from "zod";
|
|
|
14
14
|
export interface ResponseArm {
|
|
15
15
|
readonly status: number | "default" | `${1 | 2 | 3 | 4 | 5}XX`;
|
|
16
16
|
readonly schema: ZodType | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* The media types this response offers, where the document names MORE than one.
|
|
19
|
+
*
|
|
20
|
+
* Absent where it names one, which is what an application already assumes, so "one type" and
|
|
21
|
+
* "not carried" are the same state rather than two to tell apart. Present, it is the set to
|
|
22
|
+
* negotiate against: `selectContentType` takes the caller's `Accept` and these.
|
|
23
|
+
*/
|
|
24
|
+
readonly contentTypes?: readonly string[];
|
|
25
|
+
/**
|
|
26
|
+
* The headers this response declares, as the document publishes them.
|
|
27
|
+
*
|
|
28
|
+
* **Two names, because two different things need them.** `name` is the WIRE name, which is what
|
|
29
|
+
* the response sets; `property` is the name on the value the handler returned, which is where the
|
|
30
|
+
* value is read from. `@header("x-correlation-id") correlationId: string` is `x-correlation-id`
|
|
31
|
+
* on the wire and `correlationId` in the result, and they differ for any header with a hyphen.
|
|
32
|
+
*
|
|
33
|
+
* Absent where the response declares none.
|
|
34
|
+
*/
|
|
35
|
+
readonly headers?: readonly { readonly name: string; readonly property: string }[];
|
|
17
36
|
readonly when?: {
|
|
18
37
|
readonly property: string;
|
|
19
38
|
readonly value: boolean | string;
|