supertokens-web-js
Version:
SuperTokens SDK for vanilla JS for all recipes
186 lines (149 loc) β’ 6.42 kB
JavaScript
const fs = require("fs/promises");
const path = require("path");
const { exec } = require("child_process");
// Dynamic import for node-fetch because itβs an ESM module
const fetch = (...args) => import("node-fetch").then(({ default: fetch }) => fetch(...args));
const frontendJsonPath = path.join(__dirname, "..", "frontendDriverInterfaceSupported.json");
const specsDir = path.join(__dirname, "..", "specs");
const libDir = path.join(__dirname, "..", "lib", "ts", "sdk", "versions");
const BASE_URL = "https://raw.githubusercontent.com/supertokens/frontend-driver-interface/refs/heads";
async function run() {
const localFilePath = process.argv[2];
const localVersion = process.argv[3];
if (localFilePath && localVersion) {
await processLocalSpec(localFilePath, localVersion);
} else {
await processRemoteSpecs();
}
}
async function processLocalSpec(filePath, version) {
const outputDir = path.join(libDir, version);
const outputPath = path.join(outputDir, "schema.d.ts");
await fs.mkdir(outputDir, { recursive: true });
console.log(`π Using local spec file: ${filePath}`);
await execPromise(`npx openapi-typescript ${filePath} -o ${outputPath}`);
let schemaContent = await fs.readFile(outputPath, "utf-8");
schemaContent = schemaContent
.replace(/"\/\{apiBasePath\}([^"]*)":/g, (_, rest) => `"${rest}":`)
.replace(/Record<string, never>/g, "Record<string, unknown>")
.replace(/\bstatus\?\s*:/g, "status:");
await fs.writeFile(outputPath, schemaContent, "utf-8");
console.log(`β
Generated and cleaned schema for local version ${version}`);
console.log("β οΈ Skipping remote specs generation for local version");
}
async function processRemoteSpecs() {
const file = await fs.readFile(frontendJsonPath, "utf-8");
const json = JSON.parse(file);
const versions = json.versions;
console.log("π Creating specs directory...");
await fs.mkdir(specsDir, { recursive: true });
try {
for (const version of versions) {
const url = `${BASE_URL}/${version}/api_spec.yaml`;
const specPath = path.join(specsDir, `api_spec_${version}.yaml`);
console.log(`π₯ Downloading: ${url}`);
const res = await fetch(url);
if (!res.ok) {
console.error(`β Failed to fetch ${url}: ${res.statusText}`);
continue;
}
const specContent = await res.text();
await fs.writeFile(specPath, specContent, "utf-8");
console.log(`β
Saved to ${specPath}`);
}
for (const version of versions) {
const inputPath = path.join(specsDir, `api_spec_${version}.yaml`);
const outputDir = path.join(libDir, version);
const outputPath = path.join(outputDir, "schema.d.ts");
await fs.mkdir(outputDir, { recursive: true });
console.log(`π§ Generating schema for version ${version}...`);
await execPromise(`npx openapi-typescript ${inputPath} -o ${outputPath}`);
console.log(`π§Ή Post-processing ${outputPath}...`);
let schemaContent = await fs.readFile(outputPath, "utf-8");
schemaContent = schemaContent
.replace(/"\/\{apiBasePath\}([^"]*)":/g, (_, rest) => `"${rest}":`)
.replace(/Record<string, never>/g, "Record<string, unknown>")
.replace(/\bstatus\?\s*:/g, "status:");
await fs.writeFile(outputPath, schemaContent, "utf-8");
console.log(`β
Cleaned ${outputPath}`);
}
} finally {
console.log("π§Ή Cleaning up specs directory...");
await fs.rm(specsDir, { recursive: true, force: true });
console.log("β
Specs directory deleted.");
}
await generatePathsFile(versions);
console.log("β
Created paths.ts for all versions");
console.log("π All done!");
}
async function generatePathsFile(versions) {
const outputPath = path.join(libDir, "..", "paths.ts");
if (versions.length === 0) {
console.warn("β οΈ No versions found β skipping paths.ts generation.");
return;
}
if (versions.length === 1) {
const singleTemplate = (version) => `/* This file was auto-generated by scripts/generate-schema.js */
import { paths } from "./versions/${version}/schema";
export { paths };
`;
await fs.writeFile(outputPath, singleTemplate(versions[0]), "utf-8");
console.log(`β
Created single-version paths.ts for ${versions[0]}`);
} else {
const imports = versions
.map((v) => `import { paths as pathsV${v.replace(/\./g, "_")} } from "./versions/${v}/schema";`)
.join("\n");
const mergedTemplate = `type MergeMethods<M1, M2> = {
[K in keyof M1 | keyof M2]: K extends keyof M1
? K extends keyof M2
? M1[K] | M2[K]
: M1[K]
: K extends keyof M2
? M2[K]
: never;
};
type MergePaths<P1, P2> = {
[K in keyof P1 | keyof P2]: K extends keyof P1
? K extends keyof P2
? MergeMethods<P1[K], P2[K]>
: P1[K]
: K extends keyof P2
? P2[K]
: never;
};
type MergeManyPaths<T extends Record<string, any>[]> = T extends [infer First, ...infer Rest]
? First extends Record<string, any>
? Rest extends Record<string, any>[]
? MergePaths<First, MergeManyPaths<Rest>>
: First
: never
: {};
`;
const mergedTypes = `export type paths = MergeManyPaths<[${versions
.map((v) => `pathsV${v.replace(/\./g, "_")}`)
.join(", ")}]>;`;
const fullContent = `/* This file was auto-generated by scripts/generate-schema.js */
${imports}
${mergedTemplate}
${mergedTypes}
`;
await fs.writeFile(outputPath, fullContent, "utf-8");
console.log(`β
Created merged paths.ts for ${versions.length} versions`);
}
}
function execPromise(command) {
return new Promise((resolve, reject) => {
exec(command, (err, stdout, stderr) => {
if (err) {
console.error(`Error: ${stderr}`);
reject(err);
} else {
resolve(stdout);
}
});
});
}
run().catch((err) => {
console.error("Unhandled error:", err);
process.exit(1);
});