zod-codegen 1.5.1 → 1.6.1
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/.github/workflows/ci.yml +6 -0
- package/.github/workflows/release.yml +3 -3
- package/CHANGELOG.md +29 -0
- package/CONTRIBUTING.md +1 -1
- package/EXAMPLES.md +91 -12
- package/README.md +11 -4
- package/dist/scripts/add-js-extensions.d.ts +2 -0
- package/dist/scripts/add-js-extensions.d.ts.map +1 -0
- package/dist/scripts/add-js-extensions.js +66 -0
- package/dist/scripts/update-manifest.d.ts +14 -0
- package/dist/scripts/update-manifest.d.ts.map +1 -0
- package/dist/scripts/update-manifest.js +33 -0
- package/dist/src/assets/manifest.json +1 -1
- package/dist/src/cli.js +3 -3
- package/dist/src/generator.d.ts +46 -4
- package/dist/src/generator.d.ts.map +1 -1
- package/dist/src/generator.js +43 -1
- package/dist/src/interfaces/code-generator.d.ts +1 -1
- package/dist/src/interfaces/code-generator.d.ts.map +1 -1
- package/dist/src/services/code-generator.service.d.ts +5 -3
- package/dist/src/services/code-generator.service.d.ts.map +1 -1
- package/dist/src/services/code-generator.service.js +69 -1
- package/dist/src/services/file-reader.service.d.ts +2 -2
- package/dist/src/services/file-reader.service.d.ts.map +1 -1
- package/dist/src/services/file-writer.service.d.ts +1 -1
- package/dist/src/services/file-writer.service.d.ts.map +1 -1
- package/dist/src/services/import-builder.service.d.ts +1 -1
- package/dist/src/services/import-builder.service.d.ts.map +1 -1
- package/dist/src/services/type-builder.service.d.ts +1 -1
- package/dist/src/services/type-builder.service.d.ts.map +1 -1
- package/dist/src/types/generator-options.d.ts +1 -1
- package/dist/src/types/generator-options.d.ts.map +1 -1
- package/dist/src/utils/error-handler.d.ts +3 -2
- package/dist/src/utils/error-handler.d.ts.map +1 -1
- package/dist/src/utils/error-handler.js +4 -4
- package/dist/src/utils/reporter.d.ts +3 -2
- package/dist/src/utils/reporter.d.ts.map +1 -1
- package/dist/src/utils/reporter.js +7 -5
- package/dist/src/utils/signal-handler.d.ts +3 -2
- package/dist/src/utils/signal-handler.d.ts.map +1 -1
- package/dist/src/utils/signal-handler.js +4 -4
- package/examples/README.md +10 -1
- package/examples/petstore/README.md +6 -6
- package/examples/petstore/authenticated-usage.ts +1 -1
- package/examples/petstore/basic-usage.ts +1 -1
- package/examples/petstore/retry-handler-usage.ts +173 -0
- package/examples/petstore/server-variables-usage.ts +1 -1
- package/examples/petstore/type.ts +68 -47
- package/examples/pokeapi/README.md +3 -3
- package/examples/pokeapi/basic-usage.ts +1 -1
- package/examples/pokeapi/custom-client.ts +1 -1
- package/generated/type.ts +323 -0
- package/package.json +4 -7
- package/scripts/add-js-extensions.ts +79 -0
- package/scripts/update-manifest.ts +4 -2
- package/src/assets/manifest.json +1 -1
- package/src/cli.ts +7 -7
- package/src/generator.ts +51 -9
- package/src/interfaces/code-generator.ts +1 -1
- package/src/services/code-generator.service.ts +114 -8
- package/src/services/file-reader.service.ts +3 -3
- package/src/services/file-writer.service.ts +1 -1
- package/src/services/import-builder.service.ts +1 -1
- package/src/services/type-builder.service.ts +1 -1
- package/src/types/generator-options.ts +1 -1
- package/src/utils/error-handler.ts +6 -5
- package/src/utils/reporter.ts +6 -3
- package/src/utils/signal-handler.ts +10 -8
- package/tests/integration/cli-comprehensive.test.ts +123 -0
- package/tests/integration/cli.test.ts +2 -2
- package/tests/integration/error-scenarios.test.ts +240 -0
- package/tests/integration/snapshots.test.ts +131 -0
- package/tests/unit/code-generator-edge-cases.test.ts +551 -0
- package/tests/unit/code-generator.test.ts +385 -2
- package/tests/unit/file-reader.test.ts +16 -1
- package/tests/unit/generator.test.ts +19 -2
- package/tests/unit/naming-convention.test.ts +30 -1
- package/tests/unit/reporter.test.ts +63 -0
- package/tests/unit/type-builder.test.ts +131 -0
- package/tsconfig.json +3 -3
- package/dist/src/http/fetch-client.d.ts +0 -15
- package/dist/src/http/fetch-client.d.ts.map +0 -1
- package/dist/src/http/fetch-client.js +0 -140
- package/dist/src/polyfills/fetch.d.ts +0 -5
- package/dist/src/polyfills/fetch.d.ts.map +0 -1
- package/dist/src/polyfills/fetch.js +0 -18
- package/dist/src/types/http.d.ts +0 -25
- package/dist/src/types/http.d.ts.map +0 -1
- package/dist/src/types/http.js +0 -10
- package/dist/src/utils/manifest.d.ts +0 -8
- package/dist/src/utils/manifest.d.ts.map +0 -1
- package/dist/src/utils/manifest.js +0 -9
- package/dist/src/utils/tty.d.ts +0 -2
- package/dist/src/utils/tty.d.ts.map +0 -1
- package/dist/src/utils/tty.js +0 -3
- package/src/http/fetch-client.ts +0 -181
- package/src/polyfills/fetch.ts +0 -26
- package/src/types/http.ts +0 -35
- package/src/utils/manifest.ts +0 -17
- package/src/utils/tty.ts +0 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"code-generator.service.d.ts","sourceRoot":"","sources":["../../../src/services/code-generator.service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AAEjC,OAAO,KAAK,EAAC,aAAa,EAAE,aAAa,EAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"code-generator.service.d.ts","sourceRoot":"","sources":["../../../src/services/code-generator.service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AAEjC,OAAO,KAAK,EAAC,aAAa,EAAE,aAAa,EAAC,MAAM,8BAA8B,CAAC;AAC/E,OAAO,KAAK,EAAmB,eAAe,EAAgB,MAAM,kBAAkB,CAAC;AACvF,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,4BAA4B,CAAC;AAWjE,qBAAa,8BAA+B,YAAW,aAAa,EAAE,aAAa;IACjF,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAsC;IAClE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAwC;IACtE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwD;IAChF,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA+B;IAChE,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAuC;IAGhF,OAAO,CAAC,eAAe,CAAqB;IAC5C,OAAO,CAAC,iBAAiB,CAAuB;gBAEpC,OAAO,GAAE,gBAAqB;IAK1C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAGpB;IAEH,QAAQ,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM;IAMvC,WAAW,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,UAAO,GAAG,EAAE,CAAC,cAAc,GAAG,EAAE,CAAC,UAAU;IA2BhF,OAAO,CAAC,QAAQ;IAmBhB,OAAO,CAAC,YAAY;IA2CpB,OAAO,CAAC,sBAAsB;IAe9B,OAAO,CAAC,gBAAgB;IAuBxB,OAAO,CAAC,gBAAgB;IAmGxB,OAAO,CAAC,gCAAgC;IAwBxC,OAAO,CAAC,yBAAyB;IAuBjC,OAAO,CAAC,sBAAsB;IAkoB9B,OAAO,CAAC,kBAAkB;IAgE1B;;;;OAIG;IACH,OAAO,CAAC,sBAAsB;IAoC9B,OAAO,CAAC,mBAAmB;IAgH3B,OAAO,CAAC,mBAAmB;IA6D3B,OAAO,CAAC,qBAAqB;IAwF7B,OAAO,CAAC,gBAAgB;IA0CxB,OAAO,CAAC,iBAAiB;IAmCzB,OAAO,CAAC,iBAAiB;IA8BzB,OAAO,CAAC,eAAe;IAuDvB,OAAO,CAAC,wBAAwB;IA4IhC,OAAO,CAAC,gBAAgB;IAqBxB,OAAO,CAAC,6BAA6B;IAkPrC,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,aAAa;IAMrB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAuFzB,OAAO,CAAC,WAAW;IA0CnB,OAAO,CAAC,aAAa;IAmjBrB,OAAO,CAAC,iBAAiB;IAuCzB,OAAO,CAAC,qBAAqB;IAe7B,OAAO,CAAC,oBAAoB;IAyE5B,OAAO,CAAC,8BAA8B;IActC,OAAO,CAAC,wBAAwB;IAyBhC,OAAO,CAAC,yBAAyB;IA8BjC,OAAO,CAAC,wBAAwB;IAYhC,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,kBAAkB;IA8B1B;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;IAmB3B;;;;OAIG;IACH,OAAO,CAAC,0BAA0B;IAiFlC,OAAO,CAAC,eAAe;CAuCxB"}
|
|
@@ -101,6 +101,7 @@ export class TypeScriptCodeGeneratorService {
|
|
|
101
101
|
this.typeBuilder.createProperty('#baseUrl', 'string', true),
|
|
102
102
|
this.buildConstructor(openapi),
|
|
103
103
|
this.buildGetBaseRequestOptionsMethod(),
|
|
104
|
+
this.buildHandleResponseMethod(),
|
|
104
105
|
this.buildHttpRequestMethod(),
|
|
105
106
|
...methods,
|
|
106
107
|
]);
|
|
@@ -142,6 +143,16 @@ export class TypeScriptCodeGeneratorService {
|
|
|
142
143
|
]),
|
|
143
144
|
]), ts.factory.createBlock([ts.factory.createReturnStatement(ts.factory.createObjectLiteralExpression([], false))], true));
|
|
144
145
|
}
|
|
146
|
+
buildHandleResponseMethod() {
|
|
147
|
+
return ts.factory.createMethodDeclaration([ts.factory.createToken(ts.SyntaxKind.ProtectedKeyword), ts.factory.createToken(ts.SyntaxKind.AsyncKeyword)], undefined, ts.factory.createIdentifier('handleResponse'), undefined, [this.typeBuilder.createGenericType('T')], [
|
|
148
|
+
this.typeBuilder.createParameter('response', 'Response'),
|
|
149
|
+
this.typeBuilder.createParameter('method', 'string'),
|
|
150
|
+
this.typeBuilder.createParameter('path', 'string'),
|
|
151
|
+
this.typeBuilder.createParameter('options', '{params?: Record<string, string | number | boolean>; data?: unknown; contentType?: string; headers?: Record<string, string>}'),
|
|
152
|
+
], ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Promise'), [
|
|
153
|
+
ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('Response'), undefined),
|
|
154
|
+
]), ts.factory.createBlock([ts.factory.createReturnStatement(ts.factory.createIdentifier('response'))], true));
|
|
155
|
+
}
|
|
145
156
|
buildHttpRequestMethod() {
|
|
146
157
|
return ts.factory.createMethodDeclaration([ts.factory.createToken(ts.SyntaxKind.ProtectedKeyword), ts.factory.createToken(ts.SyntaxKind.AsyncKeyword)], undefined, ts.factory.createIdentifier('makeRequest'), undefined, [this.typeBuilder.createGenericType('T')], [
|
|
147
158
|
this.typeBuilder.createParameter('method', 'string'),
|
|
@@ -242,7 +253,7 @@ export class TypeScriptCodeGeneratorService {
|
|
|
242
253
|
], ts.NodeFlags.Const)),
|
|
243
254
|
// Make fetch request: merge base options with method, headers, and body
|
|
244
255
|
ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
|
|
245
|
-
ts.factory.createVariableDeclaration(ts.factory.createIdentifier('
|
|
256
|
+
ts.factory.createVariableDeclaration(ts.factory.createIdentifier('rawResponse'), undefined, undefined, ts.factory.createAwaitExpression(ts.factory.createCallExpression(ts.factory.createIdentifier('fetch'), undefined, [
|
|
246
257
|
ts.factory.createIdentifier('url'),
|
|
247
258
|
ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('Object'), ts.factory.createIdentifier('assign')), undefined, [
|
|
248
259
|
ts.factory.createObjectLiteralExpression([], false),
|
|
@@ -255,6 +266,15 @@ export class TypeScriptCodeGeneratorService {
|
|
|
255
266
|
]),
|
|
256
267
|
]))),
|
|
257
268
|
], ts.NodeFlags.Const)),
|
|
269
|
+
// Handle response through hook (allows subclasses to intercept and modify response)
|
|
270
|
+
ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList([
|
|
271
|
+
ts.factory.createVariableDeclaration(ts.factory.createIdentifier('response'), undefined, undefined, ts.factory.createAwaitExpression(ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createThis(), ts.factory.createIdentifier('handleResponse')), [ts.factory.createTypeReferenceNode(ts.factory.createIdentifier('T'), undefined)], [
|
|
272
|
+
ts.factory.createIdentifier('rawResponse'),
|
|
273
|
+
ts.factory.createIdentifier('method'),
|
|
274
|
+
ts.factory.createIdentifier('path'),
|
|
275
|
+
ts.factory.createIdentifier('options'),
|
|
276
|
+
]))),
|
|
277
|
+
], ts.NodeFlags.Const)),
|
|
258
278
|
// Check response status
|
|
259
279
|
ts.factory.createIfStatement(ts.factory.createPrefixUnaryExpression(ts.SyntaxKind.ExclamationToken, ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('response'), ts.factory.createIdentifier('ok'))), ts.factory.createThrowStatement(ts.factory.createNewExpression(ts.factory.createIdentifier('Error'), undefined, [
|
|
260
280
|
ts.factory.createTemplateExpression(ts.factory.createTemplateHead('HTTP ', 'HTTP '), [
|
|
@@ -267,6 +287,27 @@ export class TypeScriptCodeGeneratorService {
|
|
|
267
287
|
], true));
|
|
268
288
|
}
|
|
269
289
|
buildClientMethods(openapi, schemas) {
|
|
290
|
+
// Track operation IDs to detect duplicates
|
|
291
|
+
const operationIdMap = new Map();
|
|
292
|
+
// First pass: collect all operation IDs and their methods/paths
|
|
293
|
+
Object.entries(openapi.paths).forEach(([path, pathItem]) => {
|
|
294
|
+
Object.entries(pathItem)
|
|
295
|
+
.filter(([method]) => ['get', 'post', 'put', 'patch', 'delete', 'head', 'options'].includes(method))
|
|
296
|
+
.forEach(([method, methodSchema]) => {
|
|
297
|
+
const safeMethodSchema = MethodSchema.parse(methodSchema);
|
|
298
|
+
if (safeMethodSchema.operationId) {
|
|
299
|
+
const operationId = safeMethodSchema.operationId;
|
|
300
|
+
const existing = operationIdMap.get(operationId);
|
|
301
|
+
if (existing) {
|
|
302
|
+
existing.push({ method, path });
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
operationIdMap.set(operationId, [{ method, path }]);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
});
|
|
310
|
+
// Second pass: build methods, appending method name for HEAD/OPTIONS or when duplicates exist
|
|
270
311
|
return Object.entries(openapi.paths).reduce((endpoints, [path, pathItem]) => {
|
|
271
312
|
const methods = Object.entries(pathItem)
|
|
272
313
|
.filter(([method]) => ['get', 'post', 'put', 'patch', 'delete', 'head', 'options'].includes(method))
|
|
@@ -275,6 +316,23 @@ export class TypeScriptCodeGeneratorService {
|
|
|
275
316
|
if (!safeMethodSchema.operationId) {
|
|
276
317
|
return null;
|
|
277
318
|
}
|
|
319
|
+
const operationId = safeMethodSchema.operationId;
|
|
320
|
+
const methodLower = method.toLowerCase();
|
|
321
|
+
// Check if this operationId is used by multiple methods
|
|
322
|
+
const operations = operationIdMap.get(operationId);
|
|
323
|
+
const hasDuplicates = operations !== undefined && operations.length > 1;
|
|
324
|
+
// For HEAD/OPTIONS or when duplicates exist, we need to ensure uniqueness
|
|
325
|
+
// We'll handle this in transformOperationName by appending the method
|
|
326
|
+
// But we need to mark it here so transformOperationName knows to append
|
|
327
|
+
if (hasDuplicates || methodLower === 'head' || methodLower === 'options') {
|
|
328
|
+
// Temporarily modify the operationId to include method for uniqueness
|
|
329
|
+
// This will be handled in transformOperationName
|
|
330
|
+
const modifiedSchema = {
|
|
331
|
+
...safeMethodSchema,
|
|
332
|
+
operationId: `${operationId}_${methodLower}`,
|
|
333
|
+
};
|
|
334
|
+
return this.buildEndpointMethod(method, path, modifiedSchema, schemas);
|
|
335
|
+
}
|
|
278
336
|
return this.buildEndpointMethod(method, path, safeMethodSchema, schemas);
|
|
279
337
|
})
|
|
280
338
|
.filter((method) => method !== null);
|
|
@@ -284,6 +342,7 @@ export class TypeScriptCodeGeneratorService {
|
|
|
284
342
|
/**
|
|
285
343
|
* Transforms operation ID according to the configured naming convention or transformer
|
|
286
344
|
* Ensures the result is a valid TypeScript identifier
|
|
345
|
+
* For HEAD and OPTIONS methods, appends the method name to ensure uniqueness when same operationId is used
|
|
287
346
|
*/
|
|
288
347
|
transformOperationName(operationId, method, path, schema) {
|
|
289
348
|
let transformed;
|
|
@@ -307,6 +366,15 @@ export class TypeScriptCodeGeneratorService {
|
|
|
307
366
|
// Return original operationId if no transformation is configured
|
|
308
367
|
transformed = operationId;
|
|
309
368
|
}
|
|
369
|
+
// For HEAD and OPTIONS methods, append method name to ensure uniqueness
|
|
370
|
+
// This prevents duplicate method names when GET and HEAD share the same operationId
|
|
371
|
+
const methodLower = method.toLowerCase();
|
|
372
|
+
if (methodLower === 'head' || methodLower === 'options') {
|
|
373
|
+
// Only append if not already present to avoid double-appending
|
|
374
|
+
if (!transformed.toLowerCase().endsWith(`_${methodLower}`)) {
|
|
375
|
+
transformed = `${transformed}_${methodLower}`;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
310
378
|
// Sanitize to ensure valid TypeScript identifier (handles edge cases from custom transformers)
|
|
311
379
|
return this.typeBuilder.sanitizeIdentifier(transformed);
|
|
312
380
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { OpenApiFileParser, OpenApiFileReader } from '../interfaces/file-reader
|
|
2
|
-
import type { OpenApiSpecType } from '../types/openapi
|
|
1
|
+
import type { OpenApiFileParser, OpenApiFileReader } from '../interfaces/file-reader';
|
|
2
|
+
import type { OpenApiSpecType } from '../types/openapi';
|
|
3
3
|
export declare class SyncFileReaderService implements OpenApiFileReader {
|
|
4
4
|
readFile(path: string): Promise<string>;
|
|
5
5
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-reader.service.d.ts","sourceRoot":"","sources":["../../../src/services/file-reader.service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,iBAAiB,EAAE,iBAAiB,EAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"file-reader.service.d.ts","sourceRoot":"","sources":["../../../src/services/file-reader.service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,iBAAiB,EAAE,iBAAiB,EAAC,MAAM,2BAA2B,CAAC;AACpF,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,kBAAkB,CAAC;AAGtD,qBAAa,qBAAsB,YAAW,iBAAiB;IACvD,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAe9C;AAED,qBAAa,wBAAyB,YAAW,iBAAiB,CAAC,eAAe,CAAC;IACjF,KAAK,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe;CAgBvC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-writer.service.d.ts","sourceRoot":"","sources":["../../../src/services/file-writer.service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"file-writer.service.d.ts","sourceRoot":"","sources":["../../../src/services/file-writer.service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,8BAA8B,CAAC;AAE7D,qBAAa,qBAAsB,YAAW,UAAU;IAEpD,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,SAAS;gBAFT,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM;IAGpC,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAqBlD,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,SAAY,GAAG,MAAM;CAGnE"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as ts from 'typescript';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import type { ImportBuilder } from '../interfaces/code-generator
|
|
3
|
+
import type { ImportBuilder } from '../interfaces/code-generator';
|
|
4
4
|
declare const ImportOptions: z.ZodObject<{
|
|
5
5
|
defaultImport: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodBoolean>>;
|
|
6
6
|
namedImports: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodBoolean>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"import-builder.service.d.ts","sourceRoot":"","sources":["../../../src/services/import-builder.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AACtB,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"import-builder.service.d.ts","sourceRoot":"","sources":["../../../src/services/import-builder.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AACtB,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,8BAA8B,CAAC;AAKhE,QAAA,MAAM,aAAa;;;iBAGjB,CAAC;AAEH,KAAK,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAEvD,qBAAa,8BAA+B,YAAW,aAAa;IAClE,YAAY,IAAI,EAAE,CAAC,iBAAiB,EAAE;IAQtC,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,EAAE,CAAC,iBAAiB;CAqC/E"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as ts from 'typescript';
|
|
2
|
-
import type { TypeBuilder } from '../interfaces/code-generator
|
|
2
|
+
import type { TypeBuilder } from '../interfaces/code-generator';
|
|
3
3
|
export declare class TypeScriptTypeBuilderService implements TypeBuilder {
|
|
4
4
|
buildType(type: string): ts.TypeNode;
|
|
5
5
|
createProperty(name: string, type: string, isReadonly?: boolean): ts.PropertyDeclaration;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"type-builder.service.d.ts","sourceRoot":"","sources":["../../../src/services/type-builder.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"type-builder.service.d.ts","sourceRoot":"","sources":["../../../src/services/type-builder.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,8BAA8B,CAAC;AAE9D,qBAAa,4BAA6B,YAAW,WAAW;IAC9D,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,EAAE,CAAC,QAAQ;IA6BpC,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,UAAQ,GAAG,EAAE,CAAC,mBAAmB;IAYtF,eAAe,CACb,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,MAAM,GAAG,EAAE,CAAC,QAAQ,EAC3B,YAAY,CAAC,EAAE,EAAE,CAAC,UAAU,EAC5B,UAAU,UAAQ,GACjB,EAAE,CAAC,oBAAoB;IAW1B,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,EAAE,CAAC,wBAAwB;IAS5D,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAMxC,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAIjC,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;CAGnC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generator-options.d.ts","sourceRoot":"","sources":["../../../src/types/generator-options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,gBAAgB,EAAE,wBAAwB,EAAC,MAAM
|
|
1
|
+
{"version":3,"file":"generator-options.d.ts","sourceRoot":"","sources":["../../../src/types/generator-options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,gBAAgB,EAAE,wBAAwB,EAAC,MAAM,4BAA4B,CAAC;AAE3F;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;;;;;;;;OAYG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IAEpC;;;;;;;;;;;;;;;;;;OAkBG;IACH,wBAAwB,CAAC,EAAE,wBAAwB,CAAC;CACrD"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
export declare const
|
|
1
|
+
import type { Reporter } from './reporter';
|
|
2
|
+
export declare const errorReceived: (process: NodeJS.Process, startTime: bigint, reporter: Reporter) => () => void;
|
|
3
|
+
export declare const handleErrors: (process: NodeJS.Process, startTime: bigint, reporter: Reporter) => void;
|
|
3
4
|
//# sourceMappingURL=error-handler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-handler.d.ts","sourceRoot":"","sources":["../../../src/utils/error-handler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"error-handler.d.ts","sourceRoot":"","sources":["../../../src/utils/error-handler.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,YAAY,CAAC;AAEzC,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,CAAC,OAAO,EAAE,WAAW,MAAM,EAAE,UAAU,QAAQ,WAAS,IAGpG,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,SAAS,MAAM,CAAC,OAAO,EAAE,WAAW,MAAM,EAAE,UAAU,QAAQ,KAAG,IAK7F,CAAC"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { getExecutionTime } from './execution-time.js';
|
|
2
|
-
export const errorReceived = (process, startTime) => () => {
|
|
3
|
-
|
|
2
|
+
export const errorReceived = (process, startTime, reporter) => () => {
|
|
3
|
+
reporter.log(`Done after ${String(getExecutionTime(startTime))}s`);
|
|
4
4
|
process.exit(1);
|
|
5
5
|
};
|
|
6
|
-
export const handleErrors = (process, startTime) => {
|
|
6
|
+
export const handleErrors = (process, startTime, reporter) => {
|
|
7
7
|
const catchErrors = ['unhandledRejection', 'uncaughtException'];
|
|
8
8
|
catchErrors.forEach((event) => {
|
|
9
|
-
process.on(event, errorReceived(process, startTime));
|
|
9
|
+
process.on(event, errorReceived(process, startTime, reporter));
|
|
10
10
|
});
|
|
11
11
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export declare class Reporter {
|
|
2
|
-
private readonly
|
|
3
|
-
|
|
2
|
+
private readonly stdout;
|
|
3
|
+
private readonly stderr;
|
|
4
|
+
constructor(stdout: NodeJS.WriteStream, stderr?: NodeJS.WriteStream);
|
|
4
5
|
log(...args: readonly unknown[]): void;
|
|
5
6
|
error(...args: readonly unknown[]): void;
|
|
6
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../../../src/utils/reporter.ts"],"names":[],"mappings":"AAEA,qBAAa,QAAQ;
|
|
1
|
+
{"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../../../src/utils/reporter.ts"],"names":[],"mappings":"AAEA,qBAAa,QAAQ;IAEjB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBADN,MAAM,EAAE,MAAM,CAAC,WAAW,EAC1B,MAAM,GAAE,MAAM,CAAC,WAAoB;IAMtD,GAAG,CAAC,GAAG,IAAI,EAAE,SAAS,OAAO,EAAE,GAAG,IAAI;IAItC,KAAK,CAAC,GAAG,IAAI,EAAE,SAAS,OAAO,EAAE,GAAG,IAAI;CAGzC"}
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import { format } from 'node:util';
|
|
2
2
|
export class Reporter {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
stdout;
|
|
4
|
+
stderr;
|
|
5
|
+
constructor(stdout, stderr = stdout) {
|
|
6
|
+
this.stdout = stdout;
|
|
7
|
+
this.stderr = stderr;
|
|
6
8
|
this.log = this.log.bind(this);
|
|
7
9
|
this.error = this.error.bind(this);
|
|
8
10
|
}
|
|
9
11
|
log(...args) {
|
|
10
|
-
this.
|
|
12
|
+
this.stdout.write(format(...args) + '\n');
|
|
11
13
|
}
|
|
12
14
|
error(...args) {
|
|
13
|
-
this.
|
|
15
|
+
this.stderr.write(format(...args) + '\n');
|
|
14
16
|
}
|
|
15
17
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
export declare const
|
|
1
|
+
import type { Reporter } from './reporter';
|
|
2
|
+
export declare const signalReceived: (process: NodeJS.Process, startTime: bigint, event: NodeJS.Signals, reporter: Reporter) => () => void;
|
|
3
|
+
export declare const handleSignals: (process: NodeJS.Process, startTime: bigint, reporter: Reporter) => void;
|
|
3
4
|
//# sourceMappingURL=signal-handler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signal-handler.d.ts","sourceRoot":"","sources":["../../../src/utils/signal-handler.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"signal-handler.d.ts","sourceRoot":"","sources":["../../../src/utils/signal-handler.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,YAAY,CAAC;AAEzC,eAAO,MAAM,cAAc,GACxB,SAAS,MAAM,CAAC,OAAO,EAAE,WAAW,MAAM,EAAE,OAAO,MAAM,CAAC,OAAO,EAAE,UAAU,QAAQ,WAAS,IAI9F,CAAC;AAEJ,eAAO,MAAM,aAAa,GAAI,SAAS,MAAM,CAAC,OAAO,EAAE,WAAW,MAAM,EAAE,UAAU,QAAQ,KAAG,IAK9F,CAAC"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { getExecutionTime } from './execution-time.js';
|
|
2
|
-
export const signalReceived = (process, startTime, event) => () => {
|
|
3
|
-
|
|
2
|
+
export const signalReceived = (process, startTime, event, reporter) => () => {
|
|
3
|
+
reporter.log(`Done after ${String(getExecutionTime(startTime))}s`);
|
|
4
4
|
process.kill(process.pid, event);
|
|
5
5
|
process.exit(1);
|
|
6
6
|
};
|
|
7
|
-
export const handleSignals = (process, startTime) => {
|
|
7
|
+
export const handleSignals = (process, startTime, reporter) => {
|
|
8
8
|
const catchSignals = ['SIGTERM', 'SIGINT', 'SIGUSR2'];
|
|
9
9
|
catchSignals.forEach((event) => {
|
|
10
|
-
process.once(event, signalReceived(process, startTime, event));
|
|
10
|
+
process.once(event, signalReceived(process, startTime, event, reporter));
|
|
11
11
|
});
|
|
12
12
|
};
|
package/examples/README.md
CHANGED
|
@@ -24,8 +24,15 @@ zod-codegen --input ./samples/swagger-petstore.yaml --output ./examples/petstore
|
|
|
24
24
|
```bash
|
|
25
25
|
npx ts-node examples/petstore/basic-usage.ts
|
|
26
26
|
npx ts-node examples/petstore/authenticated-usage.ts
|
|
27
|
+
npx ts-node examples/petstore/server-variables-usage.ts
|
|
28
|
+
npx ts-node examples/petstore/retry-handler-usage.ts
|
|
27
29
|
```
|
|
28
30
|
|
|
31
|
+
**Additional examples:**
|
|
32
|
+
|
|
33
|
+
- `server-variables-usage.ts` - Using server variables for different environments
|
|
34
|
+
- `retry-handler-usage.ts` - Custom retry handler implementation
|
|
35
|
+
|
|
29
36
|
### ⚡ [PokéAPI](./pokeapi/)
|
|
30
37
|
|
|
31
38
|
PokéAPI is a public RESTful API that provides data about Pokémon. This example demonstrates:
|
|
@@ -37,9 +44,11 @@ PokéAPI is a public RESTful API that provides data about Pokémon. This example
|
|
|
37
44
|
**Generate the client:**
|
|
38
45
|
|
|
39
46
|
```bash
|
|
40
|
-
zod-codegen --input
|
|
47
|
+
zod-codegen --input ./samples/pokeapi-openapi.json --output ./examples/pokeapi
|
|
41
48
|
```
|
|
42
49
|
|
|
50
|
+
**Note**: PokéAPI doesn't provide an official OpenAPI specification, so we use a simplified OpenAPI spec based on their API structure.
|
|
51
|
+
|
|
43
52
|
## Structure
|
|
44
53
|
|
|
45
54
|
Each example directory contains:
|
|
@@ -11,7 +11,7 @@ After running `zod-codegen`, you'll get:
|
|
|
11
11
|
## Basic Usage
|
|
12
12
|
|
|
13
13
|
```typescript
|
|
14
|
-
import {SwaggerPetstoreOpenAPI30} from './type
|
|
14
|
+
import {SwaggerPetstoreOpenAPI30} from './type';
|
|
15
15
|
|
|
16
16
|
// Create a client instance using default server from OpenAPI spec
|
|
17
17
|
const client = new SwaggerPetstoreOpenAPI30({});
|
|
@@ -26,7 +26,7 @@ console.log('Available pets:', pets);
|
|
|
26
26
|
The generated client supports flexible server configuration:
|
|
27
27
|
|
|
28
28
|
```typescript
|
|
29
|
-
import {SwaggerPetstoreOpenAPI30, ClientOptions} from './type
|
|
29
|
+
import {SwaggerPetstoreOpenAPI30, ClientOptions} from './type';
|
|
30
30
|
|
|
31
31
|
// Option 1: Use default server (first server from OpenAPI spec)
|
|
32
32
|
const defaultClient = new SwaggerPetstoreOpenAPI30({});
|
|
@@ -56,10 +56,10 @@ const variableClient = new SwaggerPetstoreOpenAPI30({
|
|
|
56
56
|
## Example: Finding Pets
|
|
57
57
|
|
|
58
58
|
```typescript
|
|
59
|
-
import {SwaggerPetstoreOpenAPI30
|
|
59
|
+
import {SwaggerPetstoreOpenAPI30} from './type';
|
|
60
60
|
|
|
61
61
|
async function findAvailablePets() {
|
|
62
|
-
const client = new SwaggerPetstoreOpenAPI30(
|
|
62
|
+
const client = new SwaggerPetstoreOpenAPI30({});
|
|
63
63
|
|
|
64
64
|
try {
|
|
65
65
|
// Find pets by status
|
|
@@ -84,11 +84,11 @@ findAvailablePets();
|
|
|
84
84
|
## Example: Adding a Pet
|
|
85
85
|
|
|
86
86
|
```typescript
|
|
87
|
-
import {SwaggerPetstoreOpenAPI30, Pet, PetStatus
|
|
87
|
+
import {SwaggerPetstoreOpenAPI30, Pet, PetStatus} from './type';
|
|
88
88
|
import {z} from 'zod';
|
|
89
89
|
|
|
90
90
|
async function addNewPet() {
|
|
91
|
-
const client = new SwaggerPetstoreOpenAPI30(
|
|
91
|
+
const client = new SwaggerPetstoreOpenAPI30({});
|
|
92
92
|
|
|
93
93
|
const newPet: z.infer<typeof Pet> = {
|
|
94
94
|
id: 12345,
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Run with: npx ts-node examples/petstore/authenticated-usage.ts
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {SwaggerPetstoreOpenAPI30, ClientOptions} from './type
|
|
7
|
+
import {SwaggerPetstoreOpenAPI30, ClientOptions} from './type';
|
|
8
8
|
|
|
9
9
|
class AuthenticatedPetstoreAPI extends SwaggerPetstoreOpenAPI30 {
|
|
10
10
|
private apiKey: string | null = null;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Example showing how to handle special HTTP response cases (e.g., 429 retry logic)
|
|
3
|
+
* by extending the generated client and overriding the handleResponse method
|
|
4
|
+
*
|
|
5
|
+
* Run with: npx ts-node examples/petstore/retry-handler-usage.ts
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {SwaggerPetstoreOpenAPI30} from './type';
|
|
9
|
+
|
|
10
|
+
class PetstoreClientWithRetry extends SwaggerPetstoreOpenAPI30 {
|
|
11
|
+
private maxRetries = 3;
|
|
12
|
+
private retryDelay = 1000; // 1 second base delay
|
|
13
|
+
private retrying = false; // Track if we're currently in a retry to avoid infinite loops
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Override handleResponse to intercept responses and handle special cases
|
|
17
|
+
* This method is called before error checking, allowing you to:
|
|
18
|
+
* - Retry requests on specific status codes (e.g., 429 Too Many Requests)
|
|
19
|
+
* - Modify responses before they're processed
|
|
20
|
+
* - Implement custom error handling logic
|
|
21
|
+
*/
|
|
22
|
+
protected async handleResponse<T>(
|
|
23
|
+
response: Response,
|
|
24
|
+
method: string,
|
|
25
|
+
path: string,
|
|
26
|
+
options: {
|
|
27
|
+
params?: Record<string, string | number | boolean>;
|
|
28
|
+
data?: unknown;
|
|
29
|
+
contentType?: string;
|
|
30
|
+
headers?: Record<string, string>;
|
|
31
|
+
},
|
|
32
|
+
): Promise<Response> {
|
|
33
|
+
// Skip retry logic if we're already retrying to avoid infinite loops
|
|
34
|
+
if (this.retrying) {
|
|
35
|
+
return response;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Handle 429 Too Many Requests with exponential backoff retry
|
|
39
|
+
if (response.status === 429) {
|
|
40
|
+
const retryAfter = response.headers.get('Retry-After');
|
|
41
|
+
const delay = retryAfter ? parseInt(retryAfter, 10) * 1000 : this.retryDelay;
|
|
42
|
+
|
|
43
|
+
for (let attempt = 0; attempt < this.maxRetries; attempt++) {
|
|
44
|
+
console.log(
|
|
45
|
+
`⚠️ Rate limited (429). Retrying in ${delay * (attempt + 1)}ms... (Attempt ${attempt + 1}/${this.maxRetries})`,
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
// Wait before retrying with exponential backoff
|
|
49
|
+
await new Promise((resolve) => setTimeout(resolve, delay * (attempt + 1)));
|
|
50
|
+
|
|
51
|
+
// Retry the request
|
|
52
|
+
// We use retryRequest helper to avoid going through handleResponse again
|
|
53
|
+
// (which would cause infinite loops). Alternatively, if makeRequest is protected,
|
|
54
|
+
// you could call it directly, but you'd need to ensure handleResponse doesn't
|
|
55
|
+
// retry again by checking the retrying flag.
|
|
56
|
+
try {
|
|
57
|
+
this.retrying = true;
|
|
58
|
+
const retryResponse = await this.retryRequest(method, path, options);
|
|
59
|
+
this.retrying = false;
|
|
60
|
+
|
|
61
|
+
if (retryResponse.ok) {
|
|
62
|
+
console.log(`✅ Retry successful after ${attempt + 1} attempt(s)`);
|
|
63
|
+
return retryResponse;
|
|
64
|
+
}
|
|
65
|
+
// If still rate limited, continue to next retry
|
|
66
|
+
if (retryResponse.status === 429 && attempt < this.maxRetries - 1) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
// Return the response even if it's an error (will be handled by normal error flow)
|
|
70
|
+
return retryResponse;
|
|
71
|
+
} catch (error) {
|
|
72
|
+
this.retrying = false;
|
|
73
|
+
// If retry fails and we're out of attempts, throw
|
|
74
|
+
if (attempt === this.maxRetries - 1) {
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Handle other status codes if needed
|
|
82
|
+
// For example, you could handle 503 Service Unavailable similarly
|
|
83
|
+
if (response.status === 503) {
|
|
84
|
+
console.log('⚠️ Service unavailable (503). You could implement retry logic here too.');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// For all other responses, return as-is
|
|
88
|
+
return response;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Helper method to retry a request
|
|
93
|
+
* This reconstructs the request using the same parameters
|
|
94
|
+
* Note: If makeRequest is protected (not private), you could call it directly instead
|
|
95
|
+
*/
|
|
96
|
+
private async retryRequest(
|
|
97
|
+
method: string,
|
|
98
|
+
path: string,
|
|
99
|
+
options: {
|
|
100
|
+
params?: Record<string, string | number | boolean>;
|
|
101
|
+
data?: unknown;
|
|
102
|
+
contentType?: string;
|
|
103
|
+
headers?: Record<string, string>;
|
|
104
|
+
},
|
|
105
|
+
): Promise<Response> {
|
|
106
|
+
// Reconstruct the request - this duplicates logic from makeRequest
|
|
107
|
+
// but allows us to retry without going through handleResponse again
|
|
108
|
+
const baseUrl = `${(this as any)['#baseUrl']}${path}`;
|
|
109
|
+
const url =
|
|
110
|
+
options.params && Object.keys(options.params).length > 0
|
|
111
|
+
? (() => {
|
|
112
|
+
const urlObj = new URL(baseUrl);
|
|
113
|
+
Object.entries(options.params).forEach(([key, value]) => {
|
|
114
|
+
urlObj.searchParams.set(key, String(value));
|
|
115
|
+
});
|
|
116
|
+
return urlObj.toString();
|
|
117
|
+
})()
|
|
118
|
+
: baseUrl;
|
|
119
|
+
|
|
120
|
+
const baseOptions = this.getBaseRequestOptions();
|
|
121
|
+
const contentType =
|
|
122
|
+
options.contentType === 'application/x-www-form-urlencoded'
|
|
123
|
+
? 'application/x-www-form-urlencoded'
|
|
124
|
+
: 'application/json';
|
|
125
|
+
const baseHeaders = baseOptions.headers !== undefined ? baseOptions.headers : {};
|
|
126
|
+
const headers = Object.assign(
|
|
127
|
+
{},
|
|
128
|
+
baseHeaders,
|
|
129
|
+
{'Content-Type': contentType},
|
|
130
|
+
options.headers !== undefined ? options.headers : {},
|
|
131
|
+
);
|
|
132
|
+
const body =
|
|
133
|
+
options.data !== undefined
|
|
134
|
+
? options.contentType === 'application/x-www-form-urlencoded'
|
|
135
|
+
? (() => {
|
|
136
|
+
const params = new URLSearchParams();
|
|
137
|
+
Object.entries(options.data as Record<string, unknown>).forEach(([key, value]) => {
|
|
138
|
+
params.set(key, String(value));
|
|
139
|
+
});
|
|
140
|
+
return params.toString();
|
|
141
|
+
})()
|
|
142
|
+
: JSON.stringify(options.data)
|
|
143
|
+
: null;
|
|
144
|
+
|
|
145
|
+
return await fetch(url, Object.assign({}, baseOptions, {method, headers: headers, body: body}));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async function main() {
|
|
150
|
+
const client = new PetstoreClientWithRetry({});
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
console.log('🔍 Fetching pets with retry handler...\n');
|
|
154
|
+
|
|
155
|
+
// This will use the handleResponse hook if a 429 is encountered
|
|
156
|
+
const availablePets = await client.findPetsByStatus('available');
|
|
157
|
+
console.log(`✅ Found ${availablePets.length} available pets`);
|
|
158
|
+
|
|
159
|
+
if (availablePets.length > 0) {
|
|
160
|
+
console.log('\n📋 First pet details:');
|
|
161
|
+
console.log(JSON.stringify(availablePets[0], null, 2));
|
|
162
|
+
}
|
|
163
|
+
} catch (error) {
|
|
164
|
+
if (error instanceof Error) {
|
|
165
|
+
console.error('❌ Error:', error.message);
|
|
166
|
+
} else {
|
|
167
|
+
console.error('❌ Unknown error:', error);
|
|
168
|
+
}
|
|
169
|
+
process.exit(1);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
void main();
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* For a real example, generate a client from an OpenAPI spec with server variables.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import {SwaggerPetstoreOpenAPI30, ClientOptions} from './type
|
|
10
|
+
import {SwaggerPetstoreOpenAPI30, ClientOptions} from './type';
|
|
11
11
|
|
|
12
12
|
async function main() {
|
|
13
13
|
// Example 1: Use default server (first server from OpenAPI spec)
|