typespec-hono 0.12.0 → 0.13.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.
Files changed (2) hide show
  1. package/dist/src/app.js +28 -2
  2. package/package.json +2 -2
package/dist/src/app.js CHANGED
@@ -196,7 +196,26 @@ 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
+ const parts = entry.validators
211
+ .filter(([target]) => bodyProperty === undefined || target !== VALIDATOR_TARGET.body)
212
+ .map(([, name]) => `z.infer<typeof ${name}>`);
213
+ if (bodyProperty !== undefined) {
214
+ const bodySchema = entry.validators.find(([target]) => target === VALIDATOR_TARGET.body)?.[1];
215
+ if (bodySchema !== undefined) {
216
+ parts.push(`{ ${objectKey(bodyProperty)}: z.infer<typeof ${bodySchema}> }`);
217
+ }
218
+ }
200
219
  if (entry.route.rawBodyProperty !== undefined) {
201
220
  const reader = rawBodyReaderFor(entry.route.requestContentTypes);
202
221
  parts.push(`{ ${objectKey(entry.route.rawBodyProperty)}: ${reader.type} }`);
@@ -520,7 +539,14 @@ securityFor) {
520
539
  }
521
540
  // A dispatched body is in `validators` under the body target like any other, so there is
522
541
  // nothing extra to spread: `byContentType` published it there whichever parser ran.
523
- const pieces = validators.map(([target]) => `...c.req.valid(${JSON.stringify(target)})`);
542
+ const pieces = validators.map(([target]) =>
543
+ /**
544
+ * A named body is assigned rather than spread, matching the input type above and the
545
+ * document, which states the parameters and the body as separate things.
546
+ */
547
+ route.bodyProperty !== undefined && target === VALIDATOR_TARGET.body
548
+ ? `${objectKey(route.bodyProperty)}: c.req.valid(${JSON.stringify(target)})`
549
+ : `...c.req.valid(${JSON.stringify(target)})`);
524
550
  if (route.rawBodyProperty !== undefined) {
525
551
  // The bytes ARE the contract: a signature covers exactly what arrived, so parsing and
526
552
  // 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.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.14.0"
47
+ "typespec-http-zod": "^0.15.0"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@hono/zod-openapi": "^1.4.0",