UNPKG

@harboor/auth-sdk-js

Version:
103 lines (88 loc) 4.97 kB
import { writeFile, readFile } from "node:fs/promises"; import path from "node:path"; import * as os from "node:os"; import { TYPES_PREFIX } from '@harboorio/auth-backend/schema' const indent = ' ' const lnbr = os.EOL const openapi = JSON.parse(await readFile(path.resolve(process.cwd(), 'src/schema', 'openapi.json'), 'utf8')) let collectedTypes = [], tmodule = '' const schemaCode = renderSchema(openapi) const code = `import { client, processHttpRequest, type SdkRequestOptions } from '@src/http-client' import { util } from '@src/util' import type { ${collectedTypes.join(', ')} } from './schema/index' import type { AxiosRequestConfig } from 'axios' client.defaults.withCredentials = true client.defaults.responseType = 'json' client.defaults.validateStatus = function () { return true; } client.defaults.baseURL = ${(openapi.servers ?? []).find(({ description }) => /(production)/i.test(description))?.url ?? `''`} ${schemaCode} export { sdk } ` const types = `import { type AxiosInstance, type AxiosRequestConfig } from 'axios' import { type Util } from './util' import type { ${collectedTypes.join(', ')} } from './schema/index' import type { SdkRequestOptions } from './http-client' export const client: AxiosInstance export const sdk: ${TYPES_PREFIX}Sdk ${tmodule} export type { ${collectedTypes.join(', ')} }` await writeFile(path.resolve(import.meta.dirname, '..', 'src', 'index.generated.ts'), code) await writeFile(path.resolve(import.meta.dirname, '..', 'src', 'index.generated.d.ts'), types) export function renderSchema(openapi) { // sort paths by depth const endpoints = Object.keys(openapi.paths).sort((a, b) => { const asegments = a.split('/').length const bsegments = b.split('/').length return asegments < bsegments ? 1 : asegments === bsegments ? 0 : -1 }) let processedPaths = [], isInitialSegment = true, blocks = 'const sdk = {' + lnbr + indent + 'util,' + lnbr + indent + 'client,' + lnbr tmodule += 'export interface ' + TYPES_PREFIX + 'Sdk {' + lnbr + indent + 'util: Util' + lnbr + indent + 'client: AxiosInstance' + lnbr for (const url of endpoints) { const segments = url.split('/') blocks += renderSegmentRecursive(segments, { level: isInitialSegment ? 0 : 1, schema: openapi, path: '/', processedPaths, collectedTypes }) isInitialSegment = false } blocks += '}' + lnbr tmodule += '}' + lnbr return blocks } export function renderSegmentRecursive(segments, opts = { level: 0, schema: { paths: {} }, path: '/', processedPaths: [], collectedTypes: [] }) { const segment = segments.shift() opts.path = opts.path + (opts.path.endsWith('/') ? '' : '/') + segment if (opts.processedPaths.includes(opts.path)) { return segments.length > 0 ? renderSegmentRecursive(segments, opts) : '' } opts.processedPaths.push(opts.path) const name = opts.path === '/' ? 'Home' : opts.path.split('/').filter(Boolean) .map((segment) => titleCase(segment.startsWith(':') ? segment.slice(1) : segment)) .join('') let block = opts.path === '/' ? '' : indent.repeat(opts.level) + segment + ': {' + lnbr tmodule += opts.path === '/' ? '' : indent.repeat(opts.level) + segment + ': {' + lnbr if (opts.schema.paths && opts.path in opts.schema.paths) { for (const method of Object.keys(opts.schema.paths[opts.path])) { const typeName = TYPES_PREFIX + name + titleCase(method) const op = opts.schema.paths[opts.path][method] const hasBody = 'requestBody' in op block += indent.repeat(opts.level + 1) + method + `: async (${hasBody ? `json: ${typeName}Body, opts?: Partial<AxiosRequestConfig> & SdkRequestOptions` : 'opts?: Partial<AxiosRequestConfig> & SdkRequestOptions'}): Promise<${typeName}Response> => {` + lnbr block += indent.repeat(opts.level + 2) + `return (await processHttpRequest('${method}', '${opts.path}'${hasBody ? ', Object.assign({}, opts ?? {}, { data: json })' : ', opts'})) as ${typeName}Response` + lnbr block += indent.repeat(opts.level + 1) + '},' + lnbr tmodule += indent.repeat(opts.level + 1) + method + `: (${hasBody ? `json: ${typeName}Body, opts?: Partial<AxiosRequestConfig> & SdkRequestOptions` : 'opts?: Partial<AxiosRequestConfig> & SdkRequestOptions'}) => Promise<${typeName}Response>` + lnbr opts.collectedTypes.push(`${typeName}Response`) if (hasBody) opts.collectedTypes.push(`${typeName}Body`) } } if (segments.length > 0) { block += renderSegmentRecursive( segments, Object.assign({}, opts, { level: opts.level + 1 }) ) } block += opts.path === '/' ? '' : indent.repeat(opts.level) + '},' + lnbr tmodule += opts.path === '/' ? '' : indent.repeat(opts.level) + '},' + lnbr return block } function titleCase(text) { return text.slice(0, 1).toUpperCase() + text.slice(1) }