151 lines
4.6 KiB
TypeScript
151 lines
4.6 KiB
TypeScript
import * as fs from 'node:fs';
|
|
import * as path from 'node:path';
|
|
|
|
import type { ILoadedMatrixPackageEnvironment } from './package-environment.js';
|
|
|
|
export interface IResolvedRunnerTransportPlan {
|
|
readonly envName: string;
|
|
readonly root: string;
|
|
readonly mode: 'embedded' | 'external';
|
|
readonly url: string;
|
|
readonly wsUrl?: string;
|
|
readonly credentialsRef?: string;
|
|
readonly port?: number;
|
|
readonly wsPort?: number;
|
|
readonly binaryPath?: string;
|
|
readonly dataDir?: string;
|
|
readonly pidFile?: string;
|
|
}
|
|
|
|
export function resolveNatsBinary(baseDir: string, explicitBinaryPath?: string): string {
|
|
const binaryOverride = readOptionalString(explicitBinaryPath);
|
|
if (binaryOverride) {
|
|
return path.resolve(baseDir, binaryOverride);
|
|
}
|
|
|
|
const envBinary = readOptionalString(process.env.MATRIX_NATS_SERVER_BINARY)
|
|
?? readOptionalString(process.env.NATS_SERVER_BINARY);
|
|
if (envBinary) {
|
|
return path.resolve(envBinary);
|
|
}
|
|
|
|
const binaryName = process.platform === 'win32' ? 'nats-server.exe' : 'nats-server';
|
|
const searchRoots = new Set<string>([
|
|
baseDir,
|
|
path.resolve(baseDir, '..'),
|
|
path.resolve(baseDir, '..', '..'),
|
|
path.resolve(baseDir, '..', '..', '..'),
|
|
path.resolve(baseDir, '..', '..', '..', '..'),
|
|
]);
|
|
|
|
for (const root of searchRoots) {
|
|
const candidates = [
|
|
path.resolve(root, 'projects', 'matrix-3', 'packages', 'daemon', 'dist', 'bin', binaryName),
|
|
path.resolve(root, 'projects', 'nats-server', binaryName),
|
|
path.resolve(root, 'packages', 'daemon', 'dist', 'bin', binaryName),
|
|
path.resolve(root, 'nats-server', binaryName),
|
|
];
|
|
|
|
for (const candidate of candidates) {
|
|
if (pathExists(candidate)) {
|
|
return candidate;
|
|
}
|
|
}
|
|
}
|
|
|
|
const pathEntries = (process.env.PATH ?? '')
|
|
.split(path.delimiter)
|
|
.filter((entry) => entry.length > 0);
|
|
for (const entry of pathEntries) {
|
|
const candidate = path.join(entry, binaryName);
|
|
if (pathExists(candidate)) {
|
|
return candidate;
|
|
}
|
|
}
|
|
|
|
return binaryName;
|
|
}
|
|
|
|
export function resolveRunnerTransportPlan(
|
|
packageDir: string,
|
|
selectedEnvironment: ILoadedMatrixPackageEnvironment,
|
|
rootOverride?: string,
|
|
): IResolvedRunnerTransportPlan {
|
|
const nats = selectedEnvironment.environment.nats ?? {};
|
|
const root = readOptionalString(rootOverride) ?? selectedEnvironment.environment.runtime.root;
|
|
const mode = inferMode(nats.mode, nats.url);
|
|
|
|
if (mode === 'external') {
|
|
const url = readOptionalString(nats.url);
|
|
if (!url) {
|
|
throw new Error(
|
|
`Package environment "${selectedEnvironment.envName}" requires nats.url when nats.mode is external`,
|
|
);
|
|
}
|
|
const wsUrl = readOptionalString(nats.wsUrl);
|
|
const credentialsRef = readOptionalString(nats.credentialsRef);
|
|
return {
|
|
envName: selectedEnvironment.envName,
|
|
root,
|
|
mode: 'external',
|
|
url,
|
|
...(wsUrl ? { wsUrl } : {}),
|
|
...(credentialsRef ? { credentialsRef } : {}),
|
|
};
|
|
}
|
|
|
|
const port = readOptionalNumber(nats.port) ?? 4222;
|
|
const wsPort = readOptionalNumber(nats.wsPort);
|
|
const url = readOptionalString(nats.url) ?? `nats://127.0.0.1:${port}`;
|
|
const wsUrl = readOptionalString(nats.wsUrl) ?? (wsPort ? `ws://127.0.0.1:${wsPort}` : undefined);
|
|
const dataDir = path.resolve(
|
|
packageDir,
|
|
readOptionalString(nats.dataDir) ?? path.join('.matrix', 'state', selectedEnvironment.envName, 'nats'),
|
|
);
|
|
const pidFile = path.resolve(
|
|
packageDir,
|
|
readOptionalString(nats.pidFile) ?? path.join('.matrix', 'state', selectedEnvironment.envName, 'nats-server.pid'),
|
|
);
|
|
const binaryPath = resolveNatsBinary(packageDir, readOptionalString(nats.binaryPath));
|
|
|
|
return {
|
|
envName: selectedEnvironment.envName,
|
|
root,
|
|
mode: 'embedded',
|
|
url,
|
|
port,
|
|
...(wsUrl ? { wsUrl } : {}),
|
|
...(wsPort !== undefined ? { wsPort } : {}),
|
|
binaryPath,
|
|
dataDir,
|
|
pidFile,
|
|
};
|
|
}
|
|
|
|
function inferMode(mode: string | undefined, url: string | undefined): 'embedded' | 'external' {
|
|
const normalizedMode = readOptionalString(mode)?.toLowerCase();
|
|
if (normalizedMode === 'external' || readOptionalString(url)) {
|
|
return 'external';
|
|
}
|
|
return 'embedded';
|
|
}
|
|
|
|
function readOptionalString(value: unknown): string | undefined {
|
|
if (typeof value !== 'string') {
|
|
return undefined;
|
|
}
|
|
const trimmed = value.trim();
|
|
return trimmed.length > 0 ? trimmed : undefined;
|
|
}
|
|
|
|
function readOptionalNumber(value: unknown): number | undefined {
|
|
if (typeof value !== 'number' || !Number.isFinite(value) || value <= 0) {
|
|
return undefined;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
function pathExists(filePath: string): boolean {
|
|
return fs.existsSync(filePath);
|
|
}
|