fork of hey-api/openapi-ts because I need some additional things

Merge branch 'main' into refactor/2597

authored by

Dmitriy Brolnickij and committed by
GitHub
87d50042 33516370

+323 -1
+5
.changeset/fix-zod-union-deduplication.md
··· 1 + --- 2 + "@hey-api/openapi-ts": patch 3 + --- 4 + 5 + fix(parser): expand schema deduplication by including validation constraints in type ID
+210
.github/copilot-instructions.md
··· 1 + # Hey API OpenAPI TypeScript Codegen 2 + 3 + OpenAPI TypeScript is a CLI tool and library for generating TypeScript clients, SDKs, validators, and schemas from OpenAPI specifications. This is a monorepo built with pnpm workspaces, Turbo build orchestration, and TypeScript. 4 + 5 + **ALWAYS reference these instructions first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here.** 6 + 7 + ## Working Effectively 8 + 9 + ### Prerequisites and Setup 10 + 11 + - Install Node.js 20.19.0+ or 24.7.0+ (see `.nvmrc` for current version: 24.7.0) 12 + - Install pnpm globally: `npm install -g pnpm@10.15.1` 13 + - Clone the repository and run setup commands 14 + 15 + ### Bootstrap, Build, and Test 16 + 17 + ```bash 18 + # Install dependencies (takes ~1m 20s) 19 + pnpm install 20 + 21 + # Build packages only (NEVER CANCEL - takes ~2m 15s - set timeout to 180+ seconds) 22 + pnpm build --filter="@hey-api/**" 23 + 24 + # Build all including examples (NEVER CANCEL - takes ~5+ minutes - set timeout to 360+ seconds) 25 + pnpm build 26 + 27 + # Run tests (takes ~1m 5s - set timeout to 120+ seconds) 28 + # NOTE: Some network-dependent tests may fail in sandboxed environments 29 + pnpm test 30 + 31 + # Run linting (takes ~35s) 32 + pnpm lint 33 + 34 + # Run type checking (NEVER CANCEL - takes ~1m 20s - set timeout to 120+ seconds) 35 + pnpm typecheck 36 + 37 + # Format code (takes ~35s) 38 + pnpm format 39 + ``` 40 + 41 + ### Development Workflow 42 + 43 + ```bash 44 + # Start development mode for main package (watches for changes) 45 + pnpm --filter @hey-api/openapi-ts dev 46 + 47 + # Start development server for examples (e.g., fetch example) 48 + pnpm --filter @example/openapi-ts-fetch dev 49 + # Server starts on http://localhost:5173/ 50 + 51 + # Run CLI tool directly 52 + node packages/openapi-ts/bin/index.cjs --help 53 + # or after building 54 + npx @hey-api/openapi-ts --help 55 + ``` 56 + 57 + ## Build and Test Details 58 + 59 + ### **CRITICAL BUILD TIMING** 60 + 61 + - **NEVER CANCEL BUILD COMMANDS** - They may take 2-5+ minutes 62 + - `pnpm build --filter="@hey-api/**"`: ~2m 15s (packages only) 63 + - `pnpm build`: ~5+ minutes (includes docs and examples) 64 + - `pnpm install`: ~1m 20s 65 + - `pnpm test`: ~1m 5s 66 + - `pnpm typecheck`: ~1m 20s 67 + - `pnpm lint`: ~35s 68 + - `pnpm format`: ~35s 69 + 70 + ### Build Issues and Workarounds 71 + 72 + - **Docs build may fail** due to pnpm version mismatch in VitePress - this is expected in some environments 73 + - Use `pnpm build --filter="@hey-api/**"` to build packages without docs 74 + - **Some tests may fail** in sandboxed environments due to network restrictions (OpenAPI spec downloads) 75 + - **Generated test files** in `packages/openapi-ts-tests/` contain auto-generated snapshots that may have linting warnings - this is expected 76 + - **Linting issues** in `.gen/snapshots/` directories are expected for generated code 77 + 78 + ## Validation 79 + 80 + ### Manual Testing Scenarios 81 + 82 + After making changes, ALWAYS validate with these scenarios: 83 + 84 + 1. **CLI Functionality Test**: 85 + 86 + ```bash 87 + # Test CLI help 88 + node packages/openapi-ts/bin/index.cjs --help 89 + 90 + # Test CLI version 91 + node packages/openapi-ts/bin/index.cjs --version 92 + 93 + # Test basic code generation with a simple OpenAPI spec 94 + # Create a minimal test spec and generate client code 95 + node packages/openapi-ts/bin/index.cjs -i path/to/spec.json -o ./test-output --plugins "@hey-api/client-fetch" "@hey-api/typescript" 96 + ``` 97 + 98 + 2. **Example Application Test**: 99 + 100 + ```bash 101 + # Start fetch example and verify it loads 102 + pnpm --filter @example/openapi-ts-fetch dev 103 + # Should start on http://localhost:5173/ 104 + ``` 105 + 106 + 3. **Development Mode Test**: 107 + ```bash 108 + # Start dev mode and make a small change to verify rebuilding 109 + pnpm --filter @hey-api/openapi-ts dev 110 + ``` 111 + 112 + ### Pre-commit Validation 113 + 114 + ALWAYS run these commands before committing or the CI will fail: 115 + 116 + ```bash 117 + # Use lint:fix to auto-fix issues (some warnings in generated test files are expected) 118 + pnpm lint:fix 119 + 120 + # Run typecheck (can target specific packages with --filter) 121 + pnpm typecheck 122 + 123 + # Run tests (some network tests may fail in sandboxed environments) 124 + pnpm test 125 + ``` 126 + 127 + **NOTE**: Some linting warnings in generated test snapshot files (`.gen/snapshots/`) are expected and should be ignored. The `lint:fix` command will resolve actual source code issues. 128 + 129 + ## Repository Structure 130 + 131 + ### Key Packages 132 + 133 + - `packages/openapi-ts/` - Main CLI tool and library 134 + - `packages/codegen-core/` - Core code generation utilities 135 + - `packages/custom-client/` - Custom HTTP client implementations 136 + - `packages/nuxt/` - Nuxt.js integration 137 + - `packages/vite-plugin/` - Vite plugin 138 + 139 + ### Examples 140 + 141 + - `examples/openapi-ts-fetch/` - Fetch client example (React + Vite) 142 + - `examples/openapi-ts-angular/` - Angular client example 143 + - `examples/openapi-ts-tanstack-react-query/` - TanStack React Query integration 144 + - `examples/openapi-ts-vue/` - Vue.js integration 145 + - Plus many more framework-specific examples 146 + 147 + ### Configuration Files 148 + 149 + - `pnpm-workspace.yaml` - Workspace configuration 150 + - `turbo.json` - Turbo build configuration 151 + - `package.json` - Root package with workspace scripts 152 + - `.nvmrc` - Node.js version specification 153 + 154 + ## Common Tasks 155 + 156 + ### Working with the Main Package 157 + 158 + ```bash 159 + # Install deps for main package 160 + pnpm --filter @hey-api/openapi-ts install 161 + 162 + # Build main package only 163 + pnpm --filter @hey-api/openapi-ts build 164 + 165 + # Test main package only 166 + pnpm --filter @hey-api/openapi-ts test 167 + 168 + # Start dev mode for main package 169 + pnpm --filter @hey-api/openapi-ts dev 170 + ``` 171 + 172 + ### Working with Examples 173 + 174 + ```bash 175 + # List all example packages 176 + ls examples/ 177 + 178 + # Run specific example 179 + pnpm --filter @example/openapi-ts-fetch dev 180 + 181 + # Build all examples 182 + pnpm build --filter="@example/**" 183 + ``` 184 + 185 + ### Debugging and Troubleshooting 186 + 187 + - Check `turbo.json` for task dependencies and configuration 188 + - Use `pnpm list` to see installed packages 189 + - Use `pnpm why <package>` to understand dependency chains 190 + - Check individual package `package.json` files for available scripts 191 + 192 + ## CI/CD Pipeline 193 + 194 + The repository uses GitHub Actions (`.github/workflows/ci.yml`): 195 + 196 + - Tests on multiple Node.js versions (20.19.0, 22.12.0, 24.7.0) 197 + - Tests on multiple OS (macOS, Ubuntu, Windows) 198 + - Runs build, lint, typecheck, and test commands 199 + - Publishes preview packages on PRs 200 + 201 + ## Performance Expectations 202 + 203 + - **Cold install**: ~1m 20s 204 + - **Cold build**: ~2-5m depending on scope 205 + - **Incremental builds**: ~30s in dev mode 206 + - **Test suite**: ~1m 5s 207 + - **Linting**: ~35s 208 + - **Type checking**: ~1m 20s 209 + 210 + Remember: This is a complex monorepo with many dependencies. Be patient with build times and always use appropriate timeouts for long-running commands.
+27
packages/openapi-ts-tests/specs/3.1.x/validators-string-constraints-union.json
··· 1 + { 2 + "openapi": "3.1.0", 3 + "info": { 4 + "title": "String Constraints Union Test", 5 + "version": "1.0.0" 6 + }, 7 + "components": { 8 + "schemas": { 9 + "LocaleOrLanguage": { 10 + "anyOf": [ 11 + { 12 + "type": "string", 13 + "minLength": 5, 14 + "maxLength": 5, 15 + "description": "Combination of ISO 639-1 and ISO 3166-1 alpha-2 separated by a hyphen." 16 + }, 17 + { 18 + "type": "string", 19 + "minLength": 2, 20 + "maxLength": 2, 21 + "description": "ISO 639-1 language code." 22 + } 23 + ] 24 + } 25 + } 26 + } 27 + }
+8
packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/mini/validators-string-constraints-union/zod.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import * as z from 'zod/v4-mini'; 4 + 5 + export const zLocaleOrLanguage = z.union([ 6 + z.string().check(z.length(5)), 7 + z.string().check(z.length(2)) 8 + ]);
+8
packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v3/validators-string-constraints-union/zod.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import { z } from 'zod'; 4 + 5 + export const zLocaleOrLanguage = z.union([ 6 + z.string().length(5), 7 + z.string().length(2) 8 + ]);
+8
packages/openapi-ts-tests/zod/v3/__snapshots__/3.1.x/v4/validators-string-constraints-union/zod.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import { z } from 'zod/v4'; 4 + 5 + export const zLocaleOrLanguage = z.union([ 6 + z.string().length(5), 7 + z.string().length(2) 8 + ]);
+7
packages/openapi-ts-tests/zod/v3/test/3.1.x.test.ts
··· 138 138 description: 139 139 "validator schemas with merged unions (can't use .merge())", 140 140 }, 141 + { 142 + config: createConfig({ 143 + input: 'validators-string-constraints-union.json', 144 + output: 'validators-string-constraints-union', 145 + }), 146 + description: 'validator schemas with string constraints union', 147 + }, 141 148 ]; 142 149 143 150 it.each(scenarios)('$description', async ({ config }) => {
+8
packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/mini/validators-string-constraints-union/zod.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import * as z from 'zod/mini'; 4 + 5 + export const zLocaleOrLanguage = z.union([ 6 + z.string().check(z.length(5)), 7 + z.string().check(z.length(2)) 8 + ]);
+8
packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v3/validators-string-constraints-union/zod.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import { z } from 'zod/v3'; 4 + 5 + export const zLocaleOrLanguage = z.union([ 6 + z.string().length(5), 7 + z.string().length(2) 8 + ]);
+8
packages/openapi-ts-tests/zod/v4/__snapshots__/3.1.x/v4/validators-string-constraints-union/zod.gen.ts
··· 1 + // This file is auto-generated by @hey-api/openapi-ts 2 + 3 + import { z } from 'zod'; 4 + 5 + export const zLocaleOrLanguage = z.union([ 6 + z.string().length(5), 7 + z.string().length(2) 8 + ]);
+7
packages/openapi-ts-tests/zod/v4/test/3.1.x.test.ts
··· 138 138 description: 139 139 "validator schemas with merged unions (can't use .merge())", 140 140 }, 141 + { 142 + config: createConfig({ 143 + input: 'validators-string-constraints-union.json', 144 + output: 'validators-string-constraints-union', 145 + }), 146 + description: 'validator schemas with string constraints union', 147 + }, 141 148 ]; 142 149 143 150 it.each(scenarios)('$description', async ({ config }) => {
+19 -1
packages/openapi-ts/src/ir/schema.ts
··· 42 42 item.format !== undefined && detectFormat 43 43 ? `format-${item.format}` 44 44 : ''; 45 - const typeId = `${item.$ref ?? ''}${item.type ?? ''}${constant}${format}`; 45 + 46 + // Include validation constraints in the type ID to avoid incorrect deduplication 47 + const constraints = [ 48 + item.minLength !== undefined ? `minLength-${item.minLength}` : '', 49 + item.maxLength !== undefined ? `maxLength-${item.maxLength}` : '', 50 + item.minimum !== undefined ? `minimum-${item.minimum}` : '', 51 + item.maximum !== undefined ? `maximum-${item.maximum}` : '', 52 + item.exclusiveMinimum !== undefined 53 + ? `exclusiveMinimum-${item.exclusiveMinimum}` 54 + : '', 55 + item.exclusiveMaximum !== undefined 56 + ? `exclusiveMaximum-${item.exclusiveMaximum}` 57 + : '', 58 + item.minItems !== undefined ? `minItems-${item.minItems}` : '', 59 + item.maxItems !== undefined ? `maxItems-${item.maxItems}` : '', 60 + item.pattern !== undefined ? `pattern-${item.pattern}` : '', 61 + ].join(''); 62 + 63 + const typeId = `${item.$ref ?? ''}${item.type ?? ''}${constant}${format}${constraints}`; 46 64 if (!typeIds.includes(typeId)) { 47 65 typeIds.push(typeId); 48 66 uniqueItems.push(item);