import { mkdir, readFile, writeFile } from "node:fs/promises"; import path from "node:path"; import { fileURLToPath } from "node:url"; const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); const contractPath = path.join(rootDir, "contracts/admin-openapi.json"); const outputPath = path.join(rootDir, "src/shared/api/generated/endpoints.ts"); const methods = ["get", "post", "put", "patch", "delete"]; const contract = JSON.parse(await readFile(contractPath, "utf8")); const serverUrl = contract.servers?.[0]?.url || ""; const apiPathPrefix = serverUrl.startsWith("/api") ? serverUrl.slice("/api".length) : serverUrl; const endpoints = []; for (const [openapiPath, pathItem] of Object.entries(contract.paths || {})) { for (const method of methods) { const operation = pathItem[method]; if (!operation?.operationId) { continue; } const permissions = Array.isArray(operation["x-permissions"]) ? operation["x-permissions"] : operation["x-permission"] ? [operation["x-permission"]] : []; endpoints.push({ method: method.toUpperCase(), operationId: operation.operationId, path: `${apiPathPrefix}${openapiPath}`, permission: operation["x-permission"] || permissions[0], permissions }); } } endpoints.sort((left, right) => left.operationId.localeCompare(right.operationId)); const operationLines = endpoints.map((endpoint) => { return ` ${endpoint.operationId}: ${JSON.stringify(endpoint.operationId)}`; }); const endpointLines = endpoints.map((endpoint) => { const permission = endpoint.permission ? `,\n permission: ${JSON.stringify(endpoint.permission)}` : ""; const permissions = endpoint.permissions.length ? `,\n permissions: ${JSON.stringify(endpoint.permissions)}` : ""; return ` ${endpoint.operationId}: { method: ${JSON.stringify(endpoint.method)}, operationId: API_OPERATIONS.${endpoint.operationId}, path: ${JSON.stringify(endpoint.path)}${permission}${permissions} }`; }); const output = `/* eslint-disable */ // Generated by scripts/generate-api-client.mjs. Do not edit manually. import type { PermissionCode } from "@/app/permissions"; export type ApiHttpMethod = "DELETE" | "GET" | "PATCH" | "POST" | "PUT"; export interface ApiEndpoint { method: ApiHttpMethod; operationId: string; path: string; permission?: PermissionCode; permissions?: PermissionCode[]; } export const API_OPERATIONS = { ${operationLines.join(",\n")} } as const; export type ApiOperationId = (typeof API_OPERATIONS)[keyof typeof API_OPERATIONS]; export const API_ENDPOINTS: Record = { ${endpointLines.join(",\n")} }; export const API_OPERATION_IDS = Object.values(API_OPERATIONS); export const API_PERMISSION_CODES = Array.from( new Set( Object.values(API_ENDPOINTS) .flatMap((endpoint) => endpoint.permissions || (endpoint.permission ? [endpoint.permission] : [])) .filter((permission): permission is PermissionCode => Boolean(permission)) ) ); export type ApiPathParams = Record; export function apiEndpointPath(operationId: ApiOperationId, params: ApiPathParams = {}) { let endpointPath = API_ENDPOINTS[operationId].path; for (const [key, value] of Object.entries(params)) { endpointPath = endpointPath.replace(\`{\${key}}\`, encodeURIComponent(String(value))); } return endpointPath; } `; await mkdir(path.dirname(outputPath), { recursive: true }); await writeFile(outputPath, output); console.log(`Generated ${path.relative(rootDir, outputPath)} from ${path.relative(rootDir, contractPath)}`);