hono-takibi 0.9.9998 → 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 +56 -56
- package/dist/index.js +2 -2
- package/package.json +1 -1
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
|
|
@@ -278,7 +276,7 @@ export default defineConfig({
|
|
|
278
276
|
})
|
|
279
277
|
```
|
|
280
278
|
|
|
281
|
-
|
|
279
|
+
## API Reference Docs
|
|
282
280
|
|
|
283
281
|
Generate API reference Markdown with [hono-cli](https://github.com/honojs/cli) `hono request` commands:
|
|
284
282
|
|
|
@@ -314,11 +312,11 @@ import { defineConfig } from 'hono-takibi/config'
|
|
|
314
312
|
|
|
315
313
|
export default defineConfig({
|
|
316
314
|
input: 'openapi.yaml',
|
|
317
|
-
basePath: '/api',
|
|
318
|
-
// format: {}, // oxfmt FormatConfig
|
|
319
315
|
|
|
320
316
|
output: './src/routes.ts', // single-file mode; with template.define, the app entry (required)
|
|
317
|
+
basePath: '/api',
|
|
321
318
|
readonly: true,
|
|
319
|
+
// format: {}, // oxfmt FormatConfig
|
|
322
320
|
|
|
323
321
|
template: {
|
|
324
322
|
test: true,
|
|
@@ -499,7 +497,11 @@ export default defineConfig({
|
|
|
499
497
|
})
|
|
500
498
|
```
|
|
501
499
|
|
|
502
|
-
##
|
|
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
|
|
503
505
|
|
|
504
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`.
|
|
505
507
|
|
|
@@ -519,8 +521,6 @@ z.string({ error: 'Name must be a string' })
|
|
|
519
521
|
.max(50, { error: 'Name must be at most 50 characters' })
|
|
520
522
|
```
|
|
521
523
|
|
|
522
|
-
### Extension Reference
|
|
523
|
-
|
|
524
524
|
All custom message extensions follow the `x-<keyword>-message` naming convention and map directly to Zod validator error messages.
|
|
525
525
|
|
|
526
526
|
#### Common (any schema type)
|
|
@@ -602,9 +602,9 @@ All custom message extensions follow the `x-<keyword>-message` naming convention
|
|
|
602
602
|
| `x-unevaluatedProperties-message` | `unevaluatedProperties` |
|
|
603
603
|
| `x-unevaluatedItems-message` | `unevaluatedItems` |
|
|
604
604
|
|
|
605
|
-
|
|
605
|
+
### Behavior Extensions
|
|
606
606
|
|
|
607
|
-
|
|
607
|
+
#### String Pre-validation Transforms
|
|
608
608
|
|
|
609
609
|
| Extension | Generated | Value |
|
|
610
610
|
| --------------- | ----------------------------- | --------------------------------------- |
|
|
@@ -624,7 +624,7 @@ homepage:
|
|
|
624
624
|
z.string().trim().pipe(z.url())
|
|
625
625
|
```
|
|
626
626
|
|
|
627
|
-
|
|
627
|
+
#### String Validation Checks
|
|
628
628
|
|
|
629
629
|
| Extension | Generated | Value |
|
|
630
630
|
| ------------- | ------------------------ | ------ |
|
|
@@ -641,9 +641,9 @@ slug:
|
|
|
641
641
|
z.string().lowercase()
|
|
642
642
|
```
|
|
643
643
|
|
|
644
|
-
|
|
644
|
+
#### Preprocess
|
|
645
645
|
|
|
646
|
-
|
|
646
|
+
**`x-preprocess`**
|
|
647
647
|
|
|
648
648
|
```yaml
|
|
649
649
|
username:
|
|
@@ -655,9 +655,9 @@ username:
|
|
|
655
655
|
z.preprocess((val) => (typeof val === 'string' ? val.trim() : val), z.string())
|
|
656
656
|
```
|
|
657
657
|
|
|
658
|
-
|
|
658
|
+
#### Type Coercion
|
|
659
659
|
|
|
660
|
-
|
|
660
|
+
**`x-coerce`**
|
|
661
661
|
|
|
662
662
|
```yaml
|
|
663
663
|
asNumber:
|
|
@@ -674,7 +674,7 @@ z.coerce.number()
|
|
|
674
674
|
z.coerce.date()
|
|
675
675
|
```
|
|
676
676
|
|
|
677
|
-
|
|
677
|
+
**`x-stringbool`**
|
|
678
678
|
|
|
679
679
|
```yaml
|
|
680
680
|
notify:
|
|
@@ -699,27 +699,27 @@ notify:
|
|
|
699
699
|
z.stringbool({ truthy: ['yes', 'on'], falsy: ['no', 'off'], case: 'sensitive' })
|
|
700
700
|
```
|
|
701
701
|
|
|
702
|
-
|
|
702
|
+
#### Codec
|
|
703
703
|
|
|
704
|
-
|
|
704
|
+
**`x-codec`**
|
|
705
705
|
|
|
706
706
|
```yaml
|
|
707
707
|
updatedAt:
|
|
708
708
|
type: string
|
|
709
709
|
format: date-time
|
|
710
|
-
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() })'
|
|
711
711
|
```
|
|
712
712
|
|
|
713
713
|
```ts
|
|
714
714
|
z.codec(z.iso.datetime(), z.date(), {
|
|
715
|
-
decode: (
|
|
716
|
-
encode: (
|
|
715
|
+
decode: (isoString) => new Date(isoString),
|
|
716
|
+
encode: (date) => date.toISOString(),
|
|
717
717
|
})
|
|
718
718
|
```
|
|
719
719
|
|
|
720
|
-
|
|
720
|
+
#### Custom Validation
|
|
721
721
|
|
|
722
|
-
|
|
722
|
+
**`x-refine`**
|
|
723
723
|
|
|
724
724
|
```yaml
|
|
725
725
|
password:
|
|
@@ -733,7 +733,7 @@ z.string()
|
|
|
733
733
|
.refine((val) => /[A-Z]/.test(val), { message: 'Password must contain an uppercase letter' })
|
|
734
734
|
```
|
|
735
735
|
|
|
736
|
-
|
|
736
|
+
**`x-superRefine`**
|
|
737
737
|
|
|
738
738
|
```yaml
|
|
739
739
|
normalizedEmail:
|
|
@@ -750,9 +750,9 @@ z.email().superRefine((val, ctx) => {
|
|
|
750
750
|
})
|
|
751
751
|
```
|
|
752
752
|
|
|
753
|
-
|
|
753
|
+
#### Transform & Pipe
|
|
754
754
|
|
|
755
|
-
|
|
755
|
+
**`x-transform`**
|
|
756
756
|
|
|
757
757
|
```yaml
|
|
758
758
|
code:
|
|
@@ -764,7 +764,7 @@ code:
|
|
|
764
764
|
z.string().transform((val) => val.toUpperCase())
|
|
765
765
|
```
|
|
766
766
|
|
|
767
|
-
|
|
767
|
+
**`x-pipe`**
|
|
768
768
|
|
|
769
769
|
```yaml
|
|
770
770
|
port:
|
|
@@ -776,9 +776,9 @@ port:
|
|
|
776
776
|
z.string().pipe(z.number().int().positive())
|
|
777
777
|
```
|
|
778
778
|
|
|
779
|
-
|
|
779
|
+
#### Default & Fallback Values
|
|
780
780
|
|
|
781
|
-
|
|
781
|
+
**`x-prefault`**
|
|
782
782
|
|
|
783
783
|
```yaml
|
|
784
784
|
greeting:
|
|
@@ -790,7 +790,7 @@ greeting:
|
|
|
790
790
|
z.string().prefault('hello')
|
|
791
791
|
```
|
|
792
792
|
|
|
793
|
-
|
|
793
|
+
**`x-catch`**
|
|
794
794
|
|
|
795
795
|
```yaml
|
|
796
796
|
retries:
|
|
@@ -802,9 +802,9 @@ retries:
|
|
|
802
802
|
z.int().catch(0)
|
|
803
803
|
```
|
|
804
804
|
|
|
805
|
-
|
|
805
|
+
#### Immutability
|
|
806
806
|
|
|
807
|
-
|
|
807
|
+
**`x-readonly`**
|
|
808
808
|
|
|
809
809
|
```yaml
|
|
810
810
|
config:
|
|
@@ -819,9 +819,9 @@ config:
|
|
|
819
819
|
z.object({ name: z.string() }).readonly()
|
|
820
820
|
```
|
|
821
821
|
|
|
822
|
-
|
|
822
|
+
#### String Content Checks
|
|
823
823
|
|
|
824
|
-
|
|
824
|
+
**`x-startsWith` / `x-endsWith` / `x-includes`**
|
|
825
825
|
|
|
826
826
|
```yaml
|
|
827
827
|
url:
|
|
@@ -838,7 +838,7 @@ z.string().startsWith('https://').endsWith('.com')
|
|
|
838
838
|
z.string().includes('/api/')
|
|
839
839
|
```
|
|
840
840
|
|
|
841
|
-
|
|
841
|
+
#### Format-Specific Options
|
|
842
842
|
|
|
843
843
|
```yaml
|
|
844
844
|
htmlEmail:
|
|
@@ -861,23 +861,23 @@ preciseDatetime:
|
|
|
861
861
|
x-isoOffset: true
|
|
862
862
|
```
|
|
863
863
|
|
|
864
|
-
| Extension | Maps to | Values
|
|
865
|
-
| ---------------- | ------------------------------- |
|
|
866
|
-
| `x-emailPattern` | `z.email({ pattern })` | `html5` / `
|
|
867
|
-
| `x-emailRegex` | `z.email({ pattern: /.../ })` | custom regex string
|
|
868
|
-
| `x-uuidVersion` | `z.uuid({ version })` | `v1` / `v4` / `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
|
-
|
|
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)
|
|
881
881
|
|
|
882
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:
|
|
883
883
|
|
package/dist/index.js
CHANGED
|
@@ -88,8 +88,8 @@ async function honoTakibi() {
|
|
|
88
88
|
const openAPI = openAPIResult.value;
|
|
89
89
|
const jobs = makeJob(openAPI, config);
|
|
90
90
|
const results = await Promise.all(jobs.map((job) => job.run(job.output)));
|
|
91
|
-
const
|
|
92
|
-
if (
|
|
91
|
+
const e = results.find((result) => !result.ok);
|
|
92
|
+
if (e) return e;
|
|
93
93
|
return {
|
|
94
94
|
ok: true,
|
|
95
95
|
value: results.map((result) => result.ok ? result.value : "").filter((v) => v !== "").join("\n")
|