← Files RivetARCHIVED FILE

node_modules/rivet-design/dist/cli/serverHandshake.js

6.78 KB · Nov 7, 2026 · 13:16 UTC

↓ Download file

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.acquireStartLock = exports.releaseReadyStartLock = exports.isServerStartInProgress = exports.isPidAlive = exports.removeHandshake = exports.readHandshake = exports.readHandshakeResult = exports.writeHandshake = exports.handshakeFilePath = exports.serverRuntimeDirectory = exports.CLI_HANDSHAKE_VERSION = void 0;
const crypto_1 = require("crypto");
const zod_1 = require("zod");
const ProjectRuntimeRecords_1 = require("../storage/library/ProjectRuntimeRecords");
const installationChannel_1 = require("../install/installationChannel");
const fs_1 = require("fs");
const path_1 = require("path");
const logger_1 = require("../utils/logger");
const pid_1 = require("../utils/pid");
Object.defineProperty(exports, "isPidAlive", { enumerable: true, get: function () { return pid_1.isPidAlive; } });
const errnoCode_1 = require("../utils/errnoCode");
const writeFileAtomic_1 = require("../utils/writeFileAtomic");
const log = (0, logger_1.createLogger)('CliServerHandshake');
exports.CLI_HANDSHAKE_VERSION = 1;
const serverRuntimeDirectory = (projectPath, channel = (0, installationChannel_1.installationChannel)()) => channel === 'production'
    ? (0, path_1.join)(projectPath, '.rivet', 'cli')
    : (0, path_1.join)(projectPath, '.rivet', 'cli', channel);
exports.serverRuntimeDirectory = serverRuntimeDirectory;
const handshakeFilePath = (projectPath, channel = (0, installationChannel_1.installationChannel)()) => (0, path_1.join)((0, exports.serverRuntimeDirectory)(projectPath, channel), 'server.json');
exports.handshakeFilePath = handshakeFilePath;
const lockFilePath = (projectPath, channel = (0, installationChannel_1.installationChannel)()) => (0, path_1.join)((0, exports.serverRuntimeDirectory)(projectPath, channel), 'server.lock');
const OWNER_ONLY_FILE_MODE = 0o600;
const writeHandshake = (handshake, channel = (0, installationChannel_1.installationChannel)()) => {
    const filePath = (0, exports.handshakeFilePath)(handshake.projectPath, channel);
    (0, fs_1.mkdirSync)((0, path_1.dirname)(filePath), { recursive: true });
    (0, writeFileAtomic_1.writeFileAtomicSync)(filePath, JSON.stringify(handshake), {
        mode: OWNER_ONLY_FILE_MODE,
    });
};
exports.writeHandshake = writeHandshake;
const readHandshakeResult = (projectPath, channel = (0, installationChannel_1.installationChannel)()) => {
    const filePath = (0, exports.handshakeFilePath)(projectPath, channel);
    try {
        const parsed = JSON.parse((0, fs_1.readFileSync)(filePath, 'utf8'));
        if (parsed.version !== exports.CLI_HANDSHAKE_VERSION) {
            return { status: 'invalid' };
        }
        if (typeof parsed.pid !== 'number' ||
            !Number.isInteger(parsed.pid) ||
            parsed.pid <= 0 ||
            typeof parsed.rivetPort !== 'number' ||
            !Number.isInteger(parsed.rivetPort) ||
            parsed.rivetPort <= 0) {
            return { status: 'invalid' };
        }
        return { status: 'found', handshake: parsed };
    }
    catch (error) {
        if ((0, errnoCode_1.errnoCode)(error) === 'ENOENT') {
            return { status: 'missing' };
        }
        log.debug(`Unreadable CLI handshake at ${filePath}`, error);
        return { status: 'invalid' };
    }
};
exports.readHandshakeResult = readHandshakeResult;
const readHandshake = (projectPath, channel = (0, installationChannel_1.installationChannel)()) => {
    const result = (0, exports.readHandshakeResult)(projectPath, channel);
    return result.status === 'found' ? result.handshake : null;
};
exports.readHandshake = readHandshake;
const removeHandshake = (projectPath, pid) => {
    const existing = (0, exports.readHandshake)(projectPath);
    if (!existing || existing.pid !== pid) {
        return;
    }
    try {
        (0, fs_1.unlinkSync)((0, exports.handshakeFilePath)(projectPath));
    }
    catch { }
};
exports.removeHandshake = removeHandshake;
const RuntimeOwnerSchema = zod_1.z.object({
    pid: zod_1.z.number().int().positive(),
    token: zod_1.z.string().uuid(),
    phase: zod_1.z.enum(['active', 'starting', 'retiring']),
});
const fileOwnerCanWrite = (projectPath, channel) => {
    try {
        const [owner] = (0, fs_1.readFileSync)(lockFilePath(projectPath, channel), 'utf8').split('\n');
        const pid = Number(owner);
        return !Number.isInteger(pid) || pid <= 0 || (0, pid_1.isPidAlive)(pid);
    }
    catch (error) {
        return (0, errnoCode_1.errnoCode)(error) !== 'ENOENT';
    }
};
const withRuntimeOwnership = async (directory, operation) => {
    const records = await ProjectRuntimeRecords_1.ProjectRuntimeRecords.open(directory);
    try {
        return records.transaction(() => operation(records));
    }
    finally {
        records.close();
    }
};
const isServerStartInProgress = async (projectPath, channel = (0, installationChannel_1.installationChannel)()) => {
    if (fileOwnerCanWrite(projectPath, channel))
        return true;
    const directory = (0, exports.serverRuntimeDirectory)(projectPath, channel);
    if (!(0, fs_1.existsSync)((0, path_1.join)(directory, 'lifecycle.sqlite')))
        return false;
    return withRuntimeOwnership(directory, (records) => {
        const row = records.read();
        return row !== null && (0, pid_1.isPidAlive)(RuntimeOwnerSchema.parse(row).pid);
    });
};
exports.isServerStartInProgress = isServerStartInProgress;
const releaseReadyStartLock = async (projectPath, pid) => {
    await withRuntimeOwnership((0, exports.serverRuntimeDirectory)(projectPath), (records) => {
        const row = records.read();
        if (row)
            RuntimeOwnerSchema.parse(row);
        records.releaseReady(pid);
    });
};
exports.releaseReadyStartLock = releaseReadyStartLock;
const acquireStartLock = async (projectPath, channel = (0, installationChannel_1.installationChannel)()) => {
    const directory = (0, exports.serverRuntimeDirectory)(projectPath, channel);
    const token = await withRuntimeOwnership(directory, (records) => {
        if (fileOwnerCanWrite(projectPath, channel))
            return null;
        const row = records.read();
        if (row && (0, pid_1.isPidAlive)(RuntimeOwnerSchema.parse(row).pid))
            return null;
        const token = (0, crypto_1.randomUUID)();
        records.assign({ pid: process.pid, token, phase: 'active' });
        return token;
    });
    if (token === null)
        return null;
    return {
        holdForProcess: async (pid, phase = 'starting') => {
            const owner = RuntimeOwnerSchema.parse({ pid, phase, token });
            await withRuntimeOwnership(directory, (records) => records.transfer(owner));
        },
        release: async () => {
            await withRuntimeOwnership(directory, (records) => records.release(token));
        },
    };
};
exports.acquireStartLock = acquireStartLock;
//# sourceMappingURL=serverHandshake.js.map

SHA-256: 1465a968edca598d33bfd8c18a45485e7545bc1b646b2faa974c904fd2bd72e6