diff --git a/cli.js b/cli.js new file mode 100644 index 0000000..8392441 --- /dev/null +++ b/cli.js @@ -0,0 +1,159 @@ +#!/usr/bin/env bash +":"; //# comment; exec /usr/bin/env node --input-type=module - "$@" < "$0" + +import { readFile, writeFile } from "fs/promises"; +import openapi from "openapi-typescript"; +// import { resolve } from "path"; +// import { cwd } from "process"; + +const NOTICE = `// This file was auto-generated by @insertish/oapi!\n`; + +readFile("OpenAPI.json").then((data) => { + // Load and patch anything we need to + let source = data.toString(); + + if (process.env.REWRITE_ANYOF) { + source = source.replace(/"anyOf"/g, '"oneOf"'); + } + + // Parse spec + const spec = JSON.parse(source); + + // Copy index.ts + // readFile( + // resolve(cwd(), "node_modules", "@insertish", "oapi", "src", "index.ts") + // ).then((data) => writeFile("src/index.ts", data)); + + // Generate Schema + openapi(spec).then((data) => writeFile("src/schema.ts", data)); + + // Route Types + Data + { + const entries = [ + "import { paths } from './schema';", + "export type APIRoutes =", + ]; + const paths = Object.keys(spec.paths); + const queryData = {}; + + for (const path of paths) { + const data = spec.paths[path]; + const methods = Object.keys(data); + + let template = path.replace(/\{\w+\}/g, "${string}"); + + for (const method of methods) { + const OPERATION = `paths['${path}']['${method}']`; + + const route = data[method]; + const response = + Object.keys(route["responses"]).find((x) => x !== "default") || + "default"; + const contentType = Object.keys( + route["responses"][response]["content"] || {} + )[0]; + const RESPONSE = + response === "204" || !contentType + ? "undefined" + : `${OPERATION}['responses']['${response}']['content']['${contentType}']`; + + let queryParams = []; + let hasBody = false; + + if (route["parameters"]) { + for (const parameter of route["parameters"]) { + if (parameter.in === "query") { + queryParams.push(parameter.name); + } + } + } + + if (route["requestBody"]?.["content"]?.["application/json"]) { + hasBody = true; + } + + let params = "undefined"; + if (hasBody || queryParams.length > 0) { + let entries = []; + + if (queryParams.length > 0) { + entries.push(`${OPERATION}['parameters']['query']`); + } + + if (hasBody) { + entries.push( + `${OPERATION}['requestBody']['content']['application/json']` + ); + } + + params = entries.join("|"); + } + + const parts = path.split("").filter((x) => x === "/").length; + entries.push( + `| { method: '${method}', path: \`${template}\`, parts: ${parts}, params: ${params}, response: ${RESPONSE} }` + ); + + if (/\{\w+\}/.test(path)) { + entries.push( + `| { method: '${method}', path: '-${path}', parts: ${parts}, params: ${params}, response: ${RESPONSE} }` + ); + } + + queryData[path] = { + ...queryData[path], + [method]: queryParams, + }; + } + } + + const pathResolve = {}; + for (const path of paths) { + const segments = path.split("/"); + segments.shift(); + pathResolve[segments.length] = [ + ...(pathResolve[segments.length] || []), + segments.map((key) => (/\{.*\}/.test(key) ? [key] : key)), + ]; + } + + writeFile("src/routes.ts", NOTICE + entries.join("\n") + ";"); + writeFile( + "src/params.ts", + NOTICE + + "export const pathResolve = " + + JSON.stringify(pathResolve) + + ";\n" + + "export const queryParams = " + + JSON.stringify(queryData) + + ";" + ); + } + + // Type Exports + { + const entries = ["import { components } from './schema';"]; + const schemas = spec.components.schemas; + + for (const schema of Object.keys(schemas)) { + entries.push( + `export type ${schema.replace( + /\s/g, + "_" + )} = components['schemas']['${schema}'];` + ); + } + + writeFile("src/types.ts", NOTICE + entries.join("\n") + ";"); + } + + // Default Base URL + const baseURL = spec["servers"]?.[0]?.["url"]; + writeFile( + "src/baseURL.ts", + NOTICE + + `export const defaultBaseURL = ${ + baseURL ? '"' + baseURL + '"' : "undefined" + };` + ); +}); diff --git a/package.json b/package.json index 3923890..4b0c6fe 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,6 @@ "prepublish": "in-publish && pnpm build || echo Skipping build." }, "devDependencies": { - "@insertish/oapi": "0.2.6", "in-publish": "^2.0.1", "openapi-typescript": "^5.4.2", "typescript": "^5.8.3" @@ -28,5 +27,8 @@ "LICENSE", "README.md" ], - "packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39" + "packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39", + "dependencies": { + "json-with-bigint": "^3.4.4" + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a80518a..901afd4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -7,10 +7,11 @@ settings: importers: .: + dependencies: + json-with-bigint: + specifier: ^3.4.4 + version: 3.4.4 devDependencies: - '@insertish/oapi': - specifier: 0.2.6 - version: 0.2.6 in-publish: specifier: ^2.0.1 version: 2.0.1 @@ -27,10 +28,6 @@ packages: resolution: {integrity: sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==} engines: {node: '>=14'} - '@insertish/oapi@0.2.6': - resolution: {integrity: sha512-6IWpLFhygWu+hN8+bNXH3pK0hypxoS522Vn6AOhphzXrWQlDo6IhEWRWXV/XlyYQIPiugkDcqtD2p0dtB9Wftg==} - hasBin: true - argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} @@ -48,6 +45,9 @@ packages: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true + json-with-bigint@3.4.4: + resolution: {integrity: sha512-AhpYAAaZsPjU7smaBomDt1SOQshi9rEm6BlTbfVwsG1vNmeHKtEedJi62sHZzJTyKNtwzmNnrsd55kjwJ7054A==} + mime@3.0.0: resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} engines: {node: '>=10.0.0'} @@ -66,11 +66,6 @@ packages: tiny-glob@0.2.9: resolution: {integrity: sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==} - typescript@4.9.5: - resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} - engines: {node: '>=4.2.0'} - hasBin: true - typescript@5.8.3: resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} engines: {node: '>=14.17'} @@ -88,12 +83,6 @@ snapshots: '@fastify/busboy@2.1.1': {} - '@insertish/oapi@0.2.6': - dependencies: - typescript: 4.9.5 - optionalDependencies: - openapi-typescript: 5.4.2 - argparse@2.0.1: {} globalyzer@0.1.0: {} @@ -106,6 +95,8 @@ snapshots: dependencies: argparse: 2.0.1 + json-with-bigint@3.4.4: {} + mime@3.0.0: {} openapi-typescript@5.4.2: @@ -124,8 +115,6 @@ snapshots: globalyzer: 0.1.0 globrex: 0.1.2 - typescript@4.9.5: {} - typescript@5.8.3: {} undici@5.29.0: diff --git a/src/index.ts b/src/index.ts index dce7657..d72e99b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,11 +1,14 @@ // This file was auto-generated by @insertish/oapi! import type { APIRoutes } from "./routes.js"; +export type { APIRoutes } from "./routes.js"; export * from "./types.js"; import { defaultBaseURL } from "./baseURL.js"; import { pathResolve, queryParams } from "./params.js"; +import { JSONParse, JSONStringify } from 'json-with-bigint'; + type Methods = APIRoutes["method"]; type PickRoutes = APIRoutes & { method: Method }; @@ -189,7 +192,7 @@ export class API { } const passbody = ["head", "get"].includes(method) ? undefined - : JSON.stringify(body); + : JSONStringify(body); let fetchpath = `${path}?${query.toString()}`; if (fetchpath.startsWith("/")) { @@ -205,13 +208,14 @@ export class API { body: passbody, }); + const respType = config?.responseType || "json"; const data = fetchdata.status === 204 ? null - : await fetchdata[config?.responseType || "json"](); + : await fetchdata[respType === 'json' ? 'text' : respType](); if (fetchdata.ok) { - return data; + return respType === 'json' ? JSONParse(data as string) : data as never; } else { throw data; } diff --git a/tsconfig.json b/tsconfig.json index a39ec41..e72d721 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { "target": "ES2022", - "module": "ES6", - "moduleResolution": "node", + "module": "es6", + "moduleResolution": "bundler", "rootDir": "./src", "declaration": true, "declarationMap": true,