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 +208 -197
- package/dist/config/index.d.ts +569 -537
- package/dist/config/index.js +67 -44
- package/dist/core/docs/index.d.ts +1 -1
- package/dist/core/docs/index.js +1 -1
- package/dist/core/hooks/index.d.ts +1 -1
- package/dist/core/hooks/index.js +1 -1
- package/dist/core/rpc/index.d.ts +1 -1
- package/dist/core/rpc/index.js +2 -2
- package/dist/core/type/index.d.ts +1 -1
- package/dist/core/type/index.js +2 -5
- package/dist/{docs-D6Vw7rcN.js → docs-380Wcu6a.js} +12 -12
- package/dist/generator/zod-openapi-hono/openapi/index.d.ts +1 -1
- package/dist/generator/zod-openapi-hono/openapi/index.js +1 -1
- package/dist/{guard-DWr6QVCo.js → guard-zvkMUVYD.js} +6 -1
- package/dist/{hooks-9DoWHJ6y.js → hooks-Dk7Z5hMb.js} +14 -8
- package/dist/{index-CGhcPgew.d.ts → index-BFnCNZSw.d.ts} +3 -3
- package/dist/index.js +3 -3
- package/dist/{openapi-B5hESREK.js → openapi-Lc8kequ9.js} +141 -137
- package/dist/{rpc-DV5n7BOG.js → rpc-CepeggWU.js} +1 -1
- package/dist/{shared-DWuGN1cr.js → shared-siQoLLJc.js} +500 -112
- package/dist/vite-plugin/index.js +16 -16
- package/package.json +10 -12
package/README.md
CHANGED
|
@@ -4,12 +4,6 @@
|
|
|
4
4
|
|
|
5
5
|

|
|
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
|
-
'
|
|
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
|
-
'
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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: '../
|
|
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: '
|
|
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
|
-
|
|
279
|
+
## API Reference Docs
|
|
258
280
|
|
|
259
|
-
Generate API reference Markdown with [hono-cli](https://github.com/honojs/cli) `hono request` commands
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
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
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
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: '
|
|
367
|
+
import: '../schemas',
|
|
337
368
|
},
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
webhooks: {
|
|
341
|
-
output: './src/webhooks',
|
|
369
|
+
responses: {
|
|
370
|
+
output: './src/responses',
|
|
342
371
|
split: true,
|
|
343
|
-
import: '
|
|
372
|
+
import: '../responses',
|
|
344
373
|
},
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
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: '../
|
|
431
|
+
import: '../lib',
|
|
420
432
|
split: true,
|
|
421
|
-
client: 'client',
|
|
422
|
-
parseResponse: true,
|
|
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: '../
|
|
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: '../
|
|
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: '../
|
|
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: '../
|
|
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: '../
|
|
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: '../
|
|
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: '../
|
|
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: '
|
|
473
|
-
testFramework: 'vitest', // "vitest"
|
|
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',
|
|
485
|
-
curl: false, // true:
|
|
486
|
-
baseUrl: 'http://localhost:3000',
|
|
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
|
-
##
|
|
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
|
-
|
|
605
|
+
### Behavior Extensions
|
|
595
606
|
|
|
596
|
-
|
|
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
|
-
|
|
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
|
-
|
|
644
|
+
#### Preprocess
|
|
634
645
|
|
|
635
|
-
|
|
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
|
-
|
|
658
|
+
#### Type Coercion
|
|
648
659
|
|
|
649
|
-
|
|
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
|
-
|
|
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
|
-
|
|
702
|
+
#### Codec
|
|
692
703
|
|
|
693
|
-
|
|
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: (
|
|
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: (
|
|
705
|
-
encode: (
|
|
715
|
+
decode: (isoString) => new Date(isoString),
|
|
716
|
+
encode: (date) => date.toISOString(),
|
|
706
717
|
})
|
|
707
718
|
```
|
|
708
719
|
|
|
709
|
-
|
|
720
|
+
#### Custom Validation
|
|
710
721
|
|
|
711
|
-
|
|
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
|
-
|
|
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
|
-
|
|
753
|
+
#### Transform & Pipe
|
|
743
754
|
|
|
744
|
-
|
|
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
|
-
|
|
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
|
-
|
|
779
|
+
#### Default & Fallback Values
|
|
769
780
|
|
|
770
|
-
|
|
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
|
-
|
|
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
|
-
|
|
805
|
+
#### Immutability
|
|
795
806
|
|
|
796
|
-
|
|
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
|
-
|
|
822
|
+
#### String Content Checks
|
|
812
823
|
|
|
813
|
-
|
|
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
|
-
|
|
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` / `
|
|
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
|
-
|
|
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
|
|