openapi-domainify 0.1.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,303 @@
1
+ # openapi-domainify
2
+
3
+ **Split your OpenAPI spec into domain-based TypeScript services.**
4
+
5
+ Most OpenAPI generators dump everything into a single file or force you to restructure your spec. This tool reads your existing spec and generates modular, domain-separated service classes based on URL path prefixes.
6
+
7
+ ```typescript
8
+ // Your config
9
+ domains: [
10
+ { name: 'auth', prefix: '/auth/' },
11
+ { name: 'billing', prefix: '/billing/' },
12
+ { name: 'users', prefix: '/users/' },
13
+ ]
14
+
15
+ // Generated usage
16
+ api.auth.login({ email, password })
17
+ api.billing.getInvoice(invoiceId)
18
+ api.users.updateProfile(userId, data)
19
+ ```
20
+
21
+ ---
22
+
23
+ ## Why This Exists
24
+
25
+ Every OpenAPI TypeScript generator on npm does one of these:
26
+
27
+ | Tool | Approach | Problem |
28
+ |------|----------|---------|
29
+ | `openapi-typescript` | Types only | No service layer |
30
+ | `openapi-fetch` | Single client | One massive file |
31
+ | `@hey-api/openapi-ts` | Tag-based grouping | Requires spec changes |
32
+ | `orval` | React Query focused | Framework lock-in |
33
+ | `openapi-generator` | Mustache templates | 15MB dependency, Java heritage |
34
+
35
+ **openapi-domainify** uses path prefixes from your existing URLs. No spec modifications. No framework coupling. Just TypeScript.
36
+
37
+ ---
38
+
39
+ ## Installation
40
+
41
+ ```bash
42
+ npm install -D openapi-domainify
43
+ # or
44
+ pnpm add -D openapi-domainify
45
+ # or
46
+ bun add -D openapi-domainify
47
+ ```
48
+
49
+ ---
50
+
51
+ ## Quick Start
52
+
53
+ ### 1. Create config file
54
+
55
+ ```bash
56
+ npx openapi-domainify init
57
+ ```
58
+
59
+ This creates `domainify.config.ts`:
60
+
61
+ ```typescript
62
+ import { defineConfig } from 'openapi-domainify'
63
+
64
+ export default defineConfig({
65
+ // OpenAPI spec source (URL or local file path)
66
+ input: 'https://api.example.com/openapi.json',
67
+
68
+ // Where to write generated code
69
+ output: './src/api',
70
+
71
+ // Domain splitting rules (order matters - first match wins)
72
+ domains: [
73
+ { name: 'auth', prefix: '/auth/', className: 'Auth' },
74
+ { name: 'users', prefix: '/users/', className: 'Users' },
75
+ ],
76
+
77
+ // Catch-all for unmatched paths
78
+ fallback: { name: 'api', prefix: '/', className: 'Api' },
79
+ })
80
+ ```
81
+
82
+ ### 2. Generate
83
+
84
+ ```bash
85
+ npx openapi-domainify generate
86
+ ```
87
+
88
+ Output structure:
89
+ ```
90
+ src/api/
91
+ ├── index.ts # API client (regenerated each time)
92
+ ├── http.ts # HTTP client template (safe to edit)
93
+ ├── generated/ # Auto-generated (DO NOT EDIT)
94
+ │ ├── openapi.json
95
+ │ ├── openapi.ts
96
+ │ ├── auth/
97
+ │ │ ├── service.ts
98
+ │ │ └── types.ts
99
+ │ └── users/
100
+ │ ├── service.ts
101
+ │ └── types.ts
102
+ └── overrides/ # Your customizations (safe to edit)
103
+ ├── auth/
104
+ │ └── service.ts # Extend/override AuthService
105
+ └── users/
106
+ └── service.ts # Extend/override UsersService
107
+ ```
108
+
109
+ ---
110
+
111
+ ## Overrides
112
+
113
+ When the generated code has issues or you need custom methods, create an override instead of editing generated files:
114
+
115
+ ```typescript
116
+ // overrides/users/service.ts
117
+ import { UsersService as GeneratedUsersService } from '../generated/users/service'
118
+ import type { HttpClient } from '../../http'
119
+
120
+ export class UsersService extends GeneratedUsersService {
121
+ constructor(http: HttpClient) {
122
+ super(http)
123
+ }
124
+
125
+ // Override a broken method
126
+ async getUser(id: string | number) {
127
+ // Custom implementation
128
+ return this.http.get(`/users/${id}`)
129
+ }
130
+
131
+ // Add a method not in the OpenAPI spec
132
+ async customEndpoint() {
133
+ return this.http.post('/users/custom')
134
+ }
135
+ }
136
+ ```
137
+
138
+ The generator:
139
+ - **Never overwrites** existing override files
140
+ - **Always regenerates** `generated/` and `index.ts`
141
+ - **Auto-detects** if you've customized an override and uses it in `index.ts`
142
+
143
+ ---
144
+
145
+ ## Generated Code
146
+
147
+ ### Service Classes
148
+
149
+ ```typescript
150
+ // generated/users/service.ts
151
+ import type { HttpClient } from '../../http'
152
+ import type * as T from './types'
153
+
154
+ export class UsersService {
155
+ constructor(private http: HttpClient) {}
156
+
157
+ // GET /users
158
+ users(params?: T.GetUsersQuery): Promise<T.GetUsersResponse> {
159
+ return this.http.get('/users', { params })
160
+ }
161
+
162
+ // GET /users/{id}
163
+ getUser(id: string | number): Promise<T.GetUsersByIdResponse> {
164
+ return this.http.get(`/users/${id}`)
165
+ }
166
+
167
+ // POST /users
168
+ createUser(data: T.PostUsersRequest): Promise<T.PostUsersResponse> {
169
+ return this.http.post('/users', data)
170
+ }
171
+ }
172
+ ```
173
+
174
+ ### Type Extraction
175
+
176
+ Types are extracted directly from your OpenAPI spec using the TypeScript Compiler API:
177
+
178
+ ```typescript
179
+ // generated/users/types.ts
180
+
181
+ // Request types
182
+ export type PostUsersRequest = {
183
+ email: string
184
+ name: string
185
+ role?: 'admin' | 'user'
186
+ }
187
+
188
+ // Response types
189
+ export type GetUsersResponse = {
190
+ data: Array<{
191
+ id: number
192
+ email: string
193
+ name: string
194
+ }>
195
+ }
196
+ ```
197
+
198
+ ---
199
+
200
+ ## HTTP Client
201
+
202
+ The generator creates an `http.ts` template with a `FetchHttpClient` implementation. Edit it or replace with your preferred HTTP library:
203
+
204
+ ```typescript
205
+ // http.ts (generated once, safe to edit)
206
+ export interface HttpClient {
207
+ get<T>(url: string, options?: { params?: Record<string, unknown> }): Promise<T>
208
+ post<T>(url: string, body?: Record<string, unknown>): Promise<T>
209
+ put<T>(url: string, body?: Record<string, unknown>): Promise<T>
210
+ patch<T>(url: string, body?: Record<string, unknown>): Promise<T>
211
+ delete<T>(url: string, options?: { params?: Record<string, unknown> }): Promise<T>
212
+ }
213
+ ```
214
+
215
+ ---
216
+
217
+ ## Configuration Reference
218
+
219
+ ```typescript
220
+ import { defineConfig } from 'openapi-domainify'
221
+
222
+ export default defineConfig({
223
+ // Required: OpenAPI spec source
224
+ input: 'https://api.example.com/openapi.json',
225
+
226
+ // Required: Output directory
227
+ output: './src/api',
228
+
229
+ // Required: Domain splitting rules
230
+ domains: [
231
+ {
232
+ name: 'auth', // Directory name
233
+ prefix: '/auth/', // URL prefix to match
234
+ className: 'Auth' // Generated class name suffix
235
+ },
236
+ ],
237
+
238
+ // Optional: Catch-all for unmatched paths
239
+ fallback: { name: 'api', prefix: '/', className: 'Api' },
240
+
241
+ // Optional: Strip this prefix from all paths
242
+ stripPrefix: '/api/v1',
243
+
244
+ // Optional: Where to save the downloaded spec
245
+ specOutput: './src/api/generated/openapi.json',
246
+
247
+ // Optional: Custom HTTP client import path
248
+ httpClientImport: '../../http',
249
+
250
+ // Optional: Override files directory (relative to output)
251
+ overridesDir: '../overrides',
252
+
253
+ // Optional: Generate index.ts (default: true)
254
+ generateIndex: true,
255
+ })
256
+ ```
257
+
258
+ ---
259
+
260
+ ## Method Naming Convention
261
+
262
+ Generated method names follow RESTful conventions:
263
+
264
+ | HTTP Method | Path | Generated Method |
265
+ |-------------|------|------------------|
266
+ | `GET` | `/users` | `users()` |
267
+ | `GET` | `/users/{id}` | `getUser(id)` |
268
+ | `POST` | `/users` | `createUser(data)` |
269
+ | `PUT` | `/users/{id}` | `updateUser(id, data)` |
270
+ | `PATCH` | `/users/{id}` | `patchUser(id, data)` |
271
+ | `DELETE` | `/users/{id}` | `deleteUser(id)` |
272
+ | `GET` | `/users/{id}/orders` | `getUserOrders(id)` |
273
+
274
+ ---
275
+
276
+ ## How It Works
277
+
278
+ 1. **Fetch spec** — Downloads OpenAPI JSON from URL or reads local file
279
+ 2. **Generate types** — Runs `openapi-typescript` to create base TypeScript types
280
+ 3. **Parse with TS Compiler API** — Loads generated types into TypeScript's type checker
281
+ 4. **Extract endpoints** — Iterates over `paths` interface, extracting methods, params, bodies
282
+ 5. **Group by domain** — Matches each path against configured prefixes
283
+ 6. **Generate services** — Creates service classes with fully typed methods
284
+ 7. **Generate scaffolding** — Creates override templates (only if they don't exist)
285
+ 8. **Wire up index** — Generates `index.ts` that imports from overrides or generated
286
+
287
+ ---
288
+
289
+ ## Comparison
290
+
291
+ | Feature | openapi-domainify | openapi-fetch | @hey-api/openapi-ts | orval |
292
+ |---------|-------------------|---------------|---------------------|-------|
293
+ | Domain splitting | ✅ Path prefix | ❌ | ⚠️ Tags only | ❌ |
294
+ | No spec changes | ✅ | ✅ | ❌ | ✅ |
295
+ | Override system | ✅ | ❌ | ❌ | ❌ |
296
+ | Framework agnostic | ✅ | ✅ | ✅ | ❌ React Query |
297
+ | Service classes | ✅ | ❌ | ✅ | ✅ |
298
+
299
+ ---
300
+
301
+ ## License
302
+
303
+ MIT
package/bin/cli.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import '../dist/cli.js'
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node