typespec-hono 0.12.0 → 0.13.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.
Files changed (2) hide show
  1. package/dist/src/app.js +35 -2
  2. package/package.json +2 -2
package/dist/src/app.js CHANGED
@@ -196,7 +196,31 @@ function rawBodyReaderFor(contentTypes) {
196
196
  * interface would be a second source of truth that drifts.
197
197
  */
198
198
  function inputTypeOf(entry) {
199
- const parts = entry.validators.map(([, name]) => `z.infer<typeof ${name}>`);
199
+ /**
200
+ * **A body the document says must not be flattened is NAMED, not intersected.**
201
+ *
202
+ * `EmittedRoute.bodyProperty` is set for a body with an indexer. Intersected, its index signature
203
+ * is imposed on every sibling, so an optional `@query q?: string` beside a `Record<string>` body
204
+ * failed with `TS2345: 'q' is incompatible with index signature` and the generated server did not
205
+ * compile at all. It was wrong before it failed to compile, too: the document states the
206
+ * parameters and the body separately, and merged, a body key named `q` silently overwrites the
207
+ * query parameter of that name.
208
+ */
209
+ const bodyProperty = entry.route.bodyProperty;
210
+ /**
211
+ * **The body validator is found by its SCHEMA, never by its target.** A JSON body validates under
212
+ * `"json"` and a form body under `"form"`, so keying on the JSON target silently missed every
213
+ * form - measured on a clean install, `@header contentType: "application/x-www-form-urlencoded"`
214
+ * with a `Record` body still emitted the intersection and still did not compile. The schema
215
+ * identifier is the same fact whichever parser reads it.
216
+ */
217
+ const bodyName = entry.names.body;
218
+ const parts = entry.validators
219
+ .filter(([, name]) => bodyProperty === undefined || name !== bodyName)
220
+ .map(([, name]) => `z.infer<typeof ${name}>`);
221
+ if (bodyProperty !== undefined && bodyName !== undefined) {
222
+ parts.push(`{ ${objectKey(bodyProperty)}: z.infer<typeof ${bodyName}> }`);
223
+ }
200
224
  if (entry.route.rawBodyProperty !== undefined) {
201
225
  const reader = rawBodyReaderFor(entry.route.requestContentTypes);
202
226
  parts.push(`{ ${objectKey(entry.route.rawBodyProperty)}: ${reader.type} }`);
@@ -520,7 +544,16 @@ securityFor) {
520
544
  }
521
545
  // A dispatched body is in `validators` under the body target like any other, so there is
522
546
  // nothing extra to spread: `byContentType` published it there whichever parser ran.
523
- const pieces = validators.map(([target]) => `...c.req.valid(${JSON.stringify(target)})`);
547
+ // Same rule as `inputTypeOf`: the body is identified by its schema, not by its target.
548
+ const bodyTarget = validators.find(([, name]) => name === entry.names.body)?.[0];
549
+ const pieces = validators.map(([target]) =>
550
+ /**
551
+ * A named body is assigned rather than spread, matching the input type above and the
552
+ * document, which states the parameters and the body as separate things.
553
+ */
554
+ route.bodyProperty !== undefined && target === bodyTarget
555
+ ? `${objectKey(route.bodyProperty)}: c.req.valid(${JSON.stringify(target)})`
556
+ : `...c.req.valid(${JSON.stringify(target)})`);
524
557
  if (route.rawBodyProperty !== undefined) {
525
558
  // The bytes ARE the contract: a signature covers exactly what arrived, so parsing and
526
559
  // re-serialising would verify a different string than the sender signed. WHICH reader
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "typespec-hono",
3
- "version": "0.12.0",
3
+ "version": "0.13.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.14.0"
47
+ "typespec-http-zod": "^0.15.0"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@hono/zod-openapi": "^1.4.0",