const { randomBytes } = require('node:crypto'); const { readFile, rename, rm, writeFile } = require('node:fs/promises'); const { resolve } = require('node:path'); // Relative to the working directory, as before: the contract test runs the script in a temporary directory. const target = resolve(process.cwd(), 'openapi.json'); const checkOnly = process.argv.includes('--check'); // Schema generation needs no deployment configuration or real credentials. Object.assign(process.env, { DATABASE_URL: 'file::memory:', FRONTEND_ORIGIN: 'http://localhost', JWT_SECRET: randomBytes(32).toString('hex'), TOKEN_ENCRYPTION_KEY: randomBytes(32).toString('base64'), PORT: '3000', LOG_LEVEL: 'error', TBANK_GRPC_ENDPOINT: 'localhost:443', TBANK_RATE_LIMIT_PER_SECOND: '5', TBANK_CA_CERT_PATH: '', }); const { NestFactory } = require('@nestjs/core'); const { AppModule } = require('../dist/src/app.module.js'); const { API_PREFIX, createOpenApiDocument } = require('../dist/src/openapi.js'); async function render() { const app = await NestFactory.create(AppModule, { preview: true, logger: false, }); try { app.setGlobalPrefix(API_PREFIX); return JSON.stringify(createOpenApiDocument(app), null, 2) + '\n'; } finally { await app.close(); } } // Read-only: compares the runtime document with the committed artifact and never writes. async function check(content) { const current = await readFile(target, 'utf8').catch(() => null); if (current !== content) { console.error( 'openapi.json is out of date with the runtime schema. Run `npm run openapi:generate`.', ); process.exitCode = 1; return; } console.log('openapi.json matches the runtime schema.'); } // Atomic update: write a sibling temp file, then rename over the target, so a failed run never leaves partial output. async function update(content) { const temp = `${target}.${process.pid}.tmp`; try { // eslint-disable-next-line security/detect-non-literal-fs-filename -- Operator-selected working directory and fixed artifact name; no request input. await writeFile(temp, content); // eslint-disable-next-line security/detect-non-literal-fs-filename -- Operator-selected working directory and fixed artifact name; no request input. await rename(temp, target); } finally { await rm(temp, { force: true }); } } render() .then((content) => (checkOnly ? check(content) : update(content))) .catch((error) => { console.error(error); process.exitCode = 1; });