{"version":3,"file":"service-launcher.mjs","names":["#baseDir","#statePath","#completion","#state","#enqueue","#recover","#transitions","#fatal","#done","#stopping","#clearTimer","#child","#stopRequested","#timer","#startChild","#returnToPrevious","#startTrial","#handleMessage","#handleExit","#handlePreparedTimeout","#handleUpdateRequest","#handlePrepared","#beginTrial"],"sources":["../src/cloud/serviceProtocol.ts","../src/serviceLauncher.ts"],"sourcesContent":["import type { ServerSelfUpdateOutcome } from \"@t3tools/contracts\";\n\n/** Protocol 2 snapshots SQLite before trials so migrations can be rolled back safely. */\nexport const SERVICE_LAUNCHER_PROTOCOL = 2 as const;\nexport const SERVICE_LAUNCHER_CONTEXT_ENV = \"SYNAPTIC_SERVICE_LAUNCHER_CONTEXT\";\nexport const SERVICE_LAUNCHER_FILE = \"service-launcher.mjs\";\nexport const SERVICE_STATE_FILE = \"service-state.json\";\n/** Written by the launcher just before an explicit stop kills its child, so\n    the child can tell \"the service is going away\" from \"the launcher is about\n    to start my replacement\" while a pending update is recorded. */\nexport const SERVICE_STOP_MARKER_FILE = \".service-stopping\";\n\nexport interface PendingServiceUpdate {\n  readonly id: string;\n  readonly fromVersion: string;\n  readonly targetVersion: string;\n  readonly dbPath: string;\n  readonly status: \"pending\";\n}\n\nexport type ServiceUpdateRecord = PendingServiceUpdate | ServerSelfUpdateOutcome;\n\nexport interface ServiceState {\n  readonly protocol: typeof SERVICE_LAUNCHER_PROTOCOL;\n  readonly activeVersion: string;\n  readonly update?: ServiceUpdateRecord;\n}\n\n/** Context is copied from launcher-owned state when a child is spawned. */\nexport interface ServiceLauncherContext {\n  readonly protocol: typeof SERVICE_LAUNCHER_PROTOCOL;\n  readonly childVersion: string;\n  readonly update?: ServiceUpdateRecord;\n}\n\nexport type ServiceLauncherChildMessage =\n  | {\n      readonly type: \"request-update\";\n      readonly targetVersion: string;\n      readonly dbPath: string;\n    }\n  | {\n      readonly type: \"prepared\";\n      readonly updateId: string;\n    };\n\nexport type ServiceLauncherParentMessage =\n  | {\n      readonly type: \"update-accepted\";\n      readonly updateId: string;\n    }\n  | {\n      readonly type: \"update-rejected\";\n      readonly reason: string;\n    }\n  | {\n      readonly type: \"committed\";\n      readonly updateId: string;\n    };\n\nconst SEMVER_NUMBER = \"(?:0|[1-9]\\\\d*)\";\nconst SEMVER_PRERELEASE = `(?:${SEMVER_NUMBER}|[0-9]*[A-Za-z-][0-9A-Za-z-]*)`;\nconst EXACT_SERVICE_VERSION = new RegExp(\n  `^${SEMVER_NUMBER}\\\\.${SEMVER_NUMBER}\\\\.${SEMVER_NUMBER}(?:-${SEMVER_PRERELEASE}(?:\\\\.${SEMVER_PRERELEASE})*)?(?:\\\\+[0-9A-Za-z-]+(?:\\\\.[0-9A-Za-z-]+)*)?$`,\n);\n\n/** Accepts exact SemVer only: never dist-tags or ranges passed to npm or filesystem paths. */\nexport const isExactServiceVersion = (version: string): boolean =>\n  EXACT_SERVICE_VERSION.test(version);\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n  typeof value === \"object\" && value !== null && !Array.isArray(value);\n\nexport function decodeServiceUpdate(value: unknown): ServiceUpdateRecord | undefined {\n  if (!isRecord(value)) return undefined;\n  const { id, fromVersion, targetVersion, status } = value;\n  if (\n    typeof id !== \"string\" ||\n    id.trim() === \"\" ||\n    typeof fromVersion !== \"string\" ||\n    !isExactServiceVersion(fromVersion) ||\n    typeof targetVersion !== \"string\" ||\n    !isExactServiceVersion(targetVersion)\n  ) {\n    return undefined;\n  }\n  if (status === \"pending\") {\n    return typeof value.dbPath === \"string\" && value.dbPath.trim() !== \"\"\n      ? { id, fromVersion, targetVersion, dbPath: value.dbPath, status }\n      : undefined;\n  }\n  if (\n    (status === \"committed\" || status === \"rolled-back\" || status === \"failed\") &&\n    (value.reason === undefined || (typeof value.reason === \"string\" && value.reason.trim() !== \"\"))\n  ) {\n    return {\n      id,\n      fromVersion,\n      targetVersion,\n      status,\n      ...(typeof value.reason === \"string\" ? { reason: value.reason } : {}),\n    };\n  }\n  return undefined;\n}\n\n/** SemVer precedence for exact versions. Build metadata is ignored. */\nexport function compareExactServiceVersions(left: string, right: string): number {\n  const parse = (version: string) => {\n    const withoutBuild = version.split(\"+\", 1)[0] ?? version;\n    const separator = withoutBuild.indexOf(\"-\");\n    const core = separator === -1 ? withoutBuild : withoutBuild.slice(0, separator);\n    const prerelease = separator === -1 ? undefined : withoutBuild.slice(separator + 1);\n    const [major = \"0\", minor = \"0\", patch = \"0\"] = core.split(\".\");\n    return {\n      core: [BigInt(major), BigInt(minor), BigInt(patch)] as const,\n      prerelease: prerelease?.split(\".\") ?? [],\n    };\n  };\n  const a = parse(left);\n  const b = parse(right);\n  for (let index = 0; index < 3; index += 1) {\n    const x = a.core[index] ?? 0n;\n    const y = b.core[index] ?? 0n;\n    if (x !== y) return x < y ? -1 : 1;\n  }\n  if (a.prerelease.length === 0 || b.prerelease.length === 0) {\n    return a.prerelease.length === b.prerelease.length ? 0 : a.prerelease.length === 0 ? 1 : -1;\n  }\n  const count = Math.max(a.prerelease.length, b.prerelease.length);\n  for (let index = 0; index < count; index += 1) {\n    const x = a.prerelease[index];\n    const y = b.prerelease[index];\n    if (x === undefined || y === undefined) return x === undefined ? -1 : 1;\n    if (x === y) continue;\n    const xNumeric = /^\\d+$/.test(x);\n    const yNumeric = /^\\d+$/.test(y);\n    if (xNumeric && yNumeric) return BigInt(x) < BigInt(y) ? -1 : 1;\n    if (xNumeric !== yNumeric) return xNumeric ? -1 : 1;\n    return x < y ? -1 : 1;\n  }\n  return 0;\n}\n\nexport function decodeServiceState(value: unknown): ServiceState | undefined {\n  if (!isRecord(value)) return undefined;\n  const update = value.update === undefined ? undefined : decodeServiceUpdate(value.update);\n  if (\n    value.protocol !== SERVICE_LAUNCHER_PROTOCOL ||\n    typeof value.activeVersion !== \"string\" ||\n    !isExactServiceVersion(value.activeVersion) ||\n    (value.update !== undefined && update === undefined) ||\n    (update !== undefined &&\n      compareExactServiceVersions(update.targetVersion, update.fromVersion) <= 0) ||\n    (update?.status === \"pending\" && update.fromVersion !== value.activeVersion) ||\n    (update?.status === \"committed\" && update.targetVersion !== value.activeVersion) ||\n    ((update?.status === \"rolled-back\" || update?.status === \"failed\") &&\n      update.fromVersion !== value.activeVersion)\n  ) {\n    return undefined;\n  }\n  return {\n    protocol: SERVICE_LAUNCHER_PROTOCOL,\n    activeVersion: value.activeVersion,\n    ...(update === undefined ? {} : { update }),\n  };\n}\n\nexport function parseServiceState(value: string): ServiceState | undefined {\n  try {\n    return decodeServiceState(JSON.parse(value) as unknown);\n  } catch {\n    return undefined;\n  }\n}\n\n/** Detects an in-flight update across launcher protocol versions before replacing its state. */\nexport function serviceStateHasPendingUpdate(value: string): boolean {\n  try {\n    const parsed: unknown = JSON.parse(value);\n    return isRecord(parsed) && isRecord(parsed.update) && parsed.update.status === \"pending\";\n  } catch {\n    return false;\n  }\n}\n\nexport function decodeServiceLauncherContext(value: string): ServiceLauncherContext | undefined {\n  let parsed: unknown;\n  try {\n    parsed = JSON.parse(value) as unknown;\n  } catch {\n    return undefined;\n  }\n  if (\n    !isRecord(parsed) ||\n    parsed.protocol !== SERVICE_LAUNCHER_PROTOCOL ||\n    typeof parsed.childVersion !== \"string\" ||\n    !isExactServiceVersion(parsed.childVersion)\n  ) {\n    return undefined;\n  }\n  const update = parsed.update === undefined ? undefined : decodeServiceUpdate(parsed.update);\n  if (parsed.update !== undefined && update === undefined) return undefined;\n  const selectedVersion =\n    update?.status === \"pending\" || update?.status === \"committed\"\n      ? update.targetVersion\n      : update === undefined\n        ? parsed.childVersion\n        : update.fromVersion;\n  if (parsed.childVersion !== selectedVersion) {\n    return undefined;\n  }\n  return {\n    protocol: SERVICE_LAUNCHER_PROTOCOL,\n    childVersion: parsed.childVersion,\n    ...(update === undefined ? {} : { update }),\n  };\n}\n\nexport function decodeServiceLauncherChildMessage(\n  value: unknown,\n): ServiceLauncherChildMessage | undefined {\n  if (!isRecord(value)) return undefined;\n  if (\n    value.type === \"request-update\" &&\n    typeof value.targetVersion === \"string\" &&\n    typeof value.dbPath === \"string\"\n  ) {\n    return { type: value.type, targetVersion: value.targetVersion, dbPath: value.dbPath };\n  }\n  return value.type === \"prepared\" && typeof value.updateId === \"string\"\n    ? { type: value.type, updateId: value.updateId }\n    : undefined;\n}\n\nexport function decodeServiceLauncherParentMessage(\n  value: unknown,\n): ServiceLauncherParentMessage | undefined {\n  if (!isRecord(value)) return undefined;\n  if (value.type === \"update-rejected\" && typeof value.reason === \"string\") {\n    return { type: value.type, reason: value.reason };\n  }\n  if (value.type === \"update-accepted\" && typeof value.updateId === \"string\") {\n    return { type: value.type, updateId: value.updateId };\n  }\n  return value.type === \"committed\" && typeof value.updateId === \"string\"\n    ? { type: value.type, updateId: value.updateId }\n    : undefined;\n}\n","// @effect-diagnostics nodeBuiltinImport:off\n// @effect-diagnostics globalDate:off\n// @effect-diagnostics globalTimers:off\n// This file is shipped as a standalone bundle and copied to a stable path by\n// `synaptic-code service update`. Keep runtime imports limited to Node built-ins.\nimport * as NodeChildProcess from \"node:child_process\";\nimport * as NodeCrypto from \"node:crypto\";\nimport * as NodeFS from \"node:fs\";\nimport * as NodeFSP from \"node:fs/promises\";\nimport * as NodePath from \"node:path\";\n\nimport type {\n  PendingServiceUpdate,\n  ServiceLauncherChildMessage,\n  ServiceLauncherContext,\n  ServiceLauncherParentMessage,\n  ServiceState,\n  ServiceUpdateRecord,\n} from \"./cloud/serviceProtocol.ts\";\nimport {\n  compareExactServiceVersions,\n  decodeServiceLauncherChildMessage,\n  isExactServiceVersion,\n  parseServiceState,\n  SERVICE_LAUNCHER_CONTEXT_ENV,\n  SERVICE_LAUNCHER_PROTOCOL,\n  SERVICE_STATE_FILE,\n  SERVICE_STOP_MARKER_FILE,\n} from \"./cloud/serviceProtocol.ts\";\n\nconst HANDOFF_DELAY_MS = 2_000;\nconst PREPARED_TIMEOUT_MS = 120_000;\nconst TERMINATE_GRACE_MS = 5_000;\n\ntype TerminalStatus = \"committed\" | \"rolled-back\" | \"failed\";\ntype ChildRole = \"active\" | \"trial\";\n\ninterface ManagedChild {\n  readonly version: string;\n  role: ChildRole;\n  readonly process: NodeChildProcess.ChildProcess;\n}\n\nconst runtimePaths = (baseDir: string, version: string) => {\n  const versionDir = NodePath.join(baseDir, \"runtime\", \"versions\", version);\n  return {\n    versionDir,\n    entryPath: NodePath.join(versionDir, \"node_modules\", \"@dmccore\", \"code\", \"dist\", \"bin.mjs\"),\n    sentinelPath: NodePath.join(versionDir, \".install-complete\"),\n  };\n};\n\n/** SQLite persists across the main file plus its WAL and shared-memory sidecars. */\nconst DB_FILE_SUFFIXES = [\"\", \"-wal\", \"-shm\"] as const;\nconst RESTORE_MARKER = \".restore-pending\";\n\nconst databaseBackupDir = (baseDir: string, updateId: string) =>\n  NodePath.join(baseDir, \"runtime\", \"db-backup\", updateId);\n\nconst databaseBackupFile = (backupDir: string, suffix: (typeof DB_FILE_SUFFIXES)[number]) =>\n  NodePath.join(backupDir, suffix === \"\" ? \"database\" : `database${suffix}`);\n\nasync function pathExists(target: string): Promise<boolean> {\n  try {\n    await NodeFSP.access(target);\n    return true;\n  } catch (cause) {\n    if (cause instanceof Error && \"code\" in cause && cause.code === \"ENOENT\") return false;\n    throw cause;\n  }\n}\n\nasync function syncFile(filePath: string): Promise<void> {\n  const handle = await NodeFSP.open(filePath, \"r\");\n  try {\n    await handle.sync();\n  } finally {\n    await handle.close();\n  }\n}\n\nasync function syncDirectory(directory: string): Promise<void> {\n  const handle = await NodeFSP.open(directory, \"r\");\n  try {\n    await handle.sync();\n  } finally {\n    await handle.close();\n  }\n}\n\n/**\n * Snapshots the database once per update before the first trial. A completed\n * backup is never overwritten because a restarted launcher may be looking at\n * database writes from an earlier attempt by the same trial.\n */\nasync function backupDatabaseOnce(baseDir: string, pending: PendingServiceUpdate): Promise<void> {\n  const backupDir = databaseBackupDir(baseDir, pending.id);\n  if (await pathExists(backupDir)) return;\n\n  const stagingDir = `${backupDir}.staging`;\n  await NodeFSP.rm(stagingDir, { recursive: true, force: true });\n  await NodeFSP.mkdir(stagingDir, { recursive: true, mode: 0o700 });\n  try {\n    for (const suffix of DB_FILE_SUFFIXES) {\n      const source = `${pending.dbPath}${suffix}`;\n      if (suffix !== \"\" && !(await pathExists(source))) continue;\n      const destination = databaseBackupFile(stagingDir, suffix);\n      await NodeFSP.copyFile(source, destination);\n      await syncFile(destination);\n    }\n    await NodeFSP.rename(stagingDir, backupDir);\n    await syncDirectory(NodePath.dirname(backupDir));\n  } catch (cause) {\n    await NodeFSP.rm(stagingDir, { recursive: true, force: true }).catch(() => undefined);\n    throw cause;\n  }\n}\n\nconst restoreMarkerPath = (baseDir: string, updateId: string) =>\n  NodePath.join(databaseBackupDir(baseDir, updateId), RESTORE_MARKER);\n\nconst databaseRestorePending = (baseDir: string, pending: PendingServiceUpdate) =>\n  pathExists(restoreMarkerPath(baseDir, pending.id));\n\n/** Mark rollback before changing live files so launcher recovery cannot boot a partial restore. */\nasync function markDatabaseRestorePending(backupDir: string): Promise<void> {\n  const markerPath = NodePath.join(backupDir, RESTORE_MARKER);\n  if (!(await pathExists(markerPath))) {\n    const handle = await NodeFSP.open(markerPath, \"wx\", 0o600);\n    try {\n      await handle.sync();\n    } finally {\n      await handle.close();\n    }\n    await syncDirectory(backupDir);\n  }\n}\n\n/** Restore is retryable after any process crash while the backup directory remains. */\nasync function restoreDatabaseBackup(\n  baseDir: string,\n  pending: PendingServiceUpdate,\n): Promise<void> {\n  const backupDir = databaseBackupDir(baseDir, pending.id);\n  if (!(await pathExists(backupDir))) return;\n\n  await markDatabaseRestorePending(backupDir);\n  for (const suffix of DB_FILE_SUFFIXES) {\n    const target = `${pending.dbPath}${suffix}`;\n    const source = databaseBackupFile(backupDir, suffix);\n    if (await pathExists(source)) {\n      await NodeFSP.copyFile(source, target);\n      await syncFile(target);\n    } else {\n      await NodeFSP.rm(target, { force: true });\n    }\n  }\n  await syncDirectory(NodePath.dirname(pending.dbPath));\n}\n\nasync function discardDatabaseBackup(baseDir: string, updateId: string): Promise<void> {\n  const backupDir = databaseBackupDir(baseDir, updateId);\n  if (!(await pathExists(backupDir))) return;\n  await NodeFSP.rm(backupDir, { recursive: true, force: true });\n  await syncDirectory(NodePath.dirname(backupDir));\n}\n\nexport async function readServiceState(filePath: string): Promise<ServiceState> {\n  const contents = await NodeFSP.readFile(filePath, \"utf8\");\n  const state = parseServiceState(contents);\n  if (state === undefined) throw new Error(\"Service state is invalid or unsupported.\");\n  return state;\n}\n\n/** Durable same-directory replacement used for every runtime state transition. */\nexport async function writeServiceState(filePath: string, state: ServiceState): Promise<void> {\n  const directory = NodePath.dirname(filePath);\n  await NodeFSP.mkdir(directory, { recursive: true, mode: 0o700 });\n  const tempPath = NodePath.join(\n    directory,\n    `.${NodePath.basename(filePath)}.${process.pid}.${NodeCrypto.randomUUID()}`,\n  );\n  let handle: NodeFSP.FileHandle | undefined;\n  try {\n    handle = await NodeFSP.open(tempPath, \"wx\", 0o600);\n    await handle.writeFile(`${JSON.stringify(state, null, 2)}\\n`, \"utf8\");\n    await handle.sync();\n    await handle.close();\n    handle = undefined;\n    await NodeFSP.rename(tempPath, filePath);\n    const directoryHandle = await NodeFSP.open(directory, \"r\");\n    try {\n      await directoryHandle.sync();\n    } finally {\n      await directoryHandle.close();\n    }\n  } finally {\n    await handle?.close().catch(() => undefined);\n    await NodeFSP.rm(tempPath, { force: true }).catch(() => undefined);\n  }\n}\n\nasync function runtimeExists(baseDir: string, version: string): Promise<boolean> {\n  const paths = runtimePaths(baseDir, version);\n  try {\n    const [entry, sentinel] = await Promise.all([\n      NodeFSP.stat(paths.entryPath),\n      NodeFSP.readFile(paths.sentinelPath, \"utf8\"),\n    ]);\n    return entry.isFile() && sentinel.trim() === version;\n  } catch {\n    return false;\n  }\n}\n\nfunction terminalUpdate<S extends TerminalStatus>(input: {\n  readonly pending: PendingServiceUpdate;\n  readonly status: S;\n  readonly reason?: string;\n}): Exclude<ServiceUpdateRecord, PendingServiceUpdate> & { readonly status: S } {\n  return {\n    id: input.pending.id,\n    fromVersion: input.pending.fromVersion,\n    targetVersion: input.pending.targetVersion,\n    status: input.status,\n    ...(input.reason === undefined ? {} : { reason: input.reason }),\n  };\n}\n\nfunction sendMessage(\n  child: NodeChildProcess.ChildProcess,\n  message: ServiceLauncherParentMessage,\n): Promise<void> {\n  return new Promise((resolve, reject) => {\n    if (!child.connected || child.send === undefined) {\n      reject(new Error(\"service child IPC is disconnected.\"));\n      return;\n    }\n    child.send(message, (error) => (error === null ? resolve() : reject(error)));\n  });\n}\n\nfunction waitForExit(child: NodeChildProcess.ChildProcess): Promise<void> {\n  if (child.exitCode !== null || child.signalCode !== null) return Promise.resolve();\n  return new Promise((resolve) => child.once(\"exit\", () => resolve()));\n}\n\nasync function terminateChild(\n  child: NodeChildProcess.ChildProcess,\n  signal: NodeJS.Signals = \"SIGTERM\",\n): Promise<void> {\n  if (child.exitCode !== null || child.signalCode !== null) return;\n  child.kill(signal);\n  const force = setTimeout(() => child.kill(\"SIGKILL\"), TERMINATE_GRACE_MS);\n  try {\n    await waitForExit(child);\n  } finally {\n    clearTimeout(force);\n  }\n}\n\nconst stopMarkerPath = (baseDir: string) =>\n  NodePath.join(baseDir, \"runtime\", SERVICE_STOP_MARKER_FILE);\n\nexport class Launcher {\n  readonly #baseDir: string;\n  readonly #statePath: string;\n  #state: ServiceState;\n  #child: ManagedChild | null = null;\n  #timer: NodeJS.Timeout | undefined;\n  #transitions: Promise<void> = Promise.resolve();\n  #stopRequested = false;\n  #stopping = false;\n  #done = false;\n  readonly #completion = Promise.withResolvers<void>();\n\n  constructor(baseDir: string, state: ServiceState) {\n    this.#baseDir = baseDir;\n    this.#statePath = NodePath.join(baseDir, \"runtime\", SERVICE_STATE_FILE);\n    this.#state = state;\n  }\n\n  async run(): Promise<void> {\n    const onSigterm = () => void this.stop(\"SIGTERM\");\n    const onSigint = () => void this.stop(\"SIGINT\");\n    process.once(\"SIGTERM\", onSigterm);\n    process.once(\"SIGINT\", onSigint);\n    try {\n      this.#enqueue(() => this.#recover());\n      await this.#completion.promise;\n    } finally {\n      process.off(\"SIGTERM\", onSigterm);\n      process.off(\"SIGINT\", onSigint);\n    }\n  }\n\n  #enqueue(transition: () => Promise<void>): void {\n    this.#transitions = this.#transitions\n      .then(transition, transition)\n      .catch((cause: unknown) =>\n        this.#fatal(cause instanceof Error ? cause : new Error(String(cause))),\n      );\n  }\n\n  async #fatal(error: Error): Promise<void> {\n    if (this.#done) return;\n    this.#done = true;\n    this.#stopping = true;\n    this.#clearTimer();\n    const child = this.#child?.process;\n    this.#child = null;\n    if (child !== undefined) await terminateChild(child);\n    this.#completion.reject(error);\n  }\n\n  async stop(signal: NodeJS.Signals): Promise<void> {\n    // This must happen synchronously at signal receipt. A queued update\n    // transition may already be terminating the active child, and that child\n    // needs to see the marker in its shutdown finalizer. KillMode=mixed also\n    // ensures systemd signals the launcher before the rest of the cgroup.\n    try {\n      NodeFS.writeFileSync(stopMarkerPath(this.#baseDir), \"\", { mode: 0o600 });\n    } catch {\n      // Err toward keeping the tunnel; the next link or unlink reconciles it.\n    }\n    if (this.#stopRequested || this.#stopping) {\n      await this.#completion.promise.catch(() => undefined);\n      return;\n    }\n    this.#stopRequested = true;\n    this.#clearTimer();\n    this.#enqueue(async () => {\n      // Let an update transition already in progress start its replacement\n      // before this queued stop tears it down. That replacement owns the\n      // pre-activation tunnel cleanup path and observes the marker above.\n      this.#stopping = true;\n      const child = this.#child?.process;\n      this.#child = null;\n      if (child !== undefined) await terminateChild(child, signal);\n      this.#done = true;\n      this.#completion.resolve();\n    });\n    await this.#completion.promise.catch(() => undefined);\n  }\n\n  #clearTimer(): void {\n    clearTimeout(this.#timer);\n    this.#timer = undefined;\n  }\n\n  async #recover(): Promise<void> {\n    // A fresh launcher means servers are running again: any stop marker from\n    // a previous explicit stop is stale and must not make a future update\n    // handoff release its tunnel.\n    await NodeFSP.rm(stopMarkerPath(this.#baseDir), { force: true }).catch(() => undefined);\n    const update = this.#state.update;\n    if (update?.status !== \"pending\") {\n      if (update !== undefined) {\n        await discardDatabaseBackup(this.#baseDir, update.id).catch(() => undefined);\n      }\n      await this.#startChild(this.#state.activeVersion, \"active\", update);\n      return;\n    }\n    if (await databaseRestorePending(this.#baseDir, update)) {\n      await this.#returnToPrevious(update, \"failed\", \"rollback-interrupted\");\n      return;\n    }\n    if (!(await runtimeExists(this.#baseDir, update.targetVersion))) {\n      await this.#returnToPrevious(update, \"failed\", \"target-runtime-missing\");\n      return;\n    }\n    await this.#startTrial(update);\n  }\n\n  async #startTrial(pending: PendingServiceUpdate): Promise<void> {\n    // The previous child is dead here, so all three SQLite files are quiescent.\n    try {\n      await backupDatabaseOnce(this.#baseDir, pending);\n    } catch {\n      await this.#returnToPrevious(pending, \"failed\", \"db-backup-failed\");\n      return;\n    }\n    try {\n      await this.#startChild(pending.targetVersion, \"trial\", pending);\n    } catch {\n      await this.#returnToPrevious(pending, \"failed\", \"candidate-start-failed\");\n    }\n  }\n\n  async #startChild(version: string, role: ChildRole, update?: ServiceUpdateRecord): Promise<void> {\n    if (this.#stopping) return;\n    if (!(await runtimeExists(this.#baseDir, version))) {\n      throw new Error(`Selected @dmccore/code@${version} runtime is missing or incomplete.`);\n    }\n    if (this.#stopping) return;\n    const paths = runtimePaths(this.#baseDir, version);\n    const context: ServiceLauncherContext = {\n      protocol: SERVICE_LAUNCHER_PROTOCOL,\n      childVersion: version,\n      ...(update === undefined ? {} : { update }),\n    };\n    const child = NodeChildProcess.spawn(process.execPath, [paths.entryPath, \"serve\"], {\n      env: { ...process.env, [SERVICE_LAUNCHER_CONTEXT_ENV]: JSON.stringify(context) },\n      stdio: [\"inherit\", \"inherit\", \"inherit\", \"ipc\"],\n    });\n    await new Promise<void>((resolve, reject) => {\n      const onError = (error: Error) => reject(error);\n      child.once(\"error\", onError);\n      child.once(\"spawn\", () => {\n        child.removeListener(\"error\", onError);\n        child.on(\"error\", (error) => this.#enqueue(() => Promise.reject(error)));\n        resolve();\n      });\n    });\n    if (this.#stopping) {\n      await terminateChild(child);\n      return;\n    }\n\n    const managed: ManagedChild = {\n      version,\n      role,\n      process: child,\n    };\n    this.#child = managed;\n    child.on(\"message\", (value) => {\n      const message = decodeServiceLauncherChildMessage(value);\n      if (message !== undefined) this.#enqueue(() => this.#handleMessage(managed, message));\n    });\n    child.once(\"exit\", (code, signal) =>\n      this.#enqueue(() => this.#handleExit(managed, code, signal)),\n    );\n\n    if (role === \"trial\") {\n      this.#timer = setTimeout(\n        () => this.#enqueue(() => this.#handlePreparedTimeout(managed)),\n        PREPARED_TIMEOUT_MS,\n      );\n    }\n  }\n\n  async #handleMessage(child: ManagedChild, message: ServiceLauncherChildMessage): Promise<void> {\n    if (this.#child !== child || this.#stopping) return;\n    if (message.type === \"request-update\") {\n      await this.#handleUpdateRequest(child, message);\n      return;\n    }\n    await this.#handlePrepared(child, message.updateId);\n  }\n\n  async #handleUpdateRequest(\n    child: ManagedChild,\n    message: Extract<ServiceLauncherChildMessage, { readonly type: \"request-update\" }>,\n  ): Promise<void> {\n    const reject = (reason: string) =>\n      sendMessage(child.process, { type: \"update-rejected\", reason });\n    if (child.role !== \"active\") {\n      await reject(\"Only the active server can request an update.\");\n      return;\n    }\n    if (child.version !== this.#state.activeVersion) {\n      await reject(\"The requesting server is not the selected active version.\");\n      return;\n    }\n    if (this.#state.update?.status === \"pending\") {\n      await reject(\"Another server update is already pending.\");\n      return;\n    }\n    if (!isExactServiceVersion(message.targetVersion)) {\n      await reject(\"The requested target is not an exact version.\");\n      return;\n    }\n    if (compareExactServiceVersions(message.targetVersion, child.version) <= 0) {\n      await reject(\"Remote updates must select a newer server version.\");\n      return;\n    }\n    if (!NodePath.isAbsolute(message.dbPath)) {\n      await reject(\"The requested database path is not absolute.\");\n      return;\n    }\n    if (!(await runtimeExists(this.#baseDir, message.targetVersion))) {\n      await reject(\"The requested target runtime is missing or incomplete.\");\n      return;\n    }\n\n    const pending: PendingServiceUpdate = {\n      id: NodeCrypto.randomUUID(),\n      fromVersion: child.version,\n      targetVersion: message.targetVersion,\n      dbPath: message.dbPath,\n      status: \"pending\",\n    };\n    const next: ServiceState = { ...this.#state, update: pending };\n    await writeServiceState(this.#statePath, next);\n    this.#state = next;\n    await sendMessage(child.process, { type: \"update-accepted\", updateId: pending.id });\n    this.#timer = setTimeout(() => this.#enqueue(() => this.#beginTrial(child)), HANDOFF_DELAY_MS);\n  }\n\n  async #beginTrial(child: ManagedChild): Promise<void> {\n    const pending = this.#state.update;\n    if (this.#child !== child || child.role !== \"active\" || pending?.status !== \"pending\") {\n      return;\n    }\n    this.#timer = undefined;\n    this.#child = null;\n    await terminateChild(child.process);\n    await this.#startTrial(pending);\n  }\n\n  async #handlePrepared(child: ManagedChild, updateId: string): Promise<void> {\n    const pending = this.#state.update;\n    if (\n      child.role !== \"trial\" ||\n      pending?.status !== \"pending\" ||\n      pending.id !== updateId ||\n      pending.targetVersion !== child.version\n    ) {\n      if (child.role === \"trial\" && pending?.status === \"pending\") {\n        await this.#returnToPrevious(pending, \"rolled-back\", \"invalid-prepared\", child);\n        return;\n      }\n      throw new Error(\"Trial child reported prepared for an unexpected update.\");\n    }\n    this.#clearTimer();\n    const committed = terminalUpdate({ pending, status: \"committed\" });\n    const next: ServiceState = {\n      ...this.#state,\n      activeVersion: pending.targetVersion,\n      update: committed,\n    };\n    await writeServiceState(this.#statePath, next);\n    this.#state = next;\n    child.role = \"active\";\n    await discardDatabaseBackup(this.#baseDir, committed.id).catch(() => undefined);\n    await sendMessage(child.process, { type: \"committed\", updateId: committed.id });\n  }\n\n  async #handlePreparedTimeout(child: ManagedChild): Promise<void> {\n    const pending = this.#state.update;\n    if (this.#child !== child || child.role !== \"trial\" || pending?.status !== \"pending\") {\n      return;\n    }\n    this.#timer = undefined;\n    await this.#returnToPrevious(pending, \"rolled-back\", \"prepared-timeout\", child);\n  }\n\n  async #handleExit(\n    child: ManagedChild,\n    code: number | null,\n    signal: NodeJS.Signals | null,\n  ): Promise<void> {\n    if (this.#child !== child || this.#stopping) return;\n    this.#child = null;\n    if (child.role === \"trial\") {\n      this.#clearTimer();\n      const pending = this.#state.update;\n      if (pending?.status !== \"pending\") {\n        throw new Error(\"Trial child exited without matching pending state.\");\n      }\n      await this.#returnToPrevious(\n        pending,\n        \"rolled-back\",\n        `candidate-exited:${String(code ?? signal ?? \"unknown\")}`,\n      );\n      return;\n    }\n\n    this.#clearTimer();\n    const pending = this.#state.update;\n    if (pending?.status === \"pending\") {\n      await this.#startTrial(pending);\n      return;\n    }\n    throw new Error(`Active child exited unexpectedly (${String(code ?? signal ?? \"unknown\")}).`);\n  }\n\n  async #returnToPrevious(\n    pending: PendingServiceUpdate,\n    status: \"rolled-back\" | \"failed\",\n    reason: string,\n    child?: ManagedChild,\n  ): Promise<void> {\n    if (child !== undefined) {\n      this.#child = null;\n      await terminateChild(child.process);\n    }\n    await restoreDatabaseBackup(this.#baseDir, pending);\n    const outcome = terminalUpdate({ pending, status, reason });\n    const next: ServiceState = {\n      ...this.#state,\n      activeVersion: pending.fromVersion,\n      update: outcome,\n    };\n    await writeServiceState(this.#statePath, next);\n    this.#state = next;\n    await discardDatabaseBackup(this.#baseDir, pending.id).catch(() => undefined);\n    await this.#startChild(next.activeVersion, \"active\", outcome);\n  }\n}\n\nasync function main(): Promise<void> {\n  const baseDir = process.env.SYNAPTIC_HOME?.trim();\n  if (baseDir === undefined || baseDir === \"\") {\n    throw new Error(\"SYNAPTIC_HOME is required by the Synaptic Code service launcher.\");\n  }\n  const statePath = NodePath.join(baseDir, \"runtime\", SERVICE_STATE_FILE);\n  const state = await readServiceState(statePath);\n  await new Launcher(baseDir, state).run();\n}\n\nif (import.meta.main) {\n  main().catch((cause: unknown) => {\n    const error = cause instanceof Error ? cause : new Error(String(cause));\n    process.stderr.write(`[service-launcher] ${error.message}\\n`);\n    process.exitCode = 1;\n  });\n}\n"],"mappings":"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"}