import * as Duration from "effect/Duration";import * as Effect from "effect/Effect";import * as FileSystem from "effect/FileSystem";import * as Path from "effect/Path";import * as PlatformError from "effect/PlatformError";import * as Schema from "effect/Schema";import * as Option from "effect/Option";import * as Semaphore from "effect/Semaphore"; import * as ProcessRunner from "../processRunner.ts"; /** * A pinned runtime is an exact `t3@<version>` npm-installed into * <baseDir>/runtime/versions/<version>. The boot service points its unit or * launch agent here, and server self-update installs the target version here before * switching over, never `npx t3`, whose cache is ephemeral and whose * registry fetch at boot would make startup depend on the network. */ const PINNED_RUNTIME_DIR = "runtime";const PINNED_RUNTIME_INSTALL_TIMEOUT = Duration.minutes(10);// Boot-service setup and remote update can construct separate layers. Serialize// the complete install transaction across every caller in this process.const pinnedRuntimeInstallLock = Semaphore.makeUnsafe(1); export interface PinnedRuntimePaths { readonly versionDir: string; readonly entryPath: string; readonly sentinelPath: string;} export function pinnedRuntimePaths( path: Path.Path, baseDir: string, version: string,): PinnedRuntimePaths { const versionDir = path.join(baseDir, PINNED_RUNTIME_DIR, "versions", version); return { versionDir, entryPath: path.join(versionDir, "node_modules", "t3", "dist", "bin.mjs"), sentinelPath: path.join(versionDir, ".install-complete"), };} export class PinnedRuntimeInstallError extends Schema.TaggedError<PinnedRuntimeInstallError>()( "PinnedRuntimeInstallError", { step: Schema.String, exitCode: Schema.optional(Schema.Number), stdoutLength: Schema.optional(Schema.Number), stderrLength: Schema.optional(Schema.Number), cause: Schema.optional(Schema.Defect()), },) { override get message(): string { return this.exitCode === undefined ? `Pinned runtime install failed while ${this.step}.` : `Pinned runtime install failed while ${this.step} (exit code ${this.exitCode}).`; }} export class PinnedRuntimePreflightBlockedError extends Schema.TaggedError<PinnedRuntimePreflightBlockedError>()( "PinnedRuntimePreflightBlockedError", { version: Schema.String, reason: Schema.String, },) { override get message(): string { return this.reason; }} /** * Installs `t3@<version>` into the pinned runtime directory unless a complete * install is already there, and returns its paths. The sentinel is written * only after npm exits 0; checking the entry file alone is not enough. npm * extracts files before running native builds (node-pty), so a killed * install leaves a plausible-looking but broken tree behind. */interface PinnedRuntimeInstallInput { readonly baseDir: string; readonly version: string; readonly fs: FileSystem.FileSystem; readonly path: Path.Path; readonly runner: ProcessRunner.ProcessRunner["Service"]; readonly validate: ( paths: PinnedRuntimePaths, ) => Effect.Effect<void, PinnedRuntimeInstallError | PinnedRuntimePreflightBlockedError>;} const installPinnedRuntime = Effect.fn("cloud.pinned_runtime.ensure_installed")(function* ( input: PinnedRuntimeInstallInput,) { const { fs, runner } = input; const paths = pinnedRuntimePaths(input.path, input.baseDir, input.version); const [versionDirExists, entryExists, sentinel] = yield* Effect.all([ fs.exists(paths.versionDir), fs.exists(paths.entryPath), fs.readFileString(paths.sentinelPath).pipe(Effect.option), ]).pipe( Effect.mapError( (cause) => new PinnedRuntimeInstallError({ step: "checking the pinned runtime", cause }), ), ); const alreadyPinned = entryExists && Option.isSome(sentinel) && sentinel.value.trim() === input.version; if (alreadyPinned) { yield* input.validate(paths); return paths; } if (versionDirExists) { yield* fs.remove(paths.versionDir, { recursive: true, force: true }).pipe( Effect.mapError( (cause) => new PinnedRuntimeInstallError({ step: "removing an incomplete pinned runtime", cause, }), ), ); } const versionsDir = input.path.dirname(paths.versionDir); yield* fs.makeDirectory(versionsDir, { recursive: true }).pipe( Effect.mapError( (cause) => new PinnedRuntimeInstallError({ step: "preparing the pinned runtime directory", cause, }), ), ); const stagingDir = yield* fs .makeTempDirectory({ directory: versionsDir, prefix: ".staging-", }) .pipe( Effect.mapError( (cause) => new PinnedRuntimeInstallError({ step: "preparing the pinned runtime directory", cause, }), ), ); const stagingPaths: PinnedRuntimePaths = { versionDir: stagingDir, entryPath: input.path.join(stagingDir, "node_modules", "t3", "dist", "bin.mjs"), sentinelPath: input.path.join(stagingDir, ".install-complete"), }; return yield* Effect.gen(function* () { const installStep = "installing the pinned t3 runtime (this can take a few minutes)"; const installArgs = [ "install", "--prefix", stagingDir, "--no-fund", "--no-audit", `t3@${input.version}`, ]; yield* runner .run({ command: "npm", args: installArgs, // Native dependencies may compile from source on slower machines. timeout: PINNED_RUNTIME_INSTALL_TIMEOUT, }) .pipe( Effect.catchTags({ ProcessSpawnError: (error) => error.cause instanceof PlatformError.PlatformError && error.cause.reason._tag === "NotFound" ? // pnpm-managed Node installations do not include npm. Keep npm // installation semantics for the pinned runtime and native builds. runner.run({ command: "pnpm", args: ["--package=npm@11", "dlx", "npm", ...installArgs], timeout: PINNED_RUNTIME_INSTALL_TIMEOUT, }) : Effect.fail(error), }), Effect.mapError((cause) => new PinnedRuntimeInstallError({ step: installStep, cause })), Effect.filterOrFail( (result) => result.code === 0, (result) => new PinnedRuntimeInstallError({ step: installStep, exitCode: Number(result.code), stdoutLength: result.stdout.length, stderrLength: result.stderr.length, }), ), ); yield* input.validate(stagingPaths); yield* fs .writeFileString(stagingPaths.sentinelPath, `${input.version}\n`) .pipe( Effect.mapError( (cause) => new PinnedRuntimeInstallError({ step: "recording the completed install", cause }), ), ); const published = yield* fs.rename(stagingDir, paths.versionDir).pipe( Effect.as(true), Effect.catch((cause) => Effect.all([ fs.exists(paths.entryPath), fs.readFileString(paths.sentinelPath).pipe(Effect.option), ]).pipe( Effect.mapError( (checkCause) => new PinnedRuntimeInstallError({ step: "checking a concurrently published pinned runtime", cause: checkCause, }), ), Effect.flatMap(([publishedEntryExists, publishedSentinel]) => publishedEntryExists && Option.isSome(publishedSentinel) && publishedSentinel.value.trim() === input.version ? Effect.succeed(false) : Effect.fail( new PinnedRuntimeInstallError({ step: "publishing the pinned runtime", cause, }), ), ), ), ), ); if (!published) yield* input.validate(paths); return paths; }).pipe( Effect.ensuring(fs.remove(stagingDir, { recursive: true, force: true }).pipe(Effect.ignore)), );}); export const ensurePinnedRuntimeInstalled = (input: PinnedRuntimeInstallInput) => pinnedRuntimeInstallLock.withPermit(installPinnedRuntime(input));
import * as Duration from "effect/Duration";
import * as Effect from "effect/Effect";
import * as FileSystem from "effect/FileSystem";
import * as Path from "effect/Path";
import * as PlatformError from "effect/PlatformError";
import * as Schema from "effect/Schema";
import * as Option from "effect/Option";
import * as Semaphore from "effect/Semaphore";
import * as ProcessRunner from "../processRunner.ts";
/**
* A pinned runtime is an exact `t3@<version>` npm-installed into
* <baseDir>/runtime/versions/<version>. The boot service points its unit or
* launch agent here, and server self-update installs the target version here before
* switching over, never `npx t3`, whose cache is ephemeral and whose
* registry fetch at boot would make startup depend on the network.
*/
const PINNED_RUNTIME_DIR = "runtime";
const PINNED_RUNTIME_INSTALL_TIMEOUT = Duration.minutes(10);
// Boot-service setup and remote update can construct separate layers. Serialize
// the complete install transaction across every caller in this process.
const pinnedRuntimeInstallLock = Semaphore.makeUnsafe(1);
export interface PinnedRuntimePaths {
readonly versionDir: string;
readonly entryPath: string;
readonly sentinelPath: string;
}
export function pinnedRuntimePaths(
path: Path.Path,
baseDir: string,
version: string,
): PinnedRuntimePaths {
const versionDir = path.join(baseDir, PINNED_RUNTIME_DIR, "versions", version);
return {
versionDir,
entryPath: path.join(versionDir, "node_modules", "t3", "dist", "bin.mjs"),
sentinelPath: path.join(versionDir, ".install-complete"),
};
}
export class PinnedRuntimeInstallError extends Schema.TaggedError<PinnedRuntimeInstallError>()(
"PinnedRuntimeInstallError",
{
step: Schema.String,
exitCode: Schema.optional(Schema.Number),
stdoutLength: Schema.optional(Schema.Number),
stderrLength: Schema.optional(Schema.Number),
cause: Schema.optional(Schema.Defect()),
},
) {
override get message(): string {
return this.exitCode === undefined
? `Pinned runtime install failed while ${this.step}.`
: `Pinned runtime install failed while ${this.step} (exit code ${this.exitCode}).`;
}
}
export class PinnedRuntimePreflightBlockedError extends Schema.TaggedError<PinnedRuntimePreflightBlockedError>()(
"PinnedRuntimePreflightBlockedError",
{
version: Schema.String,
reason: Schema.String,
},
) {
override get message(): string {
return this.reason;
}
}
/**
* Installs `t3@<version>` into the pinned runtime directory unless a complete
* install is already there, and returns its paths. The sentinel is written
* only after npm exits 0; checking the entry file alone is not enough. npm
* extracts files before running native builds (node-pty), so a killed
* install leaves a plausible-looking but broken tree behind.
*/
interface PinnedRuntimeInstallInput {
readonly baseDir: string;
readonly version: string;
readonly fs: FileSystem.FileSystem;
readonly path: Path.Path;
readonly runner: ProcessRunner.ProcessRunner["Service"];
readonly validate: (
paths: PinnedRuntimePaths,
) => Effect.Effect<void, PinnedRuntimeInstallError | PinnedRuntimePreflightBlockedError>;
}
const installPinnedRuntime = Effect.fn("cloud.pinned_runtime.ensure_installed")(function* (
input: PinnedRuntimeInstallInput,
) {
const { fs, runner } = input;
const paths = pinnedRuntimePaths(input.path, input.baseDir, input.version);
const [versionDirExists, entryExists, sentinel] = yield* Effect.all([
fs.exists(paths.versionDir),
fs.exists(paths.entryPath),
fs.readFileString(paths.sentinelPath).pipe(Effect.option),
]).pipe(
Effect.mapError(
(cause) => new PinnedRuntimeInstallError({ step: "checking the pinned runtime", cause }),
),
);
const alreadyPinned =
entryExists && Option.isSome(sentinel) && sentinel.value.trim() === input.version;
if (alreadyPinned) {
yield* input.validate(paths);
return paths;
}
if (versionDirExists) {
yield* fs.remove(paths.versionDir, { recursive: true, force: true }).pipe(
Effect.mapError(
(cause) =>
new PinnedRuntimeInstallError({
step: "removing an incomplete pinned runtime",
cause,
}),
),
);
}
const versionsDir = input.path.dirname(paths.versionDir);
yield* fs.makeDirectory(versionsDir, { recursive: true }).pipe(
Effect.mapError(
(cause) =>
new PinnedRuntimeInstallError({
step: "preparing the pinned runtime directory",
cause,
}),
),
);
const stagingDir = yield* fs
.makeTempDirectory({
directory: versionsDir,
prefix: ".staging-",
})
.pipe(
Effect.mapError(
(cause) =>
new PinnedRuntimeInstallError({
step: "preparing the pinned runtime directory",
cause,
}),
),
);
const stagingPaths: PinnedRuntimePaths = {
versionDir: stagingDir,
entryPath: input.path.join(stagingDir, "node_modules", "t3", "dist", "bin.mjs"),
sentinelPath: input.path.join(stagingDir, ".install-complete"),
};
return yield* Effect.gen(function* () {
const installStep = "installing the pinned t3 runtime (this can take a few minutes)";
const installArgs = [
"install",
"--prefix",
stagingDir,
"--no-fund",
"--no-audit",
`t3@${input.version}`,
];
yield* runner
.run({
command: "npm",
args: installArgs,
// Native dependencies may compile from source on slower machines.
timeout: PINNED_RUNTIME_INSTALL_TIMEOUT,
})
.pipe(
Effect.catchTags({
ProcessSpawnError: (error) =>
error.cause instanceof PlatformError.PlatformError &&
error.cause.reason._tag === "NotFound"
? // pnpm-managed Node installations do not include npm. Keep npm
// installation semantics for the pinned runtime and native builds.
runner.run({
command: "pnpm",
args: ["--package=npm@11", "dlx", "npm", ...installArgs],
timeout: PINNED_RUNTIME_INSTALL_TIMEOUT,
})
: Effect.fail(error),
}),
Effect.mapError((cause) => new PinnedRuntimeInstallError({ step: installStep, cause })),
Effect.filterOrFail(
(result) => result.code === 0,
(result) =>
new PinnedRuntimeInstallError({
step: installStep,
exitCode: Number(result.code),
stdoutLength: result.stdout.length,
stderrLength: result.stderr.length,
}),
),
);
yield* input.validate(stagingPaths);
yield* fs
.writeFileString(stagingPaths.sentinelPath, `${input.version}\n`)
.pipe(
Effect.mapError(
(cause) =>
new PinnedRuntimeInstallError({ step: "recording the completed install", cause }),
),
);
const published = yield* fs.rename(stagingDir, paths.versionDir).pipe(
Effect.as(true),
Effect.catch((cause) =>
Effect.all([
fs.exists(paths.entryPath),
fs.readFileString(paths.sentinelPath).pipe(Effect.option),
]).pipe(
Effect.mapError(
(checkCause) =>
new PinnedRuntimeInstallError({
step: "checking a concurrently published pinned runtime",
cause: checkCause,
}),
),
Effect.flatMap(([publishedEntryExists, publishedSentinel]) =>
publishedEntryExists &&
Option.isSome(publishedSentinel) &&
publishedSentinel.value.trim() === input.version
? Effect.succeed(false)
: Effect.fail(
new PinnedRuntimeInstallError({
step: "publishing the pinned runtime",
cause,
}),
),
),
),
),
);
if (!published) yield* input.validate(paths);
return paths;
}).pipe(
Effect.ensuring(fs.remove(stagingDir, { recursive: true, force: true }).pipe(Effect.ignore)),
);
});
export const ensurePinnedRuntimeInstalled = (input: PinnedRuntimeInstallInput) =>
pinnedRuntimeInstallLock.withPermit(installPinnedRuntime(input));