111 lines
2.6 KiB
TypeScript
111 lines
2.6 KiB
TypeScript
import { spawnSync } from 'node:child_process';
|
|
import { createHash } from 'node:crypto';
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
import postgres from 'postgres';
|
|
|
|
export type SqlClient = ReturnType<typeof postgres>;
|
|
|
|
function loadEnvFile(filePath: string) {
|
|
if (!fs.existsSync(filePath)) {
|
|
return;
|
|
}
|
|
|
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
|
|
for (const rawLine of content.split(/\r?\n/)) {
|
|
const line = rawLine.trim();
|
|
|
|
if (!line || line.startsWith('#')) {
|
|
continue;
|
|
}
|
|
|
|
const separatorIndex = line.indexOf('=');
|
|
|
|
if (separatorIndex === -1) {
|
|
continue;
|
|
}
|
|
|
|
const key = line.slice(0, separatorIndex).trim();
|
|
let value = line.slice(separatorIndex + 1).trim();
|
|
|
|
if (
|
|
(value.startsWith('"') && value.endsWith('"')) ||
|
|
(value.startsWith("'") && value.endsWith("'"))
|
|
) {
|
|
value = value.slice(1, -1);
|
|
}
|
|
|
|
if (!(key in process.env)) {
|
|
process.env[key] = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
export function loadLocalEnv() {
|
|
loadEnvFile(path.resolve(process.cwd(), '.env.local'));
|
|
loadEnvFile(path.resolve(process.cwd(), '.env'));
|
|
}
|
|
|
|
export function requireEnv(name: string): string {
|
|
const value = process.env[name]?.trim();
|
|
|
|
if (!value) {
|
|
throw new Error(`Missing required environment variable: ${name}`);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
export function createSqlClient(): SqlClient {
|
|
loadLocalEnv();
|
|
return postgres(requireEnv('DATABASE_URL'), { prepare: false });
|
|
}
|
|
|
|
export function deterministicId(input: string): string {
|
|
const digest = createHash('sha256').update(input).digest('hex');
|
|
const part3 = `5${digest.slice(13, 16)}`;
|
|
const part4 = `a${digest.slice(17, 20)}`;
|
|
|
|
return [
|
|
digest.slice(0, 8),
|
|
digest.slice(8, 12),
|
|
part3,
|
|
part4,
|
|
digest.slice(20, 32)
|
|
].join('-');
|
|
}
|
|
|
|
export const NODE_TS_RUNTIME_ARGS = [
|
|
'--disable-warning=ExperimentalWarning',
|
|
'--disable-warning=MODULE_TYPELESS_PACKAGE_JSON',
|
|
'--experimental-strip-types'
|
|
];
|
|
|
|
export function runNodeScript(label: string, scriptPath: string, args: string[] = []) {
|
|
const result = spawnSync(
|
|
process.execPath,
|
|
[...NODE_TS_RUNTIME_ARGS, scriptPath, ...args],
|
|
{
|
|
stdio: 'inherit',
|
|
env: process.env
|
|
}
|
|
);
|
|
|
|
if (result.status !== 0) {
|
|
throw new Error(`[${label}] failed with exit code ${result.status ?? 1}`);
|
|
}
|
|
}
|
|
|
|
export function runCommand(label: string, command: string, args: string[] = []) {
|
|
const result = spawnSync(command, args, {
|
|
stdio: 'inherit',
|
|
env: process.env,
|
|
shell: process.platform === 'win32'
|
|
});
|
|
|
|
if (result.status !== 0) {
|
|
throw new Error(`[${label}] failed with exit code ${result.status ?? 1}`);
|
|
}
|
|
}
|