@automatons/typescript-client-fetch 1.0.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) tanmen (yt.prog@gmail.com)
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,17 @@
1
+ # @automatons/typescript-client-fetch
2
+ [![CI/CD](https://github.com/openapi-automatons/typescript-client-fetch/actions/workflows/ci-cd.yml/badge.svg)](https://github.com/openapi-automatons/typescript-client-fetch/actions/workflows/ci-cd.yml)
3
+ [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
4
+ [![npm downloads](https://img.shields.io/npm/dw/@automatons/typescript-client-fetch)](https://www.npmjs.com/package/@automatons/typescript-client-fetch)
5
+
6
+ ## What is @automatons/typescript-client-fetch
7
+ This is a client generator that emits a typed client built on the standard `fetch` API (no `axios` dependency).
8
+ Only use openapi-automatons.
9
+
10
+ This package is **ESM-only** and requires **Node.js >= 22**.
11
+
12
+ Each generated operation returns a `FetchResponse<T>` (`{ data, status, statusText, headers, response }`), so consumers can write `const { data } = await api.xxx()`.
13
+ Non-2xx responses are **not** thrown — inspect `response.ok` / `status` instead.
14
+
15
+ ## How can I use @automatons/typescript-client-fetch?
16
+ This library is designed to be used by [openapi-automatons](https://github.com/openapi-automatons/openapi-automatons).
17
+ Please read the [readme](https://github.com/openapi-automatons/openapi-automatons/blob/main/README.md) of [openapi-automatons](https://github.com/openapi-automatons/openapi-automatons) for how to use it.
package/index.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import { Automaton } from '@automatons/tools';
2
+
3
+ declare const generatorTypescriptFetchClient: Automaton;
4
+
5
+ export { generatorTypescriptFetchClient as default };
package/index.js ADDED
@@ -0,0 +1,482 @@
1
+ // src/generator/generate.ts
2
+ import { parser } from "@automatons/parser";
3
+
4
+ // src/generator/render.ts
5
+ import path from "path";
6
+ import { mkdir, writeFile } from "fs/promises";
7
+ import { IndentationText, Project, QuoteKind } from "ts-morph";
8
+ import { format } from "prettier";
9
+ var project = new Project({
10
+ useInMemoryFileSystem: true,
11
+ manipulationSettings: {
12
+ quoteKind: QuoteKind.Double,
13
+ indentationText: IndentationText.TwoSpaces,
14
+ useTrailingCommas: true
15
+ }
16
+ });
17
+ var counter = 0;
18
+ var render = (build) => {
19
+ const sf = project.createSourceFile(`__gen_${counter++}.ts`, "", { overwrite: true });
20
+ build(sf);
21
+ const text = sf.getFullText();
22
+ project.removeSourceFile(sf);
23
+ return text;
24
+ };
25
+ var write = async (outDir, relPath, text) => {
26
+ const outputPath = path.resolve(outDir, relPath);
27
+ await mkdir(path.dirname(outputPath), { recursive: true });
28
+ const formatted = await format(text, { parser: "typescript" });
29
+ await writeFile(outputPath, formatted, { encoding: "utf-8" });
30
+ };
31
+
32
+ // src/generator/schema.ts
33
+ var quoteKey = (name) => name.includes("-") ? `"${name}"` : name;
34
+ var nullable = (schema) => schema.nullable ? " | null" : "";
35
+ var schemaToType = (schema) => {
36
+ switch (schema.type) {
37
+ case "model":
38
+ return schema.name;
39
+ case "object": {
40
+ if (schema.properties && schema.properties.length) {
41
+ const props = schema.properties.map(
42
+ (property) => `/**
43
+ * ${property.name}
44
+ */
45
+ ${quoteKey(property.name)}${property.required ? "" : "?"}: ${schemaToType(property.schema)};`
46
+ ).join("\n");
47
+ return `{
48
+ ${props}
49
+ }${nullable(schema)}`;
50
+ }
51
+ return `object${nullable(schema)}`;
52
+ }
53
+ case "allOf":
54
+ return `(${schema.schemas.map(schemaToType).join(" & ")})`;
55
+ case "oneOf":
56
+ return `(${schema.schemas.map(schemaToType).join(" | ")})`;
57
+ case "array":
58
+ return `${schema.items ? `Array<${schemaToType(schema.items)}>` : "any[]"}${nullable(schema)}`;
59
+ case "boolean":
60
+ return `boolean${nullable(schema)}`;
61
+ case "string": {
62
+ const base = schema.enum && schema.enum.length ? schema.enum.map((value) => `"${value}"`).join(" | ") : schema.format === "date" || schema.format === "date-time" ? "Date" : schema.format === "url" ? "URL" : "string";
63
+ return `${base}${nullable(schema)}`;
64
+ }
65
+ case "integer":
66
+ case "number": {
67
+ const base = schema.enum && schema.enum.length ? schema.enum.join(" | ") : "number";
68
+ return `${base}${nullable(schema)}`;
69
+ }
70
+ default:
71
+ throw new Error(`Unsupported schema type: ${schema.type}`);
72
+ }
73
+ };
74
+
75
+ // src/generator/comment.ts
76
+ var docs = (options) => {
77
+ const tags = [];
78
+ if (options.async) tags.push({ tagName: "async" });
79
+ if (options.description) tags.push({ tagName: "description", text: options.description });
80
+ if (options.deprecated) tags.push({ tagName: "deprecated" });
81
+ if (options.readOnly) tags.push({ tagName: "readonly" });
82
+ return [{ description: options.title, tags }];
83
+ };
84
+
85
+ // src/generator/model.ts
86
+ var emitModel = (model) => render((sf) => {
87
+ model.imports.forEach(
88
+ (imported) => sf.addImportDeclaration({
89
+ namedImports: [imported.title],
90
+ moduleSpecifier: `./${imported.filename}`
91
+ })
92
+ );
93
+ sf.addTypeAlias({
94
+ isExported: true,
95
+ name: model.title,
96
+ type: schemaToType(model.schema),
97
+ docs: docs({ title: model.title })
98
+ });
99
+ });
100
+ var emitModelsIndex = (models) => render(
101
+ (sf) => models.forEach((model) => sf.addExportDeclaration({ moduleSpecifier: `./${model.filename}` }))
102
+ );
103
+
104
+ // src/generator/config.ts
105
+ var callable = (returns, arg = "") => `${returns} | Promise<${returns}> | ((${arg}) => ${returns} | Promise<${returns}>)`;
106
+ var securityType = (security) => {
107
+ if (security.type === "http" && security.scheme === "basic") {
108
+ const basic = "{username: string; password: string;}";
109
+ return `${security.name}?: ${callable(basic)};`;
110
+ }
111
+ if (security.type === "oauth2" || security.type === "openIdConnect") {
112
+ return `${security.name}?: ${callable("string", "scopes?: string[]")};`;
113
+ }
114
+ return `${security.name}?: ${callable("string")};`;
115
+ };
116
+ var emitConfig = (securities) => render((sf) => {
117
+ sf.addTypeAlias({
118
+ isExported: true,
119
+ name: "Security",
120
+ type: `{
121
+ ${securities.map(securityType).join("\n")}
122
+ }`
123
+ });
124
+ sf.addTypeAlias({
125
+ isExported: true,
126
+ name: "Config",
127
+ type: `{
128
+ fetch?: typeof fetch;
129
+ token?: string | Promise<string> | (() => string | Promise<string>);
130
+ security?: Security;
131
+ }`
132
+ });
133
+ });
134
+ var emitIndex = (hasModels, hasApis) => render((sf) => {
135
+ if (hasModels) sf.addExportDeclaration({ moduleSpecifier: "./models" });
136
+ if (hasApis) sf.addExportDeclaration({ moduleSpecifier: "./apis" });
137
+ sf.addExportDeclaration({ moduleSpecifier: "./config" });
138
+ });
139
+
140
+ // src/generator/abstractApi.ts
141
+ import { Scope } from "ts-morph";
142
+ var FETCH_STATEMENTS = [
143
+ "const DateFormat = /^\\d{4}-\\d{2}-\\d{2}([tT]\\d{2}:\\d{2}:\\d{2}(Z|[+-]\\d{2}:\\d{2})|Z)?$/;",
144
+ "const reviver = (_key: string, value: any) => typeof value === 'string' && DateFormat.test(value) ? new Date(value) : value;",
145
+ "const toQuery = (params: Record<string, unknown>): string => { const search = new URLSearchParams(); for (const [key, value] of Object.entries(params)) { if (value === undefined || value === null) continue; if (Array.isArray(value)) { value.forEach((item) => search.append(key, String(item))); } else { search.append(key, String(value)); } } return search.toString(); };"
146
+ ];
147
+ var REQUEST_STATEMENTS = [
148
+ "const { baseURL, params, headers, ...init } = config;",
149
+ "const search = toQuery(params);",
150
+ 'const url = `${baseURL}${path}${search ? `?${search}` : ""}`;',
151
+ "const _headers = new Headers(headers);",
152
+ 'if (init.body instanceof FormData) { _headers.delete("Content-Type"); }',
153
+ "const response = await this.fetch(url, { ...init, method, headers: _headers });",
154
+ "const text = await response.text();",
155
+ "const data = (text ? JSON.parse(text, reviver) : undefined) as T;",
156
+ "return { data, status: response.status, statusText: response.statusText, headers: response.headers, response };"
157
+ ];
158
+ var overload = (security) => {
159
+ const base = { scope: Scope.Protected, isAsync: true };
160
+ if (security.type === "http" && security.scheme === "basic") {
161
+ return {
162
+ ...base,
163
+ parameters: [{ name: "key", type: `"${security.name}"` }],
164
+ returnType: "Promise<{username: string; password: string;}>"
165
+ };
166
+ }
167
+ if (security.type === "oauth2" || security.type === "openIdConnect") {
168
+ return {
169
+ ...base,
170
+ parameters: [{ name: "key", type: `"${security.name}"` }, { name: "scopes", type: "string[]" }],
171
+ returnType: "Promise<string>"
172
+ };
173
+ }
174
+ return { ...base, parameters: [{ name: "key", type: `"${security.name}"` }], returnType: "Promise<string>" };
175
+ };
176
+ var emitAbstractApi = (securities) => render((sf) => {
177
+ sf.addImportDeclaration({
178
+ isTypeOnly: true,
179
+ namedImports: securities.length ? ["Config", "Security"] : ["Config"],
180
+ moduleSpecifier: "../config"
181
+ });
182
+ sf.addTypeAlias({
183
+ isExported: true,
184
+ name: "RequestConfig",
185
+ type: "Omit<RequestInit, 'headers'> & { baseURL: string; params: Record<string, unknown>; headers: Record<string, string>; }"
186
+ });
187
+ sf.addTypeAlias({
188
+ isExported: true,
189
+ name: "FetchResponse",
190
+ typeParameters: [{ name: "T", default: "unknown" }],
191
+ type: "{ data: T; status: number; statusText: string; headers: Headers; response: Response; }"
192
+ });
193
+ sf.addStatements(FETCH_STATEMENTS);
194
+ const abstractConfig = sf.addClass({ isExported: true, name: "AbstractConfig", docs: docs({ title: "AbstractConfig" }) });
195
+ if (securities.length) {
196
+ abstractConfig.addProperty({ name: "#security", isReadonly: true, type: "Security" });
197
+ abstractConfig.addConstructor({
198
+ docs: docs({ title: "constructor" }),
199
+ parameters: [{ name: "security", type: "Security", initializer: "{}" }],
200
+ statements: ["this.#security = security;"]
201
+ });
202
+ const scoped = securities.some((s) => s.type === "oauth2" || s.type === "openIdConnect");
203
+ abstractConfig.addMethod({
204
+ scope: Scope.Protected,
205
+ isAsync: true,
206
+ name: "security",
207
+ overloads: securities.map(overload),
208
+ parameters: [
209
+ { name: "key", type: "keyof Security" },
210
+ ...scoped ? [{ name: "scopes", type: "string[]", hasQuestionToken: true }] : []
211
+ ],
212
+ returnType: "Promise<string | {username: string; password: string;}>",
213
+ statements: [
214
+ "const security = this.#security[key];",
215
+ 'if (!security) { throw new Error("Unauthorized user request."); }',
216
+ `else if (security instanceof Function) { ${scoped ? "return scopes ? security(scopes) : security();" : "return security();"} }`,
217
+ "return security;"
218
+ ]
219
+ });
220
+ }
221
+ const abstractApi = sf.addClass({ isExported: true, name: "AbstractApi", docs: docs({ title: "AbstractApi" }) });
222
+ abstractApi.addProperty({ scope: Scope.Protected, name: "fetch", type: "typeof fetch", initializer: "fetch" });
223
+ abstractApi.addConstructor({
224
+ docs: docs({ title: "constructor" }),
225
+ parameters: [{ name: "config", type: "Config" }],
226
+ statements: ["if (config.fetch) { this.fetch = config.fetch; }"]
227
+ });
228
+ abstractApi.addMethod({
229
+ scope: Scope.Protected,
230
+ isAsync: true,
231
+ name: "request",
232
+ typeParameters: [{ name: "T", default: "unknown" }],
233
+ parameters: [
234
+ { name: "path", type: "string" },
235
+ { name: "method", type: "string" },
236
+ { name: "config", type: "RequestConfig" }
237
+ ],
238
+ returnType: "Promise<FetchResponse<T>>",
239
+ statements: REQUEST_STATEMENTS
240
+ });
241
+ });
242
+
243
+ // src/generator/api.ts
244
+ import { Scope as Scope2 } from "ts-morph";
245
+
246
+ // src/extractors/api.ts
247
+ var isAffectPath = (path4) => ["post", "patch", "put"].includes(path4.method);
248
+ var extractApiMeta = (api) => {
249
+ const hasTemplate = api.servers.some((server) => server.values?.length) || api.paths.some((path4) => path4.parameters?.length) || api.paths.some((path4) => path4.headers?.length);
250
+ const hasQuery = api.paths.some((path4) => path4.queries?.length) || api.paths.some((path4) => path4.cookies?.length);
251
+ const hasFormData = api.paths.some((path4) => isAffectPath(path4) ? path4.forms?.length : false);
252
+ return { hasQuery, hasTemplate, hasFormData };
253
+ };
254
+
255
+ // src/generator/api.ts
256
+ var isAffect = (path4) => ["post", "put", "patch"].includes(path4.method);
257
+ var formsOf = (path4) => isAffect(path4) && path4.forms ? path4.forms : [];
258
+ var objectType = (entries) => `{ ${entries.map((e) => `${e.name}${e.required ? "" : "?"}: ${schemaToType(e.schema)}, `).join("")}}`;
259
+ var allOptional = (entries = []) => entries.every((e) => !e.required);
260
+ var contentType = (path4) => {
261
+ const forms = formsOf(path4);
262
+ return forms.length ? forms.map((form) => form.types.map((type) => `'${type}'`).join(" | ")).join(" | ") : "'application/json'";
263
+ };
264
+ var headersType = (path4) => `{ 'Content-Type': ${contentType(path4)}, ${(path4.headers ?? []).map((h) => `${h.name}${h.required ? "" : "?"}: ${schemaToType(h.schema)}, `).join("")}}`;
265
+ var serverUnion = (servers) => servers.map((server) => `${server.name}Server`).join(" | ");
266
+ var serverDefault = (servers) => {
267
+ const [first] = servers;
268
+ return servers.length === 1 && first && !(first.values && first.values.length) ? `{ name: '${first.name}' }` : void 0;
269
+ };
270
+ var withInit = (init) => init ? { initializer: init } : {};
271
+ var queriesParam = (path4) => path4.queries?.length ? { name: "queries", type: objectType(path4.queries), ...withInit(allOptional(path4.queries) ? "{}" : void 0) } : void 0;
272
+ var cookiesParam = (path4) => path4.cookies?.length ? { name: "cookies", type: objectType(path4.cookies), ...withInit(allOptional(path4.cookies) ? "{}" : void 0) } : void 0;
273
+ var headersParam = (path4) => ({
274
+ name: "headers",
275
+ type: headersType(path4),
276
+ ...withInit(allOptional(path4.headers) ? "{ 'Content-Type': 'application/json' }" : void 0)
277
+ });
278
+ var serverParam = (path4) => path4.servers?.length ? { name: "server", type: serverUnion(path4.servers), ...withInit(serverDefault(path4.servers)) } : void 0;
279
+ var configParam = () => ({
280
+ name: "config",
281
+ type: "RequestInit",
282
+ hasQuestionToken: true
283
+ });
284
+ var compact = (items) => items.filter((item) => item !== void 0);
285
+ var requestParams = (path4) => {
286
+ const forms = formsOf(path4);
287
+ return compact([
288
+ ...(path4.parameters ?? []).map((p) => ({ name: p.name, type: schemaToType(p.schema) })),
289
+ forms.length ? { name: "form", type: forms.map((form) => schemaToType(form.schema)).join(" | ") } : void 0,
290
+ queriesParam(path4),
291
+ cookiesParam(path4),
292
+ headersParam(path4),
293
+ serverParam(path4),
294
+ configParam()
295
+ ]);
296
+ };
297
+ var configParams = (path4) => compact([queriesParam(path4), cookiesParam(path4), headersParam(path4), serverParam(path4), configParam()]);
298
+ var requestVariables = (path4) => compact([
299
+ path4.queries?.length ? "queries" : void 0,
300
+ path4.cookies?.length ? "cookies" : void 0,
301
+ "headers",
302
+ path4.servers?.length ? "server" : void 0,
303
+ "config"
304
+ ]).join(", ");
305
+ var requestBody = (api, path4) => {
306
+ const forms = formsOf(path4);
307
+ const replaces = (path4.parameters ?? []).map((p) => `.replace("{${p.name}}", template('${p.name}', ${p.name}, '${p.style ?? "simple"}', ${p.explode ?? false}))`).join("");
308
+ void api;
309
+ const generic = path4.schema ? `<${schemaToType(path4.schema)}>` : "";
310
+ const request = forms.length ? `{ ...requestConfig, body: body(headers['Content-Type'], form) }` : "requestConfig";
311
+ return compact([
312
+ `const path = "${path4.path}"${replaces};`,
313
+ `const requestConfig = await this.#config.${path4.name}(${requestVariables(path4)});`,
314
+ `return this.request${generic}(path, "${path4.method.toUpperCase()}", ${request});`
315
+ ]);
316
+ };
317
+ var configBody = (api, path4) => {
318
+ const securities = path4.securities ?? [];
319
+ const params = compact([
320
+ ...(path4.queries ?? []).map((q) => `...query("${q.name}", queries.${q.name}, '${q.style ?? "form"}', ${q.explode ?? false}),`),
321
+ ...securities.map((s) => s.type === "apiKey" && s.in === "query" ? `${s.key}: await this.security("${s.name}"),` : void 0)
322
+ ]);
323
+ const cookieStatements = compact([
324
+ ...(path4.cookies ?? []).map(
325
+ (c) => `if (cookies.${c.name}) { _cookies += Object.entries(query('${c.name}', cookies.${c.name}, 'form', ${c.explode ?? false})).reduce((pre, [key, value]) => \`\${pre}\${key}=\${value};\`, ''); }`
326
+ ),
327
+ ...securities.flatMap(
328
+ (s) => s.type === "apiKey" && s.in === "cookie" ? [
329
+ `const _cookie${s.name} = await this.security("${s.name}");`,
330
+ `if (_cookie${s.name}) { _cookies += \`${s.key}=\${_cookie${s.name}};\`; }`
331
+ ] : []
332
+ )
333
+ ]);
334
+ const headerStatements = compact([
335
+ ...(path4.headers ?? []).map(
336
+ (h) => `if (headers.${h.name}) { _headers['${h.name}'] = template('${h.name}', headers.${h.name}, 'simple', ${h.explode ?? false}); }`
337
+ ),
338
+ ...securities.map((s) => {
339
+ if (s.type === "apiKey" && s.in === "header") return `_headers['${s.key}'] = await this.security("${s.name}");`;
340
+ if (s.type === "http" && s.scheme === "bearer") return `_headers['Authorization'] = \`Bearer \${await this.security("${s.name}")}\`;`;
341
+ if (s.type === "http" && s.scheme === "basic")
342
+ return `const _basic${s.name} = await this.security("${s.name}"); _headers['Authorization'] = \`Basic \${btoa(\`\${_basic${s.name}.username}:\${_basic${s.name}.password}\`)}\`;`;
343
+ if (s.type === "oauth2" || s.type === "openIdConnect")
344
+ return `_headers['Authorization'] = \`Bearer \${await this.security("${s.name}", [${s.scopes.map((x) => `"${x}"`).join(", ")}])}\`;`;
345
+ return void 0;
346
+ })
347
+ ]);
348
+ const hasCookies = cookieStatements.length > 0;
349
+ return compact([
350
+ `const params = { ${params.join(" ")} };`,
351
+ hasCookies ? "let _cookies = '';" : void 0,
352
+ ...cookieStatements,
353
+ "const _headers = Object.fromEntries(new Headers(config?.headers)) as Record<string, string>;",
354
+ "_headers['Content-Type'] = headers['Content-Type'];",
355
+ ...headerStatements,
356
+ hasCookies ? "if (_cookies) { _headers['Cookie'] = _cookies; }" : void 0,
357
+ `return { ...config, baseURL: ${api.title}Config.server(server), params, headers: _headers };`
358
+ ]);
359
+ };
360
+ var serverTypeAlias = (server) => {
361
+ const values = server.values?.length ? `; values: { ${server.values.map((v) => `${v.name}: ${v.enums?.length ? v.enums.map((e) => `'${e}'`).join(" | ") : "string"} `).join("")}}` : "";
362
+ return `{ name: "${server.name}"${values} }`;
363
+ };
364
+ var serverMethodStatements = (servers) => [
365
+ ...servers.map((server) => {
366
+ const replaces = (server.values ?? []).map((v) => `.replace('{${v.name}}', template('${v.name}', server.values.${v.name}, 'simple', false))`).join("");
367
+ return `if ('${server.name}' === server.name) { return '${server.url}'${replaces}; }`;
368
+ }),
369
+ "throw new Error('Undefined server. please define server.');"
370
+ ];
371
+ var utilImports = (api) => {
372
+ const meta = extractApiMeta(api);
373
+ return compact([meta.hasTemplate ? "template" : void 0, meta.hasQuery ? "query" : void 0, meta.hasFormData ? "body" : void 0]);
374
+ };
375
+ var emitApi = (api, securities) => render((sf) => {
376
+ sf.addImportDeclaration({
377
+ namedImports: ["AbstractApi", "AbstractConfig", "FetchResponse", "RequestConfig"],
378
+ moduleSpecifier: "./abstractApi"
379
+ });
380
+ sf.addImportDeclaration({
381
+ namedImports: securities.length ? ["Config", "Security"] : ["Config"],
382
+ moduleSpecifier: "../config"
383
+ });
384
+ const utils = utilImports(api);
385
+ if (utils.length) sf.addImportDeclaration({ namedImports: utils, moduleSpecifier: "../utils" });
386
+ if (api.imports.length) sf.addImportDeclaration({ namedImports: api.imports.map((m) => m.title), moduleSpecifier: "../models" });
387
+ api.servers.forEach((server) => sf.addTypeAlias({ name: `${server.name}Server`, type: serverTypeAlias(server) }));
388
+ const apiClass = sf.addClass({ isExported: true, name: api.title, extends: "AbstractApi", docs: docs({ title: api.title }) });
389
+ apiClass.addProperty({ name: "#config", isReadonly: true, type: `${api.title}Config` });
390
+ apiClass.addConstructor({
391
+ docs: docs({ title: "constructor" }),
392
+ parameters: [{ name: "config", type: "Config", initializer: "{}" }],
393
+ statements: ["super(config);", `this.#config = new ${api.title}Config(${securities.length ? "config.security" : ""});`]
394
+ });
395
+ api.paths.forEach(
396
+ (path4) => apiClass.addMethod({
397
+ scope: Scope2.Public,
398
+ isAsync: true,
399
+ name: path4.name,
400
+ docs: docs({ title: path4.name, async: true }),
401
+ parameters: requestParams(path4),
402
+ returnType: `Promise<FetchResponse${path4.schema ? `<${schemaToType(path4.schema)}>` : ""}>`,
403
+ statements: requestBody(api, path4)
404
+ })
405
+ );
406
+ const configClass = sf.addClass({ name: `${api.title}Config`, extends: "AbstractConfig" });
407
+ if (securities.length)
408
+ configClass.addConstructor({
409
+ parameters: [{ name: "security", type: "Security", initializer: "{}" }],
410
+ statements: ["super(security);"]
411
+ });
412
+ configClass.addMethod({
413
+ isStatic: true,
414
+ scope: Scope2.Private,
415
+ name: "server",
416
+ parameters: [{ name: "server", type: serverUnion(api.servers.length ? api.servers : api.paths[0]?.servers ?? []) }],
417
+ statements: serverMethodStatements(api.servers.length ? api.servers : api.paths[0]?.servers ?? [])
418
+ });
419
+ api.paths.forEach(
420
+ (path4) => configClass.addMethod({
421
+ scope: Scope2.Public,
422
+ isAsync: true,
423
+ name: path4.name,
424
+ parameters: configParams(path4),
425
+ returnType: "Promise<RequestConfig>",
426
+ statements: configBody(api, path4)
427
+ })
428
+ );
429
+ });
430
+ var emitApisIndex = (apis) => render((sf) => apis.forEach((api) => sf.addExportDeclaration({ moduleSpecifier: `./${api.filename}` })));
431
+
432
+ // src/generator/statics.ts
433
+ import path3 from "path";
434
+ import { readdir, readFile } from "fs/promises";
435
+
436
+ // src/paths.ts
437
+ import path2 from "path";
438
+ var paths = {
439
+ static: path2.resolve(import.meta.dirname, "static"),
440
+ tmp: path2.resolve(import.meta.dirname, "../tmp")
441
+ };
442
+ var paths_default = paths;
443
+
444
+ // src/generator/statics.ts
445
+ var emitStatics = async (outDir) => {
446
+ const dir = path3.join(paths_default.static, "utils");
447
+ const files = await readdir(dir);
448
+ await Promise.all(
449
+ files.map(async (file) => {
450
+ const text = await readFile(path3.join(dir, file), { encoding: "utf-8" });
451
+ await write(outDir, path3.join("utils", file), text);
452
+ })
453
+ );
454
+ };
455
+
456
+ // src/generator/generate.ts
457
+ var generate = async (openapi, settings) => {
458
+ const { outDir } = settings;
459
+ const { models, apis, securities } = await parser(openapi, settings);
460
+ const tasks = [];
461
+ if (models.length) {
462
+ tasks.push(write(outDir, "models/index.ts", emitModelsIndex(models)));
463
+ models.forEach((model) => tasks.push(write(outDir, `models/${model.filename}.ts`, emitModel(model))));
464
+ }
465
+ if (apis.length) {
466
+ tasks.push(write(outDir, "apis/index.ts", emitApisIndex(apis)));
467
+ tasks.push(write(outDir, "apis/abstractApi.ts", emitAbstractApi(securities)));
468
+ apis.forEach((api) => tasks.push(write(outDir, `apis/${api.filename}.ts`, emitApi(api, securities))));
469
+ }
470
+ tasks.push(write(outDir, "index.ts", emitIndex(models.length > 0, apis.length > 0)));
471
+ tasks.push(write(outDir, "config.ts", emitConfig(securities)));
472
+ tasks.push(emitStatics(outDir));
473
+ return Promise.all(tasks);
474
+ };
475
+
476
+ // src/index.ts
477
+ var generatorTypescriptFetchClient = (openapi, settings) => generate(openapi, settings);
478
+ var index_default = generatorTypescriptFetchClient;
479
+ export {
480
+ index_default as default
481
+ };
482
+ //# sourceMappingURL=index.js.map
package/index.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/generator/generate.ts","../src/generator/render.ts","../src/generator/schema.ts","../src/generator/comment.ts","../src/generator/model.ts","../src/generator/config.ts","../src/generator/abstractApi.ts","../src/generator/api.ts","../src/extractors/api.ts","../src/generator/statics.ts","../src/paths.ts","../src/index.ts"],"sourcesContent":["import {AutomatonSettings, Openapi} from \"@automatons/tools\";\nimport {parser} from \"@automatons/parser\";\nimport {write} from \"./render\";\nimport {emitModel, emitModelsIndex} from \"./model\";\nimport {emitConfig, emitIndex} from \"./config\";\nimport {emitAbstractApi} from \"./abstractApi\";\nimport {emitApi, emitApisIndex} from \"./api\";\nimport {emitStatics} from \"./statics\";\n\nexport const generate = async (openapi: Openapi, settings: AutomatonSettings): Promise<void[]> => {\n const {outDir} = settings;\n const {models, apis, securities} = await parser(openapi, settings);\n const tasks: Promise<void>[] = [];\n\n if (models.length) {\n tasks.push(write(outDir, \"models/index.ts\", emitModelsIndex(models)));\n models.forEach((model) => tasks.push(write(outDir, `models/${model.filename}.ts`, emitModel(model))));\n }\n\n if (apis.length) {\n tasks.push(write(outDir, \"apis/index.ts\", emitApisIndex(apis)));\n tasks.push(write(outDir, \"apis/abstractApi.ts\", emitAbstractApi(securities)));\n apis.forEach((api) => tasks.push(write(outDir, `apis/${api.filename}.ts`, emitApi(api, securities))));\n }\n\n tasks.push(write(outDir, \"index.ts\", emitIndex(models.length > 0, apis.length > 0)));\n tasks.push(write(outDir, \"config.ts\", emitConfig(securities)));\n tasks.push(emitStatics(outDir));\n\n return Promise.all(tasks);\n};\n","import path from \"node:path\";\nimport {mkdir, writeFile} from \"node:fs/promises\";\nimport {IndentationText, Project, QuoteKind, SourceFile} from \"ts-morph\";\nimport {format} from \"prettier\";\n\nconst project = new Project({\n useInMemoryFileSystem: true,\n manipulationSettings: {\n quoteKind: QuoteKind.Double,\n indentationText: IndentationText.TwoSpaces,\n useTrailingCommas: true,\n },\n});\n\nlet counter = 0;\n\n/**\n * Build a TypeScript source string by manipulating a throwaway ts-morph SourceFile.\n */\nexport const render = (build: (sf: SourceFile) => void): string => {\n const sf = project.createSourceFile(`__gen_${counter++}.ts`, \"\", {overwrite: true});\n build(sf);\n const text = sf.getFullText();\n project.removeSourceFile(sf);\n return text;\n};\n\n/**\n * Format with prettier and write the file under outDir.\n */\nexport const write = async (outDir: string, relPath: string, text: string): Promise<void> => {\n const outputPath = path.resolve(outDir, relPath);\n await mkdir(path.dirname(outputPath), {recursive: true});\n const formatted = await format(text, {parser: \"typescript\"});\n await writeFile(outputPath, formatted, {encoding: \"utf-8\"});\n};\n","import {Schema} from \"@automatons/parser\";\n\nconst quoteKey = (name: string): string => (name.includes(\"-\") ? `\"${name}\"` : name);\n\nconst nullable = (schema: {nullable?: boolean}): string => (schema.nullable ? \" | null\" : \"\");\n\n/**\n * Render a parser Schema (a discriminated union) as a TypeScript type string.\n * Mirrors the previous models/* handlebars partials.\n */\nexport const schemaToType = (schema: Schema): string => {\n switch (schema.type) {\n case \"model\":\n return schema.name;\n case \"object\": {\n if (schema.properties && schema.properties.length) {\n const props = schema.properties\n .map(\n (property) =>\n `/**\\n * ${property.name}\\n */\\n${quoteKey(property.name)}${\n property.required ? \"\" : \"?\"\n }: ${schemaToType(property.schema)};`,\n )\n .join(\"\\n\");\n return `{\\n${props}\\n}${nullable(schema)}`;\n }\n return `object${nullable(schema)}`;\n }\n case \"allOf\":\n return `(${schema.schemas.map(schemaToType).join(\" & \")})`;\n case \"oneOf\":\n return `(${schema.schemas.map(schemaToType).join(\" | \")})`;\n case \"array\":\n return `${schema.items ? `Array<${schemaToType(schema.items)}>` : \"any[]\"}${nullable(schema)}`;\n case \"boolean\":\n return `boolean${nullable(schema)}`;\n case \"string\": {\n const base =\n schema.enum && schema.enum.length\n ? schema.enum.map((value) => `\"${value}\"`).join(\" | \")\n : schema.format === \"date\" || schema.format === \"date-time\"\n ? \"Date\"\n : schema.format === \"url\"\n ? \"URL\"\n : \"string\";\n return `${base}${nullable(schema)}`;\n }\n case \"integer\":\n case \"number\": {\n const base = schema.enum && schema.enum.length ? schema.enum.join(\" | \") : \"number\";\n return `${base}${nullable(schema)}`;\n }\n default:\n throw new Error(`Unsupported schema type: ${(schema as {type: string}).type}`);\n }\n};\n","import {OptionalKind, JSDocStructure, JSDocTagStructure} from \"ts-morph\";\n\nexport type CommentOptions = {\n title: string;\n async?: boolean;\n description?: string;\n deprecated?: boolean;\n readOnly?: boolean;\n};\n\n/**\n * Build a ts-morph JSDoc structure mirroring the previous comment partial.\n */\nexport const docs = (options: CommentOptions): OptionalKind<JSDocStructure>[] => {\n const tags: OptionalKind<JSDocTagStructure>[] = [];\n if (options.async) tags.push({tagName: \"async\"});\n if (options.description) tags.push({tagName: \"description\", text: options.description});\n if (options.deprecated) tags.push({tagName: \"deprecated\"});\n if (options.readOnly) tags.push({tagName: \"readonly\"});\n return [{description: options.title, tags}];\n};\n","import {Model} from \"@automatons/parser\";\nimport {render} from \"./render\";\nimport {schemaToType} from \"./schema\";\nimport {docs} from \"./comment\";\n\n/**\n * Emit a single model file: imports for referenced models + an exported type alias.\n */\nexport const emitModel = (model: Model): string =>\n render((sf) => {\n model.imports.forEach((imported) =>\n sf.addImportDeclaration({\n namedImports: [imported.title],\n moduleSpecifier: `./${imported.filename}`,\n }),\n );\n sf.addTypeAlias({\n isExported: true,\n name: model.title,\n type: schemaToType(model.schema),\n docs: docs({title: model.title}),\n });\n });\n\n/**\n * Emit models/index.ts re-exporting every model.\n */\nexport const emitModelsIndex = (models: Model[]): string =>\n render((sf) =>\n models.forEach((model) => sf.addExportDeclaration({moduleSpecifier: `./${model.filename}`})),\n );\n","import {Security} from \"@automatons/parser\";\nimport {render} from \"./render\";\n\nconst callable = (returns: string, arg = \"\"): string =>\n `${returns} | Promise<${returns}> | ((${arg}) => ${returns} | Promise<${returns}>)`;\n\nconst securityType = (security: Security): string => {\n if (security.type === \"http\" && security.scheme === \"basic\") {\n const basic = \"{username: string; password: string;}\";\n return `${security.name}?: ${callable(basic)};`;\n }\n if (security.type === \"oauth2\" || security.type === \"openIdConnect\") {\n return `${security.name}?: ${callable(\"string\", \"scopes?: string[]\")};`;\n }\n return `${security.name}?: ${callable(\"string\")};`;\n};\n\n/**\n * Emit config.ts: the Security map type and the Config type.\n */\nexport const emitConfig = (securities: Security[]): string =>\n render((sf) => {\n sf.addTypeAlias({\n isExported: true,\n name: \"Security\",\n type: `{\\n${securities.map(securityType).join(\"\\n\")}\\n}`,\n });\n sf.addTypeAlias({\n isExported: true,\n name: \"Config\",\n type: `{\n fetch?: typeof fetch;\n token?: string | Promise<string> | (() => string | Promise<string>);\n security?: Security;\n}`,\n });\n });\n\n/**\n * Emit the top-level index.ts.\n */\nexport const emitIndex = (hasModels: boolean, hasApis: boolean): string =>\n render((sf) => {\n if (hasModels) sf.addExportDeclaration({moduleSpecifier: \"./models\"});\n if (hasApis) sf.addExportDeclaration({moduleSpecifier: \"./apis\"});\n sf.addExportDeclaration({moduleSpecifier: \"./config\"});\n });\n","import {Security} from \"@automatons/parser\";\nimport {OptionalKind, MethodDeclarationOverloadStructure, Scope} from \"ts-morph\";\nimport {render} from \"./render\";\nimport {docs} from \"./comment\";\n\nconst FETCH_STATEMENTS = [\n \"const DateFormat = /^\\\\d{4}-\\\\d{2}-\\\\d{2}([tT]\\\\d{2}:\\\\d{2}:\\\\d{2}(Z|[+-]\\\\d{2}:\\\\d{2})|Z)?$/;\",\n \"const reviver = (_key: string, value: any) => typeof value === 'string' && DateFormat.test(value) ? new Date(value) : value;\",\n \"const toQuery = (params: Record<string, unknown>): string => { const search = new URLSearchParams(); for (const [key, value] of Object.entries(params)) { if (value === undefined || value === null) continue; if (Array.isArray(value)) { value.forEach((item) => search.append(key, String(item))); } else { search.append(key, String(value)); } } return search.toString(); };\",\n];\n\nconst REQUEST_STATEMENTS = [\n \"const { baseURL, params, headers, ...init } = config;\",\n \"const search = toQuery(params);\",\n 'const url = `${baseURL}${path}${search ? `?${search}` : \"\"}`;',\n \"const _headers = new Headers(headers);\",\n 'if (init.body instanceof FormData) { _headers.delete(\"Content-Type\"); }',\n \"const response = await this.fetch(url, { ...init, method, headers: _headers });\",\n \"const text = await response.text();\",\n \"const data = (text ? JSON.parse(text, reviver) : undefined) as T;\",\n \"return { data, status: response.status, statusText: response.statusText, headers: response.headers, response };\",\n];\n\nconst overload = (security: Security): OptionalKind<MethodDeclarationOverloadStructure> => {\n const base = {scope: Scope.Protected, isAsync: true};\n if (security.type === \"http\" && security.scheme === \"basic\") {\n return {\n ...base,\n parameters: [{name: \"key\", type: `\"${security.name}\"`}],\n returnType: \"Promise<{username: string; password: string;}>\",\n };\n }\n if (security.type === \"oauth2\" || security.type === \"openIdConnect\") {\n return {\n ...base,\n parameters: [{name: \"key\", type: `\"${security.name}\"`}, {name: \"scopes\", type: \"string[]\"}],\n returnType: \"Promise<string>\",\n };\n }\n return {...base, parameters: [{name: \"key\", type: `\"${security.name}\"`}], returnType: \"Promise<string>\"};\n};\n\n/**\n * Emit apis/abstractApi.ts: the shared fetch runtime plus the AbstractConfig / AbstractApi base classes.\n */\nexport const emitAbstractApi = (securities: Security[]): string =>\n render((sf) => {\n sf.addImportDeclaration({\n isTypeOnly: true,\n namedImports: securities.length ? [\"Config\", \"Security\"] : [\"Config\"],\n moduleSpecifier: \"../config\",\n });\n\n sf.addTypeAlias({\n isExported: true,\n name: \"RequestConfig\",\n type: \"Omit<RequestInit, 'headers'> & { baseURL: string; params: Record<string, unknown>; headers: Record<string, string>; }\",\n });\n sf.addTypeAlias({\n isExported: true,\n name: \"FetchResponse\",\n typeParameters: [{name: \"T\", default: \"unknown\"}],\n type: \"{ data: T; status: number; statusText: string; headers: Headers; response: Response; }\",\n });\n\n sf.addStatements(FETCH_STATEMENTS);\n\n const abstractConfig = sf.addClass({isExported: true, name: \"AbstractConfig\", docs: docs({title: \"AbstractConfig\"})});\n if (securities.length) {\n abstractConfig.addProperty({name: \"#security\", isReadonly: true, type: \"Security\"});\n abstractConfig.addConstructor({\n docs: docs({title: \"constructor\"}),\n parameters: [{name: \"security\", type: \"Security\", initializer: \"{}\"}],\n statements: [\"this.#security = security;\"],\n });\n const scoped = securities.some((s) => s.type === \"oauth2\" || s.type === \"openIdConnect\");\n abstractConfig.addMethod({\n scope: Scope.Protected,\n isAsync: true,\n name: \"security\",\n overloads: securities.map(overload),\n parameters: [\n {name: \"key\", type: \"keyof Security\"},\n ...(scoped ? [{name: \"scopes\", type: \"string[]\", hasQuestionToken: true}] : []),\n ],\n returnType: \"Promise<string | {username: string; password: string;}>\",\n statements: [\n \"const security = this.#security[key];\",\n 'if (!security) { throw new Error(\"Unauthorized user request.\"); }',\n `else if (security instanceof Function) { ${scoped ? \"return scopes ? security(scopes) : security();\" : \"return security();\"} }`,\n \"return security;\",\n ],\n });\n }\n\n const abstractApi = sf.addClass({isExported: true, name: \"AbstractApi\", docs: docs({title: \"AbstractApi\"})});\n abstractApi.addProperty({scope: Scope.Protected, name: \"fetch\", type: \"typeof fetch\", initializer: \"fetch\"});\n abstractApi.addConstructor({\n docs: docs({title: \"constructor\"}),\n parameters: [{name: \"config\", type: \"Config\"}],\n statements: [\"if (config.fetch) { this.fetch = config.fetch; }\"],\n });\n abstractApi.addMethod({\n scope: Scope.Protected,\n isAsync: true,\n name: \"request\",\n typeParameters: [{name: \"T\", default: \"unknown\"}],\n parameters: [\n {name: \"path\", type: \"string\"},\n {name: \"method\", type: \"string\"},\n {name: \"config\", type: \"RequestConfig\"},\n ],\n returnType: \"Promise<FetchResponse<T>>\",\n statements: REQUEST_STATEMENTS,\n });\n });\n","import {AffectPath, Api, Path, Schema, Security, Server} from \"@automatons/parser\";\nimport {OptionalKind, ParameterDeclarationStructure, Scope} from \"ts-morph\";\nimport {render} from \"./render\";\nimport {schemaToType} from \"./schema\";\nimport {docs} from \"./comment\";\nimport {extractApiMeta} from \"../extractors/api\";\n\nconst isAffect = (path: Path): path is AffectPath => [\"post\", \"put\", \"patch\"].includes(path.method);\nconst formsOf = (path: Path): NonNullable<AffectPath[\"forms\"]> => (isAffect(path) && path.forms ? path.forms : []);\n\ntype Entry = {name: string; required?: boolean; schema: Schema};\nconst objectType = (entries: ReadonlyArray<Entry>): string =>\n `{ ${entries.map((e) => `${e.name}${e.required ? \"\" : \"?\"}: ${schemaToType(e.schema)}, `).join(\"\")}}`;\nconst allOptional = (entries: ReadonlyArray<{required?: boolean}> = []): boolean => entries.every((e) => !e.required);\n\nconst contentType = (path: Path): string => {\n const forms = formsOf(path);\n return forms.length\n ? forms.map((form) => form.types.map((type) => `'${type}'`).join(\" | \")).join(\" | \")\n : \"'application/json'\";\n};\nconst headersType = (path: Path): string =>\n `{ 'Content-Type': ${contentType(path)}, ${(path.headers ?? [])\n .map((h) => `${h.name}${h.required ? \"\" : \"?\"}: ${schemaToType(h.schema)}, `)\n .join(\"\")}}`;\n\nconst serverUnion = (servers: Server[]): string => servers.map((server) => `${server.name}Server`).join(\" | \");\nconst serverDefault = (servers: Server[]): string | undefined => {\n const [first] = servers;\n return servers.length === 1 && first && !(first.values && first.values.length)\n ? `{ name: '${first.name}' }`\n : undefined;\n};\n\nconst withInit = (init?: string): {initializer: string} | object => (init ? {initializer: init} : {});\n\nconst queriesParam = (path: Path): OptionalKind<ParameterDeclarationStructure> | undefined =>\n path.queries?.length\n ? {name: \"queries\", type: objectType(path.queries), ...withInit(allOptional(path.queries) ? \"{}\" : undefined)}\n : undefined;\nconst cookiesParam = (path: Path): OptionalKind<ParameterDeclarationStructure> | undefined =>\n path.cookies?.length\n ? {name: \"cookies\", type: objectType(path.cookies), ...withInit(allOptional(path.cookies) ? \"{}\" : undefined)}\n : undefined;\nconst headersParam = (path: Path): OptionalKind<ParameterDeclarationStructure> => ({\n name: \"headers\",\n type: headersType(path),\n ...withInit(allOptional(path.headers) ? \"{ 'Content-Type': 'application/json' }\" : undefined),\n});\nconst serverParam = (path: Path): OptionalKind<ParameterDeclarationStructure> | undefined =>\n path.servers?.length\n ? {name: \"server\", type: serverUnion(path.servers), ...withInit(serverDefault(path.servers))}\n : undefined;\nconst configParam = (): OptionalKind<ParameterDeclarationStructure> => ({\n name: \"config\",\n type: \"RequestInit\",\n hasQuestionToken: true,\n});\nconst compact = <T>(items: (T | undefined)[]): T[] => items.filter((item): item is T => item !== undefined);\n\nconst requestParams = (path: Path): OptionalKind<ParameterDeclarationStructure>[] => {\n const forms = formsOf(path);\n return compact([\n ...(path.parameters ?? []).map((p) => ({name: p.name, type: schemaToType(p.schema)})),\n forms.length ? {name: \"form\", type: forms.map((form) => schemaToType(form.schema)).join(\" | \")} : undefined,\n queriesParam(path),\n cookiesParam(path),\n headersParam(path),\n serverParam(path),\n configParam(),\n ]);\n};\nconst configParams = (path: Path): OptionalKind<ParameterDeclarationStructure>[] =>\n compact([queriesParam(path), cookiesParam(path), headersParam(path), serverParam(path), configParam()]);\n\nconst requestVariables = (path: Path): string =>\n compact([\n path.queries?.length ? \"queries\" : undefined,\n path.cookies?.length ? \"cookies\" : undefined,\n \"headers\",\n path.servers?.length ? \"server\" : undefined,\n \"config\",\n ]).join(\", \");\n\nconst requestBody = (api: Api, path: Path): string[] => {\n const forms = formsOf(path);\n const replaces = (path.parameters ?? [])\n .map((p) => `.replace(\"{${p.name}}\", template('${p.name}', ${p.name}, '${p.style ?? \"simple\"}', ${p.explode ?? false}))`)\n .join(\"\");\n void api;\n const generic = path.schema ? `<${schemaToType(path.schema)}>` : \"\";\n const request = forms.length\n ? `{ ...requestConfig, body: body(headers['Content-Type'], form) }`\n : \"requestConfig\";\n return compact([\n `const path = \"${path.path}\"${replaces};`,\n `const requestConfig = await this.#config.${path.name}(${requestVariables(path)});`,\n `return this.request${generic}(path, \"${path.method.toUpperCase()}\", ${request});`,\n ]);\n};\n\nconst configBody = (api: Api, path: Path): string[] => {\n const securities = path.securities ?? [];\n const params = compact([\n ...(path.queries ?? []).map((q) => `...query(\"${q.name}\", queries.${q.name}, '${q.style ?? \"form\"}', ${q.explode ?? false}),`),\n ...securities.map((s) => (s.type === \"apiKey\" && s.in === \"query\" ? `${s.key}: await this.security(\"${s.name}\"),` : undefined)),\n ]);\n const cookieStatements = compact<string>([\n ...(path.cookies ?? []).map(\n (c) =>\n `if (cookies.${c.name}) { _cookies += Object.entries(query('${c.name}', cookies.${c.name}, 'form', ${c.explode ?? false})).reduce((pre, [key, value]) => \\`\\${pre}\\${key}=\\${value};\\`, ''); }`,\n ),\n ...securities.flatMap((s) =>\n s.type === \"apiKey\" && s.in === \"cookie\"\n ? [\n `const _cookie${s.name} = await this.security(\"${s.name}\");`,\n `if (_cookie${s.name}) { _cookies += \\`${s.key}=\\${_cookie${s.name}};\\`; }`,\n ]\n : [],\n ),\n ]);\n const headerStatements = compact<string>([\n ...(path.headers ?? []).map(\n (h) => `if (headers.${h.name}) { _headers['${h.name}'] = template('${h.name}', headers.${h.name}, 'simple', ${h.explode ?? false}); }`,\n ),\n ...securities.map((s) => {\n if (s.type === \"apiKey\" && s.in === \"header\") return `_headers['${s.key}'] = await this.security(\"${s.name}\");`;\n if (s.type === \"http\" && s.scheme === \"bearer\") return `_headers['Authorization'] = \\`Bearer \\${await this.security(\"${s.name}\")}\\`;`;\n if (s.type === \"http\" && s.scheme === \"basic\")\n return `const _basic${s.name} = await this.security(\"${s.name}\"); _headers['Authorization'] = \\`Basic \\${btoa(\\`\\${_basic${s.name}.username}:\\${_basic${s.name}.password}\\`)}\\`;`;\n if (s.type === \"oauth2\" || s.type === \"openIdConnect\")\n return `_headers['Authorization'] = \\`Bearer \\${await this.security(\"${s.name}\", [${s.scopes.map((x) => `\"${x}\"`).join(\", \")}])}\\`;`;\n return undefined;\n }),\n ]);\n const hasCookies = cookieStatements.length > 0;\n return compact([\n `const params = { ${params.join(\" \")} };`,\n hasCookies ? \"let _cookies = '';\" : undefined,\n ...cookieStatements,\n \"const _headers = Object.fromEntries(new Headers(config?.headers)) as Record<string, string>;\",\n \"_headers['Content-Type'] = headers['Content-Type'];\",\n ...headerStatements,\n hasCookies ? \"if (_cookies) { _headers['Cookie'] = _cookies; }\" : undefined,\n `return { ...config, baseURL: ${api.title}Config.server(server), params, headers: _headers };`,\n ]);\n};\n\nconst serverTypeAlias = (server: Server): string => {\n const values = server.values?.length\n ? `; values: { ${server.values\n .map((v) => `${v.name}: ${v.enums?.length ? v.enums.map((e) => `'${e}'`).join(\" | \") : \"string\"} `)\n .join(\"\")}}`\n : \"\";\n return `{ name: \"${server.name}\"${values} }`;\n};\n\nconst serverMethodStatements = (servers: Server[]): string[] => [\n ...servers.map((server) => {\n const replaces = (server.values ?? [])\n .map((v) => `.replace('{${v.name}}', template('${v.name}', server.values.${v.name}, 'simple', false))`)\n .join(\"\");\n return `if ('${server.name}' === server.name) { return '${server.url}'${replaces}; }`;\n }),\n \"throw new Error('Undefined server. please define server.');\",\n];\n\nconst utilImports = (api: Api): string[] => {\n const meta = extractApiMeta(api);\n return compact([meta.hasTemplate ? \"template\" : undefined, meta.hasQuery ? \"query\" : undefined, meta.hasFormData ? \"body\" : undefined]);\n};\n\n/**\n * Emit a single api file: the typed fetch client class and its request-config class.\n */\nexport const emitApi = (api: Api, securities: Security[]): string =>\n render((sf) => {\n sf.addImportDeclaration({\n namedImports: [\"AbstractApi\", \"AbstractConfig\", \"FetchResponse\", \"RequestConfig\"],\n moduleSpecifier: \"./abstractApi\",\n });\n sf.addImportDeclaration({\n namedImports: securities.length ? [\"Config\", \"Security\"] : [\"Config\"],\n moduleSpecifier: \"../config\",\n });\n const utils = utilImports(api);\n if (utils.length) sf.addImportDeclaration({namedImports: utils, moduleSpecifier: \"../utils\"});\n if (api.imports.length) sf.addImportDeclaration({namedImports: api.imports.map((m) => m.title), moduleSpecifier: \"../models\"});\n\n api.servers.forEach((server) => sf.addTypeAlias({name: `${server.name}Server`, type: serverTypeAlias(server)}));\n\n const apiClass = sf.addClass({isExported: true, name: api.title, extends: \"AbstractApi\", docs: docs({title: api.title})});\n apiClass.addProperty({name: \"#config\", isReadonly: true, type: `${api.title}Config`});\n apiClass.addConstructor({\n docs: docs({title: \"constructor\"}),\n parameters: [{name: \"config\", type: \"Config\", initializer: \"{}\"}],\n statements: [\"super(config);\", `this.#config = new ${api.title}Config(${securities.length ? \"config.security\" : \"\"});`],\n });\n api.paths.forEach((path) =>\n apiClass.addMethod({\n scope: Scope.Public,\n isAsync: true,\n name: path.name,\n docs: docs({title: path.name, async: true}),\n parameters: requestParams(path),\n returnType: `Promise<FetchResponse${path.schema ? `<${schemaToType(path.schema)}>` : \"\"}>`,\n statements: requestBody(api, path),\n }),\n );\n\n const configClass = sf.addClass({name: `${api.title}Config`, extends: \"AbstractConfig\"});\n if (securities.length)\n configClass.addConstructor({\n parameters: [{name: \"security\", type: \"Security\", initializer: \"{}\"}],\n statements: [\"super(security);\"],\n });\n configClass.addMethod({\n isStatic: true,\n scope: Scope.Private,\n name: \"server\",\n parameters: [{name: \"server\", type: serverUnion(api.servers.length ? api.servers : api.paths[0]?.servers ?? [])}],\n statements: serverMethodStatements(api.servers.length ? api.servers : api.paths[0]?.servers ?? []),\n });\n api.paths.forEach((path) =>\n configClass.addMethod({\n scope: Scope.Public,\n isAsync: true,\n name: path.name,\n parameters: configParams(path),\n returnType: \"Promise<RequestConfig>\",\n statements: configBody(api, path),\n }),\n );\n });\n\n/**\n * Emit apis/index.ts re-exporting every api.\n */\nexport const emitApisIndex = (apis: Api[]): string =>\n render((sf) => apis.forEach((api) => sf.addExportDeclaration({moduleSpecifier: `./${api.filename}`})));\n","import {AffectPath, Api, Path} from \"@automatons/parser\";\n\nconst isAffectPath = (path: Path): path is AffectPath => ['post', 'patch', 'put'].includes(path.method);\n\nexport const extractApiMeta = (api: Api) => {\n const hasTemplate = api.servers.some(server => server.values?.length)\n || api.paths.some(path => path.parameters?.length)\n || api.paths.some(path => path.headers?.length);\n const hasQuery = api.paths.some(path => path.queries?.length)\n || api.paths.some(path => path.cookies?.length);\n const hasFormData = api.paths.some(path => isAffectPath(path) ? path.forms?.length : false);\n return {hasQuery, hasTemplate, hasFormData};\n};\n","import path from \"node:path\";\nimport {readdir, readFile} from \"node:fs/promises\";\nimport paths from \"../paths\";\nimport {write} from \"./render\";\n\n/**\n * Copy the static utility files (template/query/formData/index) into the output.\n */\nexport const emitStatics = async (outDir: string): Promise<void> => {\n const dir = path.join(paths.static, \"utils\");\n const files = await readdir(dir);\n await Promise.all(\n files.map(async (file) => {\n const text = await readFile(path.join(dir, file), {encoding: \"utf-8\"});\n await write(outDir, path.join(\"utils\", file), text);\n }),\n );\n};\n","import path from \"node:path\";\n\nconst paths = {\n static: path.resolve(import.meta.dirname, \"static\"),\n tmp: path.resolve(import.meta.dirname, \"../tmp\"),\n};\nexport default paths;\n","import {Automaton} from \"@automatons/tools\";\nimport {generate} from \"./generator\";\n\nconst generatorTypescriptFetchClient: Automaton = (openapi, settings) =>\n generate(openapi, settings);\n\nexport default generatorTypescriptFetchClient;\n"],"mappings":"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","names":["Scope","path","path","Scope","path","path","path"]}
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@automatons/typescript-client-fetch",
3
+ "version": "1.0.0",
4
+ "repository": "https://github.com/openapi-automatons/typescript-client-fetch.git",
5
+ "author": "tanmen <yt.prog@gmail.com>",
6
+ "license": "MIT",
7
+ "packageManager": "yarn@1.22.22",
8
+ "type": "module",
9
+ "keywords": [
10
+ "openapi",
11
+ "openapi-automatons",
12
+ "automatons",
13
+ "fetch"
14
+ ],
15
+ "main": "index.js",
16
+ "module": "index.js",
17
+ "types": "index.d.ts",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./index.d.ts",
21
+ "import": "./index.js"
22
+ }
23
+ },
24
+ "engines": {
25
+ "node": ">=22"
26
+ },
27
+ "scripts": {
28
+ "prebuild": "depcheck",
29
+ "build": "tsup && cp -R src/static dist/ && cp package.json README.md LICENSE dist",
30
+ "lint": "eslint src",
31
+ "test": "vitest run",
32
+ "test:watch": "vitest",
33
+ "test:coverage": "vitest run --coverage",
34
+ "test:integration": "tsx scripts/pretest.ts && vitest run --config test/vitest.config.ts",
35
+ "prepare": "husky"
36
+ },
37
+ "dependencies": {
38
+ "@automatons/parser": "^1.0.0",
39
+ "@automatons/tools": "^2.0.0",
40
+ "prettier": "^3.8.3",
41
+ "ts-morph": "^28.0.0"
42
+ },
43
+ "peerDependencies": {
44
+ "object-to-formdata": "^4",
45
+ "openapi-automatons": "^2"
46
+ },
47
+ "devDependencies": {
48
+ "@commitlint/cli": "^21.0.2",
49
+ "@commitlint/config-conventional": "^21.0.2",
50
+ "@eslint/js": "^10.0.1",
51
+ "@semantic-release/changelog": "^6.0.3",
52
+ "@semantic-release/git": "^10.0.1",
53
+ "@types/node": "^24.0.0",
54
+ "@vitest/coverage-v8": "^3.2.0",
55
+ "depcheck": "^1.4.7",
56
+ "eslint": "^10.4.1",
57
+ "eslint-config-prettier": "^10.1.8",
58
+ "husky": "^9.1.7",
59
+ "lint-staged": "^17.0.7",
60
+ "object-to-formdata": "^4.5.1",
61
+ "semantic-release": "^25.0.3",
62
+ "tsup": "^8.5.0",
63
+ "tsx": "^4.19.0",
64
+ "typescript": "^6.0.3",
65
+ "typescript-eslint": "^8.46.0",
66
+ "vitest": "^3.2.0"
67
+ },
68
+ "publishConfig": {
69
+ "access": "public"
70
+ }
71
+ }
@@ -0,0 +1,13 @@
1
+ import { serialize } from 'object-to-formdata';
2
+
3
+ export const body = <T = unknown>(type: string, form: T): BodyInit | undefined => {
4
+ if (form === undefined || form === null) return undefined;
5
+ switch (type) {
6
+ case 'multipart/form-data':
7
+ return serialize(form);
8
+ case 'application/x-www-form-urlencoded':
9
+ return new URLSearchParams(form as unknown as Record<string, string>);
10
+ default:
11
+ return JSON.stringify(form);
12
+ }
13
+ }
@@ -0,0 +1,3 @@
1
+ export * from './template';
2
+ export * from './query';
3
+ export * from './body';
@@ -0,0 +1,79 @@
1
+ type ParsedUrlQueryInput = { [key: string]: string | number | boolean | ReadonlyArray<string> | ReadonlyArray<number> | ReadonlyArray<boolean> | null | undefined }
2
+
3
+ const isObject = (value: any): value is object => {
4
+ const type = typeof value
5
+ return value != null && (type == 'object' || type == 'function')
6
+ }
7
+
8
+ const queryForm =
9
+ (name: string,
10
+ value: string | number | Array<string | number> | { [key: string]: string | number },
11
+ explode: boolean): ParsedUrlQueryInput => {
12
+ if (Array.isArray(value)) {
13
+ if (explode) {
14
+ return {[name]: value.map(String)}
15
+ } else {
16
+ return {[name]: value.join(',')}
17
+ }
18
+ } else if (isObject(value)) {
19
+ if (explode) {
20
+ return value;
21
+ } else {
22
+ return {[name]: Object.entries(value).map((prop: [string, string | number]) => prop.join(',')).join(',')}
23
+ }
24
+ } else {
25
+ return {[name]: value};
26
+ }
27
+ }
28
+
29
+ const querySpaceDelimited =
30
+ (name: string,
31
+ value: string | number | Array<string | number> | { [key: string]: string | number }): ParsedUrlQueryInput => {
32
+ if (Array.isArray(value)) {
33
+ return {[name]: value.join(' ')}
34
+ } else if (isObject(value)) {
35
+ return {[name]: Object.entries(value).map((prop: [string, string | number]) => prop.join(' ')).join(' ')}
36
+ }
37
+ throw new Error(`Unsupported value: ${name}`);
38
+ }
39
+
40
+ const queryPipeDelimited =
41
+ (name: string,
42
+ value: string | number | Array<string | number> | { [key: string]: string | number }): ParsedUrlQueryInput => {
43
+ if (Array.isArray(value)) {
44
+ return {[name]: value.join('|')}
45
+ } else if (isObject(value)) {
46
+ return {[name]: Object.entries(value).map((prop: [string, string | number]) => prop.join('|')).join('|')}
47
+ }
48
+ throw new Error(`Unsupported value: ${name}`);
49
+ }
50
+
51
+ const queryDeepObject =
52
+ (name: string,
53
+ value: string | number | Array<string | number> | { [key: string]: string | number }): ParsedUrlQueryInput => {
54
+ if (!Array.isArray(value) && isObject(value)) {
55
+ return Object.keys(value).reduce((pre, cur) => ({...pre, [`${name}[${cur}]`]: value[cur]}), {});
56
+ }
57
+ throw new Error(`Unsupported value: ${name}`);
58
+ }
59
+
60
+ export const query =
61
+ (
62
+ name: string,
63
+ value: undefined | string | number | Array<string | number> | { [key: string]: string | number },
64
+ style: 'form' | 'spaceDelimited' | 'pipeDelimited' | 'deepObject',
65
+ explode: boolean): ParsedUrlQueryInput => {
66
+ if (value === undefined) return {};
67
+ switch (style) {
68
+ case 'form':
69
+ return queryForm(name, value, explode);
70
+ case 'spaceDelimited':
71
+ return querySpaceDelimited(name, value);
72
+ case 'pipeDelimited':
73
+ return queryPipeDelimited(name, value);
74
+ case 'deepObject':
75
+ return queryDeepObject(name, value);
76
+ default:
77
+ throw new Error(`Unsupported style: ${style}`);
78
+ }
79
+ }
@@ -0,0 +1,55 @@
1
+ const templateSimple = (explode: boolean, value: string | number | Array<string | number> | { [p: string]: string | number }): string => {
2
+ if (!explode) {
3
+ return value instanceof Object
4
+ ? Array.isArray(value)
5
+ ? value.join(',') : Object.entries(value).map(prop => prop.join(',')).flat().join(',')
6
+ : String(value);
7
+ }
8
+ return value instanceof Object
9
+ ? Array.isArray(value)
10
+ ? value.join(',') : Object.entries(value).map(([key, value]) => `${key}=${value}`).flat().join(',')
11
+ : String(value);
12
+ }
13
+
14
+ const templateLabel = (explode: boolean, value: string | number | Array<string | number> | { [p: string]: string | number }): string => {
15
+ if (!explode) {
16
+ return `.${value instanceof Object
17
+ ? Array.isArray(value)
18
+ ? value.join(',') : Object.entries(value).map(prop => prop.join(',')).flat().join(',')
19
+ : String(value)}`;
20
+ }
21
+ return value instanceof Object
22
+ ? Array.isArray(value)
23
+ ? value.join(',') : Object.entries(value).map(([key, value]) => `${key}=${value}`).flat().join(',')
24
+ : String(value);
25
+ }
26
+
27
+ const templateMatrix = (explode: boolean, name: string, value: string | number | Array<string | number> | { [p: string]: string | number }): string => {
28
+ if (!explode) {
29
+ return `;${name}=${value instanceof Object
30
+ ? Array.isArray(value)
31
+ ? value.join(',') : Object.entries(value).map(prop => prop.join(',')).flat().join(',')
32
+ : String(value)}`;
33
+ }
34
+ return `;${value instanceof Object
35
+ ? Array.isArray(value)
36
+ ? value.map(item => `${name}=${item}`).join(';')
37
+ : Object.entries(value).map(([key, value]) => `${key}=${value}`).flat().join(';')
38
+ : `${name}=${String(value)}`}`;
39
+ }
40
+
41
+ export const template =
42
+ (
43
+ name: string,
44
+ value: string | number | Array<string | number> | { [key: string]: string | number },
45
+ style: 'simple' | 'label' | 'matrix',
46
+ explode: boolean): string => {
47
+ if (style === 'simple') {
48
+ return templateSimple(explode, value);
49
+ } else if (style === 'label') {
50
+ return templateLabel(explode, value);
51
+ } else if (style === 'matrix') {
52
+ return templateMatrix(explode, name, value);
53
+ }
54
+ throw new Error(`Unsupported style: ${style}`)
55
+ }