@absolutejs/deploy 0.11.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -50,6 +50,13 @@ await deployer.prune({ keep: 5 });
50
50
 
51
51
  // sunset the active process through the same process-manager contract
52
52
  await deployer.stop();
53
+
54
+ // control planes can make teardown win a publish/unpublish race
55
+ const controller = new AbortController();
56
+ const pending = deployer.deploy({ signal: controller.signal });
57
+ controller.abort();
58
+ await pending.catch(() => undefined);
59
+ await deployer.stop();
53
60
  ```
54
61
 
55
62
  ## v0.0.1 surface
@@ -281,6 +288,21 @@ await installCertificateOnTarget(target, cert, {
281
288
  });
282
289
  ```
283
290
 
291
+ For hosted platforms, customers can delegate only the ACME challenge instead
292
+ of granting access to their DNS account. Ask them to CNAME
293
+ `_acme-challenge.app.customer.com` into a validation zone you control, then
294
+ map the provider write while leaving propagation checks on the public name:
295
+
296
+ ```ts
297
+ const cert = await issueCertificate({
298
+ domains: ['app.customer.com'],
299
+ dnsProvider: platformValidationDns,
300
+ email: 'ops@platform.example',
301
+ mapDnsChallengeRecord: ({ domain }) =>
302
+ `${stableDomainToken(domain)}.acme.platform.example`,
303
+ });
304
+ ```
305
+
284
306
  `generateAccountKey()` / `exportAccount()` / `importAccount()`
285
307
  round-trip the ECDSA P-256 keypair + `kid` so cert renewals reuse
286
308
  the same account (avoids Let's Encrypt's account-creation rate
@@ -61,6 +61,8 @@ export type DeployContext = {
61
61
  processManager: ProcessManager;
62
62
  verify: VerifySpec | null;
63
63
  annotations: ReleaseAnnotations;
64
+ /** Optional cancellation signal shared with custom steps and verify hooks. */
65
+ signal?: AbortSignal;
64
66
  /** When `true`, steps log what they WOULD do via hooks.onLog and do not mutate the target. */
65
67
  dryRun: boolean;
66
68
  };
@@ -89,6 +91,8 @@ type ResolvedHooks = Required<{
89
91
  [K in keyof DeployHooks]: NonNullable<DeployHooks[K]>;
90
92
  }>;
91
93
  export type DeployOptions = {
94
+ /** Cancel before the next pipeline step; custom steps may observe it directly. */
95
+ signal?: AbortSignal;
92
96
  /** Per-release annotations stored alongside the release dir as `.deploy-meta.json`. */
93
97
  annotations?: ReleaseAnnotations;
94
98
  /**
package/dist/index.js CHANGED
@@ -455,6 +455,7 @@ var createDeployer = (options) => {
455
455
  env,
456
456
  hooks,
457
457
  processManager,
458
+ signal: opts.signal,
458
459
  releaseId,
459
460
  releasePath: `${releasesPath}/${releaseId}`,
460
461
  source: options.source,
@@ -491,7 +492,8 @@ var createDeployer = (options) => {
491
492
  const runSteps = async (steps, releaseId, runOpts) => {
492
493
  const ctx = buildCtx(releaseId, {
493
494
  annotations: runOpts.annotations,
494
- dryRun: runOpts.dryRun
495
+ dryRun: runOpts.dryRun,
496
+ signal: runOpts.signal
495
497
  });
496
498
  const stepDurations = [];
497
499
  const startedAt = clock();
@@ -504,6 +506,7 @@ var createDeployer = (options) => {
504
506
  status: "in-progress"
505
507
  };
506
508
  for (const step of steps) {
509
+ ctx.signal?.throwIfAborted();
507
510
  if (completedSteps.includes(step.name) && step.name !== "verify") {
508
511
  stepDurations.push({ durationMs: 0, name: step.name, skipped: true });
509
512
  continue;
@@ -518,6 +521,7 @@ var createDeployer = (options) => {
518
521
  }
519
522
  try {
520
523
  await step.run(ctx);
524
+ ctx.signal?.throwIfAborted();
521
525
  } catch (error) {
522
526
  const err = error instanceof Error ? error : new Error(String(error));
523
527
  record.status = "failed";
@@ -578,14 +582,16 @@ var createDeployer = (options) => {
578
582
  return runSteps(options.steps ?? defaultBunPipeline(), deployOpts.resumeReleaseId, {
579
583
  alreadyCompleted: prior.completedSteps,
580
584
  annotations: prior.annotations ?? annotations,
581
- dryRun
585
+ dryRun,
586
+ signal: deployOpts.signal
582
587
  });
583
588
  }
584
589
  const releaseId = makeReleaseId(clock);
585
590
  return runSteps(options.steps ?? defaultBunPipeline(), releaseId, {
586
591
  alreadyCompleted: [],
587
592
  annotations,
588
- dryRun
593
+ dryRun,
594
+ signal: deployOpts.signal
589
595
  });
590
596
  },
591
597
  dispose: async () => {
@@ -659,5 +665,5 @@ export {
659
665
  bareManager
660
666
  };
661
667
 
662
- //# debugId=36BAF079BB9C009564756E2164756E21
668
+ //# debugId=345BC6BD58648C9564756E2164756E21
663
669
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -4,9 +4,9 @@
4
4
  "sourcesContent": [
5
5
  "/**\n * Target interface + bundled adapters (localTarget, sshTarget).\n *\n * A Target is the narrowest abstraction over \"a place I can deploy to\":\n *\n * - `exec(cmd, opts?)` — run a shell command, capture stdout/stderr/exitCode.\n * - `upload(localPath, remotePath, opts?)` — copy a local file or directory\n * to the target. Implementation is free to use whatever is fast (rsync,\n * scp, mv).\n * - `close?()` — optional teardown.\n *\n * Two adapters are bundled:\n *\n * - `localTarget` runs in a temp directory on the local filesystem. Useful\n * for tests and for \"deploy\" workflows that happen on the same host.\n * - `sshTarget` shells out to the system `ssh` and `rsync` binaries. No\n * `ssh2` npm dependency — the controller machine just needs `ssh` and\n * (optionally) `rsync` in PATH, which is universal on Mac/Linux/WSL.\n *\n * Provider-specific targets (Cloudflare Workers HTTP API, Fly Machines API,\n * AWS Fargate) don't fit \"exec + upload\" and ship as siblings later.\n */\n\nimport { mkdir } from 'node:fs/promises';\nimport { join } from 'node:path';\n\nexport type ExecOptions = {\n\t/** Working directory on the target. Default: target's root. */\n\tcwd?: string;\n\t/** Env vars to set for this command (merged onto target.env). */\n\tenv?: Record<string, string>;\n\t/** Hard kill after this many ms. Default 600_000 (10 min). 0 disables. */\n\ttimeoutMs?: number;\n\t/** Pipe stdout/stderr through here as it streams (lines, newline-stripped). */\n\tonLog?: (line: string, stream: 'stdout' | 'stderr') => void;\n\t/** Stdin payload — a string is written verbatim. */\n\tstdin?: string;\n};\n\nexport type ExecResult = {\n\tstdout: string;\n\tstderr: string;\n\texitCode: number;\n};\n\nexport type UploadOptions = {\n\t/** Exclude paths matching these globs from a directory upload. */\n\texclude?: string[];\n\t/** When uploading a directory, delete remote files not present locally. */\n\tdeleteOrphans?: boolean;\n};\n\nexport type Target = {\n\t/** Human-readable description (e.g. \"ssh root@droplet-1.example.com\"). */\n\treadonly description: string;\n\texec: (cmd: string, opts?: ExecOptions) => Promise<ExecResult>;\n\tupload: (localPath: string, remotePath: string, opts?: UploadOptions) => Promise<void>;\n\tclose?: () => Promise<void>;\n};\n\n// -----------------------------------------------------------------------------\n// localTarget\n// -----------------------------------------------------------------------------\n\nexport type LocalTargetOptions = {\n\t/** Root directory the target operates in. Created if missing. */\n\troot: string;\n\t/** Env merged into every exec. */\n\tenv?: Record<string, string>;\n};\n\nconst decodeChunks = async (\n\treader: ReadableStream<Uint8Array> | null,\n\tonLine: ((line: string) => void) | undefined,\n): Promise<string> => {\n\tif (!reader) return '';\n\tconst decoder = new TextDecoder();\n\tlet buffer = '';\n\tlet collected = '';\n\tconst stream = reader.getReader();\n\ttry {\n\t\twhile (true) {\n\t\t\tconst { done, value } = await stream.read();\n\t\t\tif (done) break;\n\t\t\tconst chunk = decoder.decode(value, { stream: true });\n\t\t\tcollected += chunk;\n\t\t\tif (!onLine) continue;\n\t\t\tbuffer += chunk;\n\t\t\tlet newline = buffer.indexOf('\\n');\n\t\t\twhile (newline !== -1) {\n\t\t\t\tconst line = buffer.slice(0, newline).replace(/\\r$/, '');\n\t\t\t\tif (line.length > 0) onLine(line);\n\t\t\t\tbuffer = buffer.slice(newline + 1);\n\t\t\t\tnewline = buffer.indexOf('\\n');\n\t\t\t}\n\t\t}\n\t\tconst tail = decoder.decode();\n\t\tcollected += tail;\n\t\tif (onLine && (buffer + tail).length > 0) onLine((buffer + tail).replace(/\\r$/, ''));\n\t} finally {\n\t\tstream.releaseLock();\n\t}\n\treturn collected;\n};\n\nconst runSpawn = async (\n\targv: string[],\n\toptions: {\n\t\tcwd?: string;\n\t\tenv?: Record<string, string>;\n\t\ttimeoutMs?: number;\n\t\tonLog?: ExecOptions['onLog'];\n\t\tstdin?: string;\n\t},\n): Promise<ExecResult> => {\n\tconst proc = Bun.spawn(argv, {\n\t\tcwd: options.cwd,\n\t\tenv: options.env,\n\t\tstderr: 'pipe',\n\t\tstdin: options.stdin === undefined ? 'ignore' : 'pipe',\n\t\tstdout: 'pipe',\n\t});\n\n\tif (options.stdin !== undefined && proc.stdin) {\n\t\t// Bun.spawn returns a FileSink for piped stdin — `write` + `end`, not a\n\t\t// WritableStream. (We use a permissive cast because @types/bun's\n\t\t// Subprocess.stdin discriminant flips based on the stdin generic.)\n\t\tconst sink = proc.stdin as unknown as {\n\t\t\twrite: (chunk: string | Uint8Array) => number | Promise<number>;\n\t\t\tend: () => void | Promise<void>;\n\t\t};\n\t\tconst wrote = sink.write(options.stdin);\n\t\tif (wrote && typeof (wrote as Promise<number>).then === 'function') {\n\t\t\tawait wrote;\n\t\t}\n\t\tconst ended = sink.end();\n\t\tif (ended && typeof (ended as Promise<void>).then === 'function') {\n\t\t\tawait ended;\n\t\t}\n\t}\n\n\tconst timeout = options.timeoutMs ?? 600_000;\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\tif (timeout > 0) {\n\t\ttimer = setTimeout(() => {\n\t\t\ttry { proc.kill(); } catch { /* already gone */ }\n\t\t}, timeout);\n\t}\n\n\tconst stdoutPromise = decodeChunks(\n\t\tproc.stdout as unknown as ReadableStream<Uint8Array>,\n\t\toptions.onLog ? (line) => options.onLog!(line, 'stdout') : undefined,\n\t);\n\tconst stderrPromise = decodeChunks(\n\t\tproc.stderr as unknown as ReadableStream<Uint8Array>,\n\t\toptions.onLog ? (line) => options.onLog!(line, 'stderr') : undefined,\n\t);\n\n\tconst [stdout, stderr, exitCode] = await Promise.all([\n\t\tstdoutPromise,\n\t\tstderrPromise,\n\t\tproc.exited,\n\t]);\n\tif (timer) clearTimeout(timer);\n\n\treturn { exitCode: exitCode ?? -1, stderr, stdout };\n};\n\nexport const localTarget = (options: LocalTargetOptions): Target => {\n\tconst baseEnv = { ...options.env };\n\tconst ensureRoot = async () => { await mkdir(options.root, { recursive: true }); };\n\n\treturn {\n\t\tdescription: `local ${options.root}`,\n\t\texec: async (cmd, opts) => {\n\t\t\tawait ensureRoot();\n\t\t\treturn runSpawn(['sh', '-c', cmd], {\n\t\t\t\tcwd: opts?.cwd ?? options.root,\n\t\t\t\tenv: { ...process.env, ...baseEnv, ...(opts?.env ?? {}) } as Record<string, string>,\n\t\t\t\tonLog: opts?.onLog,\n\t\t\t\tstdin: opts?.stdin,\n\t\t\t\ttimeoutMs: opts?.timeoutMs,\n\t\t\t});\n\t\t},\n\t\tupload: async (localPath, remotePath, opts) => {\n\t\t\tawait ensureRoot();\n\t\t\tconst dest = remotePath.startsWith('/') ? remotePath : join(options.root, remotePath);\n\t\t\tconst argv = ['rsync', '-a'];\n\t\t\tif (opts?.deleteOrphans) argv.push('--delete');\n\t\t\tfor (const pattern of opts?.exclude ?? []) argv.push('--exclude', pattern);\n\t\t\t// rsync semantics: a trailing slash on the source copies *contents*; without it the dir itself is nested.\n\t\t\targv.push(localPath, dest);\n\t\t\tconst result = await runSpawn(argv, { timeoutMs: 600_000 });\n\t\t\tif (result.exitCode !== 0) {\n\t\t\t\tthrow new Error(`local upload failed (exit ${result.exitCode}): ${result.stderr || result.stdout}`);\n\t\t\t}\n\t\t},\n\t};\n};\n\n// -----------------------------------------------------------------------------\n// sshTarget\n// -----------------------------------------------------------------------------\n\nexport type SshTargetOptions = {\n\t/** Hostname or IP of the remote. */\n\thost: string;\n\t/** Login user. Default `root`. */\n\tuser?: string;\n\t/** SSH port. Default 22. */\n\tport?: number;\n\t/** Path to SSH identity file. Default: ssh's own search. */\n\tidentity?: string;\n\t/** Extra flags appended to every `ssh` invocation. */\n\tsshFlags?: string[];\n\t/**\n\t * Use rsync for `upload`. Default true. When false, falls back to `scp`\n\t * which is universal but doesn't support delete / exclude.\n\t */\n\trsync?: boolean;\n\t/**\n\t * Env vars to forward via `ssh -o SendEnv=...`. Most remote sshd configs\n\t * accept only `LANG` and `LC_*` by default; for app env vars use the\n\t * step `env` option instead, which prepends `KEY=value` to the command.\n\t */\n\tforwardEnv?: string[];\n};\n\nconst sshTargetString = (options: SshTargetOptions): string => {\n\tconst user = options.user ?? 'root';\n\treturn `${user}@${options.host}`;\n};\n\nconst sshBaseFlags = (options: SshTargetOptions): string[] => {\n\tconst flags: string[] = [];\n\tif (options.port !== undefined && options.port !== 22) flags.push('-p', String(options.port));\n\tif (options.identity !== undefined) flags.push('-i', options.identity);\n\t// Never get stuck on a host-key prompt; treat unknown hosts as a fatal config issue rather than a UX detour.\n\tflags.push('-o', 'BatchMode=yes', '-o', 'StrictHostKeyChecking=accept-new');\n\tfor (const flag of options.sshFlags ?? []) flags.push(flag);\n\treturn flags;\n};\n\nconst shellQuote = (value: string): string => `'${value.replace(/'/g, `'\\\\''`)}'`;\n\nconst buildRemoteCmd = (cmd: string, opts: ExecOptions | undefined): string => {\n\tconst env = opts?.env;\n\tconst envPrefix = env\n\t\t? Object.entries(env).map(([k, v]) => `${k}=${shellQuote(v)}`).join(' ') + ' '\n\t\t: '';\n\tif (opts?.cwd) {\n\t\treturn `cd ${shellQuote(opts.cwd)} && ${envPrefix}${cmd}`;\n\t}\n\treturn `${envPrefix}${cmd}`;\n};\n\nexport const sshTarget = (options: SshTargetOptions): Target => {\n\tconst remote = sshTargetString(options);\n\tconst useRsync = options.rsync ?? true;\n\n\treturn {\n\t\tdescription: `ssh ${remote}${options.port && options.port !== 22 ? `:${options.port}` : ''}`,\n\t\texec: async (cmd, opts) => {\n\t\t\tconst argv = ['ssh', ...sshBaseFlags(options)];\n\t\t\tfor (const name of options.forwardEnv ?? []) argv.push('-o', `SendEnv=${name}`);\n\t\t\targv.push(remote, buildRemoteCmd(cmd, opts));\n\t\t\treturn runSpawn(argv, {\n\t\t\t\tonLog: opts?.onLog,\n\t\t\t\tstdin: opts?.stdin,\n\t\t\t\ttimeoutMs: opts?.timeoutMs,\n\t\t\t});\n\t\t},\n\t\tupload: async (localPath, remotePath, opts) => {\n\t\t\tif (useRsync) {\n\t\t\t\tconst sshCmd = ['ssh', ...sshBaseFlags(options)].map((part) => part.includes(' ') ? `'${part}'` : part).join(' ');\n\t\t\t\tconst argv = ['rsync', '-az', '-e', sshCmd];\n\t\t\t\tif (opts?.deleteOrphans) argv.push('--delete');\n\t\t\t\tfor (const pattern of opts?.exclude ?? []) argv.push('--exclude', pattern);\n\t\t\t\targv.push(localPath, `${remote}:${remotePath}`);\n\t\t\t\tconst result = await runSpawn(argv, { timeoutMs: 600_000 });\n\t\t\t\tif (result.exitCode !== 0) {\n\t\t\t\t\tthrow new Error(`rsync upload failed (exit ${result.exitCode}): ${result.stderr || result.stdout}`);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// scp fallback — no exclude, no delete. We still need -r to copy directories.\n\t\t\tconst argv = ['scp', '-r', ...sshBaseFlags(options), localPath, `${remote}:${remotePath}`];\n\t\t\tconst result = await runSpawn(argv, { timeoutMs: 600_000 });\n\t\t\tif (result.exitCode !== 0) {\n\t\t\t\tthrow new Error(`scp upload failed (exit ${result.exitCode}): ${result.stderr || result.stdout}`);\n\t\t\t}\n\t\t},\n\t};\n};\n",
6
6
  "/**\n * ProcessManager — the abstraction that turns \"files are on the target\" into\n * \"the app is running.\" Two strategies ship: `bareManager` (nohup, lowest\n * dependency) and `systemdManager` (templated unit file, the way production\n * VMs should run).\n *\n * Callers can supply their own — anything that implements `start` / `stop` /\n * `reload` / `status` against a `Target` works. PM2, supervisord, runit,\n * @absolutejs/runtime all fit if someone writes the adapter.\n */\n\nimport type { Target } from './targets';\n\nexport type ProcessManagerContext = {\n\t/** Absolute path on the target to the active release dir (the symlink target). */\n\tcurrentPath: string;\n\t/** Absolute path on the target to the new release dir we just uploaded. */\n\treleasePath: string;\n\t/** Release id (timestamped). */\n\treleaseId: string;\n\t/** App name — supplied via deployer config; used for unit names, pid files, etc. */\n\tappName: string;\n\t/** Optional env to set on the process. */\n\tenv: Record<string, string>;\n\t/** Log sink for any commands the manager runs. */\n\tonLog?: (line: string, stream: 'stdout' | 'stderr') => void;\n};\n\nexport type ProcessManager = {\n\t/** Bring the new release up. Called after the `current` symlink has been swapped. */\n\treload: (target: Target, ctx: ProcessManagerContext) => Promise<void>;\n\t/** Stop the running process. */\n\tstop?: (target: Target, ctx: ProcessManagerContext) => Promise<void>;\n\t/** Return current status (best-effort; used by callers for diagnostics). */\n\tstatus?: (target: Target, ctx: ProcessManagerContext) => Promise<'running' | 'stopped' | 'unknown'>;\n};\n\n// -----------------------------------------------------------------------------\n// bareManager — nohup background + pid file\n// -----------------------------------------------------------------------------\n\nexport type BareManagerOptions = {\n\t/** Command to run. Default `bun run start`. */\n\tcommand?: string;\n\t/** Log files inside the app's data dir (default: alongside pid). */\n\tlogFileBaseName?: string;\n};\n\nconst pidPath = (appName: string) => `/var/lib/${appName}/${appName}.pid`;\nconst logDir = (appName: string) => `/var/log/${appName}`;\n\nexport const bareManager = (options: BareManagerOptions = {}): ProcessManager => {\n\tconst command = options.command ?? 'bun run start';\n\tconst logBase = options.logFileBaseName ?? 'app';\n\n\tconst envPrefix = (env: Record<string, string>) =>\n\t\tObject.entries(env).map(([k, v]) => `${k}='${v.replace(/'/g, `'\\\\''`)}'`).join(' ');\n\n\tconst startCmd = (ctx: ProcessManagerContext): string => {\n\t\tconst env = envPrefix(ctx.env);\n\t\tconst pid = pidPath(ctx.appName);\n\t\tconst out = `${logDir(ctx.appName)}/${logBase}.out.log`;\n\t\tconst err = `${logDir(ctx.appName)}/${logBase}.err.log`;\n\t\treturn `\nmkdir -p $(dirname ${pid}) ${logDir(ctx.appName)} &&\ncd ${ctx.currentPath} &&\nnohup env ${env} sh -c '${command.replace(/'/g, `'\\\\''`)}' >> ${out} 2>> ${err} &\necho $! > ${pid}\n`.trim();\n\t};\n\n\tconst stopCmd = (ctx: ProcessManagerContext): string => `\nPID=$(cat ${pidPath(ctx.appName)} 2>/dev/null || true);\nif [ -n \"$PID\" ] && kill -0 \"$PID\" 2>/dev/null; then\n kill \"$PID\" 2>/dev/null || true;\n for i in 1 2 3 4 5; do\n if ! kill -0 \"$PID\" 2>/dev/null; then break; fi;\n sleep 1;\n done;\n kill -9 \"$PID\" 2>/dev/null || true;\nfi\nrm -f ${pidPath(ctx.appName)}\n`.trim();\n\n\treturn {\n\t\treload: async (target, ctx) => {\n\t\t\tconst stop = await target.exec(stopCmd(ctx), { onLog: ctx.onLog, timeoutMs: 30_000 });\n\t\t\tif (stop.exitCode !== 0) {\n\t\t\t\tthrow new Error(`bareManager.stop failed (exit ${stop.exitCode}): ${stop.stderr}`);\n\t\t\t}\n\t\t\tconst start = await target.exec(startCmd(ctx), { onLog: ctx.onLog, timeoutMs: 30_000 });\n\t\t\tif (start.exitCode !== 0) {\n\t\t\t\tthrow new Error(`bareManager.start failed (exit ${start.exitCode}): ${start.stderr}`);\n\t\t\t}\n\t\t},\n\t\tstatus: async (target, ctx) => {\n\t\t\tconst result = await target.exec(\n\t\t\t\t`PID=$(cat ${pidPath(ctx.appName)} 2>/dev/null || true); if [ -n \"$PID\" ] && kill -0 \"$PID\" 2>/dev/null; then echo running; else echo stopped; fi`,\n\t\t\t\t{ timeoutMs: 5_000 },\n\t\t\t);\n\t\t\tconst out = result.stdout.trim();\n\t\t\tif (out === 'running') return 'running';\n\t\t\tif (out === 'stopped') return 'stopped';\n\t\t\treturn 'unknown';\n\t\t},\n\t\tstop: async (target, ctx) => {\n\t\t\tconst result = await target.exec(stopCmd(ctx), { onLog: ctx.onLog, timeoutMs: 30_000 });\n\t\t\tif (result.exitCode !== 0) {\n\t\t\t\tthrow new Error(`bareManager.stop failed (exit ${result.exitCode}): ${result.stderr}`);\n\t\t\t}\n\t\t},\n\t};\n};\n\n// -----------------------------------------------------------------------------\n// systemdManager — generates a unit file pointing at current/, restarts via systemctl\n// -----------------------------------------------------------------------------\n\nexport type SystemdManagerOptions = {\n\t/** Unit file name (defaults to `${appName}.service`). */\n\tunitName?: string;\n\t/** ExecStart command. Default `/usr/local/bin/bun run start`. */\n\texecStart?: string;\n\t/** User to run as. Default the deploy user. */\n\tuser?: string;\n\t/** Group. Default the deploy user. */\n\tgroup?: string;\n\t/** Restart policy. Default `always`. */\n\trestart?: 'always' | 'on-failure' | 'no';\n\t/** systemctl path. Default `systemctl`. */\n\tsystemctl?: string;\n\t/** Unit file directory. Default `/etc/systemd/system`. */\n\tunitDir?: string;\n};\n\nconst renderSystemdUnit = (\n\tctx: ProcessManagerContext,\n\toptions: SystemdManagerOptions,\n): string => {\n\tconst user = options.user ?? 'deploy';\n\tconst group = options.group ?? user;\n\tconst execStart = options.execStart ?? '/usr/local/bin/bun run start';\n\tconst restart = options.restart ?? 'always';\n\tconst envLines = Object.entries(ctx.env)\n\t\t.map(([k, v]) => `Environment=${k}=${v.replace(/\"/g, '\\\\\"')}`)\n\t\t.join('\\n');\n\treturn `[Unit]\nDescription=${ctx.appName} (managed by @absolutejs/deploy)\nAfter=network.target\n\n[Service]\nType=simple\nWorkingDirectory=${ctx.currentPath}\nExecStart=${execStart}\nRestart=${restart}\nRestartSec=2\nUser=${user}\nGroup=${group}\n${envLines}\nStandardOutput=append:/var/log/${ctx.appName}/app.out.log\nStandardError=append:/var/log/${ctx.appName}/app.err.log\n\n[Install]\nWantedBy=multi-user.target\n`;\n};\n\nexport const systemdManager = (options: SystemdManagerOptions = {}): ProcessManager => {\n\tconst systemctl = options.systemctl ?? 'systemctl';\n\tconst unitDir = options.unitDir ?? '/etc/systemd/system';\n\tconst unitName = (ctx: ProcessManagerContext) => options.unitName ?? `${ctx.appName}.service`;\n\n\treturn {\n\t\treload: async (target, ctx) => {\n\t\t\tconst unit = renderSystemdUnit(ctx, options);\n\t\t\tconst name = unitName(ctx);\n\t\t\t// Write the unit via a heredoc executed by the remote shell. tee/cat work but\n\t\t\t// stdin is the cleanest path that doesn't reveal unit text in `ps`.\n\t\t\tconst writeUnit = await target.exec(\n\t\t\t\t`mkdir -p /var/log/${ctx.appName} && cat > ${unitDir}/${name}`,\n\t\t\t\t{ onLog: ctx.onLog, stdin: unit, timeoutMs: 30_000 },\n\t\t\t);\n\t\t\tif (writeUnit.exitCode !== 0) {\n\t\t\t\tthrow new Error(`systemdManager: writing unit failed (exit ${writeUnit.exitCode}): ${writeUnit.stderr}`);\n\t\t\t}\n\t\t\tconst reload = await target.exec(`${systemctl} daemon-reload`, { onLog: ctx.onLog, timeoutMs: 15_000 });\n\t\t\tif (reload.exitCode !== 0) {\n\t\t\t\tthrow new Error(`systemdManager: daemon-reload failed (exit ${reload.exitCode}): ${reload.stderr}`);\n\t\t\t}\n\t\t\tconst enable = await target.exec(`${systemctl} enable ${name}`, { onLog: ctx.onLog, timeoutMs: 15_000 });\n\t\t\tif (enable.exitCode !== 0) {\n\t\t\t\tthrow new Error(`systemdManager: enable failed (exit ${enable.exitCode}): ${enable.stderr}`);\n\t\t\t}\n\t\t\tconst restart = await target.exec(`${systemctl} restart ${name}`, { onLog: ctx.onLog, timeoutMs: 60_000 });\n\t\t\tif (restart.exitCode !== 0) {\n\t\t\t\tthrow new Error(`systemdManager: restart failed (exit ${restart.exitCode}): ${restart.stderr}`);\n\t\t\t}\n\t\t},\n\t\tstatus: async (target, ctx) => {\n\t\t\tconst result = await target.exec(`${systemctl} is-active ${unitName(ctx)} || true`, { timeoutMs: 10_000 });\n\t\t\tconst out = result.stdout.trim();\n\t\t\tif (out === 'active') return 'running';\n\t\t\tif (out === 'inactive' || out === 'failed') return 'stopped';\n\t\t\treturn 'unknown';\n\t\t},\n\t\tstop: async (target, ctx) => {\n\t\t\tconst result = await target.exec(`${systemctl} stop ${unitName(ctx)}`, { onLog: ctx.onLog, timeoutMs: 30_000 });\n\t\t\tif (result.exitCode !== 0) {\n\t\t\t\tthrow new Error(`systemdManager: stop failed (exit ${result.exitCode}): ${result.stderr}`);\n\t\t\t}\n\t\t},\n\t};\n};\n",
7
- "/**\n * createDeployer — drives a step pipeline against a Target.\n *\n * The default pipeline (`defaultBunPipeline`) is the right thing for a Bun\n * project on a Linux host: prepare → upload → install → build → link →\n * restart → verify. Callers can replace steps wholesale or splice their own\n * in via `steps: [...]`.\n *\n * Release model: every `deploy()` creates a fresh timestamped directory\n * under `<root>/releases/`, uploads into it, then atomically swaps the\n * `<root>/current` symlink. `rollback(id)` re-points the symlink and\n * reloads the process manager — no re-upload, no re-build, just a fast\n * switch.\n */\n\nimport type { ProcessManager, ProcessManagerContext } from './processManagers';\nimport { bareManager } from './processManagers';\nimport type { ExecResult, Target } from './targets';\n\nexport type Source = {\n\t/** Local directory to copy. */\n\tkind: 'directory';\n\troot: string;\n\t/** Globs excluded from upload. Defaults to common dev artifacts. */\n\texclude?: string[];\n};\n\nexport type VerifySpec =\n\t| { kind: 'http'; url: string; retries?: number; intervalMs?: number; expectStatus?: number }\n\t| { kind: 'tcp'; host: string; port: number; retries?: number; intervalMs?: number }\n\t| { kind: 'custom'; check: (ctx: DeployContext) => Promise<boolean> };\n\nexport type ReleaseAnnotations = {\n\t/** Git commit SHA being deployed (40-char hex; truncated forms accepted). */\n\tcommitSha?: string;\n\t/** Git ref (e.g. `refs/heads/main`, `v1.2.3`). */\n\tref?: string;\n\t/** Commit message (or any human-readable description). */\n\tmessage?: string;\n\t/** Committer / deployer identity. */\n\tauthor?: string;\n\t/** Arbitrary tags for downstream filtering (status pages, audits). */\n\ttags?: Record<string, string>;\n};\n\nexport type DeployContext = {\n\ttarget: Target;\n\tsource: Source;\n\treleaseId: string;\n\treleasePath: string;\n\tcurrentPath: string;\n\tappName: string;\n\tenv: Record<string, string>;\n\thooks: ResolvedHooks;\n\tprocessManager: ProcessManager;\n\tverify: VerifySpec | null;\n\tannotations: ReleaseAnnotations;\n\t/** When `true`, steps log what they WOULD do via hooks.onLog and do not mutate the target. */\n\tdryRun: boolean;\n};\n\nexport type DeployStep = {\n\tname: string;\n\trun: (ctx: DeployContext) => Promise<void>;\n};\n\nexport type DeployHooks = {\n\tonStepStart?: (step: { name: string; releaseId: string }) => void | Promise<void>;\n\tonStepEnd?: (step: { name: string; releaseId: string; durationMs: number }) => void | Promise<void>;\n\tonLog?: (line: string, stream: 'stdout' | 'stderr', step: string) => void;\n\tonError?: (error: { step: string; releaseId: string; error: Error }) => void | Promise<void>;\n};\n\ntype ResolvedHooks = Required<{\n\t[K in keyof DeployHooks]: NonNullable<DeployHooks[K]>;\n}>;\n\nexport type DeployOptions = {\n\t/** Per-release annotations stored alongside the release dir as `.deploy-meta.json`. */\n\tannotations?: ReleaseAnnotations;\n\t/**\n\t * When `true`, the deploy plan is logged but no mutation happens on the\n\t * target — steps still call `target.exec` only via `cmd === 'echo'`-style\n\t * dry-run probes. Use this from `gh actions` to verify pipeline shape\n\t * before flipping a real `current` symlink.\n\t */\n\tdryRun?: boolean;\n\t/**\n\t * Resume a previously-failed release. The deployer reads the release's\n\t * `.deploy-meta.json`, finds the step that died, and starts from there.\n\t * Steps that completed successfully are skipped. Use this when a deploy\n\t * fails on `verify` (e.g. health-check timeout) but the release is\n\t * otherwise intact on disk.\n\t */\n\tresumeReleaseId?: string;\n};\n\nexport type DeployerOptions = {\n\ttarget: Target;\n\tsource: Source;\n\t/** App name; used by ProcessManagers for unit names, pid files, log paths. Required. */\n\tappName: string;\n\t/** Where deploys live on the target. Default `/srv/<appName>`. */\n\trootPath?: string;\n\t/** Steps in order. Default: `defaultBunPipeline()`. */\n\tsteps?: DeployStep[];\n\t/** Env merged into install / build / start. */\n\tenv?: Record<string, string>;\n\t/** Process manager. Default `bareManager()`. */\n\tprocessManager?: ProcessManager;\n\t/** How to verify the deploy is up. Default null (skip verify). */\n\tverify?: VerifySpec | null;\n\thooks?: DeployHooks;\n\t/** Override `Date.now` for deterministic release ids in tests. */\n\tclock?: () => number;\n};\n\nexport type ReleaseRecord = {\n\treleaseId: string;\n\tannotations: ReleaseAnnotations;\n\tstatus: 'in-progress' | 'completed' | 'failed';\n\tfailedStep?: string;\n\tcompletedSteps: string[];\n\tstartedAt: number;\n\tendedAt?: number;\n};\n\nexport type DeployResult = {\n\treleaseId: string;\n\treleasePath: string;\n\tcurrentPath: string;\n\tdurationMs: number;\n\tsteps: { name: string; durationMs: number; skipped?: boolean }[];\n\tannotations: ReleaseAnnotations;\n};\n\nexport type Deployer = {\n\tdeploy: (options?: DeployOptions) => Promise<DeployResult>;\n\trollback: (releaseId: string) => Promise<DeployResult>;\n\t/** Stop the active release through the configured process manager. */\n\tstop: () => Promise<void>;\n\t/** Best-effort active process status from the configured process manager. */\n\tstatus: () => Promise<'running' | 'stopped' | 'unknown'>;\n\tlistReleases: () => Promise<string[]>;\n\t/** Read the deploy meta for a specific release (or null if missing). */\n\treadReleaseMeta: (releaseId: string) => Promise<ReleaseRecord | null>;\n\tprune: (options: { keep: number }) => Promise<{ removed: string[] }>;\n\tdispose: () => Promise<void>;\n};\n\nconst DEFAULT_EXCLUDES = ['node_modules', 'dist', 'build', '.git', '.DS_Store', '*.log'];\n\nconst noopHooks: ResolvedHooks = {\n\tonError: () => {},\n\tonLog: () => {},\n\tonStepEnd: () => {},\n\tonStepStart: () => {},\n};\n\nconst resolveHooks = (hooks?: DeployHooks): ResolvedHooks => ({\n\tonError: hooks?.onError ?? noopHooks.onError,\n\tonLog: hooks?.onLog ?? noopHooks.onLog,\n\tonStepEnd: hooks?.onStepEnd ?? noopHooks.onStepEnd,\n\tonStepStart: hooks?.onStepStart ?? noopHooks.onStepStart,\n});\n\nconst makeReleaseId = (clock: () => number): string => {\n\tconst t = clock();\n\tconst date = new Date(t);\n\tconst pad = (n: number, w = 2) => n.toString().padStart(w, '0');\n\treturn `${date.getUTCFullYear()}${pad(date.getUTCMonth() + 1)}${pad(date.getUTCDate())}-${pad(date.getUTCHours())}${pad(date.getUTCMinutes())}${pad(date.getUTCSeconds())}`;\n};\n\nconst requireSuccess = (label: string, result: ExecResult) => {\n\tif (result.exitCode !== 0) {\n\t\tthrow new Error(`${label} failed (exit ${result.exitCode}): ${result.stderr || result.stdout || '(no output)'}`);\n\t}\n};\n\n// -----------------------------------------------------------------------------\n// Default Bun pipeline steps\n// -----------------------------------------------------------------------------\n\nexport const defaultBunPipeline = (): DeployStep[] => [\n\t{\n\t\tname: 'prepare',\n\t\trun: async (ctx) => {\n\t\t\tconst result = await ctx.target.exec(\n\t\t\t\t`mkdir -p ${ctx.releasePath}`,\n\t\t\t\t{ onLog: (line, stream) => ctx.hooks.onLog(line, stream, 'prepare') },\n\t\t\t);\n\t\t\trequireSuccess('prepare: mkdir', result);\n\t\t},\n\t},\n\t{\n\t\tname: 'upload',\n\t\trun: async (ctx) => {\n\t\t\tif (ctx.source.kind !== 'directory') {\n\t\t\t\tthrow new Error(`Unsupported source kind: ${(ctx.source as { kind: string }).kind}`);\n\t\t\t}\n\t\t\t// Trailing slash on source = copy contents, not the dir itself.\n\t\t\tconst localPath = ctx.source.root.endsWith('/') ? ctx.source.root : `${ctx.source.root}/`;\n\t\t\tawait ctx.target.upload(localPath, ctx.releasePath, {\n\t\t\t\texclude: ctx.source.exclude ?? DEFAULT_EXCLUDES,\n\t\t\t});\n\t\t},\n\t},\n\t{\n\t\tname: 'install',\n\t\trun: async (ctx) => {\n\t\t\tconst result = await ctx.target.exec(\n\t\t\t\t`bun install --production`,\n\t\t\t\t{\n\t\t\t\t\tcwd: ctx.releasePath,\n\t\t\t\t\tenv: ctx.env,\n\t\t\t\t\tonLog: (line, stream) => ctx.hooks.onLog(line, stream, 'install'),\n\t\t\t\t\ttimeoutMs: 600_000,\n\t\t\t\t},\n\t\t\t);\n\t\t\trequireSuccess('install', result);\n\t\t},\n\t},\n\t{\n\t\tname: 'build',\n\t\trun: async (ctx) => {\n\t\t\t// Run only if the project declares a `build` script. Detect by reading package.json on the remote.\n\t\t\tconst probe = await ctx.target.exec(\n\t\t\t\t`grep -E '\"build\"\\\\s*:' package.json || true`,\n\t\t\t\t{ cwd: ctx.releasePath, timeoutMs: 10_000 },\n\t\t\t);\n\t\t\tif (!probe.stdout.includes('\"build\"')) return;\n\t\t\tconst result = await ctx.target.exec(\n\t\t\t\t`bun run build`,\n\t\t\t\t{\n\t\t\t\t\tcwd: ctx.releasePath,\n\t\t\t\t\tenv: ctx.env,\n\t\t\t\t\tonLog: (line, stream) => ctx.hooks.onLog(line, stream, 'build'),\n\t\t\t\t\ttimeoutMs: 600_000,\n\t\t\t\t},\n\t\t\t);\n\t\t\trequireSuccess('build', result);\n\t\t},\n\t},\n\t{\n\t\tname: 'link',\n\t\trun: async (ctx) => {\n\t\t\t// Atomic-ish symlink swap: write a NEW symlink to a temp name, then rename onto current.\n\t\t\tconst tmpLink = `${ctx.currentPath}.next`;\n\t\t\tconst result = await ctx.target.exec(\n\t\t\t\t`ln -sfn ${ctx.releasePath} ${tmpLink} && mv -Tf ${tmpLink} ${ctx.currentPath}`,\n\t\t\t\t{ onLog: (line, stream) => ctx.hooks.onLog(line, stream, 'link'), timeoutMs: 10_000 },\n\t\t\t);\n\t\t\trequireSuccess('link', result);\n\t\t},\n\t},\n\t{\n\t\tname: 'restart',\n\t\trun: async (ctx) => {\n\t\t\tawait ctx.processManager.reload(ctx.target, {\n\t\t\t\tappName: ctx.appName,\n\t\t\t\tcurrentPath: ctx.currentPath,\n\t\t\t\tenv: ctx.env,\n\t\t\t\tonLog: (line, stream) => ctx.hooks.onLog(line, stream, 'restart'),\n\t\t\t\treleaseId: ctx.releaseId,\n\t\t\t\treleasePath: ctx.releasePath,\n\t\t\t});\n\t\t},\n\t},\n\t{\n\t\tname: 'verify',\n\t\trun: async (ctx) => {\n\t\t\tif (!ctx.verify) return;\n\t\t\tawait runVerify(ctx);\n\t\t},\n\t},\n];\n\nconst runVerify = async (ctx: DeployContext): Promise<void> => {\n\tconst spec = ctx.verify!;\n\tif (spec.kind === 'custom') {\n\t\tconst ok = await spec.check(ctx);\n\t\tif (!ok) throw new Error('verify: custom check returned false');\n\t\treturn;\n\t}\n\tif (spec.kind === 'http') {\n\t\tconst retries = spec.retries ?? 30;\n\t\tconst intervalMs = spec.intervalMs ?? 1_000;\n\t\tconst expectStatus = spec.expectStatus ?? 200;\n\t\tfor (let attempt = 0; attempt <= retries; attempt++) {\n\t\t\tconst probe = await ctx.target.exec(\n\t\t\t\t`curl -s -o /dev/null -w '%{http_code}' --max-time 5 ${spec.url}`,\n\t\t\t\t{ timeoutMs: 10_000 },\n\t\t\t);\n\t\t\tconst code = Number(probe.stdout.trim());\n\t\t\tif (code === expectStatus) return;\n\t\t\tif (attempt < retries) await new Promise((resolve) => setTimeout(resolve, intervalMs));\n\t\t}\n\t\tthrow new Error(`verify: HTTP ${spec.url} did not return ${expectStatus} after ${retries} retries`);\n\t}\n\t// tcp\n\tconst retries = spec.retries ?? 30;\n\tconst intervalMs = spec.intervalMs ?? 1_000;\n\tfor (let attempt = 0; attempt <= retries; attempt++) {\n\t\tconst probe = await ctx.target.exec(\n\t\t\t`bash -c 'cat < /dev/tcp/${spec.host}/${spec.port}' 2>/dev/null && echo open || echo closed`,\n\t\t\t{ timeoutMs: 10_000 },\n\t\t);\n\t\tif (probe.stdout.includes('open')) return;\n\t\tif (attempt < retries) await new Promise((resolve) => setTimeout(resolve, intervalMs));\n\t}\n\tthrow new Error(`verify: TCP ${spec.host}:${spec.port} not open after ${retries} retries`);\n};\n\n// -----------------------------------------------------------------------------\n// Deployer\n// -----------------------------------------------------------------------------\n\nexport const createDeployer = (options: DeployerOptions): Deployer => {\n\tconst clock = options.clock ?? Date.now;\n\tconst hooks = resolveHooks(options.hooks);\n\tconst rootPath = options.rootPath ?? `/srv/${options.appName}`;\n\tconst currentPath = `${rootPath}/current`;\n\tconst releasesPath = `${rootPath}/releases`;\n\tconst env: Record<string, string> = { NODE_ENV: 'production', ...options.env };\n\tconst processManager = options.processManager ?? bareManager();\n\tconst verify = options.verify === undefined ? null : options.verify;\n\tlet disposed = false;\n\n\tconst buildCtx = (\n\t\treleaseId: string,\n\t\topts: { annotations: ReleaseAnnotations; dryRun: boolean },\n\t): DeployContext => ({\n\t\tannotations: opts.annotations,\n\t\tappName: options.appName,\n\t\tcurrentPath,\n\t\tdryRun: opts.dryRun,\n\t\tenv,\n\t\thooks,\n\t\tprocessManager,\n\t\treleaseId,\n\t\treleasePath: `${releasesPath}/${releaseId}`,\n\t\tsource: options.source,\n\t\ttarget: options.target,\n\t\tverify,\n\t});\n\n\tconst metaPath = (releaseId: string) => `${releasesPath}/${releaseId}/.deploy-meta.json`;\n\n\tconst writeMeta = async (releaseId: string, record: ReleaseRecord): Promise<void> => {\n\t\tconst json = JSON.stringify(record);\n\t\t// Use stdin to avoid quoting hassles + so the JSON doesn't appear in `ps`.\n\t\tconst result = await options.target.exec(`cat > ${metaPath(releaseId)}`, {\n\t\t\tstdin: json,\n\t\t\ttimeoutMs: 10_000,\n\t\t});\n\t\tif (result.exitCode !== 0) {\n\t\t\t// Non-fatal — the deploy doesn't depend on the meta file for success.\n\t\t\tconsole.warn(`[deploy] writeMeta(${releaseId}) failed: ${result.stderr || result.stdout}`);\n\t\t}\n\t};\n\n\tconst readMeta = async (releaseId: string): Promise<ReleaseRecord | null> => {\n\t\tconst result = await options.target.exec(`cat ${metaPath(releaseId)} 2>/dev/null || true`, {\n\t\t\ttimeoutMs: 10_000,\n\t\t});\n\t\tconst text = result.stdout.trim();\n\t\tif (text.length === 0) return null;\n\t\ttry {\n\t\t\treturn JSON.parse(text) as ReleaseRecord;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t};\n\n\tconst cleanOrphanedSymlink = async (): Promise<void> => {\n\t\t// A prior deploy that crashed between `ln -sfn ... current.next` and\n\t\t// `mv -Tf current.next current` leaves `current.next` dangling. Clean\n\t\t// it so the next link step's `mv -Tf` is unambiguous.\n\t\tawait options.target.exec(`rm -f ${currentPath}.next`, { timeoutMs: 5_000 });\n\t};\n\n\tconst runSteps = async (\n\t\tsteps: DeployStep[],\n\t\treleaseId: string,\n\t\trunOpts: {\n\t\t\tannotations: ReleaseAnnotations;\n\t\t\tdryRun: boolean;\n\t\t\talreadyCompleted: string[];\n\t\t},\n\t): Promise<DeployResult> => {\n\t\tconst ctx = buildCtx(releaseId, {\n\t\t\tannotations: runOpts.annotations,\n\t\t\tdryRun: runOpts.dryRun,\n\t\t});\n\t\tconst stepDurations: { name: string; durationMs: number; skipped?: boolean }[] = [];\n\t\tconst startedAt = clock();\n\t\tconst completedSteps: string[] = [...runOpts.alreadyCompleted];\n\n\t\tconst record: ReleaseRecord = {\n\t\t\tannotations: runOpts.annotations,\n\t\t\tcompletedSteps,\n\t\t\treleaseId,\n\t\t\tstartedAt,\n\t\t\tstatus: 'in-progress',\n\t\t};\n\t\t// Write the meta-record as soon as the release directory exists, which\n\t\t// `prepare` sets up. Until then we have nowhere to put it.\n\n\t\tfor (const step of steps) {\n\t\t\tif (completedSteps.includes(step.name) && step.name !== 'verify') {\n\t\t\t\t// Resume: skip steps already done. (Always re-run verify so a\n\t\t\t\t// healthy probe is recorded post-resume.)\n\t\t\t\tstepDurations.push({ durationMs: 0, name: step.name, skipped: true });\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst stepStartedAt = clock();\n\t\t\tawait hooks.onStepStart({ name: step.name, releaseId });\n\n\t\t\tif (runOpts.dryRun) {\n\t\t\t\thooks.onLog(`[dry-run] would run: ${step.name}`, 'stdout', step.name);\n\t\t\t\tstepDurations.push({ durationMs: 0, name: step.name, skipped: true });\n\t\t\t\tawait hooks.onStepEnd({ durationMs: 0, name: step.name, releaseId });\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tawait step.run(ctx);\n\t\t\t} catch (error) {\n\t\t\t\tconst err = error instanceof Error ? error : new Error(String(error));\n\t\t\t\trecord.status = 'failed';\n\t\t\t\trecord.failedStep = step.name;\n\t\t\t\trecord.endedAt = clock();\n\t\t\t\t// Best-effort meta write so resume() works later.\n\t\t\t\tawait writeMeta(releaseId, record);\n\t\t\t\tawait hooks.onError({ error: err, releaseId, step: step.name });\n\t\t\t\tthrow err;\n\t\t\t}\n\n\t\t\tconst durationMs = clock() - stepStartedAt;\n\t\t\tstepDurations.push({ durationMs, name: step.name });\n\t\t\tcompletedSteps.push(step.name);\n\t\t\tawait hooks.onStepEnd({ durationMs, name: step.name, releaseId });\n\n\t\t\t// After `prepare` (the first step that creates the dir), persist meta.\n\t\t\tif (step.name === 'prepare') {\n\t\t\t\tawait writeMeta(releaseId, record);\n\t\t\t}\n\t\t}\n\n\t\trecord.status = 'completed';\n\t\trecord.endedAt = clock();\n\t\tif (!runOpts.dryRun) await writeMeta(releaseId, record);\n\n\t\treturn {\n\t\t\tannotations: runOpts.annotations,\n\t\t\tcurrentPath,\n\t\t\tdurationMs: clock() - startedAt,\n\t\t\treleaseId,\n\t\t\treleasePath: ctx.releasePath,\n\t\t\tsteps: stepDurations,\n\t\t};\n\t};\n\n\tconst ensureRoot = async () => {\n\t\tconst result = await options.target.exec(`mkdir -p ${releasesPath}`, { timeoutMs: 10_000 });\n\t\trequireSuccess('ensureRoot', result);\n\t};\n\tconst activeProcessContext = (): ProcessManagerContext => ({\n\t\tappName: options.appName,\n\t\tcurrentPath,\n\t\tenv,\n\t\treleaseId: 'current',\n\t\treleasePath: currentPath,\n\t});\n\n\treturn {\n\t\tdeploy: async (deployOpts: DeployOptions = {}) => {\n\t\t\tif (disposed) throw new Error('Deployer is disposed');\n\t\t\tawait ensureRoot();\n\t\t\tawait cleanOrphanedSymlink();\n\n\t\t\tconst annotations = deployOpts.annotations ?? {};\n\t\t\tconst dryRun = deployOpts.dryRun ?? false;\n\n\t\t\tif (deployOpts.resumeReleaseId !== undefined) {\n\t\t\t\tconst prior = await readMeta(deployOpts.resumeReleaseId);\n\t\t\t\tif (!prior) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`resume: no .deploy-meta.json for release ${deployOpts.resumeReleaseId}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (prior.status === 'completed') {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`resume: release ${deployOpts.resumeReleaseId} already completed`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn runSteps(options.steps ?? defaultBunPipeline(), deployOpts.resumeReleaseId, {\n\t\t\t\t\talreadyCompleted: prior.completedSteps,\n\t\t\t\t\tannotations: prior.annotations ?? annotations,\n\t\t\t\t\tdryRun,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst releaseId = makeReleaseId(clock);\n\t\t\treturn runSteps(options.steps ?? defaultBunPipeline(), releaseId, {\n\t\t\t\talreadyCompleted: [],\n\t\t\t\tannotations,\n\t\t\t\tdryRun,\n\t\t\t});\n\t\t},\n\t\tdispose: async () => {\n\t\t\tdisposed = true;\n\t\t\tif (options.target.close) await options.target.close();\n\t\t},\n\t\tlistReleases: async () => {\n\t\t\tawait ensureRoot();\n\t\t\tconst result = await options.target.exec(\n\t\t\t\t`ls -1 ${releasesPath} 2>/dev/null || true`,\n\t\t\t\t{ timeoutMs: 10_000 },\n\t\t\t);\n\t\t\treturn result.stdout\n\t\t\t\t.split('\\n')\n\t\t\t\t.map((line) => line.trim())\n\t\t\t\t.filter((line) => line.length > 0)\n\t\t\t\t.sort();\n\t\t},\n\t\tprune: async ({ keep }) => {\n\t\t\tif (disposed) throw new Error('Deployer is disposed');\n\t\t\tconst all = await (async () => {\n\t\t\t\tconst result = await options.target.exec(\n\t\t\t\t\t`ls -1 ${releasesPath} 2>/dev/null || true`,\n\t\t\t\t\t{ timeoutMs: 10_000 },\n\t\t\t\t);\n\t\t\t\treturn result.stdout\n\t\t\t\t\t.split('\\n')\n\t\t\t\t\t.map((line) => line.trim())\n\t\t\t\t\t.filter((line) => line.length > 0)\n\t\t\t\t\t.sort();\n\t\t\t})();\n\t\t\tif (all.length <= keep) return { removed: [] };\n\t\t\tconst removed = all.slice(0, all.length - keep);\n\t\t\tfor (const releaseId of removed) {\n\t\t\t\tawait options.target.exec(`rm -rf ${releasesPath}/${releaseId}`, { timeoutMs: 60_000 });\n\t\t\t}\n\t\t\treturn { removed };\n\t\t},\n\t\treadReleaseMeta: readMeta,\n\t\trollback: async (releaseId) => {\n\t\t\tif (disposed) throw new Error('Deployer is disposed');\n\t\t\tawait ensureRoot();\n\t\t\tawait cleanOrphanedSymlink();\n\t\t\tconst exists = await options.target.exec(\n\t\t\t\t`test -d ${releasesPath}/${releaseId} && echo ok || echo missing`,\n\t\t\t\t{ timeoutMs: 5_000 },\n\t\t\t);\n\t\t\tif (!exists.stdout.includes('ok')) {\n\t\t\t\tthrow new Error(`rollback: release ${releaseId} not found at ${releasesPath}/${releaseId}`);\n\t\t\t}\n\t\t\t// Rollback steps: re-link + restart (no upload, no install, no build).\n\t\t\tconst rollbackSteps: DeployStep[] = defaultBunPipeline().filter((step) =>\n\t\t\t\tstep.name === 'link' || step.name === 'restart' || step.name === 'verify',\n\t\t\t);\n\t\t\tconst prior = await readMeta(releaseId);\n\t\t\treturn runSteps(rollbackSteps, releaseId, {\n\t\t\t\talreadyCompleted: [],\n\t\t\t\tannotations: prior?.annotations ?? {},\n\t\t\t\tdryRun: false,\n\t\t\t});\n\t\t},\n\t\tstatus: async () => {\n\t\t\tif (disposed) throw new Error('Deployer is disposed');\n\t\t\tif (!processManager.status) return 'unknown';\n\t\t\treturn processManager.status(options.target, activeProcessContext());\n\t\t},\n\t\tstop: async () => {\n\t\t\tif (disposed) throw new Error('Deployer is disposed');\n\t\t\tif (!processManager.stop) {\n\t\t\t\tthrow new Error('Process manager does not support stop');\n\t\t\t}\n\t\t\tawait processManager.stop(options.target, activeProcessContext());\n\t\t},\n\t};\n};\n"
7
+ "/**\n * createDeployer — drives a step pipeline against a Target.\n *\n * The default pipeline (`defaultBunPipeline`) is the right thing for a Bun\n * project on a Linux host: prepare → upload → install → build → link →\n * restart → verify. Callers can replace steps wholesale or splice their own\n * in via `steps: [...]`.\n *\n * Release model: every `deploy()` creates a fresh timestamped directory\n * under `<root>/releases/`, uploads into it, then atomically swaps the\n * `<root>/current` symlink. `rollback(id)` re-points the symlink and\n * reloads the process manager — no re-upload, no re-build, just a fast\n * switch.\n */\n\nimport type { ProcessManager, ProcessManagerContext } from './processManagers';\nimport { bareManager } from './processManagers';\nimport type { ExecResult, Target } from './targets';\n\nexport type Source = {\n\t/** Local directory to copy. */\n\tkind: 'directory';\n\troot: string;\n\t/** Globs excluded from upload. Defaults to common dev artifacts. */\n\texclude?: string[];\n};\n\nexport type VerifySpec =\n\t| { kind: 'http'; url: string; retries?: number; intervalMs?: number; expectStatus?: number }\n\t| { kind: 'tcp'; host: string; port: number; retries?: number; intervalMs?: number }\n\t| { kind: 'custom'; check: (ctx: DeployContext) => Promise<boolean> };\n\nexport type ReleaseAnnotations = {\n\t/** Git commit SHA being deployed (40-char hex; truncated forms accepted). */\n\tcommitSha?: string;\n\t/** Git ref (e.g. `refs/heads/main`, `v1.2.3`). */\n\tref?: string;\n\t/** Commit message (or any human-readable description). */\n\tmessage?: string;\n\t/** Committer / deployer identity. */\n\tauthor?: string;\n\t/** Arbitrary tags for downstream filtering (status pages, audits). */\n\ttags?: Record<string, string>;\n};\n\nexport type DeployContext = {\n\ttarget: Target;\n\tsource: Source;\n\treleaseId: string;\n\treleasePath: string;\n\tcurrentPath: string;\n\tappName: string;\n\tenv: Record<string, string>;\n\thooks: ResolvedHooks;\n\tprocessManager: ProcessManager;\n\tverify: VerifySpec | null;\n\tannotations: ReleaseAnnotations;\n\t/** Optional cancellation signal shared with custom steps and verify hooks. */\n\tsignal?: AbortSignal;\n\t/** When `true`, steps log what they WOULD do via hooks.onLog and do not mutate the target. */\n\tdryRun: boolean;\n};\n\nexport type DeployStep = {\n\tname: string;\n\trun: (ctx: DeployContext) => Promise<void>;\n};\n\nexport type DeployHooks = {\n\tonStepStart?: (step: { name: string; releaseId: string }) => void | Promise<void>;\n\tonStepEnd?: (step: { name: string; releaseId: string; durationMs: number }) => void | Promise<void>;\n\tonLog?: (line: string, stream: 'stdout' | 'stderr', step: string) => void;\n\tonError?: (error: { step: string; releaseId: string; error: Error }) => void | Promise<void>;\n};\n\ntype ResolvedHooks = Required<{\n\t[K in keyof DeployHooks]: NonNullable<DeployHooks[K]>;\n}>;\n\nexport type DeployOptions = {\n\t/** Cancel before the next pipeline step; custom steps may observe it directly. */\n\tsignal?: AbortSignal;\n\t/** Per-release annotations stored alongside the release dir as `.deploy-meta.json`. */\n\tannotations?: ReleaseAnnotations;\n\t/**\n\t * When `true`, the deploy plan is logged but no mutation happens on the\n\t * target — steps still call `target.exec` only via `cmd === 'echo'`-style\n\t * dry-run probes. Use this from `gh actions` to verify pipeline shape\n\t * before flipping a real `current` symlink.\n\t */\n\tdryRun?: boolean;\n\t/**\n\t * Resume a previously-failed release. The deployer reads the release's\n\t * `.deploy-meta.json`, finds the step that died, and starts from there.\n\t * Steps that completed successfully are skipped. Use this when a deploy\n\t * fails on `verify` (e.g. health-check timeout) but the release is\n\t * otherwise intact on disk.\n\t */\n\tresumeReleaseId?: string;\n};\n\nexport type DeployerOptions = {\n\ttarget: Target;\n\tsource: Source;\n\t/** App name; used by ProcessManagers for unit names, pid files, log paths. Required. */\n\tappName: string;\n\t/** Where deploys live on the target. Default `/srv/<appName>`. */\n\trootPath?: string;\n\t/** Steps in order. Default: `defaultBunPipeline()`. */\n\tsteps?: DeployStep[];\n\t/** Env merged into install / build / start. */\n\tenv?: Record<string, string>;\n\t/** Process manager. Default `bareManager()`. */\n\tprocessManager?: ProcessManager;\n\t/** How to verify the deploy is up. Default null (skip verify). */\n\tverify?: VerifySpec | null;\n\thooks?: DeployHooks;\n\t/** Override `Date.now` for deterministic release ids in tests. */\n\tclock?: () => number;\n};\n\nexport type ReleaseRecord = {\n\treleaseId: string;\n\tannotations: ReleaseAnnotations;\n\tstatus: 'in-progress' | 'completed' | 'failed';\n\tfailedStep?: string;\n\tcompletedSteps: string[];\n\tstartedAt: number;\n\tendedAt?: number;\n};\n\nexport type DeployResult = {\n\treleaseId: string;\n\treleasePath: string;\n\tcurrentPath: string;\n\tdurationMs: number;\n\tsteps: { name: string; durationMs: number; skipped?: boolean }[];\n\tannotations: ReleaseAnnotations;\n};\n\nexport type Deployer = {\n\tdeploy: (options?: DeployOptions) => Promise<DeployResult>;\n\trollback: (releaseId: string) => Promise<DeployResult>;\n\t/** Stop the active release through the configured process manager. */\n\tstop: () => Promise<void>;\n\t/** Best-effort active process status from the configured process manager. */\n\tstatus: () => Promise<'running' | 'stopped' | 'unknown'>;\n\tlistReleases: () => Promise<string[]>;\n\t/** Read the deploy meta for a specific release (or null if missing). */\n\treadReleaseMeta: (releaseId: string) => Promise<ReleaseRecord | null>;\n\tprune: (options: { keep: number }) => Promise<{ removed: string[] }>;\n\tdispose: () => Promise<void>;\n};\n\nconst DEFAULT_EXCLUDES = ['node_modules', 'dist', 'build', '.git', '.DS_Store', '*.log'];\n\nconst noopHooks: ResolvedHooks = {\n\tonError: () => {},\n\tonLog: () => {},\n\tonStepEnd: () => {},\n\tonStepStart: () => {},\n};\n\nconst resolveHooks = (hooks?: DeployHooks): ResolvedHooks => ({\n\tonError: hooks?.onError ?? noopHooks.onError,\n\tonLog: hooks?.onLog ?? noopHooks.onLog,\n\tonStepEnd: hooks?.onStepEnd ?? noopHooks.onStepEnd,\n\tonStepStart: hooks?.onStepStart ?? noopHooks.onStepStart,\n});\n\nconst makeReleaseId = (clock: () => number): string => {\n\tconst t = clock();\n\tconst date = new Date(t);\n\tconst pad = (n: number, w = 2) => n.toString().padStart(w, '0');\n\treturn `${date.getUTCFullYear()}${pad(date.getUTCMonth() + 1)}${pad(date.getUTCDate())}-${pad(date.getUTCHours())}${pad(date.getUTCMinutes())}${pad(date.getUTCSeconds())}`;\n};\n\nconst requireSuccess = (label: string, result: ExecResult) => {\n\tif (result.exitCode !== 0) {\n\t\tthrow new Error(`${label} failed (exit ${result.exitCode}): ${result.stderr || result.stdout || '(no output)'}`);\n\t}\n};\n\n// -----------------------------------------------------------------------------\n// Default Bun pipeline steps\n// -----------------------------------------------------------------------------\n\nexport const defaultBunPipeline = (): DeployStep[] => [\n\t{\n\t\tname: 'prepare',\n\t\trun: async (ctx) => {\n\t\t\tconst result = await ctx.target.exec(\n\t\t\t\t`mkdir -p ${ctx.releasePath}`,\n\t\t\t\t{ onLog: (line, stream) => ctx.hooks.onLog(line, stream, 'prepare') },\n\t\t\t);\n\t\t\trequireSuccess('prepare: mkdir', result);\n\t\t},\n\t},\n\t{\n\t\tname: 'upload',\n\t\trun: async (ctx) => {\n\t\t\tif (ctx.source.kind !== 'directory') {\n\t\t\t\tthrow new Error(`Unsupported source kind: ${(ctx.source as { kind: string }).kind}`);\n\t\t\t}\n\t\t\t// Trailing slash on source = copy contents, not the dir itself.\n\t\t\tconst localPath = ctx.source.root.endsWith('/') ? ctx.source.root : `${ctx.source.root}/`;\n\t\t\tawait ctx.target.upload(localPath, ctx.releasePath, {\n\t\t\t\texclude: ctx.source.exclude ?? DEFAULT_EXCLUDES,\n\t\t\t});\n\t\t},\n\t},\n\t{\n\t\tname: 'install',\n\t\trun: async (ctx) => {\n\t\t\tconst result = await ctx.target.exec(\n\t\t\t\t`bun install --production`,\n\t\t\t\t{\n\t\t\t\t\tcwd: ctx.releasePath,\n\t\t\t\t\tenv: ctx.env,\n\t\t\t\t\tonLog: (line, stream) => ctx.hooks.onLog(line, stream, 'install'),\n\t\t\t\t\ttimeoutMs: 600_000,\n\t\t\t\t},\n\t\t\t);\n\t\t\trequireSuccess('install', result);\n\t\t},\n\t},\n\t{\n\t\tname: 'build',\n\t\trun: async (ctx) => {\n\t\t\t// Run only if the project declares a `build` script. Detect by reading package.json on the remote.\n\t\t\tconst probe = await ctx.target.exec(\n\t\t\t\t`grep -E '\"build\"\\\\s*:' package.json || true`,\n\t\t\t\t{ cwd: ctx.releasePath, timeoutMs: 10_000 },\n\t\t\t);\n\t\t\tif (!probe.stdout.includes('\"build\"')) return;\n\t\t\tconst result = await ctx.target.exec(\n\t\t\t\t`bun run build`,\n\t\t\t\t{\n\t\t\t\t\tcwd: ctx.releasePath,\n\t\t\t\t\tenv: ctx.env,\n\t\t\t\t\tonLog: (line, stream) => ctx.hooks.onLog(line, stream, 'build'),\n\t\t\t\t\ttimeoutMs: 600_000,\n\t\t\t\t},\n\t\t\t);\n\t\t\trequireSuccess('build', result);\n\t\t},\n\t},\n\t{\n\t\tname: 'link',\n\t\trun: async (ctx) => {\n\t\t\t// Atomic-ish symlink swap: write a NEW symlink to a temp name, then rename onto current.\n\t\t\tconst tmpLink = `${ctx.currentPath}.next`;\n\t\t\tconst result = await ctx.target.exec(\n\t\t\t\t`ln -sfn ${ctx.releasePath} ${tmpLink} && mv -Tf ${tmpLink} ${ctx.currentPath}`,\n\t\t\t\t{ onLog: (line, stream) => ctx.hooks.onLog(line, stream, 'link'), timeoutMs: 10_000 },\n\t\t\t);\n\t\t\trequireSuccess('link', result);\n\t\t},\n\t},\n\t{\n\t\tname: 'restart',\n\t\trun: async (ctx) => {\n\t\t\tawait ctx.processManager.reload(ctx.target, {\n\t\t\t\tappName: ctx.appName,\n\t\t\t\tcurrentPath: ctx.currentPath,\n\t\t\t\tenv: ctx.env,\n\t\t\t\tonLog: (line, stream) => ctx.hooks.onLog(line, stream, 'restart'),\n\t\t\t\treleaseId: ctx.releaseId,\n\t\t\t\treleasePath: ctx.releasePath,\n\t\t\t});\n\t\t},\n\t},\n\t{\n\t\tname: 'verify',\n\t\trun: async (ctx) => {\n\t\t\tif (!ctx.verify) return;\n\t\t\tawait runVerify(ctx);\n\t\t},\n\t},\n];\n\nconst runVerify = async (ctx: DeployContext): Promise<void> => {\n\tconst spec = ctx.verify!;\n\tif (spec.kind === 'custom') {\n\t\tconst ok = await spec.check(ctx);\n\t\tif (!ok) throw new Error('verify: custom check returned false');\n\t\treturn;\n\t}\n\tif (spec.kind === 'http') {\n\t\tconst retries = spec.retries ?? 30;\n\t\tconst intervalMs = spec.intervalMs ?? 1_000;\n\t\tconst expectStatus = spec.expectStatus ?? 200;\n\t\tfor (let attempt = 0; attempt <= retries; attempt++) {\n\t\t\tconst probe = await ctx.target.exec(\n\t\t\t\t`curl -s -o /dev/null -w '%{http_code}' --max-time 5 ${spec.url}`,\n\t\t\t\t{ timeoutMs: 10_000 },\n\t\t\t);\n\t\t\tconst code = Number(probe.stdout.trim());\n\t\t\tif (code === expectStatus) return;\n\t\t\tif (attempt < retries) await new Promise((resolve) => setTimeout(resolve, intervalMs));\n\t\t}\n\t\tthrow new Error(`verify: HTTP ${spec.url} did not return ${expectStatus} after ${retries} retries`);\n\t}\n\t// tcp\n\tconst retries = spec.retries ?? 30;\n\tconst intervalMs = spec.intervalMs ?? 1_000;\n\tfor (let attempt = 0; attempt <= retries; attempt++) {\n\t\tconst probe = await ctx.target.exec(\n\t\t\t`bash -c 'cat < /dev/tcp/${spec.host}/${spec.port}' 2>/dev/null && echo open || echo closed`,\n\t\t\t{ timeoutMs: 10_000 },\n\t\t);\n\t\tif (probe.stdout.includes('open')) return;\n\t\tif (attempt < retries) await new Promise((resolve) => setTimeout(resolve, intervalMs));\n\t}\n\tthrow new Error(`verify: TCP ${spec.host}:${spec.port} not open after ${retries} retries`);\n};\n\n// -----------------------------------------------------------------------------\n// Deployer\n// -----------------------------------------------------------------------------\n\nexport const createDeployer = (options: DeployerOptions): Deployer => {\n\tconst clock = options.clock ?? Date.now;\n\tconst hooks = resolveHooks(options.hooks);\n\tconst rootPath = options.rootPath ?? `/srv/${options.appName}`;\n\tconst currentPath = `${rootPath}/current`;\n\tconst releasesPath = `${rootPath}/releases`;\n\tconst env: Record<string, string> = { NODE_ENV: 'production', ...options.env };\n\tconst processManager = options.processManager ?? bareManager();\n\tconst verify = options.verify === undefined ? null : options.verify;\n\tlet disposed = false;\n\n\tconst buildCtx = (\n\t\treleaseId: string,\n\t\topts: { annotations: ReleaseAnnotations; dryRun: boolean; signal?: AbortSignal },\n\t): DeployContext => ({\n\t\tannotations: opts.annotations,\n\t\tappName: options.appName,\n\t\tcurrentPath,\n\t\tdryRun: opts.dryRun,\n\t\tenv,\n\t\thooks,\n\t\tprocessManager,\n\t\tsignal: opts.signal,\n\t\treleaseId,\n\t\treleasePath: `${releasesPath}/${releaseId}`,\n\t\tsource: options.source,\n\t\ttarget: options.target,\n\t\tverify,\n\t});\n\n\tconst metaPath = (releaseId: string) => `${releasesPath}/${releaseId}/.deploy-meta.json`;\n\n\tconst writeMeta = async (releaseId: string, record: ReleaseRecord): Promise<void> => {\n\t\tconst json = JSON.stringify(record);\n\t\t// Use stdin to avoid quoting hassles + so the JSON doesn't appear in `ps`.\n\t\tconst result = await options.target.exec(`cat > ${metaPath(releaseId)}`, {\n\t\t\tstdin: json,\n\t\t\ttimeoutMs: 10_000,\n\t\t});\n\t\tif (result.exitCode !== 0) {\n\t\t\t// Non-fatal — the deploy doesn't depend on the meta file for success.\n\t\t\tconsole.warn(`[deploy] writeMeta(${releaseId}) failed: ${result.stderr || result.stdout}`);\n\t\t}\n\t};\n\n\tconst readMeta = async (releaseId: string): Promise<ReleaseRecord | null> => {\n\t\tconst result = await options.target.exec(`cat ${metaPath(releaseId)} 2>/dev/null || true`, {\n\t\t\ttimeoutMs: 10_000,\n\t\t});\n\t\tconst text = result.stdout.trim();\n\t\tif (text.length === 0) return null;\n\t\ttry {\n\t\t\treturn JSON.parse(text) as ReleaseRecord;\n\t\t} catch {\n\t\t\treturn null;\n\t\t}\n\t};\n\n\tconst cleanOrphanedSymlink = async (): Promise<void> => {\n\t\t// A prior deploy that crashed between `ln -sfn ... current.next` and\n\t\t// `mv -Tf current.next current` leaves `current.next` dangling. Clean\n\t\t// it so the next link step's `mv -Tf` is unambiguous.\n\t\tawait options.target.exec(`rm -f ${currentPath}.next`, { timeoutMs: 5_000 });\n\t};\n\n\tconst runSteps = async (\n\t\tsteps: DeployStep[],\n\t\treleaseId: string,\n\t\trunOpts: {\n\t\t\tannotations: ReleaseAnnotations;\n\t\t\tdryRun: boolean;\n\t\t\talreadyCompleted: string[];\n\t\t\tsignal?: AbortSignal;\n\t\t},\n\t): Promise<DeployResult> => {\n\t\tconst ctx = buildCtx(releaseId, {\n\t\t\tannotations: runOpts.annotations,\n\t\t\tdryRun: runOpts.dryRun,\n\t\t\tsignal: runOpts.signal,\n\t\t});\n\t\tconst stepDurations: { name: string; durationMs: number; skipped?: boolean }[] = [];\n\t\tconst startedAt = clock();\n\t\tconst completedSteps: string[] = [...runOpts.alreadyCompleted];\n\n\t\tconst record: ReleaseRecord = {\n\t\t\tannotations: runOpts.annotations,\n\t\t\tcompletedSteps,\n\t\t\treleaseId,\n\t\t\tstartedAt,\n\t\t\tstatus: 'in-progress',\n\t\t};\n\t\t// Write the meta-record as soon as the release directory exists, which\n\t\t// `prepare` sets up. Until then we have nowhere to put it.\n\n\t\tfor (const step of steps) {\n\t\t\tctx.signal?.throwIfAborted();\n\t\t\tif (completedSteps.includes(step.name) && step.name !== 'verify') {\n\t\t\t\t// Resume: skip steps already done. (Always re-run verify so a\n\t\t\t\t// healthy probe is recorded post-resume.)\n\t\t\t\tstepDurations.push({ durationMs: 0, name: step.name, skipped: true });\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst stepStartedAt = clock();\n\t\t\tawait hooks.onStepStart({ name: step.name, releaseId });\n\n\t\t\tif (runOpts.dryRun) {\n\t\t\t\thooks.onLog(`[dry-run] would run: ${step.name}`, 'stdout', step.name);\n\t\t\t\tstepDurations.push({ durationMs: 0, name: step.name, skipped: true });\n\t\t\t\tawait hooks.onStepEnd({ durationMs: 0, name: step.name, releaseId });\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tawait step.run(ctx);\n\t\t\t\tctx.signal?.throwIfAborted();\n\t\t\t} catch (error) {\n\t\t\t\tconst err = error instanceof Error ? error : new Error(String(error));\n\t\t\t\trecord.status = 'failed';\n\t\t\t\trecord.failedStep = step.name;\n\t\t\t\trecord.endedAt = clock();\n\t\t\t\t// Best-effort meta write so resume() works later.\n\t\t\t\tawait writeMeta(releaseId, record);\n\t\t\t\tawait hooks.onError({ error: err, releaseId, step: step.name });\n\t\t\t\tthrow err;\n\t\t\t}\n\n\t\t\tconst durationMs = clock() - stepStartedAt;\n\t\t\tstepDurations.push({ durationMs, name: step.name });\n\t\t\tcompletedSteps.push(step.name);\n\t\t\tawait hooks.onStepEnd({ durationMs, name: step.name, releaseId });\n\n\t\t\t// After `prepare` (the first step that creates the dir), persist meta.\n\t\t\tif (step.name === 'prepare') {\n\t\t\t\tawait writeMeta(releaseId, record);\n\t\t\t}\n\t\t}\n\n\t\trecord.status = 'completed';\n\t\trecord.endedAt = clock();\n\t\tif (!runOpts.dryRun) await writeMeta(releaseId, record);\n\n\t\treturn {\n\t\t\tannotations: runOpts.annotations,\n\t\t\tcurrentPath,\n\t\t\tdurationMs: clock() - startedAt,\n\t\t\treleaseId,\n\t\t\treleasePath: ctx.releasePath,\n\t\t\tsteps: stepDurations,\n\t\t};\n\t};\n\n\tconst ensureRoot = async () => {\n\t\tconst result = await options.target.exec(`mkdir -p ${releasesPath}`, { timeoutMs: 10_000 });\n\t\trequireSuccess('ensureRoot', result);\n\t};\n\tconst activeProcessContext = (): ProcessManagerContext => ({\n\t\tappName: options.appName,\n\t\tcurrentPath,\n\t\tenv,\n\t\treleaseId: 'current',\n\t\treleasePath: currentPath,\n\t});\n\n\treturn {\n\t\tdeploy: async (deployOpts: DeployOptions = {}) => {\n\t\t\tif (disposed) throw new Error('Deployer is disposed');\n\t\t\tawait ensureRoot();\n\t\t\tawait cleanOrphanedSymlink();\n\n\t\t\tconst annotations = deployOpts.annotations ?? {};\n\t\t\tconst dryRun = deployOpts.dryRun ?? false;\n\n\t\t\tif (deployOpts.resumeReleaseId !== undefined) {\n\t\t\t\tconst prior = await readMeta(deployOpts.resumeReleaseId);\n\t\t\t\tif (!prior) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`resume: no .deploy-meta.json for release ${deployOpts.resumeReleaseId}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tif (prior.status === 'completed') {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`resume: release ${deployOpts.resumeReleaseId} already completed`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\treturn runSteps(options.steps ?? defaultBunPipeline(), deployOpts.resumeReleaseId, {\n\t\t\t\t\talreadyCompleted: prior.completedSteps,\n\t\t\t\t\tannotations: prior.annotations ?? annotations,\n\t\t\t\t\tdryRun,\n\t\t\t\t\tsignal: deployOpts.signal,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst releaseId = makeReleaseId(clock);\n\t\t\treturn runSteps(options.steps ?? defaultBunPipeline(), releaseId, {\n\t\t\t\talreadyCompleted: [],\n\t\t\t\tannotations,\n\t\t\t\tdryRun,\n\t\t\t\tsignal: deployOpts.signal,\n\t\t\t});\n\t\t},\n\t\tdispose: async () => {\n\t\t\tdisposed = true;\n\t\t\tif (options.target.close) await options.target.close();\n\t\t},\n\t\tlistReleases: async () => {\n\t\t\tawait ensureRoot();\n\t\t\tconst result = await options.target.exec(\n\t\t\t\t`ls -1 ${releasesPath} 2>/dev/null || true`,\n\t\t\t\t{ timeoutMs: 10_000 },\n\t\t\t);\n\t\t\treturn result.stdout\n\t\t\t\t.split('\\n')\n\t\t\t\t.map((line) => line.trim())\n\t\t\t\t.filter((line) => line.length > 0)\n\t\t\t\t.sort();\n\t\t},\n\t\tprune: async ({ keep }) => {\n\t\t\tif (disposed) throw new Error('Deployer is disposed');\n\t\t\tconst all = await (async () => {\n\t\t\t\tconst result = await options.target.exec(\n\t\t\t\t\t`ls -1 ${releasesPath} 2>/dev/null || true`,\n\t\t\t\t\t{ timeoutMs: 10_000 },\n\t\t\t\t);\n\t\t\t\treturn result.stdout\n\t\t\t\t\t.split('\\n')\n\t\t\t\t\t.map((line) => line.trim())\n\t\t\t\t\t.filter((line) => line.length > 0)\n\t\t\t\t\t.sort();\n\t\t\t})();\n\t\t\tif (all.length <= keep) return { removed: [] };\n\t\t\tconst removed = all.slice(0, all.length - keep);\n\t\t\tfor (const releaseId of removed) {\n\t\t\t\tawait options.target.exec(`rm -rf ${releasesPath}/${releaseId}`, { timeoutMs: 60_000 });\n\t\t\t}\n\t\t\treturn { removed };\n\t\t},\n\t\treadReleaseMeta: readMeta,\n\t\trollback: async (releaseId) => {\n\t\t\tif (disposed) throw new Error('Deployer is disposed');\n\t\t\tawait ensureRoot();\n\t\t\tawait cleanOrphanedSymlink();\n\t\t\tconst exists = await options.target.exec(\n\t\t\t\t`test -d ${releasesPath}/${releaseId} && echo ok || echo missing`,\n\t\t\t\t{ timeoutMs: 5_000 },\n\t\t\t);\n\t\t\tif (!exists.stdout.includes('ok')) {\n\t\t\t\tthrow new Error(`rollback: release ${releaseId} not found at ${releasesPath}/${releaseId}`);\n\t\t\t}\n\t\t\t// Rollback steps: re-link + restart (no upload, no install, no build).\n\t\t\tconst rollbackSteps: DeployStep[] = defaultBunPipeline().filter((step) =>\n\t\t\t\tstep.name === 'link' || step.name === 'restart' || step.name === 'verify',\n\t\t\t);\n\t\t\tconst prior = await readMeta(releaseId);\n\t\t\treturn runSteps(rollbackSteps, releaseId, {\n\t\t\t\talreadyCompleted: [],\n\t\t\t\tannotations: prior?.annotations ?? {},\n\t\t\t\tdryRun: false,\n\t\t\t});\n\t\t},\n\t\tstatus: async () => {\n\t\t\tif (disposed) throw new Error('Deployer is disposed');\n\t\t\tif (!processManager.status) return 'unknown';\n\t\t\treturn processManager.status(options.target, activeProcessContext());\n\t\t},\n\t\tstop: async () => {\n\t\t\tif (disposed) throw new Error('Deployer is disposed');\n\t\t\tif (!processManager.stop) {\n\t\t\t\tthrow new Error('Process manager does not support stop');\n\t\t\t}\n\t\t\tawait processManager.stop(options.target, activeProcessContext());\n\t\t},\n\t};\n};\n"
8
8
  ],
9
- "mappings": "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",
10
- "debugId": "36BAF079BB9C009564756E2164756E21",
9
+ "mappings": ";;AAuBA;AACA;AA+CA,IAAM,eAAe,OACpB,QACA,WACqB;AAAA,EACrB,IAAI,CAAC;AAAA,IAAQ,OAAO;AAAA,EACpB,MAAM,UAAU,IAAI;AAAA,EACpB,IAAI,SAAS;AAAA,EACb,IAAI,YAAY;AAAA,EAChB,MAAM,SAAS,OAAO,UAAU;AAAA,EAChC,IAAI;AAAA,IACH,OAAO,MAAM;AAAA,MACZ,QAAQ,MAAM,UAAU,MAAM,OAAO,KAAK;AAAA,MAC1C,IAAI;AAAA,QAAM;AAAA,MACV,MAAM,QAAQ,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;AAAA,MACpD,aAAa;AAAA,MACb,IAAI,CAAC;AAAA,QAAQ;AAAA,MACb,UAAU;AAAA,MACV,IAAI,UAAU,OAAO,QAAQ;AAAA,CAAI;AAAA,MACjC,OAAO,YAAY,IAAI;AAAA,QACtB,MAAM,OAAO,OAAO,MAAM,GAAG,OAAO,EAAE,QAAQ,OAAO,EAAE;AAAA,QACvD,IAAI,KAAK,SAAS;AAAA,UAAG,OAAO,IAAI;AAAA,QAChC,SAAS,OAAO,MAAM,UAAU,CAAC;AAAA,QACjC,UAAU,OAAO,QAAQ;AAAA,CAAI;AAAA,MAC9B;AAAA,IACD;AAAA,IACA,MAAM,OAAO,QAAQ,OAAO;AAAA,IAC5B,aAAa;AAAA,IACb,IAAI,WAAW,SAAS,MAAM,SAAS;AAAA,MAAG,QAAQ,SAAS,MAAM,QAAQ,OAAO,EAAE,CAAC;AAAA,YAClF;AAAA,IACD,OAAO,YAAY;AAAA;AAAA,EAEpB,OAAO;AAAA;AAGR,IAAM,WAAW,OAChB,MACA,YAOyB;AAAA,EACzB,MAAM,OAAO,IAAI,MAAM,MAAM;AAAA,IAC5B,KAAK,QAAQ;AAAA,IACb,KAAK,QAAQ;AAAA,IACb,QAAQ;AAAA,IACR,OAAO,QAAQ,UAAU,YAAY,WAAW;AAAA,IAChD,QAAQ;AAAA,EACT,CAAC;AAAA,EAED,IAAI,QAAQ,UAAU,aAAa,KAAK,OAAO;AAAA,IAI9C,MAAM,OAAO,KAAK;AAAA,IAIlB,MAAM,QAAQ,KAAK,MAAM,QAAQ,KAAK;AAAA,IACtC,IAAI,SAAS,OAAQ,MAA0B,SAAS,YAAY;AAAA,MACnE,MAAM;AAAA,IACP;AAAA,IACA,MAAM,QAAQ,KAAK,IAAI;AAAA,IACvB,IAAI,SAAS,OAAQ,MAAwB,SAAS,YAAY;AAAA,MACjE,MAAM;AAAA,IACP;AAAA,EACD;AAAA,EAEA,MAAM,UAAU,QAAQ,aAAa;AAAA,EACrC,IAAI;AAAA,EACJ,IAAI,UAAU,GAAG;AAAA,IAChB,QAAQ,WAAW,MAAM;AAAA,MACxB,IAAI;AAAA,QAAE,KAAK,KAAK;AAAA,QAAK,MAAM;AAAA,OACzB,OAAO;AAAA,EACX;AAAA,EAEA,MAAM,gBAAgB,aACrB,KAAK,QACL,QAAQ,QAAQ,CAAC,SAAS,QAAQ,MAAO,MAAM,QAAQ,IAAI,SAC5D;AAAA,EACA,MAAM,gBAAgB,aACrB,KAAK,QACL,QAAQ,QAAQ,CAAC,SAAS,QAAQ,MAAO,MAAM,QAAQ,IAAI,SAC5D;AAAA,EAEA,OAAO,QAAQ,QAAQ,YAAY,MAAM,QAAQ,IAAI;AAAA,IACpD;AAAA,IACA;AAAA,IACA,KAAK;AAAA,EACN,CAAC;AAAA,EACD,IAAI;AAAA,IAAO,aAAa,KAAK;AAAA,EAE7B,OAAO,EAAE,UAAU,YAAY,IAAI,QAAQ,OAAO;AAAA;AAG5C,IAAM,cAAc,CAAC,YAAwC;AAAA,EACnE,MAAM,UAAU,KAAK,QAAQ,IAAI;AAAA,EACjC,MAAM,aAAa,YAAY;AAAA,IAAE,MAAM,MAAM,QAAQ,MAAM,EAAE,WAAW,KAAK,CAAC;AAAA;AAAA,EAE9E,OAAO;AAAA,IACN,aAAa,SAAS,QAAQ;AAAA,IAC9B,MAAM,OAAO,KAAK,SAAS;AAAA,MAC1B,MAAM,WAAW;AAAA,MACjB,OAAO,SAAS,CAAC,MAAM,MAAM,GAAG,GAAG;AAAA,QAClC,KAAK,MAAM,OAAO,QAAQ;AAAA,QAC1B,KAAK,KAAK,QAAQ,QAAQ,YAAa,MAAM,OAAO,CAAC,EAAG;AAAA,QACxD,OAAO,MAAM;AAAA,QACb,OAAO,MAAM;AAAA,QACb,WAAW,MAAM;AAAA,MAClB,CAAC;AAAA;AAAA,IAEF,QAAQ,OAAO,WAAW,YAAY,SAAS;AAAA,MAC9C,MAAM,WAAW;AAAA,MACjB,MAAM,OAAO,WAAW,WAAW,GAAG,IAAI,aAAa,KAAK,QAAQ,MAAM,UAAU;AAAA,MACpF,MAAM,OAAO,CAAC,SAAS,IAAI;AAAA,MAC3B,IAAI,MAAM;AAAA,QAAe,KAAK,KAAK,UAAU;AAAA,MAC7C,WAAW,WAAW,MAAM,WAAW,CAAC;AAAA,QAAG,KAAK,KAAK,aAAa,OAAO;AAAA,MAEzE,KAAK,KAAK,WAAW,IAAI;AAAA,MACzB,MAAM,SAAS,MAAM,SAAS,MAAM,EAAE,WAAW,OAAQ,CAAC;AAAA,MAC1D,IAAI,OAAO,aAAa,GAAG;AAAA,QAC1B,MAAM,IAAI,MAAM,6BAA6B,OAAO,cAAc,OAAO,UAAU,OAAO,QAAQ;AAAA,MACnG;AAAA;AAAA,EAEF;AAAA;AA+BD,IAAM,kBAAkB,CAAC,YAAsC;AAAA,EAC9D,MAAM,OAAO,QAAQ,QAAQ;AAAA,EAC7B,OAAO,GAAG,QAAQ,QAAQ;AAAA;AAG3B,IAAM,eAAe,CAAC,YAAwC;AAAA,EAC7D,MAAM,QAAkB,CAAC;AAAA,EACzB,IAAI,QAAQ,SAAS,aAAa,QAAQ,SAAS;AAAA,IAAI,MAAM,KAAK,MAAM,OAAO,QAAQ,IAAI,CAAC;AAAA,EAC5F,IAAI,QAAQ,aAAa;AAAA,IAAW,MAAM,KAAK,MAAM,QAAQ,QAAQ;AAAA,EAErE,MAAM,KAAK,MAAM,iBAAiB,MAAM,kCAAkC;AAAA,EAC1E,WAAW,QAAQ,QAAQ,YAAY,CAAC;AAAA,IAAG,MAAM,KAAK,IAAI;AAAA,EAC1D,OAAO;AAAA;AAGR,IAAM,aAAa,CAAC,UAA0B,IAAI,MAAM,QAAQ,MAAM,OAAO;AAE7E,IAAM,iBAAiB,CAAC,KAAa,SAA0C;AAAA,EAC9E,MAAM,MAAM,MAAM;AAAA,EAClB,MAAM,YAAY,MACf,OAAO,QAAQ,GAAG,EAAE,IAAI,EAAE,GAAG,OAAO,GAAG,KAAK,WAAW,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,MACzE;AAAA,EACH,IAAI,MAAM,KAAK;AAAA,IACd,OAAO,MAAM,WAAW,KAAK,GAAG,QAAQ,YAAY;AAAA,EACrD;AAAA,EACA,OAAO,GAAG,YAAY;AAAA;AAGhB,IAAM,YAAY,CAAC,YAAsC;AAAA,EAC/D,MAAM,SAAS,gBAAgB,OAAO;AAAA,EACtC,MAAM,WAAW,QAAQ,SAAS;AAAA,EAElC,OAAO;AAAA,IACN,aAAa,OAAO,SAAS,QAAQ,QAAQ,QAAQ,SAAS,KAAK,IAAI,QAAQ,SAAS;AAAA,IACxF,MAAM,OAAO,KAAK,SAAS;AAAA,MAC1B,MAAM,OAAO,CAAC,OAAO,GAAG,aAAa,OAAO,CAAC;AAAA,MAC7C,WAAW,QAAQ,QAAQ,cAAc,CAAC;AAAA,QAAG,KAAK,KAAK,MAAM,WAAW,MAAM;AAAA,MAC9E,KAAK,KAAK,QAAQ,eAAe,KAAK,IAAI,CAAC;AAAA,MAC3C,OAAO,SAAS,MAAM;AAAA,QACrB,OAAO,MAAM;AAAA,QACb,OAAO,MAAM;AAAA,QACb,WAAW,MAAM;AAAA,MAClB,CAAC;AAAA;AAAA,IAEF,QAAQ,OAAO,WAAW,YAAY,SAAS;AAAA,MAC9C,IAAI,UAAU;AAAA,QACb,MAAM,SAAS,CAAC,OAAO,GAAG,aAAa,OAAO,CAAC,EAAE,IAAI,CAAC,SAAS,KAAK,SAAS,GAAG,IAAI,IAAI,UAAU,IAAI,EAAE,KAAK,GAAG;AAAA,QAChH,MAAM,QAAO,CAAC,SAAS,OAAO,MAAM,MAAM;AAAA,QAC1C,IAAI,MAAM;AAAA,UAAe,MAAK,KAAK,UAAU;AAAA,QAC7C,WAAW,WAAW,MAAM,WAAW,CAAC;AAAA,UAAG,MAAK,KAAK,aAAa,OAAO;AAAA,QACzE,MAAK,KAAK,WAAW,GAAG,UAAU,YAAY;AAAA,QAC9C,MAAM,UAAS,MAAM,SAAS,OAAM,EAAE,WAAW,OAAQ,CAAC;AAAA,QAC1D,IAAI,QAAO,aAAa,GAAG;AAAA,UAC1B,MAAM,IAAI,MAAM,6BAA6B,QAAO,cAAc,QAAO,UAAU,QAAO,QAAQ;AAAA,QACnG;AAAA,QACA;AAAA,MACD;AAAA,MAEA,MAAM,OAAO,CAAC,OAAO,MAAM,GAAG,aAAa,OAAO,GAAG,WAAW,GAAG,UAAU,YAAY;AAAA,MACzF,MAAM,SAAS,MAAM,SAAS,MAAM,EAAE,WAAW,OAAQ,CAAC;AAAA,MAC1D,IAAI,OAAO,aAAa,GAAG;AAAA,QAC1B,MAAM,IAAI,MAAM,2BAA2B,OAAO,cAAc,OAAO,UAAU,OAAO,QAAQ;AAAA,MACjG;AAAA;AAAA,EAEF;AAAA;;ACpPD,IAAM,UAAU,CAAC,YAAoB,YAAY,WAAW;AAC5D,IAAM,SAAS,CAAC,YAAoB,YAAY;AAEzC,IAAM,cAAc,CAAC,UAA8B,CAAC,MAAsB;AAAA,EAChF,MAAM,UAAU,QAAQ,WAAW;AAAA,EACnC,MAAM,UAAU,QAAQ,mBAAmB;AAAA,EAE3C,MAAM,YAAY,CAAC,QAClB,OAAO,QAAQ,GAAG,EAAE,IAAI,EAAE,GAAG,OAAO,GAAG,MAAM,EAAE,QAAQ,MAAM,OAAO,IAAI,EAAE,KAAK,GAAG;AAAA,EAEnF,MAAM,WAAW,CAAC,QAAuC;AAAA,IACxD,MAAM,MAAM,UAAU,IAAI,GAAG;AAAA,IAC7B,MAAM,MAAM,QAAQ,IAAI,OAAO;AAAA,IAC/B,MAAM,MAAM,GAAG,OAAO,IAAI,OAAO,KAAK;AAAA,IACtC,MAAM,MAAM,GAAG,OAAO,IAAI,OAAO,KAAK;AAAA,IACtC,OAAO;AAAA,qBACY,QAAQ,OAAO,IAAI,OAAO;AAAA,KAC1C,IAAI;AAAA,YACG,cAAc,QAAQ,QAAQ,MAAM,OAAO,SAAS,WAAW;AAAA,YAC/D;AAAA,EACV,KAAK;AAAA;AAAA,EAGN,MAAM,UAAU,CAAC,QAAuC;AAAA,YAC7C,QAAQ,IAAI,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QASvB,QAAQ,IAAI,OAAO;AAAA,EACzB,KAAK;AAAA,EAEN,OAAO;AAAA,IACN,QAAQ,OAAO,QAAQ,QAAQ;AAAA,MAC9B,MAAM,OAAO,MAAM,OAAO,KAAK,QAAQ,GAAG,GAAG,EAAE,OAAO,IAAI,OAAO,WAAW,MAAO,CAAC;AAAA,MACpF,IAAI,KAAK,aAAa,GAAG;AAAA,QACxB,MAAM,IAAI,MAAM,iCAAiC,KAAK,cAAc,KAAK,QAAQ;AAAA,MAClF;AAAA,MACA,MAAM,QAAQ,MAAM,OAAO,KAAK,SAAS,GAAG,GAAG,EAAE,OAAO,IAAI,OAAO,WAAW,MAAO,CAAC;AAAA,MACtF,IAAI,MAAM,aAAa,GAAG;AAAA,QACzB,MAAM,IAAI,MAAM,kCAAkC,MAAM,cAAc,MAAM,QAAQ;AAAA,MACrF;AAAA;AAAA,IAED,QAAQ,OAAO,QAAQ,QAAQ;AAAA,MAC9B,MAAM,SAAS,MAAM,OAAO,KAC3B,aAAa,QAAQ,IAAI,OAAO,oHAChC,EAAE,WAAW,KAAM,CACpB;AAAA,MACA,MAAM,MAAM,OAAO,OAAO,KAAK;AAAA,MAC/B,IAAI,QAAQ;AAAA,QAAW,OAAO;AAAA,MAC9B,IAAI,QAAQ;AAAA,QAAW,OAAO;AAAA,MAC9B,OAAO;AAAA;AAAA,IAER,MAAM,OAAO,QAAQ,QAAQ;AAAA,MAC5B,MAAM,SAAS,MAAM,OAAO,KAAK,QAAQ,GAAG,GAAG,EAAE,OAAO,IAAI,OAAO,WAAW,MAAO,CAAC;AAAA,MACtF,IAAI,OAAO,aAAa,GAAG;AAAA,QAC1B,MAAM,IAAI,MAAM,iCAAiC,OAAO,cAAc,OAAO,QAAQ;AAAA,MACtF;AAAA;AAAA,EAEF;AAAA;AAwBD,IAAM,oBAAoB,CACzB,KACA,YACY;AAAA,EACZ,MAAM,OAAO,QAAQ,QAAQ;AAAA,EAC7B,MAAM,QAAQ,QAAQ,SAAS;AAAA,EAC/B,MAAM,YAAY,QAAQ,aAAa;AAAA,EACvC,MAAM,UAAU,QAAQ,WAAW;AAAA,EACnC,MAAM,WAAW,OAAO,QAAQ,IAAI,GAAG,EACrC,IAAI,EAAE,GAAG,OAAO,eAAe,KAAK,EAAE,QAAQ,MAAM,MAAK,GAAG,EAC5D,KAAK;AAAA,CAAI;AAAA,EACX,OAAO;AAAA,cACM,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA,mBAKC,IAAI;AAAA,YACX;AAAA,UACF;AAAA;AAAA,OAEH;AAAA,QACC;AAAA,EACN;AAAA,iCAC+B,IAAI;AAAA,gCACL,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAO7B,IAAM,iBAAiB,CAAC,UAAiC,CAAC,MAAsB;AAAA,EACtF,MAAM,YAAY,QAAQ,aAAa;AAAA,EACvC,MAAM,UAAU,QAAQ,WAAW;AAAA,EACnC,MAAM,WAAW,CAAC,QAA+B,QAAQ,YAAY,GAAG,IAAI;AAAA,EAE5E,OAAO;AAAA,IACN,QAAQ,OAAO,QAAQ,QAAQ;AAAA,MAC9B,MAAM,OAAO,kBAAkB,KAAK,OAAO;AAAA,MAC3C,MAAM,OAAO,SAAS,GAAG;AAAA,MAGzB,MAAM,YAAY,MAAM,OAAO,KAC9B,qBAAqB,IAAI,oBAAoB,WAAW,QACxD,EAAE,OAAO,IAAI,OAAO,OAAO,MAAM,WAAW,MAAO,CACpD;AAAA,MACA,IAAI,UAAU,aAAa,GAAG;AAAA,QAC7B,MAAM,IAAI,MAAM,6CAA6C,UAAU,cAAc,UAAU,QAAQ;AAAA,MACxG;AAAA,MACA,MAAM,SAAS,MAAM,OAAO,KAAK,GAAG,2BAA2B,EAAE,OAAO,IAAI,OAAO,WAAW,MAAO,CAAC;AAAA,MACtG,IAAI,OAAO,aAAa,GAAG;AAAA,QAC1B,MAAM,IAAI,MAAM,8CAA8C,OAAO,cAAc,OAAO,QAAQ;AAAA,MACnG;AAAA,MACA,MAAM,SAAS,MAAM,OAAO,KAAK,GAAG,oBAAoB,QAAQ,EAAE,OAAO,IAAI,OAAO,WAAW,MAAO,CAAC;AAAA,MACvG,IAAI,OAAO,aAAa,GAAG;AAAA,QAC1B,MAAM,IAAI,MAAM,uCAAuC,OAAO,cAAc,OAAO,QAAQ;AAAA,MAC5F;AAAA,MACA,MAAM,UAAU,MAAM,OAAO,KAAK,GAAG,qBAAqB,QAAQ,EAAE,OAAO,IAAI,OAAO,WAAW,MAAO,CAAC;AAAA,MACzG,IAAI,QAAQ,aAAa,GAAG;AAAA,QAC3B,MAAM,IAAI,MAAM,wCAAwC,QAAQ,cAAc,QAAQ,QAAQ;AAAA,MAC/F;AAAA;AAAA,IAED,QAAQ,OAAO,QAAQ,QAAQ;AAAA,MAC9B,MAAM,SAAS,MAAM,OAAO,KAAK,GAAG,uBAAuB,SAAS,GAAG,aAAa,EAAE,WAAW,IAAO,CAAC;AAAA,MACzG,MAAM,MAAM,OAAO,OAAO,KAAK;AAAA,MAC/B,IAAI,QAAQ;AAAA,QAAU,OAAO;AAAA,MAC7B,IAAI,QAAQ,cAAc,QAAQ;AAAA,QAAU,OAAO;AAAA,MACnD,OAAO;AAAA;AAAA,IAER,MAAM,OAAO,QAAQ,QAAQ;AAAA,MAC5B,MAAM,SAAS,MAAM,OAAO,KAAK,GAAG,kBAAkB,SAAS,GAAG,KAAK,EAAE,OAAO,IAAI,OAAO,WAAW,MAAO,CAAC;AAAA,MAC9G,IAAI,OAAO,aAAa,GAAG;AAAA,QAC1B,MAAM,IAAI,MAAM,qCAAqC,OAAO,cAAc,OAAO,QAAQ;AAAA,MAC1F;AAAA;AAAA,EAEF;AAAA;;ACzDD,IAAM,mBAAmB,CAAC,gBAAgB,QAAQ,SAAS,QAAQ,aAAa,OAAO;AAEvF,IAAM,YAA2B;AAAA,EAChC,SAAS,MAAM;AAAA,EACf,OAAO,MAAM;AAAA,EACb,WAAW,MAAM;AAAA,EACjB,aAAa,MAAM;AACpB;AAEA,IAAM,eAAe,CAAC,WAAwC;AAAA,EAC7D,SAAS,OAAO,WAAW,UAAU;AAAA,EACrC,OAAO,OAAO,SAAS,UAAU;AAAA,EACjC,WAAW,OAAO,aAAa,UAAU;AAAA,EACzC,aAAa,OAAO,eAAe,UAAU;AAC9C;AAEA,IAAM,gBAAgB,CAAC,UAAgC;AAAA,EACtD,MAAM,IAAI,MAAM;AAAA,EAChB,MAAM,OAAO,IAAI,KAAK,CAAC;AAAA,EACvB,MAAM,MAAM,CAAC,GAAW,IAAI,MAAM,EAAE,SAAS,EAAE,SAAS,GAAG,GAAG;AAAA,EAC9D,OAAO,GAAG,KAAK,eAAe,IAAI,IAAI,KAAK,YAAY,IAAI,CAAC,IAAI,IAAI,KAAK,WAAW,CAAC,KAAK,IAAI,KAAK,YAAY,CAAC,IAAI,IAAI,KAAK,cAAc,CAAC,IAAI,IAAI,KAAK,cAAc,CAAC;AAAA;AAGzK,IAAM,iBAAiB,CAAC,OAAe,WAAuB;AAAA,EAC7D,IAAI,OAAO,aAAa,GAAG;AAAA,IAC1B,MAAM,IAAI,MAAM,GAAG,sBAAsB,OAAO,cAAc,OAAO,UAAU,OAAO,UAAU,eAAe;AAAA,EAChH;AAAA;AAOM,IAAM,qBAAqB,MAAoB;AAAA,EACrD;AAAA,IACC,MAAM;AAAA,IACN,KAAK,OAAO,QAAQ;AAAA,MACnB,MAAM,SAAS,MAAM,IAAI,OAAO,KAC/B,YAAY,IAAI,eAChB,EAAE,OAAO,CAAC,MAAM,WAAW,IAAI,MAAM,MAAM,MAAM,QAAQ,SAAS,EAAE,CACrE;AAAA,MACA,eAAe,kBAAkB,MAAM;AAAA;AAAA,EAEzC;AAAA,EACA;AAAA,IACC,MAAM;AAAA,IACN,KAAK,OAAO,QAAQ;AAAA,MACnB,IAAI,IAAI,OAAO,SAAS,aAAa;AAAA,QACpC,MAAM,IAAI,MAAM,4BAA6B,IAAI,OAA4B,MAAM;AAAA,MACpF;AAAA,MAEA,MAAM,YAAY,IAAI,OAAO,KAAK,SAAS,GAAG,IAAI,IAAI,OAAO,OAAO,GAAG,IAAI,OAAO;AAAA,MAClF,MAAM,IAAI,OAAO,OAAO,WAAW,IAAI,aAAa;AAAA,QACnD,SAAS,IAAI,OAAO,WAAW;AAAA,MAChC,CAAC;AAAA;AAAA,EAEH;AAAA,EACA;AAAA,IACC,MAAM;AAAA,IACN,KAAK,OAAO,QAAQ;AAAA,MACnB,MAAM,SAAS,MAAM,IAAI,OAAO,KAC/B,4BACA;AAAA,QACC,KAAK,IAAI;AAAA,QACT,KAAK,IAAI;AAAA,QACT,OAAO,CAAC,MAAM,WAAW,IAAI,MAAM,MAAM,MAAM,QAAQ,SAAS;AAAA,QAChE,WAAW;AAAA,MACZ,CACD;AAAA,MACA,eAAe,WAAW,MAAM;AAAA;AAAA,EAElC;AAAA,EACA;AAAA,IACC,MAAM;AAAA,IACN,KAAK,OAAO,QAAQ;AAAA,MAEnB,MAAM,QAAQ,MAAM,IAAI,OAAO,KAC9B,+CACA,EAAE,KAAK,IAAI,aAAa,WAAW,IAAO,CAC3C;AAAA,MACA,IAAI,CAAC,MAAM,OAAO,SAAS,SAAS;AAAA,QAAG;AAAA,MACvC,MAAM,SAAS,MAAM,IAAI,OAAO,KAC/B,iBACA;AAAA,QACC,KAAK,IAAI;AAAA,QACT,KAAK,IAAI;AAAA,QACT,OAAO,CAAC,MAAM,WAAW,IAAI,MAAM,MAAM,MAAM,QAAQ,OAAO;AAAA,QAC9D,WAAW;AAAA,MACZ,CACD;AAAA,MACA,eAAe,SAAS,MAAM;AAAA;AAAA,EAEhC;AAAA,EACA;AAAA,IACC,MAAM;AAAA,IACN,KAAK,OAAO,QAAQ;AAAA,MAEnB,MAAM,UAAU,GAAG,IAAI;AAAA,MACvB,MAAM,SAAS,MAAM,IAAI,OAAO,KAC/B,WAAW,IAAI,eAAe,qBAAqB,WAAW,IAAI,eAClE,EAAE,OAAO,CAAC,MAAM,WAAW,IAAI,MAAM,MAAM,MAAM,QAAQ,MAAM,GAAG,WAAW,IAAO,CACrF;AAAA,MACA,eAAe,QAAQ,MAAM;AAAA;AAAA,EAE/B;AAAA,EACA;AAAA,IACC,MAAM;AAAA,IACN,KAAK,OAAO,QAAQ;AAAA,MACnB,MAAM,IAAI,eAAe,OAAO,IAAI,QAAQ;AAAA,QAC3C,SAAS,IAAI;AAAA,QACb,aAAa,IAAI;AAAA,QACjB,KAAK,IAAI;AAAA,QACT,OAAO,CAAC,MAAM,WAAW,IAAI,MAAM,MAAM,MAAM,QAAQ,SAAS;AAAA,QAChE,WAAW,IAAI;AAAA,QACf,aAAa,IAAI;AAAA,MAClB,CAAC;AAAA;AAAA,EAEH;AAAA,EACA;AAAA,IACC,MAAM;AAAA,IACN,KAAK,OAAO,QAAQ;AAAA,MACnB,IAAI,CAAC,IAAI;AAAA,QAAQ;AAAA,MACjB,MAAM,UAAU,GAAG;AAAA;AAAA,EAErB;AACD;AAEA,IAAM,YAAY,OAAO,QAAsC;AAAA,EAC9D,MAAM,OAAO,IAAI;AAAA,EACjB,IAAI,KAAK,SAAS,UAAU;AAAA,IAC3B,MAAM,KAAK,MAAM,KAAK,MAAM,GAAG;AAAA,IAC/B,IAAI,CAAC;AAAA,MAAI,MAAM,IAAI,MAAM,qCAAqC;AAAA,IAC9D;AAAA,EACD;AAAA,EACA,IAAI,KAAK,SAAS,QAAQ;AAAA,IACzB,MAAM,WAAU,KAAK,WAAW;AAAA,IAChC,MAAM,cAAa,KAAK,cAAc;AAAA,IACtC,MAAM,eAAe,KAAK,gBAAgB;AAAA,IAC1C,SAAS,UAAU,EAAG,WAAW,UAAS,WAAW;AAAA,MACpD,MAAM,QAAQ,MAAM,IAAI,OAAO,KAC9B,uDAAuD,KAAK,OAC5D,EAAE,WAAW,IAAO,CACrB;AAAA,MACA,MAAM,OAAO,OAAO,MAAM,OAAO,KAAK,CAAC;AAAA,MACvC,IAAI,SAAS;AAAA,QAAc;AAAA,MAC3B,IAAI,UAAU;AAAA,QAAS,MAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,WAAU,CAAC;AAAA,IACtF;AAAA,IACA,MAAM,IAAI,MAAM,gBAAgB,KAAK,sBAAsB,sBAAsB,kBAAiB;AAAA,EACnG;AAAA,EAEA,MAAM,UAAU,KAAK,WAAW;AAAA,EAChC,MAAM,aAAa,KAAK,cAAc;AAAA,EACtC,SAAS,UAAU,EAAG,WAAW,SAAS,WAAW;AAAA,IACpD,MAAM,QAAQ,MAAM,IAAI,OAAO,KAC9B,2BAA2B,KAAK,QAAQ,KAAK,iDAC7C,EAAE,WAAW,IAAO,CACrB;AAAA,IACA,IAAI,MAAM,OAAO,SAAS,MAAM;AAAA,MAAG;AAAA,IACnC,IAAI,UAAU;AAAA,MAAS,MAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,UAAU,CAAC;AAAA,EACtF;AAAA,EACA,MAAM,IAAI,MAAM,eAAe,KAAK,QAAQ,KAAK,uBAAuB,iBAAiB;AAAA;AAOnF,IAAM,iBAAiB,CAAC,YAAuC;AAAA,EACrE,MAAM,QAAQ,QAAQ,SAAS,KAAK;AAAA,EACpC,MAAM,QAAQ,aAAa,QAAQ,KAAK;AAAA,EACxC,MAAM,WAAW,QAAQ,YAAY,QAAQ,QAAQ;AAAA,EACrD,MAAM,cAAc,GAAG;AAAA,EACvB,MAAM,eAAe,GAAG;AAAA,EACxB,MAAM,MAA8B,EAAE,UAAU,iBAAiB,QAAQ,IAAI;AAAA,EAC7E,MAAM,iBAAiB,QAAQ,kBAAkB,YAAY;AAAA,EAC7D,MAAM,SAAS,QAAQ,WAAW,YAAY,OAAO,QAAQ;AAAA,EAC7D,IAAI,WAAW;AAAA,EAEf,MAAM,WAAW,CAChB,WACA,UACoB;AAAA,IACpB,aAAa,KAAK;AAAA,IAClB,SAAS,QAAQ;AAAA,IACjB;AAAA,IACA,QAAQ,KAAK;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ,KAAK;AAAA,IACb;AAAA,IACA,aAAa,GAAG,gBAAgB;AAAA,IAChC,QAAQ,QAAQ;AAAA,IAChB,QAAQ,QAAQ;AAAA,IAChB;AAAA,EACD;AAAA,EAEA,MAAM,WAAW,CAAC,cAAsB,GAAG,gBAAgB;AAAA,EAE3D,MAAM,YAAY,OAAO,WAAmB,WAAyC;AAAA,IACpF,MAAM,OAAO,KAAK,UAAU,MAAM;AAAA,IAElC,MAAM,SAAS,MAAM,QAAQ,OAAO,KAAK,SAAS,SAAS,SAAS,KAAK;AAAA,MACxE,OAAO;AAAA,MACP,WAAW;AAAA,IACZ,CAAC;AAAA,IACD,IAAI,OAAO,aAAa,GAAG;AAAA,MAE1B,QAAQ,KAAK,sBAAsB,sBAAsB,OAAO,UAAU,OAAO,QAAQ;AAAA,IAC1F;AAAA;AAAA,EAGD,MAAM,WAAW,OAAO,cAAqD;AAAA,IAC5E,MAAM,SAAS,MAAM,QAAQ,OAAO,KAAK,OAAO,SAAS,SAAS,yBAAyB;AAAA,MAC1F,WAAW;AAAA,IACZ,CAAC;AAAA,IACD,MAAM,OAAO,OAAO,OAAO,KAAK;AAAA,IAChC,IAAI,KAAK,WAAW;AAAA,MAAG,OAAO;AAAA,IAC9B,IAAI;AAAA,MACH,OAAO,KAAK,MAAM,IAAI;AAAA,MACrB,MAAM;AAAA,MACP,OAAO;AAAA;AAAA;AAAA,EAIT,MAAM,uBAAuB,YAA2B;AAAA,IAIvD,MAAM,QAAQ,OAAO,KAAK,SAAS,oBAAoB,EAAE,WAAW,KAAM,CAAC;AAAA;AAAA,EAG5E,MAAM,WAAW,OAChB,OACA,WACA,YAM2B;AAAA,IAC3B,MAAM,MAAM,SAAS,WAAW;AAAA,MAC/B,aAAa,QAAQ;AAAA,MACrB,QAAQ,QAAQ;AAAA,MAChB,QAAQ,QAAQ;AAAA,IACjB,CAAC;AAAA,IACD,MAAM,gBAA2E,CAAC;AAAA,IAClF,MAAM,YAAY,MAAM;AAAA,IACxB,MAAM,iBAA2B,CAAC,GAAG,QAAQ,gBAAgB;AAAA,IAE7D,MAAM,SAAwB;AAAA,MAC7B,aAAa,QAAQ;AAAA,MACrB;AAAA,MACA;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,IACT;AAAA,IAIA,WAAW,QAAQ,OAAO;AAAA,MACzB,IAAI,QAAQ,eAAe;AAAA,MAC3B,IAAI,eAAe,SAAS,KAAK,IAAI,KAAK,KAAK,SAAS,UAAU;AAAA,QAGjE,cAAc,KAAK,EAAE,YAAY,GAAG,MAAM,KAAK,MAAM,SAAS,KAAK,CAAC;AAAA,QACpE;AAAA,MACD;AAAA,MAEA,MAAM,gBAAgB,MAAM;AAAA,MAC5B,MAAM,MAAM,YAAY,EAAE,MAAM,KAAK,MAAM,UAAU,CAAC;AAAA,MAEtD,IAAI,QAAQ,QAAQ;AAAA,QACnB,MAAM,MAAM,wBAAwB,KAAK,QAAQ,UAAU,KAAK,IAAI;AAAA,QACpE,cAAc,KAAK,EAAE,YAAY,GAAG,MAAM,KAAK,MAAM,SAAS,KAAK,CAAC;AAAA,QACpE,MAAM,MAAM,UAAU,EAAE,YAAY,GAAG,MAAM,KAAK,MAAM,UAAU,CAAC;AAAA,QACnE;AAAA,MACD;AAAA,MAEA,IAAI;AAAA,QACH,MAAM,KAAK,IAAI,GAAG;AAAA,QAClB,IAAI,QAAQ,eAAe;AAAA,QAC1B,OAAO,OAAO;AAAA,QACf,MAAM,MAAM,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAAA,QACpE,OAAO,SAAS;AAAA,QAChB,OAAO,aAAa,KAAK;AAAA,QACzB,OAAO,UAAU,MAAM;AAAA,QAEvB,MAAM,UAAU,WAAW,MAAM;AAAA,QACjC,MAAM,MAAM,QAAQ,EAAE,OAAO,KAAK,WAAW,MAAM,KAAK,KAAK,CAAC;AAAA,QAC9D,MAAM;AAAA;AAAA,MAGP,MAAM,aAAa,MAAM,IAAI;AAAA,MAC7B,cAAc,KAAK,EAAE,YAAY,MAAM,KAAK,KAAK,CAAC;AAAA,MAClD,eAAe,KAAK,KAAK,IAAI;AAAA,MAC7B,MAAM,MAAM,UAAU,EAAE,YAAY,MAAM,KAAK,MAAM,UAAU,CAAC;AAAA,MAGhE,IAAI,KAAK,SAAS,WAAW;AAAA,QAC5B,MAAM,UAAU,WAAW,MAAM;AAAA,MAClC;AAAA,IACD;AAAA,IAEA,OAAO,SAAS;AAAA,IAChB,OAAO,UAAU,MAAM;AAAA,IACvB,IAAI,CAAC,QAAQ;AAAA,MAAQ,MAAM,UAAU,WAAW,MAAM;AAAA,IAEtD,OAAO;AAAA,MACN,aAAa,QAAQ;AAAA,MACrB;AAAA,MACA,YAAY,MAAM,IAAI;AAAA,MACtB;AAAA,MACA,aAAa,IAAI;AAAA,MACjB,OAAO;AAAA,IACR;AAAA;AAAA,EAGD,MAAM,aAAa,YAAY;AAAA,IAC9B,MAAM,SAAS,MAAM,QAAQ,OAAO,KAAK,YAAY,gBAAgB,EAAE,WAAW,IAAO,CAAC;AAAA,IAC1F,eAAe,cAAc,MAAM;AAAA;AAAA,EAEpC,MAAM,uBAAuB,OAA8B;AAAA,IAC1D,SAAS,QAAQ;AAAA,IACjB;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX,aAAa;AAAA,EACd;AAAA,EAEA,OAAO;AAAA,IACN,QAAQ,OAAO,aAA4B,CAAC,MAAM;AAAA,MACjD,IAAI;AAAA,QAAU,MAAM,IAAI,MAAM,sBAAsB;AAAA,MACpD,MAAM,WAAW;AAAA,MACjB,MAAM,qBAAqB;AAAA,MAE3B,MAAM,cAAc,WAAW,eAAe,CAAC;AAAA,MAC/C,MAAM,SAAS,WAAW,UAAU;AAAA,MAEpC,IAAI,WAAW,oBAAoB,WAAW;AAAA,QAC7C,MAAM,QAAQ,MAAM,SAAS,WAAW,eAAe;AAAA,QACvD,IAAI,CAAC,OAAO;AAAA,UACX,MAAM,IAAI,MACT,4CAA4C,WAAW,iBACxD;AAAA,QACD;AAAA,QACA,IAAI,MAAM,WAAW,aAAa;AAAA,UACjC,MAAM,IAAI,MACT,mBAAmB,WAAW,mCAC/B;AAAA,QACD;AAAA,QACA,OAAO,SAAS,QAAQ,SAAS,mBAAmB,GAAG,WAAW,iBAAiB;AAAA,UAClF,kBAAkB,MAAM;AAAA,UACxB,aAAa,MAAM,eAAe;AAAA,UAClC;AAAA,UACA,QAAQ,WAAW;AAAA,QACpB,CAAC;AAAA,MACF;AAAA,MAEA,MAAM,YAAY,cAAc,KAAK;AAAA,MACrC,OAAO,SAAS,QAAQ,SAAS,mBAAmB,GAAG,WAAW;AAAA,QACjE,kBAAkB,CAAC;AAAA,QACnB;AAAA,QACA;AAAA,QACA,QAAQ,WAAW;AAAA,MACpB,CAAC;AAAA;AAAA,IAEF,SAAS,YAAY;AAAA,MACpB,WAAW;AAAA,MACX,IAAI,QAAQ,OAAO;AAAA,QAAO,MAAM,QAAQ,OAAO,MAAM;AAAA;AAAA,IAEtD,cAAc,YAAY;AAAA,MACzB,MAAM,WAAW;AAAA,MACjB,MAAM,SAAS,MAAM,QAAQ,OAAO,KACnC,SAAS,oCACT,EAAE,WAAW,IAAO,CACrB;AAAA,MACA,OAAO,OAAO,OACZ,MAAM;AAAA,CAAI,EACV,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,EACzB,OAAO,CAAC,SAAS,KAAK,SAAS,CAAC,EAChC,KAAK;AAAA;AAAA,IAER,OAAO,SAAS,WAAW;AAAA,MAC1B,IAAI;AAAA,QAAU,MAAM,IAAI,MAAM,sBAAsB;AAAA,MACpD,MAAM,MAAM,OAAO,YAAY;AAAA,QAC9B,MAAM,SAAS,MAAM,QAAQ,OAAO,KACnC,SAAS,oCACT,EAAE,WAAW,IAAO,CACrB;AAAA,QACA,OAAO,OAAO,OACZ,MAAM;AAAA,CAAI,EACV,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,EACzB,OAAO,CAAC,SAAS,KAAK,SAAS,CAAC,EAChC,KAAK;AAAA,SACL;AAAA,MACH,IAAI,IAAI,UAAU;AAAA,QAAM,OAAO,EAAE,SAAS,CAAC,EAAE;AAAA,MAC7C,MAAM,UAAU,IAAI,MAAM,GAAG,IAAI,SAAS,IAAI;AAAA,MAC9C,WAAW,aAAa,SAAS;AAAA,QAChC,MAAM,QAAQ,OAAO,KAAK,UAAU,gBAAgB,aAAa,EAAE,WAAW,MAAO,CAAC;AAAA,MACvF;AAAA,MACA,OAAO,EAAE,QAAQ;AAAA;AAAA,IAElB,iBAAiB;AAAA,IACjB,UAAU,OAAO,cAAc;AAAA,MAC9B,IAAI;AAAA,QAAU,MAAM,IAAI,MAAM,sBAAsB;AAAA,MACpD,MAAM,WAAW;AAAA,MACjB,MAAM,qBAAqB;AAAA,MAC3B,MAAM,SAAS,MAAM,QAAQ,OAAO,KACnC,WAAW,gBAAgB,wCAC3B,EAAE,WAAW,KAAM,CACpB;AAAA,MACA,IAAI,CAAC,OAAO,OAAO,SAAS,IAAI,GAAG;AAAA,QAClC,MAAM,IAAI,MAAM,qBAAqB,0BAA0B,gBAAgB,WAAW;AAAA,MAC3F;AAAA,MAEA,MAAM,gBAA8B,mBAAmB,EAAE,OAAO,CAAC,SAChE,KAAK,SAAS,UAAU,KAAK,SAAS,aAAa,KAAK,SAAS,QAClE;AAAA,MACA,MAAM,QAAQ,MAAM,SAAS,SAAS;AAAA,MACtC,OAAO,SAAS,eAAe,WAAW;AAAA,QACzC,kBAAkB,CAAC;AAAA,QACnB,aAAa,OAAO,eAAe,CAAC;AAAA,QACpC,QAAQ;AAAA,MACT,CAAC;AAAA;AAAA,IAEF,QAAQ,YAAY;AAAA,MACnB,IAAI;AAAA,QAAU,MAAM,IAAI,MAAM,sBAAsB;AAAA,MACpD,IAAI,CAAC,eAAe;AAAA,QAAQ,OAAO;AAAA,MACnC,OAAO,eAAe,OAAO,QAAQ,QAAQ,qBAAqB,CAAC;AAAA;AAAA,IAEpE,MAAM,YAAY;AAAA,MACjB,IAAI;AAAA,QAAU,MAAM,IAAI,MAAM,sBAAsB;AAAA,MACpD,IAAI,CAAC,eAAe,MAAM;AAAA,QACzB,MAAM,IAAI,MAAM,uCAAuC;AAAA,MACxD;AAAA,MACA,MAAM,eAAe,KAAK,QAAQ,QAAQ,qBAAqB,CAAC;AAAA;AAAA,EAElE;AAAA;",
10
+ "debugId": "345BC6BD58648C9564756E2164756E21",
11
11
  "names": []
12
12
  }
package/dist/tls.d.ts CHANGED
@@ -54,6 +54,17 @@ export type IssueCertificateOptions = {
54
54
  domains: string[];
55
55
  /** DNS provider used for DNS-01 challenges (must own the zone). */
56
56
  dnsProvider: DnsProvider;
57
+ /**
58
+ * Map the public ACME challenge name to the record name written through
59
+ * `dnsProvider`. Use this for CNAME-delegated DNS-01, where customers point
60
+ * `_acme-challenge.app.example.com` at a platform-owned validation zone.
61
+ * The propagation checker still receives the public challenge name because
62
+ * that is what the certificate authority resolves.
63
+ */
64
+ mapDnsChallengeRecord?: (context: {
65
+ domain: string;
66
+ recordName: string;
67
+ }) => string;
57
68
  /** Contact email for the ACME account. */
58
69
  email: string;
59
70
  /** ACME directory URL. Default Let's Encrypt production. */
package/dist/tls.js CHANGED
@@ -326,16 +326,23 @@ var issueCertificate = async (options) => {
326
326
  const txtBytes = new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(keyAuthorization)));
327
327
  const txtValue = base64UrlEncode(txtBytes);
328
328
  const recordName = `_acme-challenge.${auth.identifier.value}`;
329
- log(`[tls] DNS-01: setting ${recordName} = "${txtValue}"`);
329
+ const providerRecordName = options.mapDnsChallengeRecord?.({
330
+ domain: auth.identifier.value,
331
+ recordName
332
+ }) ?? recordName;
333
+ if (providerRecordName.trim().length === 0) {
334
+ throw new Error("[deploy/tls] mapped DNS-01 record name must not be empty");
335
+ }
336
+ log(providerRecordName === recordName ? `[tls] DNS-01: setting ${recordName} = "${txtValue}"` : `[tls] DNS-01: setting delegated ${providerRecordName} for ${recordName} = "${txtValue}"`);
330
337
  const record = await options.dnsProvider.upsert({
331
338
  content: txtValue,
332
- name: recordName,
339
+ name: providerRecordName,
333
340
  ttl: 60,
334
341
  type: "TXT"
335
342
  });
336
343
  dnsCreated.push({
337
344
  recordId: record.id,
338
- recordName,
345
+ recordName: providerRecordName,
339
346
  recordValue: txtValue
340
347
  });
341
348
  cleanups.push(() => options.dnsProvider.delete(record.id));
@@ -525,5 +532,5 @@ export {
525
532
  AcmeError
526
533
  };
527
534
 
528
- //# debugId=F710513ADD2FEFC064756E2164756E21
535
+ //# debugId=769AB3C81A63D52E64756E2164756E21
529
536
  //# sourceMappingURL=tls.js.map
package/dist/tls.js.map CHANGED
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/tls.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * @absolutejs/deploy/tls — ACME (RFC 8555) client for Let's Encrypt and\n * compatible CAs. DNS-01 challenges only — uses the {@link DnsProvider}\n * abstraction from `./dns`, so the same Cloudflare / Route 53 /\n * Hetzner-DNS providers that point hostnames at boxes also satisfy\n * ACME's challenge requirement.\n *\n * Zero peer deps. Bun's `crypto.subtle` covers JWS signing and ECDSA\n * key generation; a small DER encoder builds the CSR.\n *\n * Public API:\n *\n * issueCertificate({ domains, dnsProvider, email })\n * → { certificatePem, privateKeyPem, account, domains }\n *\n * installCertificateOnTarget(target, cert, { certPath, keyPath, reload? })\n * → uploads PEM files via Target.upload + optional reload exec\n *\n * exportAccountKey / importAccountKey for persistence between runs\n * (reuse the same account across cert renewals — cheaper, doesn't\n * hit Let's Encrypt's account-creation rate limit)\n */\n\nimport { X509Certificate } from 'node:crypto';\nimport type { Target } from './targets';\nimport type { DnsProvider } from './dns';\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nexport const LETSENCRYPT_PRODUCTION =\n\t'https://acme-v02.api.letsencrypt.org/directory';\nexport const LETSENCRYPT_STAGING =\n\t'https://acme-staging-v02.api.letsencrypt.org/directory';\n\n// =============================================================================\n// base64url\n// =============================================================================\n\nconst base64UrlEncode = (bytes: Uint8Array | ArrayBuffer): string => {\n\tconst buf = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);\n\tlet binary = '';\n\tfor (const byte of buf) binary += String.fromCharCode(byte);\n\treturn btoa(binary).replaceAll('+', '-').replaceAll('/', '_').replaceAll('=', '');\n};\n\nconst base64UrlEncodeString = (value: string): string =>\n\tbase64UrlEncode(new TextEncoder().encode(value));\n\nconst base64UrlDecode = (value: string): Uint8Array => {\n\tconst padded = value.replaceAll('-', '+').replaceAll('_', '/').padEnd(\n\t\tMath.ceil(value.length / 4) * 4,\n\t\t'='\n\t);\n\tconst binary = atob(padded);\n\tconst bytes = new Uint8Array(binary.length);\n\tfor (let index = 0; index < binary.length; index += 1) {\n\t\tbytes[index] = binary.charCodeAt(index);\n\t}\n\treturn bytes;\n};\n\n// =============================================================================\n// DER encoding (minimal subset for CSR)\n// =============================================================================\n\nconst derLength = (length: number): Uint8Array => {\n\tif (length < 0x80) return Uint8Array.from([length]);\n\tconst bytes: number[] = [];\n\tlet remaining = length;\n\twhile (remaining > 0) {\n\t\tbytes.unshift(remaining & 0xff);\n\t\tremaining >>= 8;\n\t}\n\treturn Uint8Array.from([0x80 | bytes.length, ...bytes]);\n};\n\nconst derTag = (tag: number, payload: Uint8Array): Uint8Array => {\n\tconst length = derLength(payload.length);\n\tconst out = new Uint8Array(1 + length.length + payload.length);\n\tout[0] = tag;\n\tout.set(length, 1);\n\tout.set(payload, 1 + length.length);\n\treturn out;\n};\n\nconst derSeq = (children: Uint8Array[]): Uint8Array => {\n\tconst total = children.reduce((sum, child) => sum + child.length, 0);\n\tconst payload = new Uint8Array(total);\n\tlet offset = 0;\n\tfor (const child of children) {\n\t\tpayload.set(child, offset);\n\t\toffset += child.length;\n\t}\n\treturn derTag(0x30, payload);\n};\n\nconst derSet = (children: Uint8Array[]): Uint8Array => {\n\tconst total = children.reduce((sum, child) => sum + child.length, 0);\n\tconst payload = new Uint8Array(total);\n\tlet offset = 0;\n\tfor (const child of children) {\n\t\tpayload.set(child, offset);\n\t\toffset += child.length;\n\t}\n\treturn derTag(0x31, payload);\n};\n\nconst derInt = (value: number | Uint8Array): Uint8Array => {\n\tif (typeof value === 'number') {\n\t\t// Small unsigned int — used for the CSR version field (0).\n\t\tif (value === 0) return derTag(0x02, Uint8Array.from([0]));\n\t\tconst bytes: number[] = [];\n\t\tlet remaining = value;\n\t\twhile (remaining > 0) {\n\t\t\tbytes.unshift(remaining & 0xff);\n\t\t\tremaining >>= 8;\n\t\t}\n\t\tif ((bytes[0] as number) & 0x80) bytes.unshift(0); // ensure positive\n\t\treturn derTag(0x02, Uint8Array.from(bytes));\n\t}\n\t// Big-endian unsigned bytes — strip leading zeros, then prepend 0x00 if\n\t// the high bit is set (DER INTEGER is signed two's complement).\n\tlet start = 0;\n\twhile (start < value.length - 1 && value[start] === 0) start += 1;\n\tlet payload = value.subarray(start);\n\tif ((payload[0] as number) & 0x80) {\n\t\tconst padded = new Uint8Array(payload.length + 1);\n\t\tpadded.set(payload, 1);\n\t\tpayload = padded;\n\t}\n\treturn derTag(0x02, payload);\n};\n\nconst derOid = (oid: string): Uint8Array => {\n\tconst parts = oid.split('.').map((part) => Number.parseInt(part, 10));\n\tconst first = parts[0];\n\tconst second = parts[1];\n\tif (first === undefined || second === undefined) {\n\t\tthrow new Error(`[deploy/tls] invalid OID: ${oid}`);\n\t}\n\tconst bytes: number[] = [first * 40 + second];\n\tfor (let index = 2; index < parts.length; index += 1) {\n\t\tconst value = parts[index] as number;\n\t\tconst chunks: number[] = [];\n\t\tlet remaining = value;\n\t\tdo {\n\t\t\tchunks.unshift(remaining & 0x7f);\n\t\t\tremaining >>= 7;\n\t\t} while (remaining > 0);\n\t\tfor (let chunkIndex = 0; chunkIndex < chunks.length - 1; chunkIndex += 1) {\n\t\t\tchunks[chunkIndex] = (chunks[chunkIndex] as number) | 0x80;\n\t\t}\n\t\tbytes.push(...chunks);\n\t}\n\treturn derTag(0x06, Uint8Array.from(bytes));\n};\n\nconst derPrintableString = (value: string): Uint8Array =>\n\tderTag(0x13, new TextEncoder().encode(value));\n\nconst derUtf8String = (value: string): Uint8Array =>\n\tderTag(0x0c, new TextEncoder().encode(value));\n\nconst derIa5String = (value: string): Uint8Array =>\n\tderTag(0x16, new TextEncoder().encode(value));\n\nconst derOctetString = (payload: Uint8Array): Uint8Array =>\n\tderTag(0x04, payload);\n\nconst derBitString = (payload: Uint8Array): Uint8Array => {\n\t// 0x00 prefix = number of unused bits in the final byte; for byte-aligned\n\t// signatures + keys this is always zero.\n\tconst bits = new Uint8Array(payload.length + 1);\n\tbits[0] = 0;\n\tbits.set(payload, 1);\n\treturn derTag(0x03, bits);\n};\n\nconst derContextTag = (\n\ttag: number,\n\tpayload: Uint8Array,\n\tconstructed = true\n): Uint8Array => derTag(0xa0 + tag + (constructed ? 0 : -0x20), payload);\n\n// =============================================================================\n// ECDSA signature: raw r||s ↔ DER SEQUENCE\n// =============================================================================\n\nconst ecdsaRawToDer = (rawSig: Uint8Array): Uint8Array => {\n\tconst half = rawSig.length / 2;\n\tconst r = rawSig.subarray(0, half);\n\tconst s = rawSig.subarray(half);\n\treturn derSeq([derInt(r), derInt(s)]);\n};\n\n// =============================================================================\n// JWS (RFC 7515) Flattened JSON Serialization for ACME\n// =============================================================================\n\ntype JwsHeader = {\n\talg: 'ES256';\n\tnonce: string;\n\turl: string;\n\tjwk?: JsonWebKey;\n\tkid?: string;\n};\n\ntype JwsBody = {\n\tprotected: string;\n\tpayload: string;\n\tsignature: string;\n};\n\nconst signJws = async (\n\tprivateKey: CryptoKey,\n\theader: JwsHeader,\n\tpayload: object | string\n): Promise<JwsBody> => {\n\tconst protectedHeader = base64UrlEncodeString(JSON.stringify(header));\n\tconst payloadEncoded =\n\t\tpayload === ''\n\t\t\t? '' // POST-as-GET: empty string, NOT empty object\n\t\t\t: base64UrlEncodeString(JSON.stringify(payload));\n\tconst signingInput = new TextEncoder().encode(\n\t\t`${protectedHeader}.${payloadEncoded}`\n\t);\n\tconst rawSig = new Uint8Array(\n\t\tawait crypto.subtle.sign(\n\t\t\t{ hash: 'SHA-256', name: 'ECDSA' },\n\t\t\tprivateKey,\n\t\t\tsigningInput\n\t\t)\n\t);\n\treturn {\n\t\tpayload: payloadEncoded,\n\t\tprotected: protectedHeader,\n\t\tsignature: base64UrlEncode(rawSig)\n\t};\n};\n\n// =============================================================================\n// JWK helpers — canonical thumbprint, public-key extraction\n// =============================================================================\n\nconst exportPublicJwk = async (publicKey: CryptoKey): Promise<JsonWebKey> => {\n\tconst jwk = await crypto.subtle.exportKey('jwk', publicKey);\n\t// Strip private fields if the export included them (shouldn't, on a\n\t// public key, but be defensive).\n\treturn {\n\t\tcrv: jwk.crv,\n\t\tkty: jwk.kty,\n\t\tx: jwk.x,\n\t\ty: jwk.y\n\t};\n};\n\nconst jwkThumbprint = async (publicJwk: JsonWebKey): Promise<string> => {\n\t// RFC 7638 — canonical JSON: required fields only, lex order.\n\tconst canonical = JSON.stringify({\n\t\tcrv: publicJwk.crv,\n\t\tkty: publicJwk.kty,\n\t\tx: publicJwk.x,\n\t\ty: publicJwk.y\n\t});\n\tconst hash = await crypto.subtle.digest(\n\t\t'SHA-256',\n\t\tnew TextEncoder().encode(canonical)\n\t);\n\treturn base64UrlEncode(hash);\n};\n\n// =============================================================================\n// Account key — generation + JSON export/import for persistence\n// =============================================================================\n\nexport type AcmeAccount = {\n\tkey: CryptoKeyPair;\n\t/** Account URL — set after first registration; persisted for renewals. */\n\tkid?: string;\n};\n\nexport type AcmeAccountJson = {\n\tpublicJwk: JsonWebKey;\n\tprivateJwk: JsonWebKey;\n\tkid?: string;\n};\n\nexport const generateAccountKey = async (): Promise<AcmeAccount> => {\n\tconst key = await crypto.subtle.generateKey(\n\t\t{ name: 'ECDSA', namedCurve: 'P-256' },\n\t\ttrue,\n\t\t['sign', 'verify']\n\t);\n\treturn { key };\n};\n\nexport const exportAccount = async (\n\taccount: AcmeAccount\n): Promise<AcmeAccountJson> => {\n\tconst publicJwk = await crypto.subtle.exportKey('jwk', account.key.publicKey);\n\tconst privateJwk = await crypto.subtle.exportKey(\n\t\t'jwk',\n\t\taccount.key.privateKey\n\t);\n\treturn {\n\t\tprivateJwk,\n\t\tpublicJwk,\n\t\t...(account.kid !== undefined ? { kid: account.kid } : {})\n\t};\n};\n\nexport const importAccount = async (\n\tjson: AcmeAccountJson\n): Promise<AcmeAccount> => {\n\tconst publicKey = await crypto.subtle.importKey(\n\t\t'jwk',\n\t\tjson.publicJwk,\n\t\t{ name: 'ECDSA', namedCurve: 'P-256' },\n\t\ttrue,\n\t\t['verify']\n\t);\n\tconst privateKey = await crypto.subtle.importKey(\n\t\t'jwk',\n\t\tjson.privateJwk,\n\t\t{ name: 'ECDSA', namedCurve: 'P-256' },\n\t\ttrue,\n\t\t['sign']\n\t);\n\treturn {\n\t\tkey: { privateKey, publicKey },\n\t\t...(json.kid !== undefined ? { kid: json.kid } : {})\n\t};\n};\n\n// =============================================================================\n// CSR — Certificate Signing Request\n// =============================================================================\n\nconst buildSanExtension = (domains: string[]): Uint8Array => {\n\t// SubjectAltName extension value: SEQUENCE OF GeneralName\n\tconst generalNames = domains.map((domain) =>\n\t\tderTag(0x82, new TextEncoder().encode(domain))\n\t);\n\tconst sanSequence = derSeq(generalNames);\n\tconst extensionValue = derOctetString(sanSequence);\n\treturn derSeq([derOid('2.5.29.17'), extensionValue]);\n};\n\nconst buildExtensionRequestAttribute = (domains: string[]): Uint8Array => {\n\tconst extensions = derSeq([buildSanExtension(domains)]);\n\treturn derSeq([\n\t\tderOid('1.2.840.113549.1.9.14'), // PKCS#9 extensionRequest\n\t\tderSet([extensions])\n\t]);\n};\n\nconst generateCertKeyPair = async (): Promise<CryptoKeyPair> =>\n\tcrypto.subtle.generateKey(\n\t\t{ name: 'ECDSA', namedCurve: 'P-256' },\n\t\ttrue,\n\t\t['sign', 'verify']\n\t);\n\nconst buildCsr = async (\n\tdomains: string[],\n\tkeypair: CryptoKeyPair\n): Promise<Uint8Array> => {\n\tif (domains.length === 0) {\n\t\tthrow new Error('[deploy/tls] CSR requires at least one domain');\n\t}\n\tconst commonName = domains[0] as string;\n\n\tconst spkiDer = new Uint8Array(\n\t\tawait crypto.subtle.exportKey('spki', keypair.publicKey)\n\t);\n\n\tconst version = derInt(0);\n\tconst subject = derSeq([\n\t\tderSet([\n\t\t\tderSeq([\n\t\t\t\tderOid('2.5.4.3'), // commonName\n\t\t\t\tderUtf8String(commonName)\n\t\t\t])\n\t\t])\n\t]);\n\n\tconst attributes = derContextTag(0, buildExtensionRequestAttribute(domains));\n\n\tconst certificationRequestInfo = derSeq([\n\t\tversion,\n\t\tsubject,\n\t\tspkiDer,\n\t\tattributes\n\t]);\n\n\tconst rawSig = new Uint8Array(\n\t\tawait crypto.subtle.sign(\n\t\t\t{ hash: 'SHA-256', name: 'ECDSA' },\n\t\t\tkeypair.privateKey,\n\t\t\tcertificationRequestInfo as BufferSource\n\t\t)\n\t);\n\tconst sigDer = ecdsaRawToDer(rawSig);\n\n\tconst sigAlgorithm = derSeq([derOid('1.2.840.10045.4.3.2')]);\n\t// ecdsa-with-SHA256\n\tconst signatureBits = derBitString(sigDer);\n\n\treturn derSeq([certificationRequestInfo, sigAlgorithm, signatureBits]);\n};\n\n// =============================================================================\n// PEM\n// =============================================================================\n\nconst derToPem = (label: string, der: Uint8Array): string => {\n\tconst b64 = btoa(String.fromCharCode(...der));\n\tconst lines = b64.match(/.{1,64}/g) ?? [];\n\treturn `-----BEGIN ${label}-----\\n${lines.join('\\n')}\\n-----END ${label}-----\\n`;\n};\n\nconst exportEcPrivateKeyPem = async (\n\tkeypair: CryptoKeyPair\n): Promise<string> => {\n\tconst pkcs8 = new Uint8Array(\n\t\tawait crypto.subtle.exportKey('pkcs8', keypair.privateKey)\n\t);\n\treturn derToPem('PRIVATE KEY', pkcs8);\n};\n\n// =============================================================================\n// ACME client\n// =============================================================================\n\ntype AcmeDirectory = {\n\tnewNonce: string;\n\tnewAccount: string;\n\tnewOrder: string;\n};\n\ntype AcmeOrder = {\n\tstatus: string;\n\tidentifiers: Array<{ type: string; value: string }>;\n\tauthorizations: string[];\n\tfinalize: string;\n\tcertificate?: string;\n};\n\ntype AcmeAuthorization = {\n\tstatus: string;\n\tidentifier: { type: string; value: string };\n\tchallenges: Array<{\n\t\ttype: string;\n\t\tstatus: string;\n\t\turl: string;\n\t\ttoken: string;\n\t}>;\n};\n\nexport type IssueCertificateOptions = {\n\t/** Domain(s) to include. First is the CN; all are SANs. */\n\tdomains: string[];\n\t/** DNS provider used for DNS-01 challenges (must own the zone). */\n\tdnsProvider: DnsProvider;\n\t/** Contact email for the ACME account. */\n\temail: string;\n\t/** ACME directory URL. Default Let's Encrypt production. */\n\tdirectoryUrl?: string;\n\t/** Reuse an existing account. Pass `exportAccount`'s output via `importAccount`. */\n\taccount?: AcmeAccount;\n\t/** Override fetch (tests). Default global fetch. */\n\tfetch?: typeof fetch;\n\t/** Poll interval. Default 3 s. */\n\tpollIntervalMs?: number;\n\t/**\n\t * Max wait before notifying ACME that the DNS challenge is ready.\n\t * Set ~30-60s for Cloudflare; longer for slower providers. Default 30 s.\n\t */\n\tdnsPropagationDelayMs?: number;\n\t/** Max wait for the order to become valid. Default 5 min. */\n\torderTimeoutMs?: number;\n\t/** Status log lines. */\n\tonLog?: (line: string) => void;\n\t/** Override sleep (tests). */\n\tsleep?: (ms: number) => Promise<void>;\n\t/**\n\t * Optional pre-check: returns true when DNS has propagated globally.\n\t * Default: just wait `dnsPropagationDelayMs` then proceed. Override\n\t * for production to actually verify (e.g. resolve from multiple\n\t * public resolvers).\n\t */\n\tcheckDnsPropagated?: (\n\t\trecordName: string,\n\t\texpectedValue: string\n\t) => Promise<boolean>;\n};\n\nexport type IssuedCertificate = {\n\tcertificatePem: string;\n\tprivateKeyPem: string;\n\taccount: AcmeAccount;\n\tdomains: string[];\n};\n\nexport class AcmeError extends Error {\n\treadonly status: number;\n\treadonly body: unknown;\n\tconstructor(message: string, status: number, body: unknown) {\n\t\tsuper(message);\n\t\tthis.name = 'AcmeError';\n\t\tthis.status = status;\n\t\tthis.body = body;\n\t}\n}\n\nconst defaultSleep = (ms: number): Promise<void> =>\n\tnew Promise((resolve) => setTimeout(resolve, ms));\n\nexport const issueCertificate = async (\n\toptions: IssueCertificateOptions\n): Promise<IssuedCertificate> => {\n\tconst log = options.onLog ?? (() => {});\n\tconst sleep = options.sleep ?? defaultSleep;\n\tconst fetcher = options.fetch ?? fetch;\n\tconst directoryUrl = options.directoryUrl ?? LETSENCRYPT_PRODUCTION;\n\tconst pollMs = options.pollIntervalMs ?? 3_000;\n\tconst dnsDelay = options.dnsPropagationDelayMs ?? 30_000;\n\tconst orderTimeout = options.orderTimeoutMs ?? 5 * 60_000;\n\tconst account = options.account ?? (await generateAccountKey());\n\n\tif (options.domains.length === 0) {\n\t\tthrow new Error('[deploy/tls] at least one domain is required');\n\t}\n\n\t// 1. Fetch directory.\n\tlog(`[tls] fetching ACME directory ${directoryUrl}`);\n\tconst directoryResponse = await fetcher(directoryUrl);\n\tif (!directoryResponse.ok) {\n\t\tthrow new AcmeError(\n\t\t\t`failed to fetch ACME directory: ${directoryResponse.status}`,\n\t\t\tdirectoryResponse.status,\n\t\t\tawait directoryResponse.text()\n\t\t);\n\t}\n\tconst directory = (await directoryResponse.json()) as AcmeDirectory;\n\n\t// 2. Get initial nonce.\n\tlet nonce = await fetchNonce(fetcher, directory.newNonce);\n\n\t// Helper: signed POST. Tracks nonce; returns parsed JSON body + headers.\n\tconst post = async (\n\t\turl: string,\n\t\tpayload: object | string,\n\t\tidentification: { kid?: string; jwk?: JsonWebKey }\n\t): Promise<{ status: number; body: unknown; headers: Headers }> => {\n\t\tconst header: JwsHeader = {\n\t\t\talg: 'ES256',\n\t\t\tnonce,\n\t\t\turl,\n\t\t\t...(identification.kid !== undefined ? { kid: identification.kid } : {}),\n\t\t\t...(identification.jwk !== undefined ? { jwk: identification.jwk } : {})\n\t\t};\n\t\tconst jws = await signJws(account.key.privateKey, header, payload);\n\t\tconst response = await fetcher(url, {\n\t\t\tbody: JSON.stringify(jws),\n\t\t\theaders: { 'content-type': 'application/jose+json' },\n\t\t\tmethod: 'POST'\n\t\t});\n\t\tconst newNonce = response.headers.get('replay-nonce');\n\t\tif (newNonce !== null) nonce = newNonce;\n\t\tconst text = await response.text();\n\t\tconst body =\n\t\t\ttext.length > 0 && response.headers.get('content-type')?.includes('json')\n\t\t\t\t? JSON.parse(text)\n\t\t\t\t: text;\n\t\tif (!response.ok) {\n\t\t\tthrow new AcmeError(\n\t\t\t\t`ACME ${url} → ${response.status}`,\n\t\t\t\tresponse.status,\n\t\t\t\tbody\n\t\t\t);\n\t\t}\n\t\treturn { body, headers: response.headers, status: response.status };\n\t};\n\n\t// 3. Register the account (or reuse if kid is set).\n\tconst publicJwk = await exportPublicJwk(account.key.publicKey);\n\tif (account.kid === undefined) {\n\t\tlog(`[tls] registering ACME account for ${options.email}`);\n\t\tconst result = await post(\n\t\t\tdirectory.newAccount,\n\t\t\t{\n\t\t\t\tcontact: [`mailto:${options.email}`],\n\t\t\t\ttermsOfServiceAgreed: true\n\t\t\t},\n\t\t\t{ jwk: publicJwk }\n\t\t);\n\t\taccount.kid = result.headers.get('location') ?? undefined;\n\t\tif (account.kid === undefined) {\n\t\t\tthrow new Error(\n\t\t\t\t'[deploy/tls] ACME newAccount response missing Location header'\n\t\t\t);\n\t\t}\n\t\tlog(`[tls] account registered: ${account.kid}`);\n\t} else {\n\t\tlog(`[tls] reusing account ${account.kid}`);\n\t}\n\n\tconst accountKid = account.kid;\n\n\t// 4. Submit the order.\n\tlog(`[tls] submitting order for ${options.domains.join(', ')}`);\n\tconst orderResult = await post(\n\t\tdirectory.newOrder,\n\t\t{\n\t\t\tidentifiers: options.domains.map((domain) => ({\n\t\t\t\ttype: 'dns',\n\t\t\t\tvalue: domain\n\t\t\t}))\n\t\t},\n\t\t{ kid: accountKid }\n\t);\n\tlet order = orderResult.body as AcmeOrder;\n\tconst orderUrl = orderResult.headers.get('location');\n\tif (orderUrl === null) {\n\t\tthrow new Error(\n\t\t\t'[deploy/tls] ACME newOrder response missing Location header'\n\t\t);\n\t}\n\n\t// 5. For each authorization, complete the DNS-01 challenge.\n\tconst cleanups: Array<() => Promise<void>> = [];\n\tconst dnsCreated: Array<{\n\t\trecordId: string;\n\t\trecordName: string;\n\t\trecordValue: string;\n\t}> = [];\n\n\ttry {\n\t\tconst thumbprint = await jwkThumbprint(publicJwk);\n\n\t\tfor (const authUrl of order.authorizations) {\n\t\t\tconst authResult = await post(authUrl, '', { kid: accountKid });\n\t\t\tconst auth = authResult.body as AcmeAuthorization;\n\t\t\tconst challenge = auth.challenges.find((c) => c.type === 'dns-01');\n\t\t\tif (challenge === undefined) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`[deploy/tls] no dns-01 challenge for ${auth.identifier.value}`\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst keyAuthorization = `${challenge.token}.${thumbprint}`;\n\t\t\tconst txtBytes = new Uint8Array(\n\t\t\t\tawait crypto.subtle.digest(\n\t\t\t\t\t'SHA-256',\n\t\t\t\t\tnew TextEncoder().encode(keyAuthorization)\n\t\t\t\t)\n\t\t\t);\n\t\t\tconst txtValue = base64UrlEncode(txtBytes);\n\t\t\tconst recordName = `_acme-challenge.${auth.identifier.value}`;\n\t\t\tlog(`[tls] DNS-01: setting ${recordName} = \"${txtValue}\"`);\n\t\t\tconst record = await options.dnsProvider.upsert({\n\t\t\t\tcontent: txtValue,\n\t\t\t\tname: recordName,\n\t\t\t\tttl: 60,\n\t\t\t\ttype: 'TXT'\n\t\t\t});\n\t\t\tdnsCreated.push({\n\t\t\t\trecordId: record.id,\n\t\t\t\trecordName,\n\t\t\t\trecordValue: txtValue\n\t\t\t});\n\t\t\tcleanups.push(() => options.dnsProvider.delete(record.id));\n\n\t\t\tif (options.checkDnsPropagated !== undefined) {\n\t\t\t\tlog('[tls] waiting for DNS propagation (custom checker)…');\n\t\t\t\tconst deadline = Date.now() + dnsDelay;\n\t\t\t\twhile (Date.now() < deadline) {\n\t\t\t\t\tif (\n\t\t\t\t\t\tawait options.checkDnsPropagated(recordName, txtValue)\n\t\t\t\t\t) break;\n\t\t\t\t\tawait sleep(pollMs);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tlog(`[tls] sleeping ${dnsDelay}ms for DNS propagation`);\n\t\t\t\tawait sleep(dnsDelay);\n\t\t\t}\n\n\t\t\tlog(`[tls] notifying ACME of dns-01 readiness for ${auth.identifier.value}`);\n\t\t\tawait post(challenge.url, {}, { kid: accountKid });\n\n\t\t\t// Poll the authorization until valid/invalid.\n\t\t\tconst authDeadline = Date.now() + orderTimeout;\n\t\t\twhile (Date.now() < authDeadline) {\n\t\t\t\tconst polledResult = await post(authUrl, '', { kid: accountKid });\n\t\t\t\tconst polled = polledResult.body as AcmeAuthorization;\n\t\t\t\tif (polled.status === 'valid') break;\n\t\t\t\tif (polled.status === 'invalid') {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`[deploy/tls] authorization failed for ${auth.identifier.value}: ${JSON.stringify(polled.challenges)}`\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tawait sleep(pollMs);\n\t\t\t}\n\t\t}\n\n\t\t// 6. Finalize — generate cert keypair + CSR.\n\t\tlog('[tls] generating cert keypair + CSR');\n\t\tconst certKey = await generateCertKeyPair();\n\t\tconst csrDer = await buildCsr(options.domains, certKey);\n\t\tconst csr = base64UrlEncode(csrDer);\n\n\t\tawait post(order.finalize, { csr }, { kid: accountKid });\n\n\t\t// 7. Poll the order until valid + certificate URL appears.\n\t\tconst orderDeadline = Date.now() + orderTimeout;\n\t\twhile (Date.now() < orderDeadline) {\n\t\t\tconst refreshed = await post(orderUrl, '', { kid: accountKid });\n\t\t\torder = refreshed.body as AcmeOrder;\n\t\t\tif (order.status === 'valid' && order.certificate !== undefined) break;\n\t\t\tif (order.status === 'invalid') {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`[deploy/tls] order failed: ${JSON.stringify(order)}`\n\t\t\t\t);\n\t\t\t}\n\t\t\tawait sleep(pollMs);\n\t\t}\n\n\t\tif (order.certificate === undefined) {\n\t\t\tthrow new Error('[deploy/tls] order timed out before issuing certificate');\n\t\t}\n\n\t\t// 8. Download the cert (PEM bundle).\n\t\tlog(`[tls] downloading certificate from ${order.certificate}`);\n\t\tconst certResult = await post(order.certificate, '', { kid: accountKid });\n\t\tconst certificatePem =\n\t\t\ttypeof certResult.body === 'string'\n\t\t\t\t? certResult.body\n\t\t\t\t: String(certResult.body);\n\n\t\tconst privateKeyPem = await exportEcPrivateKeyPem(certKey);\n\n\t\treturn {\n\t\t\taccount,\n\t\t\tcertificatePem,\n\t\t\tdomains: options.domains,\n\t\t\tprivateKeyPem\n\t\t};\n\t} finally {\n\t\tfor (const cleanup of cleanups) {\n\t\t\ttry {\n\t\t\t\tawait cleanup();\n\t\t\t} catch (error) {\n\t\t\t\tlog(`[tls] cleanup failed (continuing): ${String(error)}`);\n\t\t\t}\n\t\t}\n\t\t// Suppress unused-variable lint without changing behavior.\n\t\tvoid dnsCreated;\n\t}\n};\n\nconst fetchNonce = async (\n\tfetcher: typeof fetch,\n\turl: string\n): Promise<string> => {\n\tconst response = await fetcher(url, { method: 'HEAD' });\n\tconst nonce = response.headers.get('replay-nonce');\n\tif (nonce === null) {\n\t\tthrow new Error('[deploy/tls] newNonce response missing Replay-Nonce header');\n\t}\n\treturn nonce;\n};\n\n// =============================================================================\n// installCertificateOnTarget\n// =============================================================================\n\nexport type InstallCertificateOptions = {\n\t/** Remote path for the cert chain. Default `/etc/ssl/<firstDomain>/fullchain.pem`. */\n\tcertPath?: string;\n\t/** Remote path for the private key. Default `/etc/ssl/<firstDomain>/privkey.pem`. */\n\tkeyPath?: string;\n\t/** Mode (chmod) for the cert + key. Default `600`. */\n\tmode?: string;\n\t/** Owner (chown) for the cert + key. Default unchanged. */\n\towner?: string;\n\t/** Optional reload command run after install (e.g. `'systemctl reload nginx'`). */\n\treload?: string;\n\t/** Override the writeTo helper (tests). */\n\twriteFile?: (target: Target, path: string, contents: string) => Promise<void>;\n};\n\nconst defaultWriteFile = async (\n\ttarget: Target,\n\tremotePath: string,\n\tcontents: string\n): Promise<void> => {\n\tconst escaped = contents.replaceAll(\"'\", \"'\\\\''\");\n\tawait target.exec(`mkdir -p \"$(dirname '${remotePath}')\"`);\n\tawait target.exec(`cat > '${remotePath}' <<'__ABS_TLS_EOF__'\\n${contents}__ABS_TLS_EOF__\\n`);\n\tvoid escaped;\n};\n\n/**\n * @internal — exposed for unit testing of cryptographic primitives.\n * Not part of the public API; consumers should NOT depend on this.\n */\nexport const __testing = {\n\tbase64UrlDecode,\n\tbase64UrlEncode,\n\tbase64UrlEncodeString,\n\tbuildCsr,\n\tderBitString,\n\tderInt,\n\tderOid,\n\tderSeq,\n\tderSet,\n\tecdsaRawToDer,\n\texportEcPrivateKeyPem,\n\texportPublicJwk,\n\tgenerateCertKeyPair,\n\tjwkThumbprint,\n\tsignJws\n};\n\n/**\n * Upload cert + private key to the target. Composable with the deploy\n * pipeline as a verify-step or post-deploy hook.\n */\nexport const installCertificateOnTarget = async (\n\ttarget: Target,\n\tcert: IssuedCertificate,\n\toptions: InstallCertificateOptions = {}\n): Promise<{ certPath: string; keyPath: string }> => {\n\tconst domain = cert.domains[0];\n\tif (domain === undefined) {\n\t\tthrow new Error('[deploy/tls] certificate has no domains');\n\t}\n\tconst certPath = options.certPath ?? `/etc/ssl/${domain}/fullchain.pem`;\n\tconst keyPath = options.keyPath ?? `/etc/ssl/${domain}/privkey.pem`;\n\tconst mode = options.mode ?? '600';\n\tconst writer = options.writeFile ?? defaultWriteFile;\n\n\tawait writer(target, certPath, cert.certificatePem);\n\tawait writer(target, keyPath, cert.privateKeyPem);\n\tawait target.exec(`chmod ${mode} '${certPath}' '${keyPath}'`);\n\tif (options.owner !== undefined) {\n\t\tawait target.exec(`chown ${options.owner} '${certPath}' '${keyPath}'`);\n\t}\n\tif (options.reload !== undefined) {\n\t\tawait target.exec(options.reload);\n\t}\n\n\treturn { certPath, keyPath };\n};\n\n// =============================================================================\n// Certificate inspection + renewal scheduling\n// =============================================================================\n\nexport type CertificateInspection = {\n\t/** CN + every SAN, deduplicated. */\n\tsubjects: string[];\n\t/** Issuance time, ms since epoch. */\n\tvalidFrom: number;\n\t/** Expiration time, ms since epoch. */\n\tvalidTo: number;\n\t/** Whole days remaining before `validTo`. Negative if expired. */\n\tdaysRemaining: number;\n\t/** True when the cert is past `validTo`. */\n\texpired: boolean;\n\t/** Issuer DN string (for \"Let's Encrypt vs staging vs other CA\" checks). */\n\tissuer: string;\n};\n\nconst parseSubjects = (x509: X509Certificate): string[] => {\n\tconst subjects = new Set<string>();\n\tconst cnMatch = x509.subject.match(/CN=([^,\\n]+)/);\n\tif (cnMatch !== null && cnMatch[1] !== undefined) {\n\t\tsubjects.add(cnMatch[1].trim());\n\t}\n\tconst san = x509.subjectAltName;\n\tif (san !== undefined && san !== null) {\n\t\tfor (const part of san.split(',')) {\n\t\t\tconst dnsMatch = part.trim().match(/^DNS:(.+)$/);\n\t\t\tif (dnsMatch !== null && dnsMatch[1] !== undefined) {\n\t\t\t\tsubjects.add(dnsMatch[1].trim());\n\t\t\t}\n\t\t}\n\t}\n\treturn [...subjects];\n};\n\n/**\n * Parse a PEM certificate into operator-shaped metadata. Used by\n * {@link renewCertificate} to decide whether to re-issue; also fine\n * for status pages and observability.\n */\nexport const inspectCertificate = (\n\tpem: string,\n\toptions: { now?: () => number } = {}\n): CertificateInspection => {\n\tconst x509 = new X509Certificate(pem);\n\tconst validFrom = new Date(x509.validFrom).getTime();\n\tconst validTo = new Date(x509.validTo).getTime();\n\tconst now = (options.now ?? Date.now)();\n\tconst daysRemaining = Math.floor(\n\t\t(validTo - now) / (24 * 60 * 60 * 1000)\n\t);\n\treturn {\n\t\tdaysRemaining,\n\t\texpired: validTo < now,\n\t\tissuer: x509.issuer,\n\t\tsubjects: parseSubjects(x509),\n\t\tvalidFrom,\n\t\tvalidTo\n\t};\n};\n\nexport type RenewCertificateOptions = IssueCertificateOptions & {\n\t/**\n\t * Current cert PEM. When provided, the cert's `validTo` decides\n\t * whether to re-issue; when absent, renewal always issues.\n\t */\n\tcurrentCertificatePem?: string;\n\t/**\n\t * Re-issue when fewer than this many days remain. Default 30,\n\t * matching certbot's standard schedule (Let's Encrypt issues 90-day\n\t * certs; renewing at 30 days leaves a 60-day error budget).\n\t */\n\trenewWhenDaysRemaining?: number;\n\t/** Force re-issue regardless of remaining days. Default false. */\n\tforce?: boolean;\n\t/** Override `Date.now()` for testing. */\n\tnow?: () => number;\n};\n\nexport type RenewalResult =\n\t| {\n\t\t\trenewed: true;\n\t\t\tcertificate: IssuedCertificate;\n\t\t\treason: 'forced' | 'no-current-cert' | 'expiring-soon';\n\t }\n\t| {\n\t\t\trenewed: false;\n\t\t\treason: 'still-fresh';\n\t\t\tinspection: CertificateInspection;\n\t };\n\n/**\n * Conditional cert renewal. Parses the supplied cert (if any),\n * decides whether to re-issue based on remaining days, and either\n * returns the existing cert info (\"still fresh\") or issues a new\n * one via {@link issueCertificate}.\n *\n * Wire to a scheduled function (cron / @absolutejs/sync schedule /\n * GitHub Action) running at least once a week. Idempotent: a fresh\n * cert returns `renewed: false` cheaply (one PEM parse, zero\n * network IO).\n *\n * @example\n * const result = await renewCertificate({\n * currentCertificatePem: existingPem, // omit to force first-issue\n * domains: ['api.example.com'],\n * dnsProvider: dns,\n * email: 'ops@example.com',\n * account: persistedAccount, // reuse to avoid CA rate limit\n * });\n * if (result.renewed) {\n * await installCertificateOnTarget(target, result.certificate, { reload });\n * }\n */\nexport const renewCertificate = async (\n\toptions: RenewCertificateOptions\n): Promise<RenewalResult> => {\n\tconst threshold = options.renewWhenDaysRemaining ?? 30;\n\tif (threshold < 0) {\n\t\tthrow new Error(\n\t\t\t'[deploy/tls] renewWhenDaysRemaining must be non-negative'\n\t\t);\n\t}\n\n\tif (options.force === true) {\n\t\tconst certificate = await issueCertificate(options);\n\t\treturn { certificate, reason: 'forced', renewed: true };\n\t}\n\n\tif (options.currentCertificatePem === undefined) {\n\t\tconst certificate = await issueCertificate(options);\n\t\treturn { certificate, reason: 'no-current-cert', renewed: true };\n\t}\n\n\tconst nowFn = options.now ?? Date.now;\n\tconst inspection = inspectCertificate(options.currentCertificatePem, {\n\t\tnow: nowFn\n\t});\n\n\tif (!inspection.expired && inspection.daysRemaining >= threshold) {\n\t\treturn { inspection, reason: 'still-fresh', renewed: false };\n\t}\n\n\tconst certificate = await issueCertificate(options);\n\treturn { certificate, reason: 'expiring-soon', renewed: true };\n};\n"
5
+ "/**\n * @absolutejs/deploy/tls — ACME (RFC 8555) client for Let's Encrypt and\n * compatible CAs. DNS-01 challenges only — uses the {@link DnsProvider}\n * abstraction from `./dns`, so the same Cloudflare / Route 53 /\n * Hetzner-DNS providers that point hostnames at boxes also satisfy\n * ACME's challenge requirement.\n *\n * Zero peer deps. Bun's `crypto.subtle` covers JWS signing and ECDSA\n * key generation; a small DER encoder builds the CSR.\n *\n * Public API:\n *\n * issueCertificate({ domains, dnsProvider, email })\n * → { certificatePem, privateKeyPem, account, domains }\n *\n * installCertificateOnTarget(target, cert, { certPath, keyPath, reload? })\n * → uploads PEM files via Target.upload + optional reload exec\n *\n * exportAccountKey / importAccountKey for persistence between runs\n * (reuse the same account across cert renewals — cheaper, doesn't\n * hit Let's Encrypt's account-creation rate limit)\n */\n\nimport { X509Certificate } from 'node:crypto';\nimport type { Target } from './targets';\nimport type { DnsProvider } from './dns';\n\n// =============================================================================\n// Constants\n// =============================================================================\n\nexport const LETSENCRYPT_PRODUCTION =\n\t'https://acme-v02.api.letsencrypt.org/directory';\nexport const LETSENCRYPT_STAGING =\n\t'https://acme-staging-v02.api.letsencrypt.org/directory';\n\n// =============================================================================\n// base64url\n// =============================================================================\n\nconst base64UrlEncode = (bytes: Uint8Array | ArrayBuffer): string => {\n\tconst buf = bytes instanceof Uint8Array ? bytes : new Uint8Array(bytes);\n\tlet binary = '';\n\tfor (const byte of buf) binary += String.fromCharCode(byte);\n\treturn btoa(binary).replaceAll('+', '-').replaceAll('/', '_').replaceAll('=', '');\n};\n\nconst base64UrlEncodeString = (value: string): string =>\n\tbase64UrlEncode(new TextEncoder().encode(value));\n\nconst base64UrlDecode = (value: string): Uint8Array => {\n\tconst padded = value.replaceAll('-', '+').replaceAll('_', '/').padEnd(\n\t\tMath.ceil(value.length / 4) * 4,\n\t\t'='\n\t);\n\tconst binary = atob(padded);\n\tconst bytes = new Uint8Array(binary.length);\n\tfor (let index = 0; index < binary.length; index += 1) {\n\t\tbytes[index] = binary.charCodeAt(index);\n\t}\n\treturn bytes;\n};\n\n// =============================================================================\n// DER encoding (minimal subset for CSR)\n// =============================================================================\n\nconst derLength = (length: number): Uint8Array => {\n\tif (length < 0x80) return Uint8Array.from([length]);\n\tconst bytes: number[] = [];\n\tlet remaining = length;\n\twhile (remaining > 0) {\n\t\tbytes.unshift(remaining & 0xff);\n\t\tremaining >>= 8;\n\t}\n\treturn Uint8Array.from([0x80 | bytes.length, ...bytes]);\n};\n\nconst derTag = (tag: number, payload: Uint8Array): Uint8Array => {\n\tconst length = derLength(payload.length);\n\tconst out = new Uint8Array(1 + length.length + payload.length);\n\tout[0] = tag;\n\tout.set(length, 1);\n\tout.set(payload, 1 + length.length);\n\treturn out;\n};\n\nconst derSeq = (children: Uint8Array[]): Uint8Array => {\n\tconst total = children.reduce((sum, child) => sum + child.length, 0);\n\tconst payload = new Uint8Array(total);\n\tlet offset = 0;\n\tfor (const child of children) {\n\t\tpayload.set(child, offset);\n\t\toffset += child.length;\n\t}\n\treturn derTag(0x30, payload);\n};\n\nconst derSet = (children: Uint8Array[]): Uint8Array => {\n\tconst total = children.reduce((sum, child) => sum + child.length, 0);\n\tconst payload = new Uint8Array(total);\n\tlet offset = 0;\n\tfor (const child of children) {\n\t\tpayload.set(child, offset);\n\t\toffset += child.length;\n\t}\n\treturn derTag(0x31, payload);\n};\n\nconst derInt = (value: number | Uint8Array): Uint8Array => {\n\tif (typeof value === 'number') {\n\t\t// Small unsigned int — used for the CSR version field (0).\n\t\tif (value === 0) return derTag(0x02, Uint8Array.from([0]));\n\t\tconst bytes: number[] = [];\n\t\tlet remaining = value;\n\t\twhile (remaining > 0) {\n\t\t\tbytes.unshift(remaining & 0xff);\n\t\t\tremaining >>= 8;\n\t\t}\n\t\tif ((bytes[0] as number) & 0x80) bytes.unshift(0); // ensure positive\n\t\treturn derTag(0x02, Uint8Array.from(bytes));\n\t}\n\t// Big-endian unsigned bytes — strip leading zeros, then prepend 0x00 if\n\t// the high bit is set (DER INTEGER is signed two's complement).\n\tlet start = 0;\n\twhile (start < value.length - 1 && value[start] === 0) start += 1;\n\tlet payload = value.subarray(start);\n\tif ((payload[0] as number) & 0x80) {\n\t\tconst padded = new Uint8Array(payload.length + 1);\n\t\tpadded.set(payload, 1);\n\t\tpayload = padded;\n\t}\n\treturn derTag(0x02, payload);\n};\n\nconst derOid = (oid: string): Uint8Array => {\n\tconst parts = oid.split('.').map((part) => Number.parseInt(part, 10));\n\tconst first = parts[0];\n\tconst second = parts[1];\n\tif (first === undefined || second === undefined) {\n\t\tthrow new Error(`[deploy/tls] invalid OID: ${oid}`);\n\t}\n\tconst bytes: number[] = [first * 40 + second];\n\tfor (let index = 2; index < parts.length; index += 1) {\n\t\tconst value = parts[index] as number;\n\t\tconst chunks: number[] = [];\n\t\tlet remaining = value;\n\t\tdo {\n\t\t\tchunks.unshift(remaining & 0x7f);\n\t\t\tremaining >>= 7;\n\t\t} while (remaining > 0);\n\t\tfor (let chunkIndex = 0; chunkIndex < chunks.length - 1; chunkIndex += 1) {\n\t\t\tchunks[chunkIndex] = (chunks[chunkIndex] as number) | 0x80;\n\t\t}\n\t\tbytes.push(...chunks);\n\t}\n\treturn derTag(0x06, Uint8Array.from(bytes));\n};\n\nconst derPrintableString = (value: string): Uint8Array =>\n\tderTag(0x13, new TextEncoder().encode(value));\n\nconst derUtf8String = (value: string): Uint8Array =>\n\tderTag(0x0c, new TextEncoder().encode(value));\n\nconst derIa5String = (value: string): Uint8Array =>\n\tderTag(0x16, new TextEncoder().encode(value));\n\nconst derOctetString = (payload: Uint8Array): Uint8Array =>\n\tderTag(0x04, payload);\n\nconst derBitString = (payload: Uint8Array): Uint8Array => {\n\t// 0x00 prefix = number of unused bits in the final byte; for byte-aligned\n\t// signatures + keys this is always zero.\n\tconst bits = new Uint8Array(payload.length + 1);\n\tbits[0] = 0;\n\tbits.set(payload, 1);\n\treturn derTag(0x03, bits);\n};\n\nconst derContextTag = (\n\ttag: number,\n\tpayload: Uint8Array,\n\tconstructed = true\n): Uint8Array => derTag(0xa0 + tag + (constructed ? 0 : -0x20), payload);\n\n// =============================================================================\n// ECDSA signature: raw r||s ↔ DER SEQUENCE\n// =============================================================================\n\nconst ecdsaRawToDer = (rawSig: Uint8Array): Uint8Array => {\n\tconst half = rawSig.length / 2;\n\tconst r = rawSig.subarray(0, half);\n\tconst s = rawSig.subarray(half);\n\treturn derSeq([derInt(r), derInt(s)]);\n};\n\n// =============================================================================\n// JWS (RFC 7515) Flattened JSON Serialization for ACME\n// =============================================================================\n\ntype JwsHeader = {\n\talg: 'ES256';\n\tnonce: string;\n\turl: string;\n\tjwk?: JsonWebKey;\n\tkid?: string;\n};\n\ntype JwsBody = {\n\tprotected: string;\n\tpayload: string;\n\tsignature: string;\n};\n\nconst signJws = async (\n\tprivateKey: CryptoKey,\n\theader: JwsHeader,\n\tpayload: object | string\n): Promise<JwsBody> => {\n\tconst protectedHeader = base64UrlEncodeString(JSON.stringify(header));\n\tconst payloadEncoded =\n\t\tpayload === ''\n\t\t\t? '' // POST-as-GET: empty string, NOT empty object\n\t\t\t: base64UrlEncodeString(JSON.stringify(payload));\n\tconst signingInput = new TextEncoder().encode(\n\t\t`${protectedHeader}.${payloadEncoded}`\n\t);\n\tconst rawSig = new Uint8Array(\n\t\tawait crypto.subtle.sign(\n\t\t\t{ hash: 'SHA-256', name: 'ECDSA' },\n\t\t\tprivateKey,\n\t\t\tsigningInput\n\t\t)\n\t);\n\treturn {\n\t\tpayload: payloadEncoded,\n\t\tprotected: protectedHeader,\n\t\tsignature: base64UrlEncode(rawSig)\n\t};\n};\n\n// =============================================================================\n// JWK helpers — canonical thumbprint, public-key extraction\n// =============================================================================\n\nconst exportPublicJwk = async (publicKey: CryptoKey): Promise<JsonWebKey> => {\n\tconst jwk = await crypto.subtle.exportKey('jwk', publicKey);\n\t// Strip private fields if the export included them (shouldn't, on a\n\t// public key, but be defensive).\n\treturn {\n\t\tcrv: jwk.crv,\n\t\tkty: jwk.kty,\n\t\tx: jwk.x,\n\t\ty: jwk.y\n\t};\n};\n\nconst jwkThumbprint = async (publicJwk: JsonWebKey): Promise<string> => {\n\t// RFC 7638 — canonical JSON: required fields only, lex order.\n\tconst canonical = JSON.stringify({\n\t\tcrv: publicJwk.crv,\n\t\tkty: publicJwk.kty,\n\t\tx: publicJwk.x,\n\t\ty: publicJwk.y\n\t});\n\tconst hash = await crypto.subtle.digest(\n\t\t'SHA-256',\n\t\tnew TextEncoder().encode(canonical)\n\t);\n\treturn base64UrlEncode(hash);\n};\n\n// =============================================================================\n// Account key — generation + JSON export/import for persistence\n// =============================================================================\n\nexport type AcmeAccount = {\n\tkey: CryptoKeyPair;\n\t/** Account URL — set after first registration; persisted for renewals. */\n\tkid?: string;\n};\n\nexport type AcmeAccountJson = {\n\tpublicJwk: JsonWebKey;\n\tprivateJwk: JsonWebKey;\n\tkid?: string;\n};\n\nexport const generateAccountKey = async (): Promise<AcmeAccount> => {\n\tconst key = await crypto.subtle.generateKey(\n\t\t{ name: 'ECDSA', namedCurve: 'P-256' },\n\t\ttrue,\n\t\t['sign', 'verify']\n\t);\n\treturn { key };\n};\n\nexport const exportAccount = async (\n\taccount: AcmeAccount\n): Promise<AcmeAccountJson> => {\n\tconst publicJwk = await crypto.subtle.exportKey('jwk', account.key.publicKey);\n\tconst privateJwk = await crypto.subtle.exportKey(\n\t\t'jwk',\n\t\taccount.key.privateKey\n\t);\n\treturn {\n\t\tprivateJwk,\n\t\tpublicJwk,\n\t\t...(account.kid !== undefined ? { kid: account.kid } : {})\n\t};\n};\n\nexport const importAccount = async (\n\tjson: AcmeAccountJson\n): Promise<AcmeAccount> => {\n\tconst publicKey = await crypto.subtle.importKey(\n\t\t'jwk',\n\t\tjson.publicJwk,\n\t\t{ name: 'ECDSA', namedCurve: 'P-256' },\n\t\ttrue,\n\t\t['verify']\n\t);\n\tconst privateKey = await crypto.subtle.importKey(\n\t\t'jwk',\n\t\tjson.privateJwk,\n\t\t{ name: 'ECDSA', namedCurve: 'P-256' },\n\t\ttrue,\n\t\t['sign']\n\t);\n\treturn {\n\t\tkey: { privateKey, publicKey },\n\t\t...(json.kid !== undefined ? { kid: json.kid } : {})\n\t};\n};\n\n// =============================================================================\n// CSR — Certificate Signing Request\n// =============================================================================\n\nconst buildSanExtension = (domains: string[]): Uint8Array => {\n\t// SubjectAltName extension value: SEQUENCE OF GeneralName\n\tconst generalNames = domains.map((domain) =>\n\t\tderTag(0x82, new TextEncoder().encode(domain))\n\t);\n\tconst sanSequence = derSeq(generalNames);\n\tconst extensionValue = derOctetString(sanSequence);\n\treturn derSeq([derOid('2.5.29.17'), extensionValue]);\n};\n\nconst buildExtensionRequestAttribute = (domains: string[]): Uint8Array => {\n\tconst extensions = derSeq([buildSanExtension(domains)]);\n\treturn derSeq([\n\t\tderOid('1.2.840.113549.1.9.14'), // PKCS#9 extensionRequest\n\t\tderSet([extensions])\n\t]);\n};\n\nconst generateCertKeyPair = async (): Promise<CryptoKeyPair> =>\n\tcrypto.subtle.generateKey(\n\t\t{ name: 'ECDSA', namedCurve: 'P-256' },\n\t\ttrue,\n\t\t['sign', 'verify']\n\t);\n\nconst buildCsr = async (\n\tdomains: string[],\n\tkeypair: CryptoKeyPair\n): Promise<Uint8Array> => {\n\tif (domains.length === 0) {\n\t\tthrow new Error('[deploy/tls] CSR requires at least one domain');\n\t}\n\tconst commonName = domains[0] as string;\n\n\tconst spkiDer = new Uint8Array(\n\t\tawait crypto.subtle.exportKey('spki', keypair.publicKey)\n\t);\n\n\tconst version = derInt(0);\n\tconst subject = derSeq([\n\t\tderSet([\n\t\t\tderSeq([\n\t\t\t\tderOid('2.5.4.3'), // commonName\n\t\t\t\tderUtf8String(commonName)\n\t\t\t])\n\t\t])\n\t]);\n\n\tconst attributes = derContextTag(0, buildExtensionRequestAttribute(domains));\n\n\tconst certificationRequestInfo = derSeq([\n\t\tversion,\n\t\tsubject,\n\t\tspkiDer,\n\t\tattributes\n\t]);\n\n\tconst rawSig = new Uint8Array(\n\t\tawait crypto.subtle.sign(\n\t\t\t{ hash: 'SHA-256', name: 'ECDSA' },\n\t\t\tkeypair.privateKey,\n\t\t\tcertificationRequestInfo as BufferSource\n\t\t)\n\t);\n\tconst sigDer = ecdsaRawToDer(rawSig);\n\n\tconst sigAlgorithm = derSeq([derOid('1.2.840.10045.4.3.2')]);\n\t// ecdsa-with-SHA256\n\tconst signatureBits = derBitString(sigDer);\n\n\treturn derSeq([certificationRequestInfo, sigAlgorithm, signatureBits]);\n};\n\n// =============================================================================\n// PEM\n// =============================================================================\n\nconst derToPem = (label: string, der: Uint8Array): string => {\n\tconst b64 = btoa(String.fromCharCode(...der));\n\tconst lines = b64.match(/.{1,64}/g) ?? [];\n\treturn `-----BEGIN ${label}-----\\n${lines.join('\\n')}\\n-----END ${label}-----\\n`;\n};\n\nconst exportEcPrivateKeyPem = async (\n\tkeypair: CryptoKeyPair\n): Promise<string> => {\n\tconst pkcs8 = new Uint8Array(\n\t\tawait crypto.subtle.exportKey('pkcs8', keypair.privateKey)\n\t);\n\treturn derToPem('PRIVATE KEY', pkcs8);\n};\n\n// =============================================================================\n// ACME client\n// =============================================================================\n\ntype AcmeDirectory = {\n\tnewNonce: string;\n\tnewAccount: string;\n\tnewOrder: string;\n};\n\ntype AcmeOrder = {\n\tstatus: string;\n\tidentifiers: Array<{ type: string; value: string }>;\n\tauthorizations: string[];\n\tfinalize: string;\n\tcertificate?: string;\n};\n\ntype AcmeAuthorization = {\n\tstatus: string;\n\tidentifier: { type: string; value: string };\n\tchallenges: Array<{\n\t\ttype: string;\n\t\tstatus: string;\n\t\turl: string;\n\t\ttoken: string;\n\t}>;\n};\n\nexport type IssueCertificateOptions = {\n\t/** Domain(s) to include. First is the CN; all are SANs. */\n\tdomains: string[];\n\t/** DNS provider used for DNS-01 challenges (must own the zone). */\n\tdnsProvider: DnsProvider;\n\t/**\n\t * Map the public ACME challenge name to the record name written through\n\t * `dnsProvider`. Use this for CNAME-delegated DNS-01, where customers point\n\t * `_acme-challenge.app.example.com` at a platform-owned validation zone.\n\t * The propagation checker still receives the public challenge name because\n\t * that is what the certificate authority resolves.\n\t */\n\tmapDnsChallengeRecord?: (context: {\n\t\tdomain: string;\n\t\trecordName: string;\n\t}) => string;\n\t/** Contact email for the ACME account. */\n\temail: string;\n\t/** ACME directory URL. Default Let's Encrypt production. */\n\tdirectoryUrl?: string;\n\t/** Reuse an existing account. Pass `exportAccount`'s output via `importAccount`. */\n\taccount?: AcmeAccount;\n\t/** Override fetch (tests). Default global fetch. */\n\tfetch?: typeof fetch;\n\t/** Poll interval. Default 3 s. */\n\tpollIntervalMs?: number;\n\t/**\n\t * Max wait before notifying ACME that the DNS challenge is ready.\n\t * Set ~30-60s for Cloudflare; longer for slower providers. Default 30 s.\n\t */\n\tdnsPropagationDelayMs?: number;\n\t/** Max wait for the order to become valid. Default 5 min. */\n\torderTimeoutMs?: number;\n\t/** Status log lines. */\n\tonLog?: (line: string) => void;\n\t/** Override sleep (tests). */\n\tsleep?: (ms: number) => Promise<void>;\n\t/**\n\t * Optional pre-check: returns true when DNS has propagated globally.\n\t * Default: just wait `dnsPropagationDelayMs` then proceed. Override\n\t * for production to actually verify (e.g. resolve from multiple\n\t * public resolvers).\n\t */\n\tcheckDnsPropagated?: (\n\t\trecordName: string,\n\t\texpectedValue: string\n\t) => Promise<boolean>;\n};\n\nexport type IssuedCertificate = {\n\tcertificatePem: string;\n\tprivateKeyPem: string;\n\taccount: AcmeAccount;\n\tdomains: string[];\n};\n\nexport class AcmeError extends Error {\n\treadonly status: number;\n\treadonly body: unknown;\n\tconstructor(message: string, status: number, body: unknown) {\n\t\tsuper(message);\n\t\tthis.name = 'AcmeError';\n\t\tthis.status = status;\n\t\tthis.body = body;\n\t}\n}\n\nconst defaultSleep = (ms: number): Promise<void> =>\n\tnew Promise((resolve) => setTimeout(resolve, ms));\n\nexport const issueCertificate = async (\n\toptions: IssueCertificateOptions\n): Promise<IssuedCertificate> => {\n\tconst log = options.onLog ?? (() => {});\n\tconst sleep = options.sleep ?? defaultSleep;\n\tconst fetcher = options.fetch ?? fetch;\n\tconst directoryUrl = options.directoryUrl ?? LETSENCRYPT_PRODUCTION;\n\tconst pollMs = options.pollIntervalMs ?? 3_000;\n\tconst dnsDelay = options.dnsPropagationDelayMs ?? 30_000;\n\tconst orderTimeout = options.orderTimeoutMs ?? 5 * 60_000;\n\tconst account = options.account ?? (await generateAccountKey());\n\n\tif (options.domains.length === 0) {\n\t\tthrow new Error('[deploy/tls] at least one domain is required');\n\t}\n\n\t// 1. Fetch directory.\n\tlog(`[tls] fetching ACME directory ${directoryUrl}`);\n\tconst directoryResponse = await fetcher(directoryUrl);\n\tif (!directoryResponse.ok) {\n\t\tthrow new AcmeError(\n\t\t\t`failed to fetch ACME directory: ${directoryResponse.status}`,\n\t\t\tdirectoryResponse.status,\n\t\t\tawait directoryResponse.text()\n\t\t);\n\t}\n\tconst directory = (await directoryResponse.json()) as AcmeDirectory;\n\n\t// 2. Get initial nonce.\n\tlet nonce = await fetchNonce(fetcher, directory.newNonce);\n\n\t// Helper: signed POST. Tracks nonce; returns parsed JSON body + headers.\n\tconst post = async (\n\t\turl: string,\n\t\tpayload: object | string,\n\t\tidentification: { kid?: string; jwk?: JsonWebKey }\n\t): Promise<{ status: number; body: unknown; headers: Headers }> => {\n\t\tconst header: JwsHeader = {\n\t\t\talg: 'ES256',\n\t\t\tnonce,\n\t\t\turl,\n\t\t\t...(identification.kid !== undefined ? { kid: identification.kid } : {}),\n\t\t\t...(identification.jwk !== undefined ? { jwk: identification.jwk } : {})\n\t\t};\n\t\tconst jws = await signJws(account.key.privateKey, header, payload);\n\t\tconst response = await fetcher(url, {\n\t\t\tbody: JSON.stringify(jws),\n\t\t\theaders: { 'content-type': 'application/jose+json' },\n\t\t\tmethod: 'POST'\n\t\t});\n\t\tconst newNonce = response.headers.get('replay-nonce');\n\t\tif (newNonce !== null) nonce = newNonce;\n\t\tconst text = await response.text();\n\t\tconst body =\n\t\t\ttext.length > 0 && response.headers.get('content-type')?.includes('json')\n\t\t\t\t? JSON.parse(text)\n\t\t\t\t: text;\n\t\tif (!response.ok) {\n\t\t\tthrow new AcmeError(\n\t\t\t\t`ACME ${url} → ${response.status}`,\n\t\t\t\tresponse.status,\n\t\t\t\tbody\n\t\t\t);\n\t\t}\n\t\treturn { body, headers: response.headers, status: response.status };\n\t};\n\n\t// 3. Register the account (or reuse if kid is set).\n\tconst publicJwk = await exportPublicJwk(account.key.publicKey);\n\tif (account.kid === undefined) {\n\t\tlog(`[tls] registering ACME account for ${options.email}`);\n\t\tconst result = await post(\n\t\t\tdirectory.newAccount,\n\t\t\t{\n\t\t\t\tcontact: [`mailto:${options.email}`],\n\t\t\t\ttermsOfServiceAgreed: true\n\t\t\t},\n\t\t\t{ jwk: publicJwk }\n\t\t);\n\t\taccount.kid = result.headers.get('location') ?? undefined;\n\t\tif (account.kid === undefined) {\n\t\t\tthrow new Error(\n\t\t\t\t'[deploy/tls] ACME newAccount response missing Location header'\n\t\t\t);\n\t\t}\n\t\tlog(`[tls] account registered: ${account.kid}`);\n\t} else {\n\t\tlog(`[tls] reusing account ${account.kid}`);\n\t}\n\n\tconst accountKid = account.kid;\n\n\t// 4. Submit the order.\n\tlog(`[tls] submitting order for ${options.domains.join(', ')}`);\n\tconst orderResult = await post(\n\t\tdirectory.newOrder,\n\t\t{\n\t\t\tidentifiers: options.domains.map((domain) => ({\n\t\t\t\ttype: 'dns',\n\t\t\t\tvalue: domain\n\t\t\t}))\n\t\t},\n\t\t{ kid: accountKid }\n\t);\n\tlet order = orderResult.body as AcmeOrder;\n\tconst orderUrl = orderResult.headers.get('location');\n\tif (orderUrl === null) {\n\t\tthrow new Error(\n\t\t\t'[deploy/tls] ACME newOrder response missing Location header'\n\t\t);\n\t}\n\n\t// 5. For each authorization, complete the DNS-01 challenge.\n\tconst cleanups: Array<() => Promise<void>> = [];\n\tconst dnsCreated: Array<{\n\t\trecordId: string;\n\t\trecordName: string;\n\t\trecordValue: string;\n\t}> = [];\n\n\ttry {\n\t\tconst thumbprint = await jwkThumbprint(publicJwk);\n\n\t\tfor (const authUrl of order.authorizations) {\n\t\t\tconst authResult = await post(authUrl, '', { kid: accountKid });\n\t\t\tconst auth = authResult.body as AcmeAuthorization;\n\t\t\tconst challenge = auth.challenges.find((c) => c.type === 'dns-01');\n\t\t\tif (challenge === undefined) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`[deploy/tls] no dns-01 challenge for ${auth.identifier.value}`\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst keyAuthorization = `${challenge.token}.${thumbprint}`;\n\t\t\tconst txtBytes = new Uint8Array(\n\t\t\t\tawait crypto.subtle.digest(\n\t\t\t\t\t'SHA-256',\n\t\t\t\t\tnew TextEncoder().encode(keyAuthorization)\n\t\t\t\t)\n\t\t\t);\n\t\t\tconst txtValue = base64UrlEncode(txtBytes);\n\t\t\tconst recordName = `_acme-challenge.${auth.identifier.value}`;\n\t\t\tconst providerRecordName =\n\t\t\t\toptions.mapDnsChallengeRecord?.({\n\t\t\t\t\tdomain: auth.identifier.value,\n\t\t\t\t\trecordName\n\t\t\t\t}) ?? recordName;\n\t\t\tif (providerRecordName.trim().length === 0) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t'[deploy/tls] mapped DNS-01 record name must not be empty'\n\t\t\t\t);\n\t\t\t}\n\t\t\tlog(\n\t\t\t\tproviderRecordName === recordName\n\t\t\t\t\t? `[tls] DNS-01: setting ${recordName} = \"${txtValue}\"`\n\t\t\t\t\t: `[tls] DNS-01: setting delegated ${providerRecordName} for ${recordName} = \"${txtValue}\"`\n\t\t\t);\n\t\t\tconst record = await options.dnsProvider.upsert({\n\t\t\t\tcontent: txtValue,\n\t\t\t\tname: providerRecordName,\n\t\t\t\tttl: 60,\n\t\t\t\ttype: 'TXT'\n\t\t\t});\n\t\t\tdnsCreated.push({\n\t\t\t\trecordId: record.id,\n\t\t\t\trecordName: providerRecordName,\n\t\t\t\trecordValue: txtValue\n\t\t\t});\n\t\t\tcleanups.push(() => options.dnsProvider.delete(record.id));\n\n\t\t\tif (options.checkDnsPropagated !== undefined) {\n\t\t\t\tlog('[tls] waiting for DNS propagation (custom checker)…');\n\t\t\t\tconst deadline = Date.now() + dnsDelay;\n\t\t\t\twhile (Date.now() < deadline) {\n\t\t\t\t\tif (\n\t\t\t\t\t\tawait options.checkDnsPropagated(recordName, txtValue)\n\t\t\t\t\t) break;\n\t\t\t\t\tawait sleep(pollMs);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tlog(`[tls] sleeping ${dnsDelay}ms for DNS propagation`);\n\t\t\t\tawait sleep(dnsDelay);\n\t\t\t}\n\n\t\t\tlog(`[tls] notifying ACME of dns-01 readiness for ${auth.identifier.value}`);\n\t\t\tawait post(challenge.url, {}, { kid: accountKid });\n\n\t\t\t// Poll the authorization until valid/invalid.\n\t\t\tconst authDeadline = Date.now() + orderTimeout;\n\t\t\twhile (Date.now() < authDeadline) {\n\t\t\t\tconst polledResult = await post(authUrl, '', { kid: accountKid });\n\t\t\t\tconst polled = polledResult.body as AcmeAuthorization;\n\t\t\t\tif (polled.status === 'valid') break;\n\t\t\t\tif (polled.status === 'invalid') {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`[deploy/tls] authorization failed for ${auth.identifier.value}: ${JSON.stringify(polled.challenges)}`\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tawait sleep(pollMs);\n\t\t\t}\n\t\t}\n\n\t\t// 6. Finalize — generate cert keypair + CSR.\n\t\tlog('[tls] generating cert keypair + CSR');\n\t\tconst certKey = await generateCertKeyPair();\n\t\tconst csrDer = await buildCsr(options.domains, certKey);\n\t\tconst csr = base64UrlEncode(csrDer);\n\n\t\tawait post(order.finalize, { csr }, { kid: accountKid });\n\n\t\t// 7. Poll the order until valid + certificate URL appears.\n\t\tconst orderDeadline = Date.now() + orderTimeout;\n\t\twhile (Date.now() < orderDeadline) {\n\t\t\tconst refreshed = await post(orderUrl, '', { kid: accountKid });\n\t\t\torder = refreshed.body as AcmeOrder;\n\t\t\tif (order.status === 'valid' && order.certificate !== undefined) break;\n\t\t\tif (order.status === 'invalid') {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`[deploy/tls] order failed: ${JSON.stringify(order)}`\n\t\t\t\t);\n\t\t\t}\n\t\t\tawait sleep(pollMs);\n\t\t}\n\n\t\tif (order.certificate === undefined) {\n\t\t\tthrow new Error('[deploy/tls] order timed out before issuing certificate');\n\t\t}\n\n\t\t// 8. Download the cert (PEM bundle).\n\t\tlog(`[tls] downloading certificate from ${order.certificate}`);\n\t\tconst certResult = await post(order.certificate, '', { kid: accountKid });\n\t\tconst certificatePem =\n\t\t\ttypeof certResult.body === 'string'\n\t\t\t\t? certResult.body\n\t\t\t\t: String(certResult.body);\n\n\t\tconst privateKeyPem = await exportEcPrivateKeyPem(certKey);\n\n\t\treturn {\n\t\t\taccount,\n\t\t\tcertificatePem,\n\t\t\tdomains: options.domains,\n\t\t\tprivateKeyPem\n\t\t};\n\t} finally {\n\t\tfor (const cleanup of cleanups) {\n\t\t\ttry {\n\t\t\t\tawait cleanup();\n\t\t\t} catch (error) {\n\t\t\t\tlog(`[tls] cleanup failed (continuing): ${String(error)}`);\n\t\t\t}\n\t\t}\n\t\t// Suppress unused-variable lint without changing behavior.\n\t\tvoid dnsCreated;\n\t}\n};\n\nconst fetchNonce = async (\n\tfetcher: typeof fetch,\n\turl: string\n): Promise<string> => {\n\tconst response = await fetcher(url, { method: 'HEAD' });\n\tconst nonce = response.headers.get('replay-nonce');\n\tif (nonce === null) {\n\t\tthrow new Error('[deploy/tls] newNonce response missing Replay-Nonce header');\n\t}\n\treturn nonce;\n};\n\n// =============================================================================\n// installCertificateOnTarget\n// =============================================================================\n\nexport type InstallCertificateOptions = {\n\t/** Remote path for the cert chain. Default `/etc/ssl/<firstDomain>/fullchain.pem`. */\n\tcertPath?: string;\n\t/** Remote path for the private key. Default `/etc/ssl/<firstDomain>/privkey.pem`. */\n\tkeyPath?: string;\n\t/** Mode (chmod) for the cert + key. Default `600`. */\n\tmode?: string;\n\t/** Owner (chown) for the cert + key. Default unchanged. */\n\towner?: string;\n\t/** Optional reload command run after install (e.g. `'systemctl reload nginx'`). */\n\treload?: string;\n\t/** Override the writeTo helper (tests). */\n\twriteFile?: (target: Target, path: string, contents: string) => Promise<void>;\n};\n\nconst defaultWriteFile = async (\n\ttarget: Target,\n\tremotePath: string,\n\tcontents: string\n): Promise<void> => {\n\tconst escaped = contents.replaceAll(\"'\", \"'\\\\''\");\n\tawait target.exec(`mkdir -p \"$(dirname '${remotePath}')\"`);\n\tawait target.exec(`cat > '${remotePath}' <<'__ABS_TLS_EOF__'\\n${contents}__ABS_TLS_EOF__\\n`);\n\tvoid escaped;\n};\n\n/**\n * @internal — exposed for unit testing of cryptographic primitives.\n * Not part of the public API; consumers should NOT depend on this.\n */\nexport const __testing = {\n\tbase64UrlDecode,\n\tbase64UrlEncode,\n\tbase64UrlEncodeString,\n\tbuildCsr,\n\tderBitString,\n\tderInt,\n\tderOid,\n\tderSeq,\n\tderSet,\n\tecdsaRawToDer,\n\texportEcPrivateKeyPem,\n\texportPublicJwk,\n\tgenerateCertKeyPair,\n\tjwkThumbprint,\n\tsignJws\n};\n\n/**\n * Upload cert + private key to the target. Composable with the deploy\n * pipeline as a verify-step or post-deploy hook.\n */\nexport const installCertificateOnTarget = async (\n\ttarget: Target,\n\tcert: IssuedCertificate,\n\toptions: InstallCertificateOptions = {}\n): Promise<{ certPath: string; keyPath: string }> => {\n\tconst domain = cert.domains[0];\n\tif (domain === undefined) {\n\t\tthrow new Error('[deploy/tls] certificate has no domains');\n\t}\n\tconst certPath = options.certPath ?? `/etc/ssl/${domain}/fullchain.pem`;\n\tconst keyPath = options.keyPath ?? `/etc/ssl/${domain}/privkey.pem`;\n\tconst mode = options.mode ?? '600';\n\tconst writer = options.writeFile ?? defaultWriteFile;\n\n\tawait writer(target, certPath, cert.certificatePem);\n\tawait writer(target, keyPath, cert.privateKeyPem);\n\tawait target.exec(`chmod ${mode} '${certPath}' '${keyPath}'`);\n\tif (options.owner !== undefined) {\n\t\tawait target.exec(`chown ${options.owner} '${certPath}' '${keyPath}'`);\n\t}\n\tif (options.reload !== undefined) {\n\t\tawait target.exec(options.reload);\n\t}\n\n\treturn { certPath, keyPath };\n};\n\n// =============================================================================\n// Certificate inspection + renewal scheduling\n// =============================================================================\n\nexport type CertificateInspection = {\n\t/** CN + every SAN, deduplicated. */\n\tsubjects: string[];\n\t/** Issuance time, ms since epoch. */\n\tvalidFrom: number;\n\t/** Expiration time, ms since epoch. */\n\tvalidTo: number;\n\t/** Whole days remaining before `validTo`. Negative if expired. */\n\tdaysRemaining: number;\n\t/** True when the cert is past `validTo`. */\n\texpired: boolean;\n\t/** Issuer DN string (for \"Let's Encrypt vs staging vs other CA\" checks). */\n\tissuer: string;\n};\n\nconst parseSubjects = (x509: X509Certificate): string[] => {\n\tconst subjects = new Set<string>();\n\tconst cnMatch = x509.subject.match(/CN=([^,\\n]+)/);\n\tif (cnMatch !== null && cnMatch[1] !== undefined) {\n\t\tsubjects.add(cnMatch[1].trim());\n\t}\n\tconst san = x509.subjectAltName;\n\tif (san !== undefined && san !== null) {\n\t\tfor (const part of san.split(',')) {\n\t\t\tconst dnsMatch = part.trim().match(/^DNS:(.+)$/);\n\t\t\tif (dnsMatch !== null && dnsMatch[1] !== undefined) {\n\t\t\t\tsubjects.add(dnsMatch[1].trim());\n\t\t\t}\n\t\t}\n\t}\n\treturn [...subjects];\n};\n\n/**\n * Parse a PEM certificate into operator-shaped metadata. Used by\n * {@link renewCertificate} to decide whether to re-issue; also fine\n * for status pages and observability.\n */\nexport const inspectCertificate = (\n\tpem: string,\n\toptions: { now?: () => number } = {}\n): CertificateInspection => {\n\tconst x509 = new X509Certificate(pem);\n\tconst validFrom = new Date(x509.validFrom).getTime();\n\tconst validTo = new Date(x509.validTo).getTime();\n\tconst now = (options.now ?? Date.now)();\n\tconst daysRemaining = Math.floor(\n\t\t(validTo - now) / (24 * 60 * 60 * 1000)\n\t);\n\treturn {\n\t\tdaysRemaining,\n\t\texpired: validTo < now,\n\t\tissuer: x509.issuer,\n\t\tsubjects: parseSubjects(x509),\n\t\tvalidFrom,\n\t\tvalidTo\n\t};\n};\n\nexport type RenewCertificateOptions = IssueCertificateOptions & {\n\t/**\n\t * Current cert PEM. When provided, the cert's `validTo` decides\n\t * whether to re-issue; when absent, renewal always issues.\n\t */\n\tcurrentCertificatePem?: string;\n\t/**\n\t * Re-issue when fewer than this many days remain. Default 30,\n\t * matching certbot's standard schedule (Let's Encrypt issues 90-day\n\t * certs; renewing at 30 days leaves a 60-day error budget).\n\t */\n\trenewWhenDaysRemaining?: number;\n\t/** Force re-issue regardless of remaining days. Default false. */\n\tforce?: boolean;\n\t/** Override `Date.now()` for testing. */\n\tnow?: () => number;\n};\n\nexport type RenewalResult =\n\t| {\n\t\t\trenewed: true;\n\t\t\tcertificate: IssuedCertificate;\n\t\t\treason: 'forced' | 'no-current-cert' | 'expiring-soon';\n\t }\n\t| {\n\t\t\trenewed: false;\n\t\t\treason: 'still-fresh';\n\t\t\tinspection: CertificateInspection;\n\t };\n\n/**\n * Conditional cert renewal. Parses the supplied cert (if any),\n * decides whether to re-issue based on remaining days, and either\n * returns the existing cert info (\"still fresh\") or issues a new\n * one via {@link issueCertificate}.\n *\n * Wire to a scheduled function (cron / @absolutejs/sync schedule /\n * GitHub Action) running at least once a week. Idempotent: a fresh\n * cert returns `renewed: false` cheaply (one PEM parse, zero\n * network IO).\n *\n * @example\n * const result = await renewCertificate({\n * currentCertificatePem: existingPem, // omit to force first-issue\n * domains: ['api.example.com'],\n * dnsProvider: dns,\n * email: 'ops@example.com',\n * account: persistedAccount, // reuse to avoid CA rate limit\n * });\n * if (result.renewed) {\n * await installCertificateOnTarget(target, result.certificate, { reload });\n * }\n */\nexport const renewCertificate = async (\n\toptions: RenewCertificateOptions\n): Promise<RenewalResult> => {\n\tconst threshold = options.renewWhenDaysRemaining ?? 30;\n\tif (threshold < 0) {\n\t\tthrow new Error(\n\t\t\t'[deploy/tls] renewWhenDaysRemaining must be non-negative'\n\t\t);\n\t}\n\n\tif (options.force === true) {\n\t\tconst certificate = await issueCertificate(options);\n\t\treturn { certificate, reason: 'forced', renewed: true };\n\t}\n\n\tif (options.currentCertificatePem === undefined) {\n\t\tconst certificate = await issueCertificate(options);\n\t\treturn { certificate, reason: 'no-current-cert', renewed: true };\n\t}\n\n\tconst nowFn = options.now ?? Date.now;\n\tconst inspection = inspectCertificate(options.currentCertificatePem, {\n\t\tnow: nowFn\n\t});\n\n\tif (!inspection.expired && inspection.daysRemaining >= threshold) {\n\t\treturn { inspection, reason: 'still-fresh', renewed: false };\n\t}\n\n\tconst certificate = await issueCertificate(options);\n\treturn { certificate, reason: 'expiring-soon', renewed: true };\n};\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "F710513ADD2FEFC064756E2164756E21",
7
+ "mappings": ";;AAuBA;AAQO,IAAM,yBACZ;AACM,IAAM,sBACZ;AAMD,IAAM,kBAAkB,CAAC,UAA4C;AAAA,EACpE,MAAM,MAAM,iBAAiB,aAAa,QAAQ,IAAI,WAAW,KAAK;AAAA,EACtE,IAAI,SAAS;AAAA,EACb,WAAW,QAAQ;AAAA,IAAK,UAAU,OAAO,aAAa,IAAI;AAAA,EAC1D,OAAO,KAAK,MAAM,EAAE,WAAW,KAAK,GAAG,EAAE,WAAW,KAAK,GAAG,EAAE,WAAW,KAAK,EAAE;AAAA;AAGjF,IAAM,wBAAwB,CAAC,UAC9B,gBAAgB,IAAI,YAAY,EAAE,OAAO,KAAK,CAAC;AAEhD,IAAM,kBAAkB,CAAC,UAA8B;AAAA,EACtD,MAAM,SAAS,MAAM,WAAW,KAAK,GAAG,EAAE,WAAW,KAAK,GAAG,EAAE,OAC9D,KAAK,KAAK,MAAM,SAAS,CAAC,IAAI,GAC9B,GACD;AAAA,EACA,MAAM,SAAS,KAAK,MAAM;AAAA,EAC1B,MAAM,QAAQ,IAAI,WAAW,OAAO,MAAM;AAAA,EAC1C,SAAS,QAAQ,EAAG,QAAQ,OAAO,QAAQ,SAAS,GAAG;AAAA,IACtD,MAAM,SAAS,OAAO,WAAW,KAAK;AAAA,EACvC;AAAA,EACA,OAAO;AAAA;AAOR,IAAM,YAAY,CAAC,WAA+B;AAAA,EACjD,IAAI,SAAS;AAAA,IAAM,OAAO,WAAW,KAAK,CAAC,MAAM,CAAC;AAAA,EAClD,MAAM,QAAkB,CAAC;AAAA,EACzB,IAAI,YAAY;AAAA,EAChB,OAAO,YAAY,GAAG;AAAA,IACrB,MAAM,QAAQ,YAAY,GAAI;AAAA,IAC9B,cAAc;AAAA,EACf;AAAA,EACA,OAAO,WAAW,KAAK,CAAC,MAAO,MAAM,QAAQ,GAAG,KAAK,CAAC;AAAA;AAGvD,IAAM,SAAS,CAAC,KAAa,YAAoC;AAAA,EAChE,MAAM,SAAS,UAAU,QAAQ,MAAM;AAAA,EACvC,MAAM,MAAM,IAAI,WAAW,IAAI,OAAO,SAAS,QAAQ,MAAM;AAAA,EAC7D,IAAI,KAAK;AAAA,EACT,IAAI,IAAI,QAAQ,CAAC;AAAA,EACjB,IAAI,IAAI,SAAS,IAAI,OAAO,MAAM;AAAA,EAClC,OAAO;AAAA;AAGR,IAAM,SAAS,CAAC,aAAuC;AAAA,EACtD,MAAM,QAAQ,SAAS,OAAO,CAAC,KAAK,UAAU,MAAM,MAAM,QAAQ,CAAC;AAAA,EACnE,MAAM,UAAU,IAAI,WAAW,KAAK;AAAA,EACpC,IAAI,SAAS;AAAA,EACb,WAAW,SAAS,UAAU;AAAA,IAC7B,QAAQ,IAAI,OAAO,MAAM;AAAA,IACzB,UAAU,MAAM;AAAA,EACjB;AAAA,EACA,OAAO,OAAO,IAAM,OAAO;AAAA;AAG5B,IAAM,SAAS,CAAC,aAAuC;AAAA,EACtD,MAAM,QAAQ,SAAS,OAAO,CAAC,KAAK,UAAU,MAAM,MAAM,QAAQ,CAAC;AAAA,EACnE,MAAM,UAAU,IAAI,WAAW,KAAK;AAAA,EACpC,IAAI,SAAS;AAAA,EACb,WAAW,SAAS,UAAU;AAAA,IAC7B,QAAQ,IAAI,OAAO,MAAM;AAAA,IACzB,UAAU,MAAM;AAAA,EACjB;AAAA,EACA,OAAO,OAAO,IAAM,OAAO;AAAA;AAG5B,IAAM,SAAS,CAAC,UAA2C;AAAA,EAC1D,IAAI,OAAO,UAAU,UAAU;AAAA,IAE9B,IAAI,UAAU;AAAA,MAAG,OAAO,OAAO,GAAM,WAAW,KAAK,CAAC,CAAC,CAAC,CAAC;AAAA,IACzD,MAAM,QAAkB,CAAC;AAAA,IACzB,IAAI,YAAY;AAAA,IAChB,OAAO,YAAY,GAAG;AAAA,MACrB,MAAM,QAAQ,YAAY,GAAI;AAAA,MAC9B,cAAc;AAAA,IACf;AAAA,IACA,IAAK,MAAM,KAAgB;AAAA,MAAM,MAAM,QAAQ,CAAC;AAAA,IAChD,OAAO,OAAO,GAAM,WAAW,KAAK,KAAK,CAAC;AAAA,EAC3C;AAAA,EAGA,IAAI,QAAQ;AAAA,EACZ,OAAO,QAAQ,MAAM,SAAS,KAAK,MAAM,WAAW;AAAA,IAAG,SAAS;AAAA,EAChE,IAAI,UAAU,MAAM,SAAS,KAAK;AAAA,EAClC,IAAK,QAAQ,KAAgB,KAAM;AAAA,IAClC,MAAM,SAAS,IAAI,WAAW,QAAQ,SAAS,CAAC;AAAA,IAChD,OAAO,IAAI,SAAS,CAAC;AAAA,IACrB,UAAU;AAAA,EACX;AAAA,EACA,OAAO,OAAO,GAAM,OAAO;AAAA;AAG5B,IAAM,SAAS,CAAC,QAA4B;AAAA,EAC3C,MAAM,QAAQ,IAAI,MAAM,GAAG,EAAE,IAAI,CAAC,SAAS,OAAO,SAAS,MAAM,EAAE,CAAC;AAAA,EACpE,MAAM,QAAQ,MAAM;AAAA,EACpB,MAAM,SAAS,MAAM;AAAA,EACrB,IAAI,UAAU,aAAa,WAAW,WAAW;AAAA,IAChD,MAAM,IAAI,MAAM,6BAA6B,KAAK;AAAA,EACnD;AAAA,EACA,MAAM,QAAkB,CAAC,QAAQ,KAAK,MAAM;AAAA,EAC5C,SAAS,QAAQ,EAAG,QAAQ,MAAM,QAAQ,SAAS,GAAG;AAAA,IACrD,MAAM,QAAQ,MAAM;AAAA,IACpB,MAAM,SAAmB,CAAC;AAAA,IAC1B,IAAI,YAAY;AAAA,IAChB,GAAG;AAAA,MACF,OAAO,QAAQ,YAAY,GAAI;AAAA,MAC/B,cAAc;AAAA,IACf,SAAS,YAAY;AAAA,IACrB,SAAS,aAAa,EAAG,aAAa,OAAO,SAAS,GAAG,cAAc,GAAG;AAAA,MACzE,OAAO,cAAe,OAAO,cAAyB;AAAA,IACvD;AAAA,IACA,MAAM,KAAK,GAAG,MAAM;AAAA,EACrB;AAAA,EACA,OAAO,OAAO,GAAM,WAAW,KAAK,KAAK,CAAC;AAAA;AAM3C,IAAM,gBAAgB,CAAC,UACtB,OAAO,IAAM,IAAI,YAAY,EAAE,OAAO,KAAK,CAAC;AAK7C,IAAM,iBAAiB,CAAC,YACvB,OAAO,GAAM,OAAO;AAErB,IAAM,eAAe,CAAC,YAAoC;AAAA,EAGzD,MAAM,OAAO,IAAI,WAAW,QAAQ,SAAS,CAAC;AAAA,EAC9C,KAAK,KAAK;AAAA,EACV,KAAK,IAAI,SAAS,CAAC;AAAA,EACnB,OAAO,OAAO,GAAM,IAAI;AAAA;AAGzB,IAAM,gBAAgB,CACrB,KACA,SACA,cAAc,SACE,OAAO,MAAO,OAAO,cAAc,IAAI,MAAQ,OAAO;AAMvE,IAAM,gBAAgB,CAAC,WAAmC;AAAA,EACzD,MAAM,OAAO,OAAO,SAAS;AAAA,EAC7B,MAAM,IAAI,OAAO,SAAS,GAAG,IAAI;AAAA,EACjC,MAAM,IAAI,OAAO,SAAS,IAAI;AAAA,EAC9B,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;AAAA;AAqBrC,IAAM,UAAU,OACf,YACA,QACA,YACsB;AAAA,EACtB,MAAM,kBAAkB,sBAAsB,KAAK,UAAU,MAAM,CAAC;AAAA,EACpE,MAAM,iBACL,YAAY,KACT,KACA,sBAAsB,KAAK,UAAU,OAAO,CAAC;AAAA,EACjD,MAAM,eAAe,IAAI,YAAY,EAAE,OACtC,GAAG,mBAAmB,gBACvB;AAAA,EACA,MAAM,SAAS,IAAI,WAClB,MAAM,OAAO,OAAO,KACnB,EAAE,MAAM,WAAW,MAAM,QAAQ,GACjC,YACA,YACD,CACD;AAAA,EACA,OAAO;AAAA,IACN,SAAS;AAAA,IACT,WAAW;AAAA,IACX,WAAW,gBAAgB,MAAM;AAAA,EAClC;AAAA;AAOD,IAAM,kBAAkB,OAAO,cAA8C;AAAA,EAC5E,MAAM,MAAM,MAAM,OAAO,OAAO,UAAU,OAAO,SAAS;AAAA,EAG1D,OAAO;AAAA,IACN,KAAK,IAAI;AAAA,IACT,KAAK,IAAI;AAAA,IACT,GAAG,IAAI;AAAA,IACP,GAAG,IAAI;AAAA,EACR;AAAA;AAGD,IAAM,gBAAgB,OAAO,cAA2C;AAAA,EAEvE,MAAM,YAAY,KAAK,UAAU;AAAA,IAChC,KAAK,UAAU;AAAA,IACf,KAAK,UAAU;AAAA,IACf,GAAG,UAAU;AAAA,IACb,GAAG,UAAU;AAAA,EACd,CAAC;AAAA,EACD,MAAM,OAAO,MAAM,OAAO,OAAO,OAChC,WACA,IAAI,YAAY,EAAE,OAAO,SAAS,CACnC;AAAA,EACA,OAAO,gBAAgB,IAAI;AAAA;AAmBrB,IAAM,qBAAqB,YAAkC;AAAA,EACnE,MAAM,MAAM,MAAM,OAAO,OAAO,YAC/B,EAAE,MAAM,SAAS,YAAY,QAAQ,GACrC,MACA,CAAC,QAAQ,QAAQ,CAClB;AAAA,EACA,OAAO,EAAE,IAAI;AAAA;AAGP,IAAM,gBAAgB,OAC5B,YAC8B;AAAA,EAC9B,MAAM,YAAY,MAAM,OAAO,OAAO,UAAU,OAAO,QAAQ,IAAI,SAAS;AAAA,EAC5E,MAAM,aAAa,MAAM,OAAO,OAAO,UACtC,OACA,QAAQ,IAAI,UACb;AAAA,EACA,OAAO;AAAA,IACN;AAAA,IACA;AAAA,OACI,QAAQ,QAAQ,YAAY,EAAE,KAAK,QAAQ,IAAI,IAAI,CAAC;AAAA,EACzD;AAAA;AAGM,IAAM,gBAAgB,OAC5B,SAC0B;AAAA,EAC1B,MAAM,YAAY,MAAM,OAAO,OAAO,UACrC,OACA,KAAK,WACL,EAAE,MAAM,SAAS,YAAY,QAAQ,GACrC,MACA,CAAC,QAAQ,CACV;AAAA,EACA,MAAM,aAAa,MAAM,OAAO,OAAO,UACtC,OACA,KAAK,YACL,EAAE,MAAM,SAAS,YAAY,QAAQ,GACrC,MACA,CAAC,MAAM,CACR;AAAA,EACA,OAAO;AAAA,IACN,KAAK,EAAE,YAAY,UAAU;AAAA,OACzB,KAAK,QAAQ,YAAY,EAAE,KAAK,KAAK,IAAI,IAAI,CAAC;AAAA,EACnD;AAAA;AAOD,IAAM,oBAAoB,CAAC,YAAkC;AAAA,EAE5D,MAAM,eAAe,QAAQ,IAAI,CAAC,WACjC,OAAO,KAAM,IAAI,YAAY,EAAE,OAAO,MAAM,CAAC,CAC9C;AAAA,EACA,MAAM,cAAc,OAAO,YAAY;AAAA,EACvC,MAAM,iBAAiB,eAAe,WAAW;AAAA,EACjD,OAAO,OAAO,CAAC,OAAO,WAAW,GAAG,cAAc,CAAC;AAAA;AAGpD,IAAM,iCAAiC,CAAC,YAAkC;AAAA,EACzE,MAAM,aAAa,OAAO,CAAC,kBAAkB,OAAO,CAAC,CAAC;AAAA,EACtD,OAAO,OAAO;AAAA,IACb,OAAO,uBAAuB;AAAA,IAC9B,OAAO,CAAC,UAAU,CAAC;AAAA,EACpB,CAAC;AAAA;AAGF,IAAM,sBAAsB,YAC3B,OAAO,OAAO,YACb,EAAE,MAAM,SAAS,YAAY,QAAQ,GACrC,MACA,CAAC,QAAQ,QAAQ,CAClB;AAED,IAAM,WAAW,OAChB,SACA,YACyB;AAAA,EACzB,IAAI,QAAQ,WAAW,GAAG;AAAA,IACzB,MAAM,IAAI,MAAM,+CAA+C;AAAA,EAChE;AAAA,EACA,MAAM,aAAa,QAAQ;AAAA,EAE3B,MAAM,UAAU,IAAI,WACnB,MAAM,OAAO,OAAO,UAAU,QAAQ,QAAQ,SAAS,CACxD;AAAA,EAEA,MAAM,UAAU,OAAO,CAAC;AAAA,EACxB,MAAM,UAAU,OAAO;AAAA,IACtB,OAAO;AAAA,MACN,OAAO;AAAA,QACN,OAAO,SAAS;AAAA,QAChB,cAAc,UAAU;AAAA,MACzB,CAAC;AAAA,IACF,CAAC;AAAA,EACF,CAAC;AAAA,EAED,MAAM,aAAa,cAAc,GAAG,+BAA+B,OAAO,CAAC;AAAA,EAE3E,MAAM,2BAA2B,OAAO;AAAA,IACvC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD,CAAC;AAAA,EAED,MAAM,SAAS,IAAI,WAClB,MAAM,OAAO,OAAO,KACnB,EAAE,MAAM,WAAW,MAAM,QAAQ,GACjC,QAAQ,YACR,wBACD,CACD;AAAA,EACA,MAAM,SAAS,cAAc,MAAM;AAAA,EAEnC,MAAM,eAAe,OAAO,CAAC,OAAO,qBAAqB,CAAC,CAAC;AAAA,EAE3D,MAAM,gBAAgB,aAAa,MAAM;AAAA,EAEzC,OAAO,OAAO,CAAC,0BAA0B,cAAc,aAAa,CAAC;AAAA;AAOtE,IAAM,WAAW,CAAC,OAAe,QAA4B;AAAA,EAC5D,MAAM,MAAM,KAAK,OAAO,aAAa,GAAG,GAAG,CAAC;AAAA,EAC5C,MAAM,QAAQ,IAAI,MAAM,UAAU,KAAK,CAAC;AAAA,EACxC,OAAO,cAAc;AAAA,EAAe,MAAM,KAAK;AAAA,CAAI;AAAA,WAAe;AAAA;AAAA;AAGnE,IAAM,wBAAwB,OAC7B,YACqB;AAAA,EACrB,MAAM,QAAQ,IAAI,WACjB,MAAM,OAAO,OAAO,UAAU,SAAS,QAAQ,UAAU,CAC1D;AAAA,EACA,OAAO,SAAS,eAAe,KAAK;AAAA;AAAA;AAwF9B,MAAM,kBAAkB,MAAM;AAAA,EAC3B;AAAA,EACA;AAAA,EACT,WAAW,CAAC,SAAiB,QAAgB,MAAe;AAAA,IAC3D,MAAM,OAAO;AAAA,IACb,KAAK,OAAO;AAAA,IACZ,KAAK,SAAS;AAAA,IACd,KAAK,OAAO;AAAA;AAEd;AAEA,IAAM,eAAe,CAAC,OACrB,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,EAAE,CAAC;AAE1C,IAAM,mBAAmB,OAC/B,YACgC;AAAA,EAChC,MAAM,MAAM,QAAQ,UAAU,MAAM;AAAA,EACpC,MAAM,QAAQ,QAAQ,SAAS;AAAA,EAC/B,MAAM,UAAU,QAAQ,SAAS;AAAA,EACjC,MAAM,eAAe,QAAQ,gBAAgB;AAAA,EAC7C,MAAM,SAAS,QAAQ,kBAAkB;AAAA,EACzC,MAAM,WAAW,QAAQ,yBAAyB;AAAA,EAClD,MAAM,eAAe,QAAQ,kBAAkB,IAAI;AAAA,EACnD,MAAM,UAAU,QAAQ,WAAY,MAAM,mBAAmB;AAAA,EAE7D,IAAI,QAAQ,QAAQ,WAAW,GAAG;AAAA,IACjC,MAAM,IAAI,MAAM,8CAA8C;AAAA,EAC/D;AAAA,EAGA,IAAI,iCAAiC,cAAc;AAAA,EACnD,MAAM,oBAAoB,MAAM,QAAQ,YAAY;AAAA,EACpD,IAAI,CAAC,kBAAkB,IAAI;AAAA,IAC1B,MAAM,IAAI,UACT,mCAAmC,kBAAkB,UACrD,kBAAkB,QAClB,MAAM,kBAAkB,KAAK,CAC9B;AAAA,EACD;AAAA,EACA,MAAM,YAAa,MAAM,kBAAkB,KAAK;AAAA,EAGhD,IAAI,QAAQ,MAAM,WAAW,SAAS,UAAU,QAAQ;AAAA,EAGxD,MAAM,OAAO,OACZ,KACA,SACA,mBACkE;AAAA,IAClE,MAAM,SAAoB;AAAA,MACzB,KAAK;AAAA,MACL;AAAA,MACA;AAAA,SACI,eAAe,QAAQ,YAAY,EAAE,KAAK,eAAe,IAAI,IAAI,CAAC;AAAA,SAClE,eAAe,QAAQ,YAAY,EAAE,KAAK,eAAe,IAAI,IAAI,CAAC;AAAA,IACvE;AAAA,IACA,MAAM,MAAM,MAAM,QAAQ,QAAQ,IAAI,YAAY,QAAQ,OAAO;AAAA,IACjE,MAAM,WAAW,MAAM,QAAQ,KAAK;AAAA,MACnC,MAAM,KAAK,UAAU,GAAG;AAAA,MACxB,SAAS,EAAE,gBAAgB,wBAAwB;AAAA,MACnD,QAAQ;AAAA,IACT,CAAC;AAAA,IACD,MAAM,WAAW,SAAS,QAAQ,IAAI,cAAc;AAAA,IACpD,IAAI,aAAa;AAAA,MAAM,QAAQ;AAAA,IAC/B,MAAM,OAAO,MAAM,SAAS,KAAK;AAAA,IACjC,MAAM,OACL,KAAK,SAAS,KAAK,SAAS,QAAQ,IAAI,cAAc,GAAG,SAAS,MAAM,IACrE,KAAK,MAAM,IAAI,IACf;AAAA,IACJ,IAAI,CAAC,SAAS,IAAI;AAAA,MACjB,MAAM,IAAI,UACT,QAAQ,cAAQ,SAAS,UACzB,SAAS,QACT,IACD;AAAA,IACD;AAAA,IACA,OAAO,EAAE,MAAM,SAAS,SAAS,SAAS,QAAQ,SAAS,OAAO;AAAA;AAAA,EAInE,MAAM,YAAY,MAAM,gBAAgB,QAAQ,IAAI,SAAS;AAAA,EAC7D,IAAI,QAAQ,QAAQ,WAAW;AAAA,IAC9B,IAAI,sCAAsC,QAAQ,OAAO;AAAA,IACzD,MAAM,SAAS,MAAM,KACpB,UAAU,YACV;AAAA,MACC,SAAS,CAAC,UAAU,QAAQ,OAAO;AAAA,MACnC,sBAAsB;AAAA,IACvB,GACA,EAAE,KAAK,UAAU,CAClB;AAAA,IACA,QAAQ,MAAM,OAAO,QAAQ,IAAI,UAAU,KAAK;AAAA,IAChD,IAAI,QAAQ,QAAQ,WAAW;AAAA,MAC9B,MAAM,IAAI,MACT,+DACD;AAAA,IACD;AAAA,IACA,IAAI,6BAA6B,QAAQ,KAAK;AAAA,EAC/C,EAAO;AAAA,IACN,IAAI,yBAAyB,QAAQ,KAAK;AAAA;AAAA,EAG3C,MAAM,aAAa,QAAQ;AAAA,EAG3B,IAAI,8BAA8B,QAAQ,QAAQ,KAAK,IAAI,GAAG;AAAA,EAC9D,MAAM,cAAc,MAAM,KACzB,UAAU,UACV;AAAA,IACC,aAAa,QAAQ,QAAQ,IAAI,CAAC,YAAY;AAAA,MAC7C,MAAM;AAAA,MACN,OAAO;AAAA,IACR,EAAE;AAAA,EACH,GACA,EAAE,KAAK,WAAW,CACnB;AAAA,EACA,IAAI,QAAQ,YAAY;AAAA,EACxB,MAAM,WAAW,YAAY,QAAQ,IAAI,UAAU;AAAA,EACnD,IAAI,aAAa,MAAM;AAAA,IACtB,MAAM,IAAI,MACT,6DACD;AAAA,EACD;AAAA,EAGA,MAAM,WAAuC,CAAC;AAAA,EAC9C,MAAM,aAID,CAAC;AAAA,EAEN,IAAI;AAAA,IACH,MAAM,aAAa,MAAM,cAAc,SAAS;AAAA,IAEhD,WAAW,WAAW,MAAM,gBAAgB;AAAA,MAC3C,MAAM,aAAa,MAAM,KAAK,SAAS,IAAI,EAAE,KAAK,WAAW,CAAC;AAAA,MAC9D,MAAM,OAAO,WAAW;AAAA,MACxB,MAAM,YAAY,KAAK,WAAW,KAAK,CAAC,MAAM,EAAE,SAAS,QAAQ;AAAA,MACjE,IAAI,cAAc,WAAW;AAAA,QAC5B,MAAM,IAAI,MACT,wCAAwC,KAAK,WAAW,OACzD;AAAA,MACD;AAAA,MACA,MAAM,mBAAmB,GAAG,UAAU,SAAS;AAAA,MAC/C,MAAM,WAAW,IAAI,WACpB,MAAM,OAAO,OAAO,OACnB,WACA,IAAI,YAAY,EAAE,OAAO,gBAAgB,CAC1C,CACD;AAAA,MACA,MAAM,WAAW,gBAAgB,QAAQ;AAAA,MACzC,MAAM,aAAa,mBAAmB,KAAK,WAAW;AAAA,MACtD,MAAM,qBACL,QAAQ,wBAAwB;AAAA,QAC/B,QAAQ,KAAK,WAAW;AAAA,QACxB;AAAA,MACD,CAAC,KAAK;AAAA,MACP,IAAI,mBAAmB,KAAK,EAAE,WAAW,GAAG;AAAA,QAC3C,MAAM,IAAI,MACT,0DACD;AAAA,MACD;AAAA,MACA,IACC,uBAAuB,aACpB,yBAAyB,iBAAiB,cAC1C,mCAAmC,0BAA0B,iBAAiB,WAClF;AAAA,MACA,MAAM,SAAS,MAAM,QAAQ,YAAY,OAAO;AAAA,QAC/C,SAAS;AAAA,QACT,MAAM;AAAA,QACN,KAAK;AAAA,QACL,MAAM;AAAA,MACP,CAAC;AAAA,MACD,WAAW,KAAK;AAAA,QACf,UAAU,OAAO;AAAA,QACjB,YAAY;AAAA,QACZ,aAAa;AAAA,MACd,CAAC;AAAA,MACD,SAAS,KAAK,MAAM,QAAQ,YAAY,OAAO,OAAO,EAAE,CAAC;AAAA,MAEzD,IAAI,QAAQ,uBAAuB,WAAW;AAAA,QAC7C,IAAI,0DAAoD;AAAA,QACxD,MAAM,WAAW,KAAK,IAAI,IAAI;AAAA,QAC9B,OAAO,KAAK,IAAI,IAAI,UAAU;AAAA,UAC7B,IACC,MAAM,QAAQ,mBAAmB,YAAY,QAAQ;AAAA,YACpD;AAAA,UACF,MAAM,MAAM,MAAM;AAAA,QACnB;AAAA,MACD,EAAO;AAAA,QACN,IAAI,kBAAkB,gCAAgC;AAAA,QACtD,MAAM,MAAM,QAAQ;AAAA;AAAA,MAGrB,IAAI,gDAAgD,KAAK,WAAW,OAAO;AAAA,MAC3E,MAAM,KAAK,UAAU,KAAK,CAAC,GAAG,EAAE,KAAK,WAAW,CAAC;AAAA,MAGjD,MAAM,eAAe,KAAK,IAAI,IAAI;AAAA,MAClC,OAAO,KAAK,IAAI,IAAI,cAAc;AAAA,QACjC,MAAM,eAAe,MAAM,KAAK,SAAS,IAAI,EAAE,KAAK,WAAW,CAAC;AAAA,QAChE,MAAM,SAAS,aAAa;AAAA,QAC5B,IAAI,OAAO,WAAW;AAAA,UAAS;AAAA,QAC/B,IAAI,OAAO,WAAW,WAAW;AAAA,UAChC,MAAM,IAAI,MACT,yCAAyC,KAAK,WAAW,UAAU,KAAK,UAAU,OAAO,UAAU,GACpG;AAAA,QACD;AAAA,QACA,MAAM,MAAM,MAAM;AAAA,MACnB;AAAA,IACD;AAAA,IAGA,IAAI,qCAAqC;AAAA,IACzC,MAAM,UAAU,MAAM,oBAAoB;AAAA,IAC1C,MAAM,SAAS,MAAM,SAAS,QAAQ,SAAS,OAAO;AAAA,IACtD,MAAM,MAAM,gBAAgB,MAAM;AAAA,IAElC,MAAM,KAAK,MAAM,UAAU,EAAE,IAAI,GAAG,EAAE,KAAK,WAAW,CAAC;AAAA,IAGvD,MAAM,gBAAgB,KAAK,IAAI,IAAI;AAAA,IACnC,OAAO,KAAK,IAAI,IAAI,eAAe;AAAA,MAClC,MAAM,YAAY,MAAM,KAAK,UAAU,IAAI,EAAE,KAAK,WAAW,CAAC;AAAA,MAC9D,QAAQ,UAAU;AAAA,MAClB,IAAI,MAAM,WAAW,WAAW,MAAM,gBAAgB;AAAA,QAAW;AAAA,MACjE,IAAI,MAAM,WAAW,WAAW;AAAA,QAC/B,MAAM,IAAI,MACT,8BAA8B,KAAK,UAAU,KAAK,GACnD;AAAA,MACD;AAAA,MACA,MAAM,MAAM,MAAM;AAAA,IACnB;AAAA,IAEA,IAAI,MAAM,gBAAgB,WAAW;AAAA,MACpC,MAAM,IAAI,MAAM,yDAAyD;AAAA,IAC1E;AAAA,IAGA,IAAI,sCAAsC,MAAM,aAAa;AAAA,IAC7D,MAAM,aAAa,MAAM,KAAK,MAAM,aAAa,IAAI,EAAE,KAAK,WAAW,CAAC;AAAA,IACxE,MAAM,iBACL,OAAO,WAAW,SAAS,WACxB,WAAW,OACX,OAAO,WAAW,IAAI;AAAA,IAE1B,MAAM,gBAAgB,MAAM,sBAAsB,OAAO;AAAA,IAEzD,OAAO;AAAA,MACN;AAAA,MACA;AAAA,MACA,SAAS,QAAQ;AAAA,MACjB;AAAA,IACD;AAAA,YACC;AAAA,IACD,WAAW,WAAW,UAAU;AAAA,MAC/B,IAAI;AAAA,QACH,MAAM,QAAQ;AAAA,QACb,OAAO,OAAO;AAAA,QACf,IAAI,sCAAsC,OAAO,KAAK,GAAG;AAAA;AAAA,IAE3D;AAAA;AAAA;AAMF,IAAM,aAAa,OAClB,SACA,QACqB;AAAA,EACrB,MAAM,WAAW,MAAM,QAAQ,KAAK,EAAE,QAAQ,OAAO,CAAC;AAAA,EACtD,MAAM,QAAQ,SAAS,QAAQ,IAAI,cAAc;AAAA,EACjD,IAAI,UAAU,MAAM;AAAA,IACnB,MAAM,IAAI,MAAM,4DAA4D;AAAA,EAC7E;AAAA,EACA,OAAO;AAAA;AAsBR,IAAM,mBAAmB,OACxB,QACA,YACA,aACmB;AAAA,EACnB,MAAM,UAAU,SAAS,WAAW,KAAK,OAAO;AAAA,EAChD,MAAM,OAAO,KAAK,wBAAwB,eAAe;AAAA,EACzD,MAAM,OAAO,KAAK,UAAU;AAAA,EAAoC;AAAA,CAA2B;AAAA;AAQrF,IAAM,YAAY;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACD;AAMO,IAAM,6BAA6B,OACzC,QACA,MACA,UAAqC,CAAC,MACc;AAAA,EACpD,MAAM,SAAS,KAAK,QAAQ;AAAA,EAC5B,IAAI,WAAW,WAAW;AAAA,IACzB,MAAM,IAAI,MAAM,yCAAyC;AAAA,EAC1D;AAAA,EACA,MAAM,WAAW,QAAQ,YAAY,YAAY;AAAA,EACjD,MAAM,UAAU,QAAQ,WAAW,YAAY;AAAA,EAC/C,MAAM,OAAO,QAAQ,QAAQ;AAAA,EAC7B,MAAM,SAAS,QAAQ,aAAa;AAAA,EAEpC,MAAM,OAAO,QAAQ,UAAU,KAAK,cAAc;AAAA,EAClD,MAAM,OAAO,QAAQ,SAAS,KAAK,aAAa;AAAA,EAChD,MAAM,OAAO,KAAK,SAAS,SAAS,cAAc,UAAU;AAAA,EAC5D,IAAI,QAAQ,UAAU,WAAW;AAAA,IAChC,MAAM,OAAO,KAAK,SAAS,QAAQ,UAAU,cAAc,UAAU;AAAA,EACtE;AAAA,EACA,IAAI,QAAQ,WAAW,WAAW;AAAA,IACjC,MAAM,OAAO,KAAK,QAAQ,MAAM;AAAA,EACjC;AAAA,EAEA,OAAO,EAAE,UAAU,QAAQ;AAAA;AAsB5B,IAAM,gBAAgB,CAAC,SAAoC;AAAA,EAC1D,MAAM,WAAW,IAAI;AAAA,EACrB,MAAM,UAAU,KAAK,QAAQ,MAAM,cAAc;AAAA,EACjD,IAAI,YAAY,QAAQ,QAAQ,OAAO,WAAW;AAAA,IACjD,SAAS,IAAI,QAAQ,GAAG,KAAK,CAAC;AAAA,EAC/B;AAAA,EACA,MAAM,MAAM,KAAK;AAAA,EACjB,IAAI,QAAQ,aAAa,QAAQ,MAAM;AAAA,IACtC,WAAW,QAAQ,IAAI,MAAM,GAAG,GAAG;AAAA,MAClC,MAAM,WAAW,KAAK,KAAK,EAAE,MAAM,YAAY;AAAA,MAC/C,IAAI,aAAa,QAAQ,SAAS,OAAO,WAAW;AAAA,QACnD,SAAS,IAAI,SAAS,GAAG,KAAK,CAAC;AAAA,MAChC;AAAA,IACD;AAAA,EACD;AAAA,EACA,OAAO,CAAC,GAAG,QAAQ;AAAA;AAQb,IAAM,qBAAqB,CACjC,KACA,UAAkC,CAAC,MACR;AAAA,EAC3B,MAAM,OAAO,IAAI,gBAAgB,GAAG;AAAA,EACpC,MAAM,YAAY,IAAI,KAAK,KAAK,SAAS,EAAE,QAAQ;AAAA,EACnD,MAAM,UAAU,IAAI,KAAK,KAAK,OAAO,EAAE,QAAQ;AAAA,EAC/C,MAAM,OAAO,QAAQ,OAAO,KAAK,KAAK;AAAA,EACtC,MAAM,gBAAgB,KAAK,OACzB,UAAU,QAAQ,KAAK,KAAK,KAAK,KACnC;AAAA,EACA,OAAO;AAAA,IACN;AAAA,IACA,SAAS,UAAU;AAAA,IACnB,QAAQ,KAAK;AAAA,IACb,UAAU,cAAc,IAAI;AAAA,IAC5B;AAAA,IACA;AAAA,EACD;AAAA;AAwDM,IAAM,mBAAmB,OAC/B,YAC4B;AAAA,EAC5B,MAAM,YAAY,QAAQ,0BAA0B;AAAA,EACpD,IAAI,YAAY,GAAG;AAAA,IAClB,MAAM,IAAI,MACT,0DACD;AAAA,EACD;AAAA,EAEA,IAAI,QAAQ,UAAU,MAAM;AAAA,IAC3B,MAAM,eAAc,MAAM,iBAAiB,OAAO;AAAA,IAClD,OAAO,EAAE,2BAAa,QAAQ,UAAU,SAAS,KAAK;AAAA,EACvD;AAAA,EAEA,IAAI,QAAQ,0BAA0B,WAAW;AAAA,IAChD,MAAM,eAAc,MAAM,iBAAiB,OAAO;AAAA,IAClD,OAAO,EAAE,2BAAa,QAAQ,mBAAmB,SAAS,KAAK;AAAA,EAChE;AAAA,EAEA,MAAM,QAAQ,QAAQ,OAAO,KAAK;AAAA,EAClC,MAAM,aAAa,mBAAmB,QAAQ,uBAAuB;AAAA,IACpE,KAAK;AAAA,EACN,CAAC;AAAA,EAED,IAAI,CAAC,WAAW,WAAW,WAAW,iBAAiB,WAAW;AAAA,IACjE,OAAO,EAAE,YAAY,QAAQ,eAAe,SAAS,MAAM;AAAA,EAC5D;AAAA,EAEA,MAAM,cAAc,MAAM,iBAAiB,OAAO;AAAA,EAClD,OAAO,EAAE,aAAa,QAAQ,iBAAiB,SAAS,KAAK;AAAA;",
8
+ "debugId": "769AB3C81A63D52E64756E2164756E21",
9
9
  "names": []
10
10
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/deploy",
3
- "version": "0.11.0",
3
+ "version": "0.13.0",
4
4
  "description": "Generic Bun-project deploy pipeline. A Target (localTarget / sshTarget) is anywhere you can exec + upload — DigitalOcean droplets, Linode, Hetzner, Vultr, your own boxes. Bundled pipeline: prepare → upload → install → build → link → restart → verify. Atomic symlink swap, release history, prune, hooks. SSH shells out to system ssh/rsync — zero ssh2 deps.",
5
5
  "repository": {
6
6
  "type": "git",