chore: use bigint parse/stringify

chore: move cli generator into this repo
This commit is contained in:
izzy
2025-10-22 17:47:18 +01:00
parent f654e4be7d
commit 0b9ccca95b
5 changed files with 181 additions and 27 deletions
+159
View File
@@ -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"
};`
);
});
+4 -2
View File
@@ -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"
}
}
+9 -20
View File
@@ -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:
+7 -3
View File
@@ -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<Method extends Methods> = 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;
}
+2 -2
View File
@@ -1,8 +1,8 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ES6",
"moduleResolution": "node",
"module": "es6",
"moduleResolution": "bundler",
"rootDir": "./src",
"declaration": true,
"declarationMap": true,