hono-takibi 0.9.9997 → 0.9.9999

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 CHANGED
@@ -4,12 +4,6 @@
4
4
 
5
5
  ![img](https://raw.githubusercontent.com/nakita628/hono-takibi/refs/heads/main/assets/img/hono-takibi.png)
6
6
 
7
- ```bash
8
- npm install -D hono-takibi
9
- ```
10
-
11
- ## OpenAPI to Hono Code Generator
12
-
13
7
  **[Hono Takibi](https://www.npmjs.com/package/hono-takibi)** generates type-safe [Hono](https://hono.dev/) code from [OpenAPI](https://www.openapis.org/) / [TypeSpec](https://typespec.io/) specifications.
14
8
 
15
9
  - OpenAPI schemas to [Zod](https://zod.dev/) schemas
@@ -19,6 +13,10 @@ npm install -D hono-takibi
19
13
  - RPC client, mock server, TypeScript types
20
14
  - API reference docs with [hono-cli](https://github.com/honojs/cli) commands
21
15
 
16
+ ```bash
17
+ npm install -D hono-takibi
18
+ ```
19
+
22
20
  ## Quick Start
23
21
 
24
22
  ### CLI
@@ -36,9 +34,7 @@ import { defineConfig } from 'hono-takibi/config'
36
34
 
37
35
  export default defineConfig({
38
36
  input: 'openapi.yaml',
39
- 'zod-openapi': {
40
- output: './src/routes.ts',
41
- },
37
+ output: './src/routes.ts',
42
38
  })
43
39
  ```
44
40
 
@@ -127,13 +123,11 @@ Generate a complete app structure with handler stubs and test files:
127
123
  ```ts
128
124
  export default defineConfig({
129
125
  input: 'openapi.yaml',
130
- 'zod-openapi': {
131
- output: './src/routes.ts',
132
- template: {
133
- test: true,
134
- pathAlias: '@/',
135
- testFramework: 'bun', // "vitest" (default) | "vite-plus" | "bun"
136
- },
126
+ output: './src/routes.ts',
127
+ template: {
128
+ test: true,
129
+ pathAlias: '@/',
130
+ testFramework: 'bun', // "vitest" (default) | "vite-plus" | "bun"
137
131
  },
138
132
  })
139
133
  ```
@@ -201,6 +195,34 @@ export const api = app.openapi(getHealthRoute, getHealthRouteHandler)
201
195
  export default app
202
196
  ```
203
197
 
198
+ #### `define: true`
199
+
200
+ ```ts
201
+ export default defineConfig({
202
+ input: 'openapi.yaml',
203
+ output: './src/index.ts',
204
+ template: { define: true },
205
+ })
206
+ ```
207
+
208
+ ```ts
209
+ // src/routes/users.ts
210
+ export const getUsersIdRoute = defineOpenAPIRoute({
211
+ route: createRoute({
212
+ method: 'get',
213
+ path: '/users/{id}',
214
+ request: { params: z.object({ id: z.string() }) },
215
+ responses: {
216
+ 200: { description: 'ok', content: { 'application/json': { schema: UserSchema } } },
217
+ },
218
+ }),
219
+ handler: async (c) => {},
220
+ addRoute: true,
221
+ })
222
+ ```
223
+
224
+ Component schemas go to `components/index.ts` (override with `components.output`). Set `template.output` to change the route directory (default `./src/routes`).
225
+
204
226
  ## Client Library Integrations
205
227
 
206
228
  Supported: SWR, TanStack Query, Preact Query, Solid Query, Vue Query, Svelte Query, Angular Query, RPC Client.
@@ -210,7 +232,7 @@ export default defineConfig({
210
232
  input: 'openapi.yaml',
211
233
  'tanstack-query': {
212
234
  output: './src/tanstack-query',
213
- import: '../client',
235
+ import: '../lib',
214
236
  split: true,
215
237
  client: 'client',
216
238
  },
@@ -237,7 +259,7 @@ export default defineConfig({
237
259
  input: 'openapi.yaml',
238
260
  test: {
239
261
  output: './src/test.ts',
240
- import: '../index',
262
+ import: '.',
241
263
  testFramework: 'bun', // "vitest" (default) | "vite-plus" | "bun"
242
264
  },
243
265
  })
@@ -254,9 +276,9 @@ export default defineConfig({
254
276
  })
255
277
  ```
256
278
 
257
- ### API Reference Docs
279
+ ## API Reference Docs
258
280
 
259
- Generate API reference Markdown with [hono-cli](https://github.com/honojs/cli) `hono request` commands that can be run directly without starting a server:
281
+ Generate API reference Markdown with [hono-cli](https://github.com/honojs/cli) `hono request` commands:
260
282
 
261
283
  ```ts
262
284
  export default defineConfig({
@@ -268,7 +290,7 @@ export default defineConfig({
268
290
  })
269
291
  ```
270
292
 
271
- To generate `curl` commands instead of `hono request`:
293
+ Set `curl: true` with `baseUrl` to generate `curl` commands for a running server:
272
294
 
273
295
  ```ts
274
296
  export default defineConfig({
@@ -283,212 +305,203 @@ export default defineConfig({
283
305
 
284
306
  ## Full Config Reference
285
307
 
308
+ Some options are mutually exclusive: `output` ↔ `routes`, `components.output` ↔ per-type components, `template.define` ↔ `routeHandler`.
309
+
286
310
  ```ts
287
- // hono-takibi.config.ts
288
311
  import { defineConfig } from 'hono-takibi/config'
289
312
 
290
313
  export default defineConfig({
291
- // OpenAPI spec file (.yaml, .json, or .tsp)
292
314
  input: 'openapi.yaml',
293
315
 
294
- // Base path prefix for all routes
316
+ output: './src/routes.ts', // single-file mode; with template.define, the app entry (required)
295
317
  basePath: '/api',
318
+ readonly: true,
319
+ // format: {}, // oxfmt FormatConfig
320
+
321
+ template: {
322
+ test: true,
323
+ routeHandler: false, // true: RouteHandler exports
324
+ define: false, // true: defineOpenAPIRoute output
325
+ // output: './src/routes', // define mode dir (default ./src/routes)
326
+ pathAlias: '@/',
327
+ testFramework: 'vitest', // "vitest" | "vite-plus" | "bun"
328
+ },
296
329
 
297
- // oxfmt FormatConfig for generated code output
298
- // @see https://www.npmjs.com/package/oxfmt
299
- // format: {},
300
-
301
- // Main code generation (Zod + OpenAPI + Hono)
302
- 'zod-openapi': {
303
- // Output: use 'output' for single file, or 'routes' for split mode (mutually exclusive)
304
- output: './src/routes.ts',
305
- readonly: true, // Add 'as const' to generated schemas
306
-
307
- // Template generation (app entry point + handler stubs + tests)
308
- template: {
309
- test: true, // Generate test files
310
- routeHandler: false, // false: inline .openapi() (default), true: RouteHandler exports
311
- pathAlias: '@/', // TypeScript path alias for imports
312
- testFramework: 'vitest', // "vitest" (default) | "vite-plus" | "bun" — test import source
313
- },
330
+ exportSchemas: true,
331
+ exportSchemasTypes: true,
332
+ exportResponses: true,
333
+ exportParameters: true,
334
+ exportParametersTypes: true,
335
+ exportExamples: true,
336
+ exportRequestBodies: true,
337
+ exportHeaders: true,
338
+ exportHeadersTypes: true,
339
+ exportSecuritySchemes: true,
340
+ exportLinks: true,
341
+ exportCallbacks: true,
342
+ exportPathItems: true,
343
+ exportMediaTypes: true,
344
+ exportMediaTypesTypes: true,
345
+
346
+ routes: {
347
+ output: './src/routes',
348
+ split: true,
349
+ import: '@packages/routes',
350
+ },
351
+
352
+ webhooks: {
353
+ output: './src/webhooks',
354
+ split: true,
355
+ import: '@packages/webhooks',
356
+ },
314
357
 
315
- // Export options (OpenAPI Components Object)
316
- exportSchemas: true,
317
- exportSchemasTypes: true,
318
- exportResponses: true,
319
- exportParameters: true,
320
- exportParametersTypes: true,
321
- exportExamples: true,
322
- exportRequestBodies: true,
323
- exportHeaders: true,
324
- exportHeadersTypes: true,
325
- exportSecuritySchemes: true,
326
- exportLinks: true,
327
- exportCallbacks: true,
328
- exportPathItems: true,
329
- exportMediaTypes: true,
330
- exportMediaTypesTypes: true,
331
-
332
- // Split routes into separate files
333
- routes: {
334
- output: './src/routes',
358
+ // `output` (single file) and the per-type fields below (split) are mutually exclusive.
359
+ // `exportTypes` applies only to schemas / parameters / headers / mediaTypes.
360
+ components: {
361
+ output: './src/components/index.ts',
362
+
363
+ schemas: {
364
+ output: './src/schemas',
365
+ exportTypes: true,
335
366
  split: true,
336
- import: '@packages/routes',
367
+ import: '../schemas',
337
368
  },
338
-
339
- // Split webhooks into separate files
340
- webhooks: {
341
- output: './src/webhooks',
369
+ responses: {
370
+ output: './src/responses',
342
371
  split: true,
343
- import: '@packages/webhooks',
372
+ import: '../responses',
344
373
  },
345
-
346
- // Split components into separate files
347
- components: {
348
- schemas: {
349
- output: './src/schemas',
350
- exportTypes: true,
351
- split: true,
352
- import: '../schemas',
353
- },
354
- responses: {
355
- output: './src/responses',
356
- split: true,
357
- import: '../responses',
358
- },
359
- parameters: {
360
- output: './src/parameters',
361
- exportTypes: true,
362
- split: true,
363
- import: '../parameters',
364
- },
365
- examples: {
366
- output: './src/examples',
367
- split: true,
368
- import: '../examples',
369
- },
370
- requestBodies: {
371
- output: './src/requestBodies',
372
- split: true,
373
- import: '../requestBodies',
374
- },
375
- headers: {
376
- output: './src/headers',
377
- exportTypes: true,
378
- split: true,
379
- import: '../headers',
380
- },
381
- securitySchemes: {
382
- output: './src/securitySchemes',
383
- split: true,
384
- import: '../securitySchemes',
385
- },
386
- links: {
387
- output: './src/links',
388
- split: true,
389
- import: '../links',
390
- },
391
- callbacks: {
392
- output: './src/callbacks',
393
- split: true,
394
- import: '../callbacks',
395
- },
396
- pathItems: {
397
- output: './src/pathItems',
398
- split: true,
399
- import: '../pathItems',
400
- },
401
- mediaTypes: {
402
- output: './src/mediaTypes',
403
- exportTypes: true,
404
- split: true,
405
- import: '../mediaTypes',
406
- },
374
+ parameters: {
375
+ output: './src/parameters',
376
+ exportTypes: true,
377
+ split: true,
378
+ import: '../parameters',
379
+ },
380
+ examples: {
381
+ output: './src/examples',
382
+ split: true,
383
+ import: '../examples',
384
+ },
385
+ requestBodies: {
386
+ output: './src/requestBodies',
387
+ split: true,
388
+ import: '../requestBodies',
389
+ },
390
+ headers: {
391
+ output: './src/headers',
392
+ exportTypes: true,
393
+ split: true,
394
+ import: '../headers',
395
+ },
396
+ securitySchemes: {
397
+ output: './src/securitySchemes',
398
+ split: true,
399
+ import: '../securitySchemes',
400
+ },
401
+ links: {
402
+ output: './src/links',
403
+ split: true,
404
+ import: '../links',
405
+ },
406
+ callbacks: {
407
+ output: './src/callbacks',
408
+ split: true,
409
+ import: '../callbacks',
410
+ },
411
+ pathItems: {
412
+ output: './src/pathItems',
413
+ split: true,
414
+ import: '../pathItems',
415
+ },
416
+ mediaTypes: {
417
+ output: './src/mediaTypes',
418
+ exportTypes: true,
419
+ split: true,
420
+ import: '../mediaTypes',
407
421
  },
408
422
  },
409
423
 
410
- // TypeScript type generation
411
424
  type: {
412
425
  output: './src/types.ts',
413
426
  readonly: true,
414
427
  },
415
428
 
416
- // RPC client generation
417
429
  rpc: {
418
430
  output: './src/rpc',
419
- import: '../client', // Import path for the Hono RPC client
431
+ import: '../lib',
420
432
  split: true,
421
- client: 'client', // Export name of the client instance
422
- parseResponse: true, // Use parseResponse for type-safe responses
433
+ client: 'client',
434
+ parseResponse: true,
435
+ docs: false, // operation summary/description as JSDoc
423
436
  },
424
437
 
425
- // Client library integrations (SWR, TanStack Query, Preact Query, Solid Query, Vue Query, Svelte Query, Angular Query)
426
438
  swr: {
427
439
  output: './src/swr',
428
- import: '../client',
440
+ import: '../lib',
429
441
  split: true,
430
442
  client: 'client',
431
443
  },
432
444
  'tanstack-query': {
433
445
  output: './src/tanstack-query',
434
- import: '../client',
446
+ import: '../lib',
435
447
  split: true,
436
448
  client: 'client',
437
449
  },
438
450
  'preact-query': {
439
451
  output: './src/preact-query',
440
- import: '../client',
452
+ import: '../lib',
441
453
  split: true,
442
454
  client: 'client',
443
455
  },
444
456
  'solid-query': {
445
457
  output: './src/solid-query',
446
- import: '../client',
458
+ import: '../lib',
447
459
  split: true,
448
460
  client: 'client',
449
461
  },
450
462
  'vue-query': {
451
463
  output: './src/vue-query',
452
- import: '../client',
464
+ import: '../lib',
453
465
  split: true,
454
466
  client: 'client',
455
467
  },
456
468
  'svelte-query': {
457
469
  output: './src/svelte-query',
458
- import: '../client',
470
+ import: '../lib',
459
471
  split: true,
460
472
  client: 'client',
461
473
  },
462
474
  'angular-query': {
463
475
  output: './src/angular-query',
464
- import: '../client',
476
+ import: '../lib',
465
477
  split: true,
466
478
  client: 'client',
467
479
  },
468
480
 
469
- // Test generation
470
481
  test: {
471
482
  output: './src/test.ts',
472
- import: '../index', // Import path for the app instance
473
- testFramework: 'vitest', // "vitest" (default) | "vite-plus" | "bun" — test import source
483
+ import: '.',
484
+ testFramework: 'vitest', // "vitest" | "vite-plus" | "bun"
474
485
  },
475
486
 
476
- // Mock server generation
477
487
  mock: {
478
488
  output: './src/mock.ts',
479
489
  },
480
490
 
481
- // API reference docs generation
482
491
  docs: {
483
492
  output: './docs/api.md',
484
- entry: 'src/index.ts', // App entry point for hono request commands
485
- curl: false, // true: generate curl commands (requires baseUrl), false: hono request (default)
486
- baseUrl: 'http://localhost:3000', // Base URL for curl commands (required when curl: true)
493
+ entry: 'src/index.ts',
494
+ curl: false, // true: curl commands (requires baseUrl); false: hono request
495
+ baseUrl: 'http://localhost:3000',
487
496
  },
488
497
  })
489
498
  ```
490
499
 
491
- ## Custom Validation Error Messages
500
+ ## Vendor Extensions (x-\*)
501
+
502
+ hono-takibi reads `x-*` vendor extensions on your OpenAPI / JSON Schema to customize the generated Zod. Each extension maps 1:1 to a Zod feature.
503
+
504
+ ### Custom Validation Error Messages
492
505
 
493
506
  Use `x-*` vendor extensions to attach custom Zod error messages, with **one extension per JSON Schema keyword** (1:1 mapping). The extension name follows the pattern `x-<jsonSchemaKeyword>-message` (e.g. `x-minLength-message`, `x-pattern-message`), plus four generic forms: `x-error-message`, `x-required-message`, `x-const-message`, `x-enum-message`.
494
507
 
@@ -508,8 +521,6 @@ z.string({ error: 'Name must be a string' })
508
521
  .max(50, { error: 'Name must be at most 50 characters' })
509
522
  ```
510
523
 
511
- ### Extension Reference
512
-
513
524
  All custom message extensions follow the `x-<keyword>-message` naming convention and map directly to Zod validator error messages.
514
525
 
515
526
  #### Common (any schema type)
@@ -591,9 +602,9 @@ All custom message extensions follow the `x-<keyword>-message` naming convention
591
602
  | `x-unevaluatedProperties-message` | `unevaluatedProperties` |
592
603
  | `x-unevaluatedItems-message` | `unevaluatedItems` |
593
604
 
594
- ## Behavior Extensions
605
+ ### Behavior Extensions
595
606
 
596
- ### String Pre-validation Transforms
607
+ #### String Pre-validation Transforms
597
608
 
598
609
  | Extension | Generated | Value |
599
610
  | --------------- | ----------------------------- | --------------------------------------- |
@@ -613,7 +624,7 @@ homepage:
613
624
  z.string().trim().pipe(z.url())
614
625
  ```
615
626
 
616
- ### String Validation Checks
627
+ #### String Validation Checks
617
628
 
618
629
  | Extension | Generated | Value |
619
630
  | ------------- | ------------------------ | ------ |
@@ -630,9 +641,9 @@ slug:
630
641
  z.string().lowercase()
631
642
  ```
632
643
 
633
- ### Preprocess (Input Normalization)
644
+ #### Preprocess
634
645
 
635
- #### `x-preprocess`
646
+ **`x-preprocess`**
636
647
 
637
648
  ```yaml
638
649
  username:
@@ -644,9 +655,9 @@ username:
644
655
  z.preprocess((val) => (typeof val === 'string' ? val.trim() : val), z.string())
645
656
  ```
646
657
 
647
- ### Type Coercion
658
+ #### Type Coercion
648
659
 
649
- #### `x-coerce`
660
+ **`x-coerce`**
650
661
 
651
662
  ```yaml
652
663
  asNumber:
@@ -663,7 +674,7 @@ z.coerce.number()
663
674
  z.coerce.date()
664
675
  ```
665
676
 
666
- #### `x-stringbool`
677
+ **`x-stringbool`**
667
678
 
668
679
  ```yaml
669
680
  notify:
@@ -688,27 +699,27 @@ notify:
688
699
  z.stringbool({ truthy: ['yes', 'on'], falsy: ['no', 'off'], case: 'sensitive' })
689
700
  ```
690
701
 
691
- ### Codec (Bidirectional Transform)
702
+ #### Codec
692
703
 
693
- #### `x-codec`
704
+ **`x-codec`**
694
705
 
695
706
  ```yaml
696
707
  updatedAt:
697
708
  type: string
698
709
  format: date-time
699
- x-codec: 'z.codec(z.iso.datetime(), z.date(), { decode: (val) => new Date(val), encode: (val) => val.toISOString() })'
710
+ x-codec: 'z.codec(z.iso.datetime(), z.date(), { decode: (isoString) => new Date(isoString), encode: (date) => date.toISOString() })'
700
711
  ```
701
712
 
702
713
  ```ts
703
714
  z.codec(z.iso.datetime(), z.date(), {
704
- decode: (val) => new Date(val),
705
- encode: (val) => val.toISOString(),
715
+ decode: (isoString) => new Date(isoString),
716
+ encode: (date) => date.toISOString(),
706
717
  })
707
718
  ```
708
719
 
709
- ### Custom Validation
720
+ #### Custom Validation
710
721
 
711
- #### `x-refine`
722
+ **`x-refine`**
712
723
 
713
724
  ```yaml
714
725
  password:
@@ -722,7 +733,7 @@ z.string()
722
733
  .refine((val) => /[A-Z]/.test(val), { message: 'Password must contain an uppercase letter' })
723
734
  ```
724
735
 
725
- #### `x-superRefine`
736
+ **`x-superRefine`**
726
737
 
727
738
  ```yaml
728
739
  normalizedEmail:
@@ -739,9 +750,9 @@ z.email().superRefine((val, ctx) => {
739
750
  })
740
751
  ```
741
752
 
742
- ### Transform & Pipe
753
+ #### Transform & Pipe
743
754
 
744
- #### `x-transform`
755
+ **`x-transform`**
745
756
 
746
757
  ```yaml
747
758
  code:
@@ -753,7 +764,7 @@ code:
753
764
  z.string().transform((val) => val.toUpperCase())
754
765
  ```
755
766
 
756
- #### `x-pipe`
767
+ **`x-pipe`**
757
768
 
758
769
  ```yaml
759
770
  port:
@@ -765,9 +776,9 @@ port:
765
776
  z.string().pipe(z.number().int().positive())
766
777
  ```
767
778
 
768
- ### Default & Fallback Values
779
+ #### Default & Fallback Values
769
780
 
770
- #### `x-prefault`
781
+ **`x-prefault`**
771
782
 
772
783
  ```yaml
773
784
  greeting:
@@ -779,7 +790,7 @@ greeting:
779
790
  z.string().prefault('hello')
780
791
  ```
781
792
 
782
- #### `x-catch`
793
+ **`x-catch`**
783
794
 
784
795
  ```yaml
785
796
  retries:
@@ -791,9 +802,9 @@ retries:
791
802
  z.int().catch(0)
792
803
  ```
793
804
 
794
- ### Immutability
805
+ #### Immutability
795
806
 
796
- #### `x-readonly`
807
+ **`x-readonly`**
797
808
 
798
809
  ```yaml
799
810
  config:
@@ -808,9 +819,9 @@ config:
808
819
  z.object({ name: z.string() }).readonly()
809
820
  ```
810
821
 
811
- ### String Content Checks
822
+ #### String Content Checks
812
823
 
813
- #### `x-startsWith` / `x-endsWith` / `x-includes`
824
+ **`x-startsWith` / `x-endsWith` / `x-includes`**
814
825
 
815
826
  ```yaml
816
827
  url:
@@ -827,7 +838,7 @@ z.string().startsWith('https://').endsWith('.com')
827
838
  z.string().includes('/api/')
828
839
  ```
829
840
 
830
- ### Format-Specific Options
841
+ #### Format-Specific Options
831
842
 
832
843
  ```yaml
833
844
  htmlEmail:
@@ -850,23 +861,23 @@ preciseDatetime:
850
861
  x-isoOffset: true
851
862
  ```
852
863
 
853
- | Extension | Maps to | Values |
854
- | ---------------- | ------------------------------- | -------------------------------- |
855
- | `x-emailPattern` | `z.email({ pattern })` | `html5` / `browser` / `unicode` |
856
- | `x-emailRegex` | `z.email({ pattern: /.../ })` | custom regex string |
857
- | `x-uuidVersion` | `z.uuid({ version })` | `v1` / `v4` / `v6` / `v7` / `v8` |
858
- | `x-urlProtocol` | `z.url({ protocol: /.../ })` | regex string |
859
- | `x-urlHostname` | `z.url({ hostname: /.../ })` | regex string |
860
- | `x-urlNormalize` | `z.url({ normalize })` | `true` / `false` |
861
- | `x-isoPrecision` | `z.iso.datetime({ precision })` | fractional second digits |
862
- | `x-isoOffset` | `z.iso.datetime({ offset })` | `true` / `false` |
863
- | `x-isoLocal` | `z.iso.datetime({ local })` | `true` / `false` |
864
- | `x-macDelimiter` | `z.mac({ delimiter })` | `:` / `-` / `.` |
865
- | `x-jwtAlg` | `z.jwt({ alg })` | `HS256` etc. |
866
- | `x-hashAlg` | `z.hash(alg, ...)` | `sha256` etc. |
867
- | `x-hashEnc` | `z.hash(alg, { enc })` | `hex` / `base64` / `base64url` |
868
-
869
- ## Branded Types
864
+ | Extension | Maps to | Values |
865
+ | ---------------- | ------------------------------- | ----------------------------------------------------- |
866
+ | `x-emailPattern` | `z.email({ pattern })` | `html5` / `rfc5322` / `unicode` |
867
+ | `x-emailRegex` | `z.email({ pattern: /.../ })` | custom regex string |
868
+ | `x-uuidVersion` | `z.uuid({ version })` | `v1` / `v2` / `v3` / `v4` / `v5` / `v6` / `v7` / `v8` |
869
+ | `x-urlProtocol` | `z.url({ protocol: /.../ })` | regex string |
870
+ | `x-urlHostname` | `z.url({ hostname: /.../ })` | regex string |
871
+ | `x-urlNormalize` | `z.url({ normalize })` | `true` / `false` |
872
+ | `x-isoPrecision` | `z.iso.datetime({ precision })` | fractional second digits |
873
+ | `x-isoOffset` | `z.iso.datetime({ offset })` | `true` / `false` |
874
+ | `x-isoLocal` | `z.iso.datetime({ local })` | `true` / `false` |
875
+ | `x-macDelimiter` | `z.mac({ delimiter })` | `:` / `-` / `.` |
876
+ | `x-jwtAlg` | `z.jwt({ alg })` | `HS256` etc. |
877
+ | `x-hashAlg` | `z.hash(alg, ...)` | `sha256` etc. |
878
+ | `x-hashEnc` | `z.hash(alg, { enc })` | `hex` / `base64` / `base64url` |
879
+
880
+ ### Branded Types (x-brand)
870
881
 
871
882
  Use the `x-brand` vendor extension to generate [Zod branded types](https://zod.dev/api?id=branded-types), creating nominal types that are structurally identical but semantically distinct:
872
883