UNPKG

@sourceregistry/node-ovsdb

Version:
217 lines (216 loc) 7.34 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.generateTypesFromSchema = generateTypesFromSchema; exports.generateTypesFile = generateTypesFile; exports.readSchemaFile = readSchemaFile; exports.fetchSchemaFromOvsdb = fetchSchemaFromOvsdb; exports.createGeneratorClientOptions = createGeneratorClientOptions; const promises_1 = require("node:fs/promises"); const index_1 = require("./index"); const OVSDB_IMPORT_TYPES = ["OvsMap", "OvsSet", "Uuid"]; /** * Generates TypeScript types from an OVSDB schema. * * The output is designed for direct use as the generic database model for * {@link OVSDBClient}. */ function generateTypesFromSchema(options) { const { schema } = options; const importFrom = options.importFrom ?? "@sourceregistry/node-ovsdb"; const databaseTypeName = options.databaseTypeName ?? `${toPascalCase(schema.name)}Database`; const importLine = `import type {${OVSDB_IMPORT_TYPES.join(", ")}} from "${importFrom}";`; const tableNames = Object.keys(schema.tables).sort((left, right) => left.localeCompare(right)); const rowInterfaces = tableNames.map((tableName) => { const interfaceName = `${toPascalCase(tableName)}Row`; const table = schema.tables[tableName]; const propertyLines = Object.keys(table.columns) .sort((left, right) => left.localeCompare(right)) .map((columnName) => { const column = table.columns[columnName]; const tsType = renderColumnType(column.type); const optional = isOptionalColumn(column.type) ? "?" : ""; return ` ${toPropertyKey(columnName)}${optional}: ${tsType};`; }); return [ `/**`, ` * Row model for the \`${tableName}\` table in the \`${schema.name}\` schema.`, ` */`, `export interface ${interfaceName} {`, ...propertyLines, `}` ].join("\n"); }); const databaseLines = [ `/**`, ` * Generated database model for the \`${schema.name}\` schema.`, ` */`, `export interface ${databaseTypeName} {`, ...tableNames.map((tableName) => ` ${toPropertyKey(tableName)}: ${toPascalCase(tableName)}Row;`), `}` ]; const tableNamesConst = [ `/**`, ` * Table names available in the \`${schema.name}\` schema.`, ` */`, `export const ${databaseTypeName}TableNames = ${JSON.stringify(tableNames)} as const;` ]; return [ `/* eslint-disable */`, `/*`, ` * This file was generated by ovsdb-generate.`, ` * Schema: ${schema.name}@${schema.version}`, ` */`, "", importLine, "", ...rowInterfaces.flatMap((block) => [block, ""]), ...databaseLines, "", ...tableNamesConst ].join("\n").trimEnd() + "\n"; } /** * Loads a schema from a file or a live OVSDB server and optionally writes the * generated TypeScript output to disk. */ async function generateTypesFile(options) { const schema = await loadSchema(options); const output = generateTypesFromSchema({ schema, databaseTypeName: options.databaseTypeName, importFrom: options.importFrom }); if (options.outputPath) { await (0, promises_1.writeFile)(options.outputPath, output, "utf8"); } return output; } /** * Reads a schema JSON file from disk. */ async function readSchemaFile(schemaPath) { const raw = await (0, promises_1.readFile)(schemaPath, "utf8"); return JSON.parse(raw); } /** * Reads a schema from a live OVSDB server. */ async function fetchSchemaFromOvsdb(options) { const client = new index_1.OVSDBClient(await createGeneratorClientOptions(options)); try { await client.connect(); return await client.getSchema(options.databaseName ?? "Open_vSwitch"); } finally { await client.close(); } } async function loadSchema(options) { if (options.schemaPath) { return await readSchemaFile(options.schemaPath); } return await fetchSchemaFromOvsdb({ socketPath: options.socketPath, host: options.host, port: options.port, tls: options.tls, tlsInsecure: options.tlsInsecure, tlsServername: options.tlsServername, tlsCaFile: options.tlsCaFile, tlsCertFile: options.tlsCertFile, tlsKeyFile: options.tlsKeyFile, databaseName: options.databaseName }); } /** * Builds client transport options for live schema introspection. */ async function createGeneratorClientOptions(options) { if (!options.host) { return { socketPath: options.socketPath }; } return { host: options.host, port: options.port, tls: options.tls, tlsOptions: { servername: options.tlsServername ?? options.host, rejectUnauthorized: options.tlsInsecure ? false : undefined, ca: options.tlsCaFile ? await (0, promises_1.readFile)(options.tlsCaFile, "utf8") : undefined, cert: options.tlsCertFile ? await (0, promises_1.readFile)(options.tlsCertFile, "utf8") : undefined, key: options.tlsKeyFile ? await (0, promises_1.readFile)(options.tlsKeyFile, "utf8") : undefined } }; } function renderColumnType(type) { if (typeof type === "string") { return renderAtomicType(type); } if ("value" in type && type.value !== undefined) { return `OvsMap<${renderBaseType(type.key)}, ${renderBaseType(type.value)}>`; } if (isScalarType(type)) { return renderBaseType(type.key); } return `OvsSet<${renderBaseType(type.key)}>`; } function isOptionalColumn(type) { if (typeof type === "string") { return false; } return type.min === 0; } function isScalarType(type) { const min = type.min ?? 1; const max = type.max ?? 1; return min === 1 && max === 1; } function renderBaseType(baseType) { if (typeof baseType === "string") { return renderAtomicType(baseType); } if (baseType.enum) { return renderEnum(baseType.enum); } return renderAtomicType(baseType.type); } function renderAtomicType(type) { switch (type) { case "integer": case "real": return "number"; case "boolean": return "boolean"; case "string": return "string"; case "uuid": return "Uuid"; default: return "unknown"; } } function renderEnum(value) { if (Array.isArray(value) && value[0] === "set" && Array.isArray(value[1])) { const members = value[1] .map((member) => typeof member === "string" ? JSON.stringify(member) : renderJsonValue(member)) .join(" | "); return members || "never"; } return renderJsonValue(value); } function renderJsonValue(value) { return JSON.stringify(value); } function toPascalCase(value) { return value .replace(/[^a-zA-Z0-9]+/g, " ") .split(" ") .filter(Boolean) .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) .join(""); } function toPropertyKey(value) { return /^[$A-Z_][0-9A-Z_$]*$/i.test(value) ? value : JSON.stringify(value); }