@sdk-it/typescript 0.40.0 → 0.42.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/README.md +4 -4
- package/dist/index.js +26 -18
- package/dist/index.js.map +2 -2
- package/dist/lib/emitters/zod.d.ts.map +1 -1
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -50,7 +50,7 @@ await generate(spec, {
|
|
|
50
50
|
|
|
51
51
|
### Format Generated Code
|
|
52
52
|
|
|
53
|
-
You can format the generated code using the `formatCode` option.
|
|
53
|
+
You can format the generated code using the `formatCode` option. Useful when committing generated code to source control.
|
|
54
54
|
|
|
55
55
|
```typescript
|
|
56
56
|
import { generate } from '@sdk-it/typescript';
|
|
@@ -86,7 +86,7 @@ bun ./openapi.ts
|
|
|
86
86
|
```typescript
|
|
87
87
|
import { OpenStatus } from './client';
|
|
88
88
|
|
|
89
|
-
const client = new
|
|
89
|
+
const client = new OpenStatus({
|
|
90
90
|
baseUrl: 'https://api.openstatus.dev/v1/',
|
|
91
91
|
});
|
|
92
92
|
|
|
@@ -95,9 +95,9 @@ const [result, error] = await client.request('GET /status_report', {});
|
|
|
95
95
|
|
|
96
96
|
## Using with Your Favorite Frameworks
|
|
97
97
|
|
|
98
|
-
The SDK works
|
|
98
|
+
The SDK works on its own, but you might want native integration with your frameworks:
|
|
99
99
|
|
|
100
100
|
- [React Query](../../docs/react-query.md)
|
|
101
101
|
- [Angular](../../docs/angular.md)
|
|
102
102
|
|
|
103
|
-
Let us know what
|
|
103
|
+
Let us know what you're using, and we'll help you integrate it.
|
package/dist/index.js
CHANGED
|
@@ -173,7 +173,7 @@ var ZodEmitter = class {
|
|
|
173
173
|
normal(type, schema, required = false, nullable = false) {
|
|
174
174
|
switch (type) {
|
|
175
175
|
case "string": {
|
|
176
|
-
const defaultVal = schema
|
|
176
|
+
const defaultVal = (schema["x-zod-type"] === "date" || schema["x-zod-type"] === "coerce-date") && schema.default ? `new Date(${JSON.stringify(schema.default)})` : JSON.stringify(schema.default);
|
|
177
177
|
return `${this.string(schema)}${this.#suffixes(defaultVal, required, nullable)}`;
|
|
178
178
|
}
|
|
179
179
|
case "number":
|
|
@@ -182,7 +182,7 @@ var ZodEmitter = class {
|
|
|
182
182
|
return `${base}${this.#suffixes(defaultValue, required, nullable)}`;
|
|
183
183
|
}
|
|
184
184
|
case "boolean":
|
|
185
|
-
return
|
|
185
|
+
return `${schema["x-zod-type"] === "coerce-boolean" ? "z.coerce.boolean()" : "z.boolean()"}${this.#suffixes(schema.default, required, nullable)}`;
|
|
186
186
|
case "object":
|
|
187
187
|
return `${this.#object(schema)}${this.#suffixes(JSON.stringify(schema.default), required, nullable)}`;
|
|
188
188
|
// required always
|
|
@@ -253,7 +253,7 @@ var ZodEmitter = class {
|
|
|
253
253
|
* Handle a `string` schema with possible format keywords (JSON Schema).
|
|
254
254
|
*/
|
|
255
255
|
string(schema) {
|
|
256
|
-
let base = "z.string()";
|
|
256
|
+
let base = schema["x-zod-type"] === "coerce-string" ? "z.coerce.string()" : "z.string()";
|
|
257
257
|
if (schema.contentEncoding === "binary") {
|
|
258
258
|
base = "z.instanceof(Blob)";
|
|
259
259
|
return base;
|
|
@@ -261,39 +261,45 @@ var ZodEmitter = class {
|
|
|
261
261
|
switch (schema.format) {
|
|
262
262
|
case "date-time":
|
|
263
263
|
case "datetime":
|
|
264
|
-
|
|
264
|
+
if (schema["x-zod-type"] === "coerce-date") {
|
|
265
|
+
base = "z.coerce.date()";
|
|
266
|
+
} else if (schema["x-zod-type"] === "date") {
|
|
267
|
+
base = "z.date()";
|
|
268
|
+
} else {
|
|
269
|
+
base += ".datetime()";
|
|
270
|
+
}
|
|
265
271
|
break;
|
|
266
272
|
case "date":
|
|
267
|
-
base
|
|
273
|
+
base += ".date()";
|
|
268
274
|
break;
|
|
269
275
|
case "time":
|
|
270
|
-
base
|
|
276
|
+
base += " /* optionally add .regex(...) for HH:MM:SS format */";
|
|
271
277
|
break;
|
|
272
278
|
case "email":
|
|
273
|
-
base
|
|
279
|
+
base += ".email()";
|
|
274
280
|
break;
|
|
275
281
|
case "uuid":
|
|
276
|
-
base
|
|
282
|
+
base += ".uuid()";
|
|
277
283
|
break;
|
|
278
284
|
case "url":
|
|
279
285
|
case "uri":
|
|
280
|
-
base
|
|
286
|
+
base += ".url()";
|
|
281
287
|
break;
|
|
282
288
|
case "ipv4":
|
|
283
|
-
base
|
|
289
|
+
base += '.ip({version: "v4"})';
|
|
284
290
|
break;
|
|
285
291
|
case "ipv6":
|
|
286
|
-
base
|
|
292
|
+
base += '.ip({version: "v6"})';
|
|
287
293
|
break;
|
|
288
294
|
case "phone":
|
|
289
|
-
base
|
|
295
|
+
base += " /* or add .regex(...) for phone formats */";
|
|
290
296
|
break;
|
|
291
297
|
case "byte":
|
|
292
298
|
case "binary":
|
|
293
299
|
base = "z.instanceof(Blob)";
|
|
294
300
|
break;
|
|
295
301
|
case "int64":
|
|
296
|
-
base
|
|
302
|
+
base += " /* or z.bigint() if your app can handle it */";
|
|
297
303
|
break;
|
|
298
304
|
default:
|
|
299
305
|
break;
|
|
@@ -307,14 +313,16 @@ var ZodEmitter = class {
|
|
|
307
313
|
*/
|
|
308
314
|
#number(schema) {
|
|
309
315
|
let defaultValue = schema.default;
|
|
310
|
-
let base
|
|
316
|
+
let base;
|
|
311
317
|
if (schema.format === "int64") {
|
|
312
|
-
base = "z.bigint()";
|
|
318
|
+
base = schema["x-zod-type"] === "coerce-bigint" ? "z.coerce.bigint()" : "z.bigint()";
|
|
313
319
|
if (schema.default !== void 0) {
|
|
314
320
|
defaultValue = `BigInt(${schema.default})`;
|
|
315
321
|
}
|
|
322
|
+
} else {
|
|
323
|
+
base = schema["x-zod-type"] === "coerce-number" ? "z.coerce.number()" : "z.number()";
|
|
316
324
|
}
|
|
317
|
-
if (schema.
|
|
325
|
+
if (schema.type === "integer" && schema.format !== "int64") {
|
|
318
326
|
base += ".int()";
|
|
319
327
|
}
|
|
320
328
|
if (typeof schema.exclusiveMinimum === "number") {
|
|
@@ -694,7 +702,7 @@ var TypeScriptEmitter = class {
|
|
|
694
702
|
case "date-time":
|
|
695
703
|
case "datetime":
|
|
696
704
|
case "date":
|
|
697
|
-
type = "
|
|
705
|
+
type = "string";
|
|
698
706
|
break;
|
|
699
707
|
case "binary":
|
|
700
708
|
case "byte":
|
|
@@ -1302,7 +1310,7 @@ function operationSchema(ir, operation, type) {
|
|
|
1302
1310
|
}
|
|
1303
1311
|
|
|
1304
1312
|
// packages/typescript/src/lib/http/dispatcher.txt
|
|
1305
|
-
var dispatcher_default = "export type Unionize<T> = T extends [infer Single extends OutputType]\n ? InstanceType<Single>\n : T extends readonly [...infer Tuple extends OutputType[]]\n ? { [I in keyof Tuple]: InstanceType<Tuple[I]> }[number]\n : never;\n\nexport type InstanceType<T> =\n T extends Type<infer U>\n ? U\n : T extends { type: Type<infer U> }\n ? U\n : T extends Array<unknown>\n ? Unionize<T>\n : never;\n\ntype ResponseData<T extends OutputType[]> =\n Extract<InstanceType<T>, SuccessfulResponse> extends SuccessfulResponse<\n infer P\n >\n ? P\n : unknown;\n\ntype ResponseMapper<T extends OutputType[], R> = (data: ResponseData<T>) => R;\n\nexport interface Type<T> {\n new (...args: any[]): T;\n}\nexport type Parser = (\n response: Response,\n) => Promise<unknown> | ReadableStream<any> | SSEListener;\nexport type OutputType =\n | Type<APIResponse>\n | { parser: Parser; type: Type<APIResponse> };\n\nexport const fetchType = z\n .function()\n .args(z.instanceof(Request))\n .returns(z.promise(z.instanceof(Response)))\n .optional();\n\nexport async function parse<T extends OutputType[], R = ResponseData<T>>(\n outputs: T,\n response: Response,\n mapper
|
|
1313
|
+
var dispatcher_default = "export type Unionize<T> = T extends [infer Single extends OutputType]\n ? InstanceType<Single>\n : T extends readonly [...infer Tuple extends OutputType[]]\n ? { [I in keyof Tuple]: InstanceType<Tuple[I]> }[number]\n : never;\n\nexport type InstanceType<T> =\n T extends Type<infer U>\n ? U\n : T extends { type: Type<infer U> }\n ? U\n : T extends Array<unknown>\n ? Unionize<T>\n : never;\n\ntype ResponseData<T extends OutputType[]> =\n Extract<InstanceType<T>, SuccessfulResponse> extends SuccessfulResponse<\n infer P\n >\n ? P\n : unknown;\n\ntype ResponseMapper<T extends OutputType[], R> = (data: ResponseData<T>) => R;\n\nexport interface Type<T> {\n new (...args: any[]): T;\n}\nexport type Parser = (\n response: Response,\n) => Promise<unknown> | ReadableStream<any> | SSEListener;\nexport type OutputType =\n | Type<APIResponse>\n | { parser: Parser; type: Type<APIResponse> };\n\nexport const fetchType = z\n .function()\n .args(z.instanceof(Request))\n .returns(z.promise(z.instanceof(Response)))\n .optional();\n\nexport async function parse<T extends OutputType[]>(\n outputs: T,\n response: Response,\n): Promise<Extract<Unionize<T>, SuccessfulResponse<unknown>>>;\nexport async function parse<T extends OutputType[], R>(\n outputs: T,\n response: Response,\n mapper: ResponseMapper<T, R>,\n): Promise<RebindSuccessPayload<Extract<Unionize<T>, SuccessfulResponse<unknown>>, R>>;\nexport async function parse<T extends OutputType[], R = ResponseData<T>>(\n outputs: T,\n response: Response,\n mapper?: ResponseMapper<T, R>,\n) {\n let output: typeof APIResponse | null = null;\n let parser: Parser = buffered;\n for (const outputType of outputs) {\n if ('parser' in outputType) {\n parser = outputType.parser;\n if (isTypeOf(outputType.type, APIResponse)) {\n if (response.status === outputType.type.status) {\n output = outputType.type;\n break;\n }\n }\n } else if (isTypeOf(outputType, APIResponse)) {\n if (response.status === outputType.status) {\n output = outputType;\n break;\n }\n }\n }\n\n if (response.ok) {\n const data = (await parser(response)) as ResponseData<T>;\n const mapped = mapper ? mapper(data) : data;\n const apiresponse = (output || APIResponse).create(\n response.status,\n response.headers,\n mapped,\n );\n\n return apiresponse as any;\n }\n\n throw (output || APIError).create(\n response.status,\n response.headers,\n await parser(response),\n );\n}\n\nexport function isTypeOf<T extends Type<APIResponse>>(\n instance: any,\n baseType: T,\n): instance is T {\n if (instance === baseType) {\n return true;\n }\n const prototype = Object.getPrototypeOf(instance);\n if (prototype === null) {\n return false;\n }\n return isTypeOf(prototype, baseType);\n}\n\nexport class Dispatcher {\n #interceptors: Interceptor[] = [];\n #fetch: z.infer<typeof fetchType>;\n constructor(interceptors: Interceptor[], fetch?: z.infer<typeof fetchType>) {\n this.#interceptors = interceptors;\n this.#fetch = fetch;\n }\n\n async send<T extends OutputType[]>(\n config: RequestConfig,\n outputs: T,\n signal?: AbortSignal,\n ): Promise<Extract<Unionize<T>, SuccessfulResponse<unknown>>>;\n async send<T extends OutputType[], R>(\n config: RequestConfig,\n outputs: T,\n signal?: AbortSignal,\n mapper?: ResponseMapper<T, R>,\n ): Promise<RebindSuccessPayload<Extract<Unionize<T>, SuccessfulResponse<unknown>>, R>>;\n async send<T extends OutputType[], R = ResponseData<T>>(\n config: RequestConfig,\n outputs: T,\n signal?: AbortSignal,\n mapper?: ResponseMapper<T, R>,\n ) {\n for (const interceptor of this.#interceptors) {\n if (interceptor.before) {\n config = await interceptor.before(config);\n }\n }\n\n let response = await (this.#fetch ?? fetch)(\n new Request(config.url, config.init),\n {\n ...config.init,\n signal: signal,\n },\n );\n\n for (let i = this.#interceptors.length - 1; i >= 0; i--) {\n const interceptor = this.#interceptors[i];\n if (interceptor.after) {\n response = await interceptor.after(response.clone());\n }\n }\n\n if (mapper) {\n return await parse(outputs, response, mapper);\n }\n return await parse(outputs, response);\n }\n}\n";
|
|
1306
1314
|
|
|
1307
1315
|
// packages/typescript/src/lib/http/interceptors.txt
|
|
1308
1316
|
var interceptors_default = "export interface Interceptor {\n before?: (config: RequestConfig) => Promise<RequestConfig> | RequestConfig;\n after?: (response: Response) => Promise<Response> | Response;\n}\n\nexport const createHeadersInterceptor = (\n headers: Record<string, string | undefined>,\n requestHeaders: HeadersInit,\n):Interceptor => {\n return {\n before({init, url}) {\n // Priority Levels\n // 1. Headers Input\n // 2. Request Headers\n // 3. Default Headers\n\n for (const [key, value] of new Headers(requestHeaders)) {\n // Only set the header if it doesn't already exist and has a value\n // even though these headers are passed at operation level\n // still they are lower priority compared to the headers input\n if (value !== undefined && !init.headers.has(key)) {\n init.headers.set(key, value);\n }\n }\n\n for (const [key, value] of Object.entries(headers)) {\n // Only set the header if it doesn't already exist and has a value\n if (value !== undefined && !init.headers.has(key)) {\n init.headers.set(key, value);\n }\n }\n\n return {init, url};\n },\n };\n};\n\nexport const createBaseUrlInterceptor = (baseUrl: string): Interceptor => {\n return {\n before({ init, url }) {\n if (url.protocol === 'local:') {\n return {\n init,\n url: new URL(url.href.replace('local://', baseUrl))\n };\n }\n return { init, url };\n },\n };\n};\n\nexport const logInterceptor: Interceptor = {\n before({ url, init }) {\n console.log('Request:', { url, init });\n return { url, init };\n },\n after(response) {\n console.log('Response:', response);\n return response;\n },\n};\n\n/**\n * Creates an interceptor that logs detailed information about requests and responses.\n * @param options Configuration options for the logger\n * @returns An interceptor object with before and after handlers\n */\nexport const createDetailedLogInterceptor = (options?: {\n logLevel?: 'debug' | 'info' | 'warn' | 'error';\n includeRequestBody?: boolean;\n includeResponseBody?: boolean;\n}) => {\n const logLevel = options?.logLevel || 'info';\n const includeRequestBody = options?.includeRequestBody || false;\n const includeResponseBody = options?.includeResponseBody || false;\n\n return {\n async before(request: Request) {\n const logData = {\n url: request.url,\n method: request.method,\n contentType: request.headers.get('Content-Type'),\n headers: Object.fromEntries([...request.headers.entries()]),\n };\n\n console[logLevel]('\u{1F680} Outgoing Request:', logData);\n\n if (includeRequestBody) {\n try {\n // Clone the request to avoid consuming the body stream\n const clonedRequest = request.clone();\n if (clonedRequest.headers.get('Content-Type')?.includes('application/json')) {\n const body = await clonedRequest.json().catch(() => null);\n console[logLevel]('Request Body:', body);\n } else {\n const body = await clonedRequest.text().catch(() => null);\n console[logLevel]('Request Body:', body);\n }\n } catch (error) {\n console.error('Could not log request body:', error);\n }\n }\n\n return request;\n },\n\n async after(response: Response) {\n const logData = {\n status: response.status,\n statusText: response.statusText,\n url: response.url,\n headers: Object.fromEntries([...response.headers.entries()]),\n };\n\n console[logLevel]('\u{1F4E5} Incoming Response:', logData);\n\n if (includeResponseBody && response.body) {\n try {\n // Clone the response to avoid consuming the body stream\n const clonedResponse = response.clone();\n if (clonedResponse.headers.get('Content-Type')?.includes('application/json')) {\n const body = await clonedResponse.json().catch(() => null);\n console[logLevel]('Response Body:', body);\n } else {\n const body = await clonedResponse.text().catch(() => null);\n if (body) {\n console[logLevel]('Response Body:', body.substring(0, 500) + (body.length > 500 ? '...' : ''));\n } else {\n console[logLevel]('No response body');\n }\n }\n } catch (error) {\n console.error('Could not log response body:', error);\n }\n }\n\n return response;\n },\n };\n};\n";
|