@absolutejs/deploy 0.19.0 → 0.20.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.
Files changed (54) hide show
  1. package/dist/cloudTarget.d.ts +4 -4
  2. package/dist/cloudflare.d.ts +2 -2
  3. package/dist/cloudflare.js.map +2 -2
  4. package/dist/deployer.d.ts +9 -9
  5. package/dist/digitalocean.d.ts +5 -5
  6. package/dist/digitalocean.js +8 -2
  7. package/dist/digitalocean.js.map +5 -5
  8. package/dist/digitaloceanDns.d.ts +2 -2
  9. package/dist/digitaloceanDns.js +8 -2
  10. package/dist/digitaloceanDns.js.map +6 -6
  11. package/dist/digitaloceanInfrastructure.js +8 -2
  12. package/dist/digitaloceanInfrastructure.js.map +6 -6
  13. package/dist/digitaloceanIngress.js +8 -2
  14. package/dist/digitaloceanIngress.js.map +5 -5
  15. package/dist/dns.d.ts +1 -1
  16. package/dist/dns.js.map +2 -2
  17. package/dist/env.d.ts +1 -1
  18. package/dist/env.js.map +2 -2
  19. package/dist/gcp.js +6 -2
  20. package/dist/gcp.js.map +3 -3
  21. package/dist/hetzner.d.ts +3 -3
  22. package/dist/hetzner.js +8 -2
  23. package/dist/hetzner.js.map +5 -5
  24. package/dist/hetznerDns.d.ts +2 -2
  25. package/dist/hetznerDns.js.map +2 -2
  26. package/dist/hetznerInfrastructure.js +8 -2
  27. package/dist/hetznerInfrastructure.js.map +7 -7
  28. package/dist/index.d.ts +10 -10
  29. package/dist/index.js +64 -16
  30. package/dist/index.js.map +5 -5
  31. package/dist/linode.d.ts +3 -3
  32. package/dist/linode.js +8 -2
  33. package/dist/linode.js.map +5 -5
  34. package/dist/linodeInfrastructure.js +13 -3
  35. package/dist/linodeInfrastructure.js.map +7 -7
  36. package/dist/managedPreview.d.ts +1 -0
  37. package/dist/managedPreview.js +3 -2
  38. package/dist/managedPreview.js.map +3 -3
  39. package/dist/preview.d.ts +3 -3
  40. package/dist/preview.js +5 -2
  41. package/dist/preview.js.map +3 -3
  42. package/dist/processManagers.d.ts +4 -4
  43. package/dist/route53.d.ts +2 -2
  44. package/dist/route53.js.map +2 -2
  45. package/dist/targets.d.ts +1 -1
  46. package/dist/tls.d.ts +5 -5
  47. package/dist/tls.js +5 -2
  48. package/dist/tls.js.map +3 -3
  49. package/dist/vultr.d.ts +4 -4
  50. package/dist/vultr.js +8 -2
  51. package/dist/vultr.js.map +5 -5
  52. package/dist/vultrInfrastructure.js +8 -2
  53. package/dist/vultrInfrastructure.js.map +7 -7
  54. package/package.json +1 -1
@@ -227,7 +227,13 @@ var sshTarget = (options) => {
227
227
  }
228
228
  return;
229
229
  }
230
- const argv = ["scp", "-r", ...sshBaseFlags(options), localPath, `${remote}:${remotePath}`];
230
+ const argv = [
231
+ "scp",
232
+ "-r",
233
+ ...sshBaseFlags(options),
234
+ localPath,
235
+ `${remote}:${remotePath}`
236
+ ];
231
237
  const result = await runSpawn(argv, { timeoutMs: 600000 });
232
238
  if (result.exitCode !== 0) {
233
239
  throw new Error(`scp upload failed (exit ${result.exitCode}): ${result.stderr || result.stdout}`);
@@ -548,5 +554,5 @@ export {
548
554
  createDigitalOceanIngressProvider
549
555
  };
550
556
 
551
- //# debugId=C41516659DD2857764756E2164756E21
557
+ //# debugId=F4C1D4B40E8CFA0264756E2164756E21
552
558
  //# sourceMappingURL=digitaloceanIngress.js.map
@@ -3,12 +3,12 @@
3
3
  "sources": ["../src/edgeIngress.ts", "../src/targets.ts", "../src/cloudTarget.ts", "../src/digitalocean.ts", "../src/digitaloceanIngress.ts"],
4
4
  "sourcesContent": [
5
5
  "/** Provider-neutral lifecycle for a global ingress in front of regional edge pools. */\n\nexport type EdgeIngressProtocol = \"tcp\" | \"http\" | \"https\";\n\nexport type EdgeIngressBackend = {\n /** Provider-native regional pool id (for example a DO regional LB UUID or GCP group self-link). */\n resourceId: string;\n region: string;\n /** Lower values are preferred during regional failover. */\n priority?: number;\n};\n\nexport type EdgeIngressSpec = {\n backends: readonly EdgeIngressBackend[];\n healthCheck: {\n path?: string;\n port: number;\n protocol: EdgeIngressProtocol;\n };\n idempotencyKey: string;\n listener: {\n port: number;\n protocol: EdgeIngressProtocol;\n targetPort: number;\n tlsPassthrough?: boolean;\n };\n name: string;\n};\n\nexport type EdgeIngressState = \"provisioning\" | \"ready\" | \"degraded\";\n\nexport type EdgeIngress = {\n addresses: string[];\n backends: EdgeIngressBackend[];\n id: string;\n name: string;\n provider: string;\n state: EdgeIngressState;\n};\n\nexport type EdgeIngressCapabilities = {\n automaticHealthFailover: boolean;\n global: boolean;\n tlsPassthrough: boolean;\n};\n\nexport type EdgeIngressProvider = {\n capabilities: EdgeIngressCapabilities;\n getIngress: (name: string) => Promise<EdgeIngress | null>;\n name: string;\n reconcileIngress: (spec: EdgeIngressSpec) => Promise<EdgeIngress>;\n removeIngress: (name: string, idempotencyKey: string) => Promise<void>;\n};\n\nconst validPort = (port: number) =>\n Number.isInteger(port) && port >= 1 && port <= 65_535;\n\nexport class EdgeIngressValidationError extends Error {}\n\nexport const validateEdgeIngressSpec = (spec: EdgeIngressSpec) => {\n if (!/^[a-z]([-a-z0-9]*[a-z0-9])?$/.test(spec.name))\n throw new EdgeIngressValidationError(\"Invalid edge ingress name\");\n if (spec.backends.length === 0)\n throw new EdgeIngressValidationError(\n \"Edge ingress requires at least one regional backend\",\n );\n if (!validPort(spec.listener.port) || !validPort(spec.listener.targetPort))\n throw new EdgeIngressValidationError(\"Invalid edge ingress listener port\");\n if (!validPort(spec.healthCheck.port))\n throw new EdgeIngressValidationError(\"Invalid edge ingress health port\");\n const resources = new Set<string>();\n for (const backend of spec.backends) {\n if (!backend.resourceId || !backend.region)\n throw new EdgeIngressValidationError(\"Invalid edge ingress backend\");\n if (resources.has(backend.resourceId))\n throw new EdgeIngressValidationError(\"Duplicate edge ingress backend\");\n resources.add(backend.resourceId);\n }\n if (\n spec.listener.tlsPassthrough &&\n spec.listener.protocol !== \"https\" &&\n spec.listener.protocol !== \"tcp\"\n )\n throw new EdgeIngressValidationError(\n \"TLS passthrough requires an HTTPS or TCP listener\",\n );\n\n return spec;\n};\n\nexport const normalizedEdgeIngressBackends = (\n backends: readonly EdgeIngressBackend[],\n) =>\n [...backends].sort(\n (left, right) =>\n (left.priority ?? Number.MAX_SAFE_INTEGER) -\n (right.priority ?? Number.MAX_SAFE_INTEGER) ||\n left.region.localeCompare(right.region) ||\n left.resourceId.localeCompare(right.resourceId),\n );\n",
6
- "/**\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",
7
- "/**\n * Shared \"cloud-provider Target\" plumbing used by the\n * provider-specific adapters (`./digitalocean`, `./hetzner`, future\n * `./linode`, `./vultr`, etc.).\n *\n * The provider supplies a small `CloudTargetHooks` bag that knows\n * the provider's:\n *\n * - find-by-name lookup\n * - create call (closure over create params)\n * - fetch-by-id (used to poll for `active`)\n * - destroy-by-id\n * - status + ipv4 + id extraction from the provider's Server shape\n * - readiness predicate (status reached the terminal \"running\" value)\n *\n * `createCloudTarget()` does the universal machinery: provision-or-\n * reuse, poll until ready + IPv4, wait for SSH probe, build\n * `sshTarget` against the IPv4, return the Target wrapped with\n * `{ id, ipv4, destroy() }`.\n *\n * The public adapter (e.g. `digitalOceanTarget`) is a 30-line facade\n * that wires its provider-specific bits and renames `id` → `dropletId`\n * on the way out.\n */\n\nimport type { Target } from './targets';\nimport { sshTarget } from './targets';\n\n/** Provider-specific hooks. Keep these pure of network IO timing — the helper schedules. */\nexport type CloudTargetHooks<Server, Id = number> = {\n\t/** Find a server by name. Returns undefined if absent. */\n\tfindByName: (name: string) => Promise<Server | undefined>;\n\t/** Create the server. Closure over provider-specific create params. */\n\tcreate: () => Promise<Server>;\n\t/** Fetch a fresh copy of the server by id. Used to poll. */\n\tfetch: (id: Id) => Promise<Server>;\n\t/** Destroy a server by id. 404 should be treated as idempotent success. */\n\tdestroy: (id: Id) => Promise<void>;\n\t/** True when the server has reached its terminal \"running\" status. */\n\tisReady: (server: Server) => boolean;\n\t/** Extract the provider-assigned id (number for DO/Hetzner/Linode, string for Vultr). */\n\tgetId: (server: Server) => Id;\n\t/** Extract the public IPv4. Returns undefined while one is being assigned. */\n\tgetIpv4: (server: Server) => string | undefined;\n\t/** Extract the current status as a string (for log lines). */\n\tgetStatus: (server: Server) => string;\n};\n\nexport type CloudTargetOptions = {\n\t/** Provider's idempotency key (server name). */\n\tname: string;\n\t/** Region / location label — used in the \"creating\" log line. */\n\tregion: string;\n\n\t/** SSH login user. Default `'root'`. */\n\tuser?: string;\n\t/** SSH identity file. */\n\tidentity?: string;\n\t/** SSH port. Default 22. */\n\tport?: number;\n\n\t/** Default 5 min. */\n\tprovisionTimeoutMs?: number;\n\t/** Default 2 min. */\n\tsshReadinessTimeoutMs?: number;\n\t/** Default 5 s. */\n\tpollIntervalMs?: number;\n\n\t/** Called with status updates. */\n\tonLog?: (line: string) => void;\n\t/** Override SSH probe — tests skip real TCP IO. */\n\tprobeSsh?: (host: string, port: number) => Promise<boolean>;\n\t/** Override sleep — tests skip real waits. */\n\tsleep?: (ms: number) => Promise<void>;\n\t/** Override clock — tests inject deterministic timestamps. */\n\tnow?: () => number;\n\n\t/**\n\t * Short log prefix, e.g. `'[do]'` or `'[hetzner]'`. Threaded through\n\t * every log line so multi-provider deploys distinguish output.\n\t */\n\tlogPrefix: string;\n\t/**\n\t * Provider's word for the entity in log copy — `'droplet'` for DO,\n\t * `'server'` for Hetzner. Preserves provider-accurate output.\n\t */\n\tentityWord: string;\n\t/**\n\t * Build the Target's `description` field. Receives the resolved\n\t * IPv4 + the wrapped sshTarget description.\n\t */\n\tdescribeTarget: (sshDescription: string) => string;\n};\n\nexport type CloudTargetResult<Id = number> = {\n\tid: Id;\n\tipv4: string;\n\tdescription: string;\n\texec: Target['exec'];\n\tupload: Target['upload'];\n\tclose?: Target['close'];\n\tdestroy: () => Promise<void>;\n};\n\nconst defaultSleep = (ms: number): Promise<void> =>\n\tnew Promise((resolve) => setTimeout(resolve, ms));\n\nconst defaultProbeSsh = async (host: string, port: number): Promise<boolean> => {\n\tconst PROBE_TIMEOUT_MS = 2_000;\n\treturn new Promise<boolean>((resolve) => {\n\t\tlet settled = false;\n\t\tconst settle = (value: boolean) => {\n\t\t\tif (settled) return;\n\t\t\tsettled = true;\n\t\t\tresolve(value);\n\t\t};\n\t\tconst timer = setTimeout(() => settle(false), PROBE_TIMEOUT_MS);\n\t\tBun.connect({\n\t\t\thostname: host,\n\t\t\tport,\n\t\t\tsocket: {\n\t\t\t\tdata: () => {},\n\t\t\t\terror: () => {\n\t\t\t\t\tclearTimeout(timer);\n\t\t\t\t\tsettle(false);\n\t\t\t\t},\n\t\t\t\topen: (socket) => {\n\t\t\t\t\tclearTimeout(timer);\n\t\t\t\t\tsocket.end();\n\t\t\t\t\tsettle(true);\n\t\t\t\t}\n\t\t\t}\n\t\t}).catch(() => {\n\t\t\tclearTimeout(timer);\n\t\t\tsettle(false);\n\t\t});\n\t});\n};\n\n/**\n * The shared provision-or-reuse + wait-for-ready + wait-for-SSH\n * pipeline. Provider-specific adapters wire their `CloudTargetHooks`\n * + their option-shape mapping and return a typed result.\n */\nexport const createCloudTarget = async <Server, Id = number>(\n\thooks: CloudTargetHooks<Server, Id>,\n\toptions: CloudTargetOptions\n): Promise<CloudTargetResult<Id>> => {\n\tconst log = options.onLog ?? (() => {});\n\tconst probeSsh = options.probeSsh ?? defaultProbeSsh;\n\tconst sleep = options.sleep ?? defaultSleep;\n\tconst now = options.now ?? Date.now;\n\tconst pollMs = options.pollIntervalMs ?? 5_000;\n\tconst provisionTimeout = options.provisionTimeoutMs ?? 5 * 60_000;\n\tconst sshTimeout = options.sshReadinessTimeoutMs ?? 2 * 60_000;\n\tconst port = options.port ?? 22;\n\tconst prefix = options.logPrefix;\n\tconst noun = options.entityWord;\n\n\tconst existing = await hooks.findByName(options.name);\n\tlet current: Server;\n\tif (existing === undefined) {\n\t\tlog(`${prefix} creating ${noun} \"${options.name}\" in ${options.region}`);\n\t\tcurrent = await hooks.create();\n\t} else {\n\t\tlog(\n\t\t\t`${prefix} reusing ${noun} \"${options.name}\" (id ${hooks.getId(existing)}, status ${hooks.getStatus(existing)})`\n\t\t);\n\t\tcurrent = existing;\n\t}\n\n\t// Wait for status=ready AND public IPv4 assigned.\n\tconst provisionStart = now();\n\tlet ipv4 = hooks.getIpv4(current);\n\twhile (!hooks.isReady(current) || ipv4 === undefined) {\n\t\tif (now() - provisionStart > provisionTimeout) {\n\t\t\tthrow new Error(\n\t\t\t\t`${prefix} provision timeout after ${provisionTimeout}ms — ${noun} ${hooks.getId(current)} status \"${hooks.getStatus(current)}\", ipv4 ${ipv4 ?? '(unassigned)'}`\n\t\t\t);\n\t\t}\n\t\tawait sleep(pollMs);\n\t\tcurrent = await hooks.fetch(hooks.getId(current));\n\t\tipv4 = hooks.getIpv4(current);\n\t\tlog(\n\t\t\t`${prefix} poll: status=${hooks.getStatus(current)} ipv4=${ipv4 ?? '(none yet)'}`\n\t\t);\n\t}\n\tlog(`${prefix} ${noun} ready at ${ipv4}`);\n\n\t// Wait for SSH readiness.\n\tconst sshStart = now();\n\twhile (!(await probeSsh(ipv4, port))) {\n\t\tif (now() - sshStart > sshTimeout) {\n\t\t\tthrow new Error(\n\t\t\t\t`${prefix} SSH readiness timeout after ${sshTimeout}ms — ${ipv4}:${port} did not accept connections`\n\t\t\t);\n\t\t}\n\t\tawait sleep(pollMs);\n\t\tlog(`${prefix} waiting on ssh ${ipv4}:${port}`);\n\t}\n\tlog(`${prefix} ssh ready at ${ipv4}:${port}`);\n\n\tconst ssh = sshTarget({\n\t\thost: ipv4,\n\t\t...(options.user !== undefined ? { user: options.user } : {}),\n\t\t...(options.identity !== undefined ? { identity: options.identity } : {}),\n\t\t...(options.port !== undefined ? { port: options.port } : {})\n\t});\n\n\tconst id = hooks.getId(current);\n\tconst resolvedIpv4 = ipv4;\n\n\treturn {\n\t\tdescription: options.describeTarget(ssh.description),\n\t\tdestroy: () =>\n\t\t\thooks.destroy(id).then(() => {\n\t\t\t\tlog(`${prefix} destroyed ${noun} ${id}`);\n\t\t\t}),\n\t\texec: ssh.exec,\n\t\tid,\n\t\tipv4: resolvedIpv4,\n\t\tupload: ssh.upload,\n\t\t...(ssh.close !== undefined ? { close: ssh.close } : {})\n\t};\n};\n",
8
- "/**\n * @absolutejs/deploy/digitalocean — provision-or-reuse Target adapter\n * for DigitalOcean droplets.\n *\n * What it does:\n *\n * 1. Looks up a droplet by `name`. If present and active, reuses it.\n * 2. If not present, creates it via the DO v2 API and waits for\n * `status === 'active'` with a public IPv4 assigned.\n * 3. Waits for SSH readiness (TCP connect on port 22 with backoff,\n * or a caller-supplied probe).\n * 4. Returns a Target that wraps sshTarget against the droplet's\n * public IPv4, plus `dropletId`, `ipv4`, and a `destroy()` helper.\n *\n * Idempotent by name — calling twice with the same name returns the\n * same droplet, no duplicates created. If multiple droplets share\n * the name, throws (the caller has drifted state to clean up).\n *\n * Narrow DigitalOceanClientLike interface keeps the dots-on-the-i\n * SDK out as a hard dep. Default client uses `fetch` against\n * `api.digitalocean.com`; pass your own for retry / observability.\n */\n\nimport type { Target } from './targets';\nimport { createCloudTarget, type CloudTargetHooks } from './cloudTarget';\n\nconst DO_API_BASE = 'https://api.digitalocean.com/v2';\n\n/**\n * Minimal subset of DO API calls we make. Lets callers BYO a client\n * with retry / observability / etc. (e.g. wrap got, undici, or a\n * tenant-scoped client that injects different tokens per call).\n */\nexport type DigitalOceanClientLike = {\n\trequest: <T = unknown>(\n\t\tmethod: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',\n\t\tpath: string,\n\t\tbody?: unknown\n\t) => Promise<T>;\n};\n\n/** A DigitalOcean droplet record, narrowed to what we inspect. */\nexport type DigitalOceanDroplet = {\n\tid: number;\n\tname: string;\n\tstatus: 'new' | 'active' | 'off' | 'archive';\n\tregion?: { slug: string };\n\tsize_slug?: string;\n\tnetworks: {\n\t\tv4: Array<{ ip_address: string; type: 'public' | 'private' }>;\n\t\tv6?: Array<{ ip_address: string; type: 'public' | 'private' }>;\n\t};\n\ttags?: string[];\n};\n\nexport type DigitalOceanTargetOptions = {\n\t/** API token (https://cloud.digitalocean.com/account/api/tokens). Required unless `client` is set. */\n\ttoken?: string;\n\t/** Custom client. Overrides token-built default. */\n\tclient?: DigitalOceanClientLike;\n\n\t// ── Droplet shape ────────────────────────────────────────────────\n\t/** Droplet name. Also the idempotency key. */\n\tname: string;\n\t/** Region slug — `'nyc3'`, `'sfo3'`, `'ams3'`, etc. */\n\tregion: string;\n\t/** Size slug — `'s-1vcpu-1gb'`, `'s-2vcpu-4gb'`, etc. */\n\tsize: string;\n\t/** Image slug, snapshot id, or backup id. e.g. `'ubuntu-22-04-x64'`. */\n\timage: string | number;\n\t/** SSH key fingerprints OR numeric ids. At least one required to ssh in. */\n\tsshKeys: ReadonlyArray<string | number>;\n\t/** Tags applied at creation. Useful for `listDroplets({ tag })`. */\n\ttags?: ReadonlyArray<string>;\n\t/** cloud-init user data — a shell script or YAML config. */\n\tuserData?: string;\n\t/** VPC UUID. Defaults to the account's default VPC for the region. */\n\tvpcUuid?: string;\n\t/** Enable IPv6. Default false. */\n\tipv6?: boolean;\n\t/** Enable monitoring agent. Default false. */\n\tmonitoring?: boolean;\n\n\t// ── SSH wrap ────────────────────────────────────────────────────\n\t/** SSH login user. Default `'root'`. */\n\tuser?: string;\n\t/** Path to SSH identity file forwarded to sshTarget. */\n\tidentity?: string;\n\t/** SSH port. Default 22. */\n\tport?: number;\n\n\t// ── Timing ──────────────────────────────────────────────────────\n\t/** Max time to wait for droplet `active` + IPv4. Default 5 min. */\n\tprovisionTimeoutMs?: number;\n\t/** Max time to wait for SSH probe to succeed. Default 2 min. */\n\tsshReadinessTimeoutMs?: number;\n\t/** Poll interval for provision + ssh probe. Default 5 s. */\n\tpollIntervalMs?: number;\n\n\t// ── Observability + injection points ───────────────────────────\n\t/** Called with status updates (one line each). Default: noop. */\n\tonLog?: (line: string) => void;\n\t/**\n\t * Override the SSH readiness probe. Default opens a TCP socket to\n\t * `host:port`. Tests pass a fake probe to skip real network IO.\n\t */\n\tprobeSsh?: (host: string, port: number) => Promise<boolean>;\n\t/**\n\t * Sleep used between polls. Default `setTimeout`-based. Tests can\n\t * pass a synchronous resolver to skip real waits.\n\t */\n\tsleep?: (ms: number) => Promise<void>;\n\t/** Wall clock. Defaults to `Date.now`. Tests can swap. */\n\tnow?: () => number;\n};\n\nexport type DigitalOceanTarget = Target & {\n\treadonly dropletId: number;\n\treadonly ipv4: string;\n\t/** Destroy the droplet via the DO API. */\n\tdestroy: () => Promise<void>;\n};\n\nexport class DigitalOceanError 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 = 'DigitalOceanError';\n\t\tthis.status = status;\n\t\tthis.body = body;\n\t}\n}\n\n/**\n * fetch-backed default client. Talks JSON to `api.digitalocean.com/v2`.\n * Throws DigitalOceanError on non-2xx with the response body attached\n * so the caller can switch on `err.status`.\n */\nexport const createDigitalOceanClient = (\n\ttoken: string,\n\toptions: { baseUrl?: string; fetch?: typeof fetch } = {}\n): DigitalOceanClientLike => {\n\tconst base = options.baseUrl ?? DO_API_BASE;\n\tconst f = options.fetch ?? fetch;\n\treturn {\n\t\trequest: async <T>(\n\t\t\tmethod: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',\n\t\t\tpath: string,\n\t\t\tbody?: unknown\n\t\t): Promise<T> => {\n\t\t\tconst init: RequestInit = {\n\t\t\t\theaders: {\n\t\t\t\t\tauthorization: `Bearer ${token}`,\n\t\t\t\t\t'content-type': 'application/json'\n\t\t\t\t},\n\t\t\t\tmethod\n\t\t\t};\n\t\t\tif (body !== undefined) init.body = JSON.stringify(body);\n\t\t\tconst response = await f(`${base}${path}`, init);\n\t\t\tif (response.status === 204) return undefined as T;\n\t\t\tconst text = await response.text();\n\t\t\tconst parsed = text.length > 0 ? JSON.parse(text) : undefined;\n\t\t\tif (!response.ok) {\n\t\t\t\tthrow new DigitalOceanError(\n\t\t\t\t\t`DigitalOcean API ${method} ${path} failed: ${response.status} ${response.statusText}`,\n\t\t\t\t\tresponse.status,\n\t\t\t\t\tparsed\n\t\t\t\t);\n\t\t\t}\n\t\t\treturn parsed as T;\n\t\t}\n\t};\n};\n\nconst resolveClient = (\n\toptions: Pick<DigitalOceanTargetOptions, 'client' | 'token'>\n): DigitalOceanClientLike => {\n\tif (options.client !== undefined) return options.client;\n\tif (options.token !== undefined && options.token.length > 0) {\n\t\treturn createDigitalOceanClient(options.token);\n\t}\n\tthrow new Error(\n\t\t'[deploy/digitalocean] either `token` or `client` must be provided'\n\t);\n};\n\nconst publicIpv4 = (droplet: DigitalOceanDroplet): string | undefined =>\n\tdroplet.networks.v4.find((net) => net.type === 'public')?.ip_address;\n\n/**\n * Find a droplet by name. Returns undefined if absent.\n * Throws if more than one droplet shares the name (drifted state).\n */\nexport const findDigitalOceanDroplet = async (\n\tclient: DigitalOceanClientLike,\n\tname: string\n): Promise<DigitalOceanDroplet | undefined> => {\n\t// DO's list endpoint supports `name=` exact-match filtering.\n\tconst body = await client.request<{ droplets: DigitalOceanDroplet[] }>(\n\t\t'GET',\n\t\t`/droplets?name=${encodeURIComponent(name)}`\n\t);\n\tconst matches = body.droplets.filter((droplet) => droplet.name === name);\n\tif (matches.length === 0) return undefined;\n\tif (matches.length > 1) {\n\t\tthrow new Error(\n\t\t\t`[deploy/digitalocean] multiple droplets named \"${name}\" (${matches\n\t\t\t\t.map((droplet) => droplet.id)\n\t\t\t\t.join(', ')}). Resolve manually before adopting.`\n\t\t);\n\t}\n\treturn matches[0];\n};\n\n/** List droplets, optionally filtered by tag. Useful for cleanup tasks. */\nexport const listDigitalOceanDroplets = async (options: {\n\ttoken?: string;\n\tclient?: DigitalOceanClientLike;\n\ttag?: string;\n}): Promise<DigitalOceanDroplet[]> => {\n\tconst client = resolveClient(options);\n\tconst path =\n\t\toptions.tag !== undefined\n\t\t\t? `/droplets?tag_name=${encodeURIComponent(options.tag)}`\n\t\t\t: '/droplets';\n\tconst body = await client.request<{ droplets: DigitalOceanDroplet[] }>(\n\t\t'GET',\n\t\tpath\n\t);\n\treturn body.droplets;\n};\n\n/** Destroy a droplet by id. No-op if already gone. */\nexport const destroyDigitalOceanDroplet = async (options: {\n\ttoken?: string;\n\tclient?: DigitalOceanClientLike;\n\tid: number;\n}): Promise<void> => {\n\tconst client = resolveClient(options);\n\ttry {\n\t\tawait client.request('DELETE', `/droplets/${options.id}`);\n\t} catch (error) {\n\t\tif (error instanceof DigitalOceanError && error.status === 404) {\n\t\t\treturn; // already destroyed — idempotent\n\t\t}\n\t\tthrow error;\n\t}\n};\n\n/**\n * Provision-or-reuse a DO droplet by name, wait for SSH, return a\n * Target. Idempotent: same name → same droplet.\n */\nexport const digitalOceanTarget = async (\n\toptions: DigitalOceanTargetOptions\n): Promise<DigitalOceanTarget> => {\n\tconst client = resolveClient(options);\n\n\tconst hooks: CloudTargetHooks<DigitalOceanDroplet> = {\n\t\tcreate: async () => {\n\t\t\tconst created = await client.request<{ droplet: DigitalOceanDroplet }>(\n\t\t\t\t'POST',\n\t\t\t\t'/droplets',\n\t\t\t\t{\n\t\t\t\t\tname: options.name,\n\t\t\t\t\tregion: options.region,\n\t\t\t\t\tsize: options.size,\n\t\t\t\t\timage: options.image,\n\t\t\t\t\tssh_keys: [...options.sshKeys],\n\t\t\t\t\t...(options.tags !== undefined\n\t\t\t\t\t\t? { tags: [...options.tags] }\n\t\t\t\t\t\t: {}),\n\t\t\t\t\t...(options.userData !== undefined\n\t\t\t\t\t\t? { user_data: options.userData }\n\t\t\t\t\t\t: {}),\n\t\t\t\t\t...(options.vpcUuid !== undefined\n\t\t\t\t\t\t? { vpc_uuid: options.vpcUuid }\n\t\t\t\t\t\t: {}),\n\t\t\t\t\t...(options.ipv6 === true ? { ipv6: true } : {}),\n\t\t\t\t\t...(options.monitoring === true ? { monitoring: true } : {})\n\t\t\t\t}\n\t\t\t);\n\t\t\treturn created.droplet;\n\t\t},\n\t\tdestroy: (id) => destroyDigitalOceanDroplet({ client, id }),\n\t\tfetch: async (id) => {\n\t\t\tconst refreshed: { droplet: DigitalOceanDroplet } = await client.request(\n\t\t\t\t'GET',\n\t\t\t\t`/droplets/${id}`\n\t\t\t);\n\t\t\treturn refreshed.droplet;\n\t\t},\n\t\tfindByName: (name) => findDigitalOceanDroplet(client, name),\n\t\tgetId: (droplet) => droplet.id,\n\t\tgetIpv4: publicIpv4,\n\t\tgetStatus: (droplet) => droplet.status,\n\t\tisReady: (droplet) => droplet.status === 'active'\n\t};\n\n\tconst result = await createCloudTarget(hooks, {\n\t\tdescribeTarget: (sshDescription) =>\n\t\t\t`digitalocean droplet \"${options.name}\" (${sshDescription})`,\n\t\tentityWord: 'droplet',\n\t\tlogPrefix: '[do]',\n\t\tname: options.name,\n\t\tregion: options.region,\n\t\t...(options.user !== undefined ? { user: options.user } : {}),\n\t\t...(options.identity !== undefined ? { identity: options.identity } : {}),\n\t\t...(options.port !== undefined ? { port: options.port } : {}),\n\t\t...(options.provisionTimeoutMs !== undefined\n\t\t\t? { provisionTimeoutMs: options.provisionTimeoutMs }\n\t\t\t: {}),\n\t\t...(options.sshReadinessTimeoutMs !== undefined\n\t\t\t? { sshReadinessTimeoutMs: options.sshReadinessTimeoutMs }\n\t\t\t: {}),\n\t\t...(options.pollIntervalMs !== undefined\n\t\t\t? { pollIntervalMs: options.pollIntervalMs }\n\t\t\t: {}),\n\t\t...(options.onLog !== undefined ? { onLog: options.onLog } : {}),\n\t\t...(options.probeSsh !== undefined ? { probeSsh: options.probeSsh } : {}),\n\t\t...(options.sleep !== undefined ? { sleep: options.sleep } : {}),\n\t\t...(options.now !== undefined ? { now: options.now } : {})\n\t});\n\n\treturn {\n\t\tdescription: result.description,\n\t\tdestroy: result.destroy,\n\t\tdropletId: result.id,\n\t\texec: result.exec,\n\t\tipv4: result.ipv4,\n\t\tupload: result.upload,\n\t\t...(result.close !== undefined ? { close: result.close } : {})\n\t};\n};\n",
6
+ "/**\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 /** Working directory on the target. Default: target's root. */\n cwd?: string;\n /** Env vars to set for this command (merged onto target.env). */\n env?: Record<string, string>;\n /** Hard kill after this many ms. Default 600_000 (10 min). 0 disables. */\n timeoutMs?: number;\n /** Pipe stdout/stderr through here as it streams (lines, newline-stripped). */\n onLog?: (line: string, stream: \"stdout\" | \"stderr\") => void;\n /** Stdin payload — a string is written verbatim. */\n stdin?: string;\n};\n\nexport type ExecResult = {\n stdout: string;\n stderr: string;\n exitCode: number;\n};\n\nexport type UploadOptions = {\n /** Exclude paths matching these globs from a directory upload. */\n exclude?: string[];\n /** When uploading a directory, delete remote files not present locally. */\n deleteOrphans?: boolean;\n};\n\nexport type Target = {\n /** Human-readable description (e.g. \"ssh root@droplet-1.example.com\"). */\n readonly description: string;\n exec: (cmd: string, opts?: ExecOptions) => Promise<ExecResult>;\n upload: (\n localPath: string,\n remotePath: string,\n opts?: UploadOptions,\n ) => Promise<void>;\n close?: () => Promise<void>;\n};\n\n// -----------------------------------------------------------------------------\n// localTarget\n// -----------------------------------------------------------------------------\n\nexport type LocalTargetOptions = {\n /** Root directory the target operates in. Created if missing. */\n root: string;\n /** Env merged into every exec. */\n env?: Record<string, string>;\n};\n\nconst decodeChunks = async (\n reader: ReadableStream<Uint8Array> | null,\n onLine: ((line: string) => void) | undefined,\n): Promise<string> => {\n if (!reader) return \"\";\n const decoder = new TextDecoder();\n let buffer = \"\";\n let collected = \"\";\n const stream = reader.getReader();\n try {\n while (true) {\n const { done, value } = await stream.read();\n if (done) break;\n const chunk = decoder.decode(value, { stream: true });\n collected += chunk;\n if (!onLine) continue;\n buffer += chunk;\n let newline = buffer.indexOf(\"\\n\");\n while (newline !== -1) {\n const line = buffer.slice(0, newline).replace(/\\r$/, \"\");\n if (line.length > 0) onLine(line);\n buffer = buffer.slice(newline + 1);\n newline = buffer.indexOf(\"\\n\");\n }\n }\n const tail = decoder.decode();\n collected += tail;\n if (onLine && (buffer + tail).length > 0)\n onLine((buffer + tail).replace(/\\r$/, \"\"));\n } finally {\n stream.releaseLock();\n }\n return collected;\n};\n\nconst runSpawn = async (\n argv: string[],\n options: {\n cwd?: string;\n env?: Record<string, string>;\n timeoutMs?: number;\n onLog?: ExecOptions[\"onLog\"];\n stdin?: string;\n },\n): Promise<ExecResult> => {\n const proc = Bun.spawn(argv, {\n cwd: options.cwd,\n env: options.env,\n stderr: \"pipe\",\n stdin: options.stdin === undefined ? \"ignore\" : \"pipe\",\n stdout: \"pipe\",\n });\n\n if (options.stdin !== undefined && proc.stdin) {\n // Bun.spawn returns a FileSink for piped stdin — `write` + `end`, not a\n // WritableStream. (We use a permissive cast because @types/bun's\n // Subprocess.stdin discriminant flips based on the stdin generic.)\n const sink = proc.stdin as unknown as {\n write: (chunk: string | Uint8Array) => number | Promise<number>;\n end: () => void | Promise<void>;\n };\n const wrote = sink.write(options.stdin);\n if (wrote && typeof (wrote as Promise<number>).then === \"function\") {\n await wrote;\n }\n const ended = sink.end();\n if (ended && typeof (ended as Promise<void>).then === \"function\") {\n await ended;\n }\n }\n\n const timeout = options.timeoutMs ?? 600_000;\n let timer: ReturnType<typeof setTimeout> | undefined;\n if (timeout > 0) {\n timer = setTimeout(() => {\n try {\n proc.kill();\n } catch {\n /* already gone */\n }\n }, timeout);\n }\n\n const stdoutPromise = decodeChunks(\n proc.stdout as unknown as ReadableStream<Uint8Array>,\n options.onLog ? (line) => options.onLog!(line, \"stdout\") : undefined,\n );\n const stderrPromise = decodeChunks(\n proc.stderr as unknown as ReadableStream<Uint8Array>,\n options.onLog ? (line) => options.onLog!(line, \"stderr\") : undefined,\n );\n\n const [stdout, stderr, exitCode] = await Promise.all([\n stdoutPromise,\n stderrPromise,\n proc.exited,\n ]);\n if (timer) clearTimeout(timer);\n\n return { exitCode: exitCode ?? -1, stderr, stdout };\n};\n\nexport const localTarget = (options: LocalTargetOptions): Target => {\n const baseEnv = { ...options.env };\n const ensureRoot = async () => {\n await mkdir(options.root, { recursive: true });\n };\n\n return {\n description: `local ${options.root}`,\n exec: async (cmd, opts) => {\n await ensureRoot();\n return runSpawn([\"sh\", \"-c\", cmd], {\n cwd: opts?.cwd ?? options.root,\n env: { ...process.env, ...baseEnv, ...(opts?.env ?? {}) } as Record<\n string,\n string\n >,\n onLog: opts?.onLog,\n stdin: opts?.stdin,\n timeoutMs: opts?.timeoutMs,\n });\n },\n upload: async (localPath, remotePath, opts) => {\n await ensureRoot();\n const dest = remotePath.startsWith(\"/\")\n ? remotePath\n : join(options.root, remotePath);\n const argv = [\"rsync\", \"-a\"];\n if (opts?.deleteOrphans) argv.push(\"--delete\");\n for (const pattern of opts?.exclude ?? [])\n argv.push(\"--exclude\", pattern);\n // rsync semantics: a trailing slash on the source copies *contents*; without it the dir itself is nested.\n argv.push(localPath, dest);\n const result = await runSpawn(argv, { timeoutMs: 600_000 });\n if (result.exitCode !== 0) {\n throw new Error(\n `local upload failed (exit ${result.exitCode}): ${result.stderr || result.stdout}`,\n );\n }\n },\n };\n};\n\n// -----------------------------------------------------------------------------\n// sshTarget\n// -----------------------------------------------------------------------------\n\nexport type SshTargetOptions = {\n /** Hostname or IP of the remote. */\n host: string;\n /** Login user. Default `root`. */\n user?: string;\n /** SSH port. Default 22. */\n port?: number;\n /** Path to SSH identity file. Default: ssh's own search. */\n identity?: string;\n /** Extra flags appended to every `ssh` invocation. */\n sshFlags?: string[];\n /**\n * Use rsync for `upload`. Default true. When false, falls back to `scp`\n * which is universal but doesn't support delete / exclude.\n */\n rsync?: boolean;\n /**\n * Env vars to forward via `ssh -o SendEnv=...`. Most remote sshd configs\n * accept only `LANG` and `LC_*` by default; for app env vars use the\n * step `env` option instead, which prepends `KEY=value` to the command.\n */\n forwardEnv?: string[];\n};\n\nconst sshTargetString = (options: SshTargetOptions): string => {\n const user = options.user ?? \"root\";\n return `${user}@${options.host}`;\n};\n\nconst sshBaseFlags = (options: SshTargetOptions): string[] => {\n const flags: string[] = [];\n if (options.port !== undefined && options.port !== 22)\n flags.push(\"-p\", String(options.port));\n if (options.identity !== undefined) flags.push(\"-i\", options.identity);\n // Never get stuck on a host-key prompt; treat unknown hosts as a fatal config issue rather than a UX detour.\n flags.push(\"-o\", \"BatchMode=yes\", \"-o\", \"StrictHostKeyChecking=accept-new\");\n for (const flag of options.sshFlags ?? []) flags.push(flag);\n return flags;\n};\n\nconst shellQuote = (value: string): string =>\n `'${value.replace(/'/g, `'\\\\''`)}'`;\n\nconst buildRemoteCmd = (cmd: string, opts: ExecOptions | undefined): string => {\n const env = opts?.env;\n const envPrefix = env\n ? Object.entries(env)\n .map(([k, v]) => `${k}=${shellQuote(v)}`)\n .join(\" \") + \" \"\n : \"\";\n if (opts?.cwd) {\n return `cd ${shellQuote(opts.cwd)} && ${envPrefix}${cmd}`;\n }\n return `${envPrefix}${cmd}`;\n};\n\nexport const sshTarget = (options: SshTargetOptions): Target => {\n const remote = sshTargetString(options);\n const useRsync = options.rsync ?? true;\n\n return {\n description: `ssh ${remote}${options.port && options.port !== 22 ? `:${options.port}` : \"\"}`,\n exec: async (cmd, opts) => {\n const argv = [\"ssh\", ...sshBaseFlags(options)];\n for (const name of options.forwardEnv ?? [])\n argv.push(\"-o\", `SendEnv=${name}`);\n argv.push(remote, buildRemoteCmd(cmd, opts));\n return runSpawn(argv, {\n onLog: opts?.onLog,\n stdin: opts?.stdin,\n timeoutMs: opts?.timeoutMs,\n });\n },\n upload: async (localPath, remotePath, opts) => {\n if (useRsync) {\n const sshCmd = [\"ssh\", ...sshBaseFlags(options)]\n .map((part) => (part.includes(\" \") ? `'${part}'` : part))\n .join(\" \");\n const argv = [\"rsync\", \"-az\", \"-e\", sshCmd];\n if (opts?.deleteOrphans) argv.push(\"--delete\");\n for (const pattern of opts?.exclude ?? [])\n argv.push(\"--exclude\", pattern);\n argv.push(localPath, `${remote}:${remotePath}`);\n const result = await runSpawn(argv, { timeoutMs: 600_000 });\n if (result.exitCode !== 0) {\n throw new Error(\n `rsync upload failed (exit ${result.exitCode}): ${result.stderr || result.stdout}`,\n );\n }\n return;\n }\n // scp fallback — no exclude, no delete. We still need -r to copy directories.\n const argv = [\n \"scp\",\n \"-r\",\n ...sshBaseFlags(options),\n localPath,\n `${remote}:${remotePath}`,\n ];\n const result = await runSpawn(argv, { timeoutMs: 600_000 });\n if (result.exitCode !== 0) {\n throw new Error(\n `scp upload failed (exit ${result.exitCode}): ${result.stderr || result.stdout}`,\n );\n }\n },\n };\n};\n",
7
+ "/**\n * Shared \"cloud-provider Target\" plumbing used by the\n * provider-specific adapters (`./digitalocean`, `./hetzner`, future\n * `./linode`, `./vultr`, etc.).\n *\n * The provider supplies a small `CloudTargetHooks` bag that knows\n * the provider's:\n *\n * - find-by-name lookup\n * - create call (closure over create params)\n * - fetch-by-id (used to poll for `active`)\n * - destroy-by-id\n * - status + ipv4 + id extraction from the provider's Server shape\n * - readiness predicate (status reached the terminal \"running\" value)\n *\n * `createCloudTarget()` does the universal machinery: provision-or-\n * reuse, poll until ready + IPv4, wait for SSH probe, build\n * `sshTarget` against the IPv4, return the Target wrapped with\n * `{ id, ipv4, destroy() }`.\n *\n * The public adapter (e.g. `digitalOceanTarget`) is a 30-line facade\n * that wires its provider-specific bits and renames `id` → `dropletId`\n * on the way out.\n */\n\nimport type { Target } from \"./targets\";\nimport { sshTarget } from \"./targets\";\n\n/** Provider-specific hooks. Keep these pure of network IO timing — the helper schedules. */\nexport type CloudTargetHooks<Server, Id = number> = {\n /** Find a server by name. Returns undefined if absent. */\n findByName: (name: string) => Promise<Server | undefined>;\n /** Create the server. Closure over provider-specific create params. */\n create: () => Promise<Server>;\n /** Fetch a fresh copy of the server by id. Used to poll. */\n fetch: (id: Id) => Promise<Server>;\n /** Destroy a server by id. 404 should be treated as idempotent success. */\n destroy: (id: Id) => Promise<void>;\n /** True when the server has reached its terminal \"running\" status. */\n isReady: (server: Server) => boolean;\n /** Extract the provider-assigned id (number for DO/Hetzner/Linode, string for Vultr). */\n getId: (server: Server) => Id;\n /** Extract the public IPv4. Returns undefined while one is being assigned. */\n getIpv4: (server: Server) => string | undefined;\n /** Extract the current status as a string (for log lines). */\n getStatus: (server: Server) => string;\n};\n\nexport type CloudTargetOptions = {\n /** Provider's idempotency key (server name). */\n name: string;\n /** Region / location label — used in the \"creating\" log line. */\n region: string;\n\n /** SSH login user. Default `'root'`. */\n user?: string;\n /** SSH identity file. */\n identity?: string;\n /** SSH port. Default 22. */\n port?: number;\n\n /** Default 5 min. */\n provisionTimeoutMs?: number;\n /** Default 2 min. */\n sshReadinessTimeoutMs?: number;\n /** Default 5 s. */\n pollIntervalMs?: number;\n\n /** Called with status updates. */\n onLog?: (line: string) => void;\n /** Override SSH probe — tests skip real TCP IO. */\n probeSsh?: (host: string, port: number) => Promise<boolean>;\n /** Override sleep — tests skip real waits. */\n sleep?: (ms: number) => Promise<void>;\n /** Override clock — tests inject deterministic timestamps. */\n now?: () => number;\n\n /**\n * Short log prefix, e.g. `'[do]'` or `'[hetzner]'`. Threaded through\n * every log line so multi-provider deploys distinguish output.\n */\n logPrefix: string;\n /**\n * Provider's word for the entity in log copy — `'droplet'` for DO,\n * `'server'` for Hetzner. Preserves provider-accurate output.\n */\n entityWord: string;\n /**\n * Build the Target's `description` field. Receives the resolved\n * IPv4 + the wrapped sshTarget description.\n */\n describeTarget: (sshDescription: string) => string;\n};\n\nexport type CloudTargetResult<Id = number> = {\n id: Id;\n ipv4: string;\n description: string;\n exec: Target[\"exec\"];\n upload: Target[\"upload\"];\n close?: Target[\"close\"];\n destroy: () => Promise<void>;\n};\n\nconst defaultSleep = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\nconst defaultProbeSsh = async (\n host: string,\n port: number,\n): Promise<boolean> => {\n const PROBE_TIMEOUT_MS = 2_000;\n return new Promise<boolean>((resolve) => {\n let settled = false;\n const settle = (value: boolean) => {\n if (settled) return;\n settled = true;\n resolve(value);\n };\n const timer = setTimeout(() => settle(false), PROBE_TIMEOUT_MS);\n Bun.connect({\n hostname: host,\n port,\n socket: {\n data: () => {},\n error: () => {\n clearTimeout(timer);\n settle(false);\n },\n open: (socket) => {\n clearTimeout(timer);\n socket.end();\n settle(true);\n },\n },\n }).catch(() => {\n clearTimeout(timer);\n settle(false);\n });\n });\n};\n\n/**\n * The shared provision-or-reuse + wait-for-ready + wait-for-SSH\n * pipeline. Provider-specific adapters wire their `CloudTargetHooks`\n * + their option-shape mapping and return a typed result.\n */\nexport const createCloudTarget = async <Server, Id = number>(\n hooks: CloudTargetHooks<Server, Id>,\n options: CloudTargetOptions,\n): Promise<CloudTargetResult<Id>> => {\n const log = options.onLog ?? (() => {});\n const probeSsh = options.probeSsh ?? defaultProbeSsh;\n const sleep = options.sleep ?? defaultSleep;\n const now = options.now ?? Date.now;\n const pollMs = options.pollIntervalMs ?? 5_000;\n const provisionTimeout = options.provisionTimeoutMs ?? 5 * 60_000;\n const sshTimeout = options.sshReadinessTimeoutMs ?? 2 * 60_000;\n const port = options.port ?? 22;\n const prefix = options.logPrefix;\n const noun = options.entityWord;\n\n const existing = await hooks.findByName(options.name);\n let current: Server;\n if (existing === undefined) {\n log(`${prefix} creating ${noun} \"${options.name}\" in ${options.region}`);\n current = await hooks.create();\n } else {\n log(\n `${prefix} reusing ${noun} \"${options.name}\" (id ${hooks.getId(existing)}, status ${hooks.getStatus(existing)})`,\n );\n current = existing;\n }\n\n // Wait for status=ready AND public IPv4 assigned.\n const provisionStart = now();\n let ipv4 = hooks.getIpv4(current);\n while (!hooks.isReady(current) || ipv4 === undefined) {\n if (now() - provisionStart > provisionTimeout) {\n throw new Error(\n `${prefix} provision timeout after ${provisionTimeout}ms — ${noun} ${hooks.getId(current)} status \"${hooks.getStatus(current)}\", ipv4 ${ipv4 ?? \"(unassigned)\"}`,\n );\n }\n await sleep(pollMs);\n current = await hooks.fetch(hooks.getId(current));\n ipv4 = hooks.getIpv4(current);\n log(\n `${prefix} poll: status=${hooks.getStatus(current)} ipv4=${ipv4 ?? \"(none yet)\"}`,\n );\n }\n log(`${prefix} ${noun} ready at ${ipv4}`);\n\n // Wait for SSH readiness.\n const sshStart = now();\n while (!(await probeSsh(ipv4, port))) {\n if (now() - sshStart > sshTimeout) {\n throw new Error(\n `${prefix} SSH readiness timeout after ${sshTimeout}ms — ${ipv4}:${port} did not accept connections`,\n );\n }\n await sleep(pollMs);\n log(`${prefix} waiting on ssh ${ipv4}:${port}`);\n }\n log(`${prefix} ssh ready at ${ipv4}:${port}`);\n\n const ssh = sshTarget({\n host: ipv4,\n ...(options.user !== undefined ? { user: options.user } : {}),\n ...(options.identity !== undefined ? { identity: options.identity } : {}),\n ...(options.port !== undefined ? { port: options.port } : {}),\n });\n\n const id = hooks.getId(current);\n const resolvedIpv4 = ipv4;\n\n return {\n description: options.describeTarget(ssh.description),\n destroy: () =>\n hooks.destroy(id).then(() => {\n log(`${prefix} destroyed ${noun} ${id}`);\n }),\n exec: ssh.exec,\n id,\n ipv4: resolvedIpv4,\n upload: ssh.upload,\n ...(ssh.close !== undefined ? { close: ssh.close } : {}),\n };\n};\n",
8
+ "/**\n * @absolutejs/deploy/digitalocean — provision-or-reuse Target adapter\n * for DigitalOcean droplets.\n *\n * What it does:\n *\n * 1. Looks up a droplet by `name`. If present and active, reuses it.\n * 2. If not present, creates it via the DO v2 API and waits for\n * `status === 'active'` with a public IPv4 assigned.\n * 3. Waits for SSH readiness (TCP connect on port 22 with backoff,\n * or a caller-supplied probe).\n * 4. Returns a Target that wraps sshTarget against the droplet's\n * public IPv4, plus `dropletId`, `ipv4`, and a `destroy()` helper.\n *\n * Idempotent by name — calling twice with the same name returns the\n * same droplet, no duplicates created. If multiple droplets share\n * the name, throws (the caller has drifted state to clean up).\n *\n * Narrow DigitalOceanClientLike interface keeps the dots-on-the-i\n * SDK out as a hard dep. Default client uses `fetch` against\n * `api.digitalocean.com`; pass your own for retry / observability.\n */\n\nimport type { Target } from \"./targets\";\nimport { createCloudTarget, type CloudTargetHooks } from \"./cloudTarget\";\n\nconst DO_API_BASE = \"https://api.digitalocean.com/v2\";\n\n/**\n * Minimal subset of DO API calls we make. Lets callers BYO a client\n * with retry / observability / etc. (e.g. wrap got, undici, or a\n * tenant-scoped client that injects different tokens per call).\n */\nexport type DigitalOceanClientLike = {\n request: <T = unknown>(\n method: \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\",\n path: string,\n body?: unknown,\n ) => Promise<T>;\n};\n\n/** A DigitalOcean droplet record, narrowed to what we inspect. */\nexport type DigitalOceanDroplet = {\n id: number;\n name: string;\n status: \"new\" | \"active\" | \"off\" | \"archive\";\n region?: { slug: string };\n size_slug?: string;\n networks: {\n v4: Array<{ ip_address: string; type: \"public\" | \"private\" }>;\n v6?: Array<{ ip_address: string; type: \"public\" | \"private\" }>;\n };\n tags?: string[];\n};\n\nexport type DigitalOceanTargetOptions = {\n /** API token (https://cloud.digitalocean.com/account/api/tokens). Required unless `client` is set. */\n token?: string;\n /** Custom client. Overrides token-built default. */\n client?: DigitalOceanClientLike;\n\n // ── Droplet shape ────────────────────────────────────────────────\n /** Droplet name. Also the idempotency key. */\n name: string;\n /** Region slug — `'nyc3'`, `'sfo3'`, `'ams3'`, etc. */\n region: string;\n /** Size slug — `'s-1vcpu-1gb'`, `'s-2vcpu-4gb'`, etc. */\n size: string;\n /** Image slug, snapshot id, or backup id. e.g. `'ubuntu-22-04-x64'`. */\n image: string | number;\n /** SSH key fingerprints OR numeric ids. At least one required to ssh in. */\n sshKeys: ReadonlyArray<string | number>;\n /** Tags applied at creation. Useful for `listDroplets({ tag })`. */\n tags?: ReadonlyArray<string>;\n /** cloud-init user data — a shell script or YAML config. */\n userData?: string;\n /** VPC UUID. Defaults to the account's default VPC for the region. */\n vpcUuid?: string;\n /** Enable IPv6. Default false. */\n ipv6?: boolean;\n /** Enable monitoring agent. Default false. */\n monitoring?: boolean;\n\n // ── SSH wrap ────────────────────────────────────────────────────\n /** SSH login user. Default `'root'`. */\n user?: string;\n /** Path to SSH identity file forwarded to sshTarget. */\n identity?: string;\n /** SSH port. Default 22. */\n port?: number;\n\n // ── Timing ──────────────────────────────────────────────────────\n /** Max time to wait for droplet `active` + IPv4. Default 5 min. */\n provisionTimeoutMs?: number;\n /** Max time to wait for SSH probe to succeed. Default 2 min. */\n sshReadinessTimeoutMs?: number;\n /** Poll interval for provision + ssh probe. Default 5 s. */\n pollIntervalMs?: number;\n\n // ── Observability + injection points ───────────────────────────\n /** Called with status updates (one line each). Default: noop. */\n onLog?: (line: string) => void;\n /**\n * Override the SSH readiness probe. Default opens a TCP socket to\n * `host:port`. Tests pass a fake probe to skip real network IO.\n */\n probeSsh?: (host: string, port: number) => Promise<boolean>;\n /**\n * Sleep used between polls. Default `setTimeout`-based. Tests can\n * pass a synchronous resolver to skip real waits.\n */\n sleep?: (ms: number) => Promise<void>;\n /** Wall clock. Defaults to `Date.now`. Tests can swap. */\n now?: () => number;\n};\n\nexport type DigitalOceanTarget = Target & {\n readonly dropletId: number;\n readonly ipv4: string;\n /** Destroy the droplet via the DO API. */\n destroy: () => Promise<void>;\n};\n\nexport class DigitalOceanError extends Error {\n readonly status: number;\n readonly body: unknown;\n constructor(message: string, status: number, body: unknown) {\n super(message);\n this.name = \"DigitalOceanError\";\n this.status = status;\n this.body = body;\n }\n}\n\n/**\n * fetch-backed default client. Talks JSON to `api.digitalocean.com/v2`.\n * Throws DigitalOceanError on non-2xx with the response body attached\n * so the caller can switch on `err.status`.\n */\nexport const createDigitalOceanClient = (\n token: string,\n options: { baseUrl?: string; fetch?: typeof fetch } = {},\n): DigitalOceanClientLike => {\n const base = options.baseUrl ?? DO_API_BASE;\n const f = options.fetch ?? fetch;\n return {\n request: async <T>(\n method: \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\",\n path: string,\n body?: unknown,\n ): Promise<T> => {\n const init: RequestInit = {\n headers: {\n authorization: `Bearer ${token}`,\n \"content-type\": \"application/json\",\n },\n method,\n };\n if (body !== undefined) init.body = JSON.stringify(body);\n const response = await f(`${base}${path}`, init);\n if (response.status === 204) return undefined as T;\n const text = await response.text();\n const parsed = text.length > 0 ? JSON.parse(text) : undefined;\n if (!response.ok) {\n throw new DigitalOceanError(\n `DigitalOcean API ${method} ${path} failed: ${response.status} ${response.statusText}`,\n response.status,\n parsed,\n );\n }\n return parsed as T;\n },\n };\n};\n\nconst resolveClient = (\n options: Pick<DigitalOceanTargetOptions, \"client\" | \"token\">,\n): DigitalOceanClientLike => {\n if (options.client !== undefined) return options.client;\n if (options.token !== undefined && options.token.length > 0) {\n return createDigitalOceanClient(options.token);\n }\n throw new Error(\n \"[deploy/digitalocean] either `token` or `client` must be provided\",\n );\n};\n\nconst publicIpv4 = (droplet: DigitalOceanDroplet): string | undefined =>\n droplet.networks.v4.find((net) => net.type === \"public\")?.ip_address;\n\n/**\n * Find a droplet by name. Returns undefined if absent.\n * Throws if more than one droplet shares the name (drifted state).\n */\nexport const findDigitalOceanDroplet = async (\n client: DigitalOceanClientLike,\n name: string,\n): Promise<DigitalOceanDroplet | undefined> => {\n // DO's list endpoint supports `name=` exact-match filtering.\n const body = await client.request<{ droplets: DigitalOceanDroplet[] }>(\n \"GET\",\n `/droplets?name=${encodeURIComponent(name)}`,\n );\n const matches = body.droplets.filter((droplet) => droplet.name === name);\n if (matches.length === 0) return undefined;\n if (matches.length > 1) {\n throw new Error(\n `[deploy/digitalocean] multiple droplets named \"${name}\" (${matches\n .map((droplet) => droplet.id)\n .join(\", \")}). Resolve manually before adopting.`,\n );\n }\n return matches[0];\n};\n\n/** List droplets, optionally filtered by tag. Useful for cleanup tasks. */\nexport const listDigitalOceanDroplets = async (options: {\n token?: string;\n client?: DigitalOceanClientLike;\n tag?: string;\n}): Promise<DigitalOceanDroplet[]> => {\n const client = resolveClient(options);\n const path =\n options.tag !== undefined\n ? `/droplets?tag_name=${encodeURIComponent(options.tag)}`\n : \"/droplets\";\n const body = await client.request<{ droplets: DigitalOceanDroplet[] }>(\n \"GET\",\n path,\n );\n return body.droplets;\n};\n\n/** Destroy a droplet by id. No-op if already gone. */\nexport const destroyDigitalOceanDroplet = async (options: {\n token?: string;\n client?: DigitalOceanClientLike;\n id: number;\n}): Promise<void> => {\n const client = resolveClient(options);\n try {\n await client.request(\"DELETE\", `/droplets/${options.id}`);\n } catch (error) {\n if (error instanceof DigitalOceanError && error.status === 404) {\n return; // already destroyed — idempotent\n }\n throw error;\n }\n};\n\n/**\n * Provision-or-reuse a DO droplet by name, wait for SSH, return a\n * Target. Idempotent: same name → same droplet.\n */\nexport const digitalOceanTarget = async (\n options: DigitalOceanTargetOptions,\n): Promise<DigitalOceanTarget> => {\n const client = resolveClient(options);\n\n const hooks: CloudTargetHooks<DigitalOceanDroplet> = {\n create: async () => {\n const created = await client.request<{ droplet: DigitalOceanDroplet }>(\n \"POST\",\n \"/droplets\",\n {\n name: options.name,\n region: options.region,\n size: options.size,\n image: options.image,\n ssh_keys: [...options.sshKeys],\n ...(options.tags !== undefined ? { tags: [...options.tags] } : {}),\n ...(options.userData !== undefined\n ? { user_data: options.userData }\n : {}),\n ...(options.vpcUuid !== undefined\n ? { vpc_uuid: options.vpcUuid }\n : {}),\n ...(options.ipv6 === true ? { ipv6: true } : {}),\n ...(options.monitoring === true ? { monitoring: true } : {}),\n },\n );\n return created.droplet;\n },\n destroy: (id) => destroyDigitalOceanDroplet({ client, id }),\n fetch: async (id) => {\n const refreshed: { droplet: DigitalOceanDroplet } = await client.request(\n \"GET\",\n `/droplets/${id}`,\n );\n return refreshed.droplet;\n },\n findByName: (name) => findDigitalOceanDroplet(client, name),\n getId: (droplet) => droplet.id,\n getIpv4: publicIpv4,\n getStatus: (droplet) => droplet.status,\n isReady: (droplet) => droplet.status === \"active\",\n };\n\n const result = await createCloudTarget(hooks, {\n describeTarget: (sshDescription) =>\n `digitalocean droplet \"${options.name}\" (${sshDescription})`,\n entityWord: \"droplet\",\n logPrefix: \"[do]\",\n name: options.name,\n region: options.region,\n ...(options.user !== undefined ? { user: options.user } : {}),\n ...(options.identity !== undefined ? { identity: options.identity } : {}),\n ...(options.port !== undefined ? { port: options.port } : {}),\n ...(options.provisionTimeoutMs !== undefined\n ? { provisionTimeoutMs: options.provisionTimeoutMs }\n : {}),\n ...(options.sshReadinessTimeoutMs !== undefined\n ? { sshReadinessTimeoutMs: options.sshReadinessTimeoutMs }\n : {}),\n ...(options.pollIntervalMs !== undefined\n ? { pollIntervalMs: options.pollIntervalMs }\n : {}),\n ...(options.onLog !== undefined ? { onLog: options.onLog } : {}),\n ...(options.probeSsh !== undefined ? { probeSsh: options.probeSsh } : {}),\n ...(options.sleep !== undefined ? { sleep: options.sleep } : {}),\n ...(options.now !== undefined ? { now: options.now } : {}),\n });\n\n return {\n description: result.description,\n destroy: result.destroy,\n dropletId: result.id,\n exec: result.exec,\n ipv4: result.ipv4,\n upload: result.upload,\n ...(result.close !== undefined ? { close: result.close } : {}),\n };\n};\n",
9
9
  "/** DigitalOcean Global Load Balancer adapter for regional edge pools. */\nimport {\n createDigitalOceanClient,\n type DigitalOceanClientLike,\n} from \"./digitalocean\";\nimport {\n normalizedEdgeIngressBackends,\n validateEdgeIngressSpec,\n type EdgeIngress,\n type EdgeIngressProvider,\n type EdgeIngressSpec,\n} from \"./edgeIngress\";\n\ntype DigitalOceanLoadBalancer = {\n id: string;\n ip?: string;\n ipv6?: string;\n name: string;\n status?: \"new\" | \"active\" | \"errored\";\n type?: \"GLOBAL\" | \"REGIONAL\" | \"REGIONAL_NETWORK\";\n};\n\nexport type DigitalOceanIngressProviderOptions = {\n client?: DigitalOceanClientLike;\n networkStack?: \"IPV4\" | \"DUALSTACK\";\n token?: string;\n};\n\nconst resolveClient = (options: DigitalOceanIngressProviderOptions) => {\n if (options.client) return options.client;\n if (options.token) return createDigitalOceanClient(options.token);\n throw new Error(\n \"[deploy/digitalocean-ingress] either `token` or `client` must be provided\",\n );\n};\n\nconst normalize = (\n loadBalancer: DigitalOceanLoadBalancer,\n backends: EdgeIngressSpec[\"backends\"] = [],\n): EdgeIngress => ({\n addresses: [loadBalancer.ip, loadBalancer.ipv6].filter(\n (address): address is string => Boolean(address),\n ),\n backends: normalizedEdgeIngressBackends(backends),\n id: `digitalocean:${loadBalancer.id}`,\n name: loadBalancer.name,\n provider: \"digitalocean\",\n state:\n loadBalancer.status === \"active\"\n ? \"ready\"\n : loadBalancer.status === \"errored\"\n ? \"degraded\"\n : \"provisioning\",\n});\n\nexport const createDigitalOceanIngressProvider = (\n options: DigitalOceanIngressProviderOptions,\n): EdgeIngressProvider => {\n const client = resolveClient(options);\n const find = async (name: string) => {\n const response = await client.request<{\n load_balancers: DigitalOceanLoadBalancer[];\n }>(\"GET\", \"/load_balancers\");\n const matches = response.load_balancers.filter(\n (loadBalancer) => loadBalancer.name === name,\n );\n if (matches.length > 1)\n throw new Error(\n `[deploy/digitalocean-ingress] multiple load balancers named ${name}`,\n );\n\n return matches[0] ?? null;\n };\n\n return {\n capabilities: {\n automaticHealthFailover: true,\n global: true,\n tlsPassthrough: true,\n },\n getIngress: async (name) => {\n const existing = await find(name);\n\n return existing ? normalize(existing) : null;\n },\n name: \"digitalocean\",\n reconcileIngress: async (spec) => {\n validateEdgeIngressSpec(spec);\n if (spec.listener.protocol === \"tcp\")\n throw new Error(\n \"[deploy/digitalocean-ingress] Global Load Balancers require an HTTP-family listener\",\n );\n const backends = normalizedEdgeIngressBackends(spec.backends);\n const existing = await find(spec.name);\n const body = {\n forwarding_rules: [\n {\n entry_port: spec.listener.port,\n entry_protocol: spec.listener.protocol,\n target_port: spec.listener.targetPort,\n target_protocol: spec.listener.protocol,\n ...(spec.listener.tlsPassthrough ? { tls_passthrough: true } : {}),\n },\n ],\n glb_settings: {\n region_priorities: Object.fromEntries(\n backends.map((backend, index) => [\n backend.region,\n backend.priority ?? index + 1,\n ]),\n ),\n target_load_balancer_ids: backends.map(\n (backend) => backend.resourceId,\n ),\n target_port: spec.listener.targetPort,\n target_protocol: spec.listener.protocol,\n },\n health_check: {\n path: spec.healthCheck.path ?? \"/healthz\",\n port: spec.healthCheck.port,\n protocol: spec.healthCheck.protocol,\n },\n name: spec.name,\n network_stack: options.networkStack ?? \"DUALSTACK\",\n type: \"GLOBAL\",\n };\n const response = existing\n ? await client.request<{ load_balancer: DigitalOceanLoadBalancer }>(\n \"PUT\",\n `/load_balancers/${encodeURIComponent(existing.id)}`,\n body,\n )\n : await client.request<{ load_balancer: DigitalOceanLoadBalancer }>(\n \"POST\",\n \"/load_balancers\",\n body,\n );\n\n return normalize(response.load_balancer, backends);\n },\n removeIngress: async (name) => {\n const existing = await find(name);\n if (existing)\n await client.request(\n \"DELETE\",\n `/load_balancers/${encodeURIComponent(existing.id)}`,\n );\n },\n };\n};\n"
10
10
  ],
11
- "mappings": "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",
12
- "debugId": "C41516659DD2857764756E2164756E21",
11
+ "mappings": "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",
12
+ "debugId": "F4C1D4B40E8CFA0264756E2164756E21",
13
13
  "names": []
14
14
  }
package/dist/dns.d.ts CHANGED
@@ -9,7 +9,7 @@
9
9
  * provider per zone.
10
10
  */
11
11
  /** Record types we care about for deploy workflows. */
12
- export type DnsRecordType = 'A' | 'AAAA' | 'CNAME' | 'TXT' | 'MX';
12
+ export type DnsRecordType = "A" | "AAAA" | "CNAME" | "TXT" | "MX";
13
13
  /** A DNS record as it exists in the provider. */
14
14
  export type DnsRecord = {
15
15
  /** Provider-assigned id (Cloudflare uuid, Route 53 set name, etc.). */
package/dist/dns.js.map CHANGED
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/dns.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * DNS provider contract for `@absolutejs/deploy`. Provider-specific\n * adapters (Cloudflare, Route 53, etc.) implement `DnsProvider`; the\n * deploy pipeline composes them with a cloud `Target` to point a\n * hostname at a freshly-provisioned IP.\n *\n * Operations are zone-scoped: a `DnsProvider` instance is bound to\n * one zone at construction time. Multi-zone hosts construct one\n * provider per zone.\n */\n\n/** Record types we care about for deploy workflows. */\nexport type DnsRecordType = 'A' | 'AAAA' | 'CNAME' | 'TXT' | 'MX';\n\n/** A DNS record as it exists in the provider. */\nexport type DnsRecord = {\n\t/** Provider-assigned id (Cloudflare uuid, Route 53 set name, etc.). */\n\tid: string;\n\t/** Fully-qualified record name (`api.example.com.`). */\n\tname: string;\n\ttype: DnsRecordType;\n\t/** Record value — IP for A/AAAA, hostname for CNAME, text for TXT, etc. */\n\tcontent: string;\n\tttl?: number;\n\t/**\n\t * Cloudflare's orange-cloud proxy flag. Other providers ignore this\n\t * field. Default behavior is provider-specific: Cloudflare defaults\n\t * to `true` (proxied) for new records when omitted.\n\t */\n\tproxied?: boolean;\n\tcomment?: string;\n};\n\n/** Desired state for an upsert. */\nexport type DnsRecordSpec = {\n\t/** Record name. May be `'@'` for the zone apex or relative ('api'). */\n\tname: string;\n\ttype: DnsRecordType;\n\tcontent: string;\n\tttl?: number;\n\tproxied?: boolean;\n\tcomment?: string;\n};\n\nexport type DnsRecordFilter = {\n\tname?: string;\n\ttype?: DnsRecordType;\n};\n\n/**\n * Zone-scoped DNS operations. Implementations: `cloudflareProvider`,\n * future `route53Provider`, etc.\n */\nexport type DnsProvider = {\n\t/** Human-readable description for logs (`'cloudflare zone \"example.com\"'`). */\n\treadonly description: string;\n\t/** List records, optionally filtered by name + type. */\n\tlist: (filter?: DnsRecordFilter) => Promise<DnsRecord[]>;\n\t/**\n\t * Find one record by exact (name, type). Returns undefined if absent.\n\t * Throws if multiple records share that key (drifted state — multiple\n\t * A records pointing at different IPs, etc.).\n\t */\n\tfind: (key: {\n\t\tname: string;\n\t\ttype: DnsRecordType;\n\t}) => Promise<DnsRecord | undefined>;\n\tcreate: (spec: DnsRecordSpec) => Promise<DnsRecord>;\n\tupdate: (id: string, spec: DnsRecordSpec) => Promise<DnsRecord>;\n\tdelete: (id: string) => Promise<void>;\n\t/**\n\t * Create or update so the (name, type) record matches `spec`. The\n\t * canonical \"point this DNS at this IP\" entry point — idempotent.\n\t */\n\tupsert: (spec: DnsRecordSpec) => Promise<DnsRecord>;\n};\n\n/**\n * Compose a DNS provider with a cloud Target's IPv4. Idempotently\n * ensures an A record (name → target.ipv4) exists. Returns the\n * resulting DNS record.\n *\n * Usage:\n *\n * const target = await digitalOceanTarget({ ... });\n * const dns = cloudflareProvider({ token, zoneId });\n * await ensureDnsForTarget(dns, {\n * name: 'api.example.com',\n * target,\n * ttl: 60,\n * proxied: false,\n * });\n */\nexport const ensureDnsForTarget = async (\n\tprovider: DnsProvider,\n\toptions: {\n\t\t/** Record name (FQDN, relative, or `'@'` for zone apex). */\n\t\tname: string;\n\t\t/** Cloud Target with an `ipv4` field. */\n\t\ttarget: { ipv4: string };\n\t\t/** Default 300s. */\n\t\tttl?: number;\n\t\t/** Cloudflare orange-cloud. Other providers ignore. */\n\t\tproxied?: boolean;\n\t\t/** Optional record comment for audit trails. */\n\t\tcomment?: string;\n\t}\n): Promise<DnsRecord> =>\n\tprovider.upsert({\n\t\tcontent: options.target.ipv4,\n\t\tname: options.name,\n\t\ttype: 'A',\n\t\t...(options.ttl !== undefined ? { ttl: options.ttl } : {}),\n\t\t...(options.proxied !== undefined ? { proxied: options.proxied } : {}),\n\t\t...(options.comment !== undefined ? { comment: options.comment } : {})\n\t});\n"
5
+ "/**\n * DNS provider contract for `@absolutejs/deploy`. Provider-specific\n * adapters (Cloudflare, Route 53, etc.) implement `DnsProvider`; the\n * deploy pipeline composes them with a cloud `Target` to point a\n * hostname at a freshly-provisioned IP.\n *\n * Operations are zone-scoped: a `DnsProvider` instance is bound to\n * one zone at construction time. Multi-zone hosts construct one\n * provider per zone.\n */\n\n/** Record types we care about for deploy workflows. */\nexport type DnsRecordType = \"A\" | \"AAAA\" | \"CNAME\" | \"TXT\" | \"MX\";\n\n/** A DNS record as it exists in the provider. */\nexport type DnsRecord = {\n /** Provider-assigned id (Cloudflare uuid, Route 53 set name, etc.). */\n id: string;\n /** Fully-qualified record name (`api.example.com.`). */\n name: string;\n type: DnsRecordType;\n /** Record value — IP for A/AAAA, hostname for CNAME, text for TXT, etc. */\n content: string;\n ttl?: number;\n /**\n * Cloudflare's orange-cloud proxy flag. Other providers ignore this\n * field. Default behavior is provider-specific: Cloudflare defaults\n * to `true` (proxied) for new records when omitted.\n */\n proxied?: boolean;\n comment?: string;\n};\n\n/** Desired state for an upsert. */\nexport type DnsRecordSpec = {\n /** Record name. May be `'@'` for the zone apex or relative ('api'). */\n name: string;\n type: DnsRecordType;\n content: string;\n ttl?: number;\n proxied?: boolean;\n comment?: string;\n};\n\nexport type DnsRecordFilter = {\n name?: string;\n type?: DnsRecordType;\n};\n\n/**\n * Zone-scoped DNS operations. Implementations: `cloudflareProvider`,\n * future `route53Provider`, etc.\n */\nexport type DnsProvider = {\n /** Human-readable description for logs (`'cloudflare zone \"example.com\"'`). */\n readonly description: string;\n /** List records, optionally filtered by name + type. */\n list: (filter?: DnsRecordFilter) => Promise<DnsRecord[]>;\n /**\n * Find one record by exact (name, type). Returns undefined if absent.\n * Throws if multiple records share that key (drifted state — multiple\n * A records pointing at different IPs, etc.).\n */\n find: (key: {\n name: string;\n type: DnsRecordType;\n }) => Promise<DnsRecord | undefined>;\n create: (spec: DnsRecordSpec) => Promise<DnsRecord>;\n update: (id: string, spec: DnsRecordSpec) => Promise<DnsRecord>;\n delete: (id: string) => Promise<void>;\n /**\n * Create or update so the (name, type) record matches `spec`. The\n * canonical \"point this DNS at this IP\" entry point — idempotent.\n */\n upsert: (spec: DnsRecordSpec) => Promise<DnsRecord>;\n};\n\n/**\n * Compose a DNS provider with a cloud Target's IPv4. Idempotently\n * ensures an A record (name → target.ipv4) exists. Returns the\n * resulting DNS record.\n *\n * Usage:\n *\n * const target = await digitalOceanTarget({ ... });\n * const dns = cloudflareProvider({ token, zoneId });\n * await ensureDnsForTarget(dns, {\n * name: 'api.example.com',\n * target,\n * ttl: 60,\n * proxied: false,\n * });\n */\nexport const ensureDnsForTarget = async (\n provider: DnsProvider,\n options: {\n /** Record name (FQDN, relative, or `'@'` for zone apex). */\n name: string;\n /** Cloud Target with an `ipv4` field. */\n target: { ipv4: string };\n /** Default 300s. */\n ttl?: number;\n /** Cloudflare orange-cloud. Other providers ignore. */\n proxied?: boolean;\n /** Optional record comment for audit trails. */\n comment?: string;\n },\n): Promise<DnsRecord> =>\n provider.upsert({\n content: options.target.ipv4,\n name: options.name,\n type: \"A\",\n ...(options.ttl !== undefined ? { ttl: options.ttl } : {}),\n ...(options.proxied !== undefined ? { proxied: options.proxied } : {}),\n ...(options.comment !== undefined ? { comment: options.comment } : {}),\n });\n"
6
6
  ],
7
- "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6FO,IAAM,qBAAqB,OACjC,UACA,YAaA,SAAS,OAAO;AAAA,EACf,SAAS,QAAQ,OAAO;AAAA,EACxB,MAAM,QAAQ;AAAA,EACd,MAAM;AAAA,KACF,QAAQ,QAAQ,YAAY,EAAE,KAAK,QAAQ,IAAI,IAAI,CAAC;AAAA,KACpD,QAAQ,YAAY,YAAY,EAAE,SAAS,QAAQ,QAAQ,IAAI,CAAC;AAAA,KAChE,QAAQ,YAAY,YAAY,EAAE,SAAS,QAAQ,QAAQ,IAAI,CAAC;AACrE,CAAC;",
7
+ "mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6FO,IAAM,qBAAqB,OAChC,UACA,YAaA,SAAS,OAAO;AAAA,EACd,SAAS,QAAQ,OAAO;AAAA,EACxB,MAAM,QAAQ;AAAA,EACd,MAAM;AAAA,KACF,QAAQ,QAAQ,YAAY,EAAE,KAAK,QAAQ,IAAI,IAAI,CAAC;AAAA,KACpD,QAAQ,YAAY,YAAY,EAAE,SAAS,QAAQ,QAAQ,IAAI,CAAC;AAAA,KAChE,QAAQ,YAAY,YAAY,EAAE,SAAS,QAAQ,QAAQ,IAAI,CAAC;AACtE,CAAC;",
8
8
  "debugId": "7B62074E80B4756564756E2164756E21",
9
9
  "names": []
10
10
  }
package/dist/env.d.ts CHANGED
@@ -17,7 +17,7 @@
17
17
  * shell start scripts source. One `KEY=value` per line, no
18
18
  * newlines inside values.
19
19
  */
20
- import type { Target } from './targets';
20
+ import type { Target } from "./targets";
21
21
  /**
22
22
  * Minimal interface this module needs to read secret values. The
23
23
  * `SecretBroker` from `@absolutejs/secrets` satisfies it structurally;
package/dist/env.js.map CHANGED
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/env.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * @absolutejs/deploy/env — push environment-variable files to remote\n * targets and fan-out secret rotations across the fleet.\n *\n * Composes with `@absolutejs/secrets`: that library handles the\n * in-process side (resolve, rotate, redact, in-process listeners);\n * this module handles the deploy-side (push values to remote env\n * files, atomic swap, conditional service reload).\n *\n * Narrow `SecretSource` interface — `@absolutejs/secrets`'\n * `SecretBroker` satisfies it structurally without a hard dep\n * either direction.\n *\n * Standard format: a remote env file at\n * `/etc/<appName>.env` (or wherever you choose) that systemd reads\n * via `EnvironmentFile=`, Docker reads via `--env-file`, and most\n * shell start scripts source. One `KEY=value` per line, no\n * newlines inside values.\n */\n\nimport type { Target } from './targets';\n\n// =============================================================================\n// SecretSource — narrow interface @absolutejs/secrets' broker satisfies\n// =============================================================================\n\n/**\n * Minimal interface this module needs to read secret values. The\n * `SecretBroker` from `@absolutejs/secrets` satisfies it structurally;\n * any other implementation (a Map, an AWS Secrets Manager client\n * wrapper, etc.) works as long as it exposes `resolve(name)`.\n */\nexport type SecretSource = {\n\tresolve: (\n\t\tname: string\n\t) => Promise<{ value: string; fingerprint?: string } | null>;\n};\n\n// =============================================================================\n// Env-file (de)serialization\n// =============================================================================\n\nconst KEY_PATTERN = /^[A-Z_][A-Z0-9_]*$/;\nconst NEEDS_QUOTING = /[\\s\"'`$\\\\#&|;<>(){}*?!]/;\n\nconst validateKey = (key: string): void => {\n\tif (!KEY_PATTERN.test(key)) {\n\t\tthrow new Error(\n\t\t\t`[deploy/env] invalid env key \"${key}\" — must match /^[A-Z_][A-Z0-9_]*$/`\n\t\t);\n\t}\n};\n\nconst serializeLine = (key: string, value: string): string => {\n\tvalidateKey(key);\n\tif (value.includes('\\n') || value.includes('\\r')) {\n\t\tthrow new Error(\n\t\t\t`[deploy/env] value for \"${key}\" contains a newline — env files cannot represent multi-line values. Use a separate file path for multi-line secrets.`\n\t\t);\n\t}\n\tif (NEEDS_QUOTING.test(value) || value.startsWith('=') || value === '') {\n\t\tconst escaped = value.replaceAll('\\\\', '\\\\\\\\').replaceAll('\"', '\\\\\"');\n\t\treturn `${key}=\"${escaped}\"`;\n\t}\n\treturn `${key}=${value}`;\n};\n\nexport const serializeEnvFile = (values: Record<string, string>): string => {\n\tconst lines: string[] = [];\n\tfor (const key of Object.keys(values).sort()) {\n\t\tconst value = values[key];\n\t\tif (value === undefined) continue;\n\t\tlines.push(serializeLine(key, value));\n\t}\n\treturn `${lines.join('\\n')}\\n`;\n};\n\nconst unquoteValue = (raw: string): string => {\n\tif (\n\t\traw.length >= 2 &&\n\t\traw.startsWith('\"') &&\n\t\traw.endsWith('\"')\n\t) {\n\t\tconst inner = raw.slice(1, -1);\n\t\treturn inner.replaceAll('\\\\\"', '\"').replaceAll('\\\\\\\\', '\\\\');\n\t}\n\tif (\n\t\traw.length >= 2 &&\n\t\traw.startsWith(\"'\") &&\n\t\traw.endsWith(\"'\")\n\t) {\n\t\treturn raw.slice(1, -1);\n\t}\n\treturn raw;\n};\n\n/**\n * Parse a remote env file's contents. Ignores blank lines and `#`\n * comments. Lines that don't match `KEY=VALUE` throw — the deploy\n * primitive owns the file, so unknown content is loud, not silent.\n */\nexport const parseEnvFile = (text: string): Record<string, string> => {\n\tconst result: Record<string, string> = {};\n\tfor (const rawLine of text.split('\\n')) {\n\t\tconst line = rawLine.trim();\n\t\tif (line.length === 0 || line.startsWith('#')) continue;\n\t\tconst eq = line.indexOf('=');\n\t\tif (eq <= 0) {\n\t\t\tthrow new Error(`[deploy/env] malformed env line: \"${rawLine}\"`);\n\t\t}\n\t\tconst key = line.slice(0, eq).trim();\n\t\tconst value = unquoteValue(line.slice(eq + 1).trim());\n\t\tvalidateKey(key);\n\t\tresult[key] = value;\n\t}\n\treturn result;\n};\n\n// =============================================================================\n// Deployment spec + sync primitive\n// =============================================================================\n\nexport type EnvDeployment = {\n\t/** The cloud Target whose remote filesystem we write to. */\n\ttarget: Target;\n\t/** Remote path for the env file (e.g. `/etc/myapp.env`). */\n\tremotePath: string;\n\t/**\n\t * Names of secrets this deployment consumes from the SecretSource.\n\t * Each name is resolved before the file is written.\n\t */\n\tsecretNames?: ReadonlyArray<string>;\n\t/**\n\t * Non-secret env vars to merge in (NODE_ENV, PORT, LOG_LEVEL).\n\t * If an `extras` key collides with a `secretNames` entry, throws —\n\t * silent overrides are worse than a loud rejection.\n\t */\n\textras?: Record<string, string>;\n\t/** chmod for the file. Default `'600'`. */\n\tmode?: string;\n\t/** chown for the file. Default unchanged. */\n\towner?: string;\n\t/**\n\t * Command to run after the file changes. Skipped when the diff is\n\t * empty — bouncing a service for an identical file is the wrong\n\t * default.\n\t */\n\treload?: string;\n};\n\nexport type EnvSyncResult = {\n\t/** Keys present in the new spec but not the existing file. */\n\tadded: string[];\n\t/** Keys whose values changed (fingerprints, NOT plaintext, for safety). */\n\tchanged: string[];\n\t/** Keys removed from the spec (still in the existing file). */\n\tremoved: string[];\n\t/** Keys whose values matched — file wasn't rewritten for these. */\n\tunchanged: string[];\n\t/** True iff `deployment.reload` was invoked. */\n\treloaded: boolean;\n\t/** True iff the file content changed (atomic-write happened). */\n\twrote: boolean;\n\t/** Remote path that was synced. */\n\tremotePath: string;\n};\n\nconst shellQuote = (value: string): string =>\n\t`'${value.replaceAll(\"'\", \"'\\\\''\")}'`;\n\nconst tryReadRemoteFile = async (\n\ttarget: Target,\n\tremotePath: string\n): Promise<string | undefined> => {\n\tconst result = await target.exec(\n\t\t`if [ -f ${shellQuote(remotePath)} ]; then cat ${shellQuote(remotePath)}; else echo __ABS_DEPLOY_ENV_ABSENT__; fi`\n\t);\n\tif (result.exitCode !== 0) {\n\t\tthrow new Error(\n\t\t\t`[deploy/env] failed to read ${remotePath}: exit ${result.exitCode}: ${result.stderr || result.stdout}`\n\t\t);\n\t}\n\tif (result.stdout.trim() === '__ABS_DEPLOY_ENV_ABSENT__') return undefined;\n\treturn result.stdout;\n};\n\nconst mergeValues = (\n\tdeployment: EnvDeployment,\n\tresolved: Record<string, string>\n): Record<string, string> => {\n\tconst out: Record<string, string> = {};\n\tfor (const [key, value] of Object.entries(deployment.extras ?? {})) {\n\t\tvalidateKey(key);\n\t\tout[key] = value;\n\t}\n\tfor (const [key, value] of Object.entries(resolved)) {\n\t\tif (out[key] !== undefined) {\n\t\t\tthrow new Error(\n\t\t\t\t`[deploy/env] \"${key}\" defined in BOTH extras and secretNames for ${deployment.remotePath} — remove one`\n\t\t\t);\n\t\t}\n\t\tout[key] = value;\n\t}\n\treturn out;\n};\n\nconst diffEnv = (\n\tprevious: Record<string, string>,\n\tnext: Record<string, string>\n): Pick<EnvSyncResult, 'added' | 'changed' | 'removed' | 'unchanged'> => {\n\tconst added: string[] = [];\n\tconst changed: string[] = [];\n\tconst removed: string[] = [];\n\tconst unchanged: string[] = [];\n\tconst allKeys = new Set([...Object.keys(previous), ...Object.keys(next)]);\n\tfor (const key of [...allKeys].sort()) {\n\t\tconst prev = previous[key];\n\t\tconst curr = next[key];\n\t\tif (curr === undefined) removed.push(key);\n\t\telse if (prev === undefined) added.push(key);\n\t\telse if (prev === curr) unchanged.push(key);\n\t\telse changed.push(key);\n\t}\n\treturn { added, changed, removed, unchanged };\n};\n\nconst writeRemoteFileAtomically = async (\n\ttarget: Target,\n\tremotePath: string,\n\tcontents: string,\n\tmode: string,\n\towner?: string\n): Promise<void> => {\n\tconst tempPath = `${remotePath}.new.${Math.floor(Date.now() / 1000)}`;\n\tconst dir = remotePath.split('/').slice(0, -1).join('/') || '/';\n\tconst mkdir = await target.exec(`mkdir -p ${shellQuote(dir)}`);\n\tif (mkdir.exitCode !== 0) {\n\t\tthrow new Error(\n\t\t\t`[deploy/env] mkdir ${dir} failed: ${mkdir.stderr || mkdir.stdout}`\n\t\t);\n\t}\n\t// Write via stdin redirect — no shell escaping of the env file content.\n\tconst writeResult = await target.exec(`cat > ${shellQuote(tempPath)}`, {\n\t\tstdin: contents\n\t});\n\tif (writeResult.exitCode !== 0) {\n\t\tthrow new Error(\n\t\t\t`[deploy/env] write to ${tempPath} failed: ${writeResult.stderr || writeResult.stdout}`\n\t\t);\n\t}\n\tconst chmod = await target.exec(\n\t\t`chmod ${shellQuote(mode)} ${shellQuote(tempPath)}`\n\t);\n\tif (chmod.exitCode !== 0) {\n\t\tthrow new Error(\n\t\t\t`[deploy/env] chmod failed: ${chmod.stderr || chmod.stdout}`\n\t\t);\n\t}\n\tif (owner !== undefined) {\n\t\tconst chown = await target.exec(\n\t\t\t`chown ${shellQuote(owner)} ${shellQuote(tempPath)}`\n\t\t);\n\t\tif (chown.exitCode !== 0) {\n\t\t\tthrow new Error(\n\t\t\t\t`[deploy/env] chown failed: ${chown.stderr || chown.stdout}`\n\t\t\t);\n\t\t}\n\t}\n\tconst mv = await target.exec(\n\t\t`mv ${shellQuote(tempPath)} ${shellQuote(remotePath)}`\n\t);\n\tif (mv.exitCode !== 0) {\n\t\tthrow new Error(`[deploy/env] mv failed: ${mv.stderr || mv.stdout}`);\n\t}\n};\n\n/**\n * Push an env file to a single target. Atomic: write to a `.new`\n * tempfile, chmod / chown, then `mv` into place. Diffs the merged\n * values against the existing file content and skips the\n * write+reload when nothing changed.\n */\nexport const syncEnvToTarget = async (\n\tdeployment: EnvDeployment,\n\tvalues: Record<string, string>\n): Promise<EnvSyncResult> => {\n\tconst mode = deployment.mode ?? '600';\n\tconst merged = mergeValues(deployment, values);\n\tconst previousText = await tryReadRemoteFile(\n\t\tdeployment.target,\n\t\tdeployment.remotePath\n\t);\n\tconst previous = previousText === undefined ? {} : parseEnvFile(previousText);\n\tconst diff = diffEnv(previous, merged);\n\tconst changed = diff.added.length + diff.changed.length + diff.removed.length;\n\n\tif (changed === 0) {\n\t\treturn {\n\t\t\t...diff,\n\t\t\treloaded: false,\n\t\t\tremotePath: deployment.remotePath,\n\t\t\twrote: false\n\t\t};\n\t}\n\n\tconst nextText = serializeEnvFile(merged);\n\tawait writeRemoteFileAtomically(\n\t\tdeployment.target,\n\t\tdeployment.remotePath,\n\t\tnextText,\n\t\tmode,\n\t\tdeployment.owner\n\t);\n\n\tlet reloaded = false;\n\tif (deployment.reload !== undefined) {\n\t\tconst reload = await deployment.target.exec(deployment.reload);\n\t\tif (reload.exitCode !== 0) {\n\t\t\tthrow new Error(\n\t\t\t\t`[deploy/env] reload command failed: ${reload.stderr || reload.stdout}`\n\t\t\t);\n\t\t}\n\t\treloaded = true;\n\t}\n\n\treturn {\n\t\t...diff,\n\t\treloaded,\n\t\tremotePath: deployment.remotePath,\n\t\twrote: true\n\t};\n};\n\n// =============================================================================\n// Multi-target fan-out via a SecretSource\n// =============================================================================\n\nconst resolveSecrets = async (\n\tsource: SecretSource,\n\tnames: ReadonlyArray<string>\n): Promise<Record<string, string>> => {\n\tconst out: Record<string, string> = {};\n\tfor (const name of names) {\n\t\tconst resolved = await source.resolve(name);\n\t\tif (resolved === null) {\n\t\t\tthrow new Error(\n\t\t\t\t`[deploy/env] secret \"${name}\" not found in source`\n\t\t\t);\n\t\t}\n\t\tout[name] = resolved.value;\n\t}\n\treturn out;\n};\n\nexport type DeploymentSyncResult = {\n\tdeployment: EnvDeployment;\n\tresult?: EnvSyncResult;\n\terror?: Error;\n};\n\n/**\n * Fan-out push: for each deployment, resolve its `secretNames` via\n * the source, merge with `extras`, and push to the target. Resolves\n * are fresh every call — so calling this again after\n * `broker.rotate(name)` propagates the new value to every\n * deployment that uses it.\n *\n * Best-effort across the fan-out: a per-target failure is captured\n * in the result, but doesn't stop the rest. Operator inspects the\n * returned array, fixes the broken target, re-runs.\n */\nexport const syncSecretsToDeployments = async (\n\tsource: SecretSource,\n\tdeployments: ReadonlyArray<EnvDeployment>\n): Promise<DeploymentSyncResult[]> => {\n\tconst results: DeploymentSyncResult[] = [];\n\tfor (const deployment of deployments) {\n\t\ttry {\n\t\t\tconst resolved = await resolveSecrets(\n\t\t\t\tsource,\n\t\t\t\tdeployment.secretNames ?? []\n\t\t\t);\n\t\t\tconst result = await syncEnvToTarget(deployment, resolved);\n\t\t\tresults.push({ deployment, result });\n\t\t} catch (error) {\n\t\t\tresults.push({\n\t\t\t\tdeployment,\n\t\t\t\terror: error instanceof Error ? error : new Error(String(error))\n\t\t\t});\n\t\t}\n\t}\n\treturn results;\n};\n\n/**\n * Returns just the deployments that use a given secret name. Useful\n * for \"rotate one secret and propagate ONLY to the consumers\" —\n * `syncSecretsToDeployments(source, deploymentsUsing(name, deployments))`.\n */\nexport const deploymentsUsing = (\n\tname: string,\n\tdeployments: ReadonlyArray<EnvDeployment>\n): EnvDeployment[] =>\n\tdeployments.filter((d) => (d.secretNames ?? []).includes(name));\n"
5
+ "/**\n * @absolutejs/deploy/env — push environment-variable files to remote\n * targets and fan-out secret rotations across the fleet.\n *\n * Composes with `@absolutejs/secrets`: that library handles the\n * in-process side (resolve, rotate, redact, in-process listeners);\n * this module handles the deploy-side (push values to remote env\n * files, atomic swap, conditional service reload).\n *\n * Narrow `SecretSource` interface — `@absolutejs/secrets`'\n * `SecretBroker` satisfies it structurally without a hard dep\n * either direction.\n *\n * Standard format: a remote env file at\n * `/etc/<appName>.env` (or wherever you choose) that systemd reads\n * via `EnvironmentFile=`, Docker reads via `--env-file`, and most\n * shell start scripts source. One `KEY=value` per line, no\n * newlines inside values.\n */\n\nimport type { Target } from \"./targets\";\n\n// =============================================================================\n// SecretSource — narrow interface @absolutejs/secrets' broker satisfies\n// =============================================================================\n\n/**\n * Minimal interface this module needs to read secret values. The\n * `SecretBroker` from `@absolutejs/secrets` satisfies it structurally;\n * any other implementation (a Map, an AWS Secrets Manager client\n * wrapper, etc.) works as long as it exposes `resolve(name)`.\n */\nexport type SecretSource = {\n resolve: (\n name: string,\n ) => Promise<{ value: string; fingerprint?: string } | null>;\n};\n\n// =============================================================================\n// Env-file (de)serialization\n// =============================================================================\n\nconst KEY_PATTERN = /^[A-Z_][A-Z0-9_]*$/;\nconst NEEDS_QUOTING = /[\\s\"'`$\\\\#&|;<>(){}*?!]/;\n\nconst validateKey = (key: string): void => {\n if (!KEY_PATTERN.test(key)) {\n throw new Error(\n `[deploy/env] invalid env key \"${key}\" — must match /^[A-Z_][A-Z0-9_]*$/`,\n );\n }\n};\n\nconst serializeLine = (key: string, value: string): string => {\n validateKey(key);\n if (value.includes(\"\\n\") || value.includes(\"\\r\")) {\n throw new Error(\n `[deploy/env] value for \"${key}\" contains a newline — env files cannot represent multi-line values. Use a separate file path for multi-line secrets.`,\n );\n }\n if (NEEDS_QUOTING.test(value) || value.startsWith(\"=\") || value === \"\") {\n const escaped = value.replaceAll(\"\\\\\", \"\\\\\\\\\").replaceAll('\"', '\\\\\"');\n return `${key}=\"${escaped}\"`;\n }\n return `${key}=${value}`;\n};\n\nexport const serializeEnvFile = (values: Record<string, string>): string => {\n const lines: string[] = [];\n for (const key of Object.keys(values).sort()) {\n const value = values[key];\n if (value === undefined) continue;\n lines.push(serializeLine(key, value));\n }\n return `${lines.join(\"\\n\")}\\n`;\n};\n\nconst unquoteValue = (raw: string): string => {\n if (raw.length >= 2 && raw.startsWith('\"') && raw.endsWith('\"')) {\n const inner = raw.slice(1, -1);\n return inner.replaceAll('\\\\\"', '\"').replaceAll(\"\\\\\\\\\", \"\\\\\");\n }\n if (raw.length >= 2 && raw.startsWith(\"'\") && raw.endsWith(\"'\")) {\n return raw.slice(1, -1);\n }\n return raw;\n};\n\n/**\n * Parse a remote env file's contents. Ignores blank lines and `#`\n * comments. Lines that don't match `KEY=VALUE` throw — the deploy\n * primitive owns the file, so unknown content is loud, not silent.\n */\nexport const parseEnvFile = (text: string): Record<string, string> => {\n const result: Record<string, string> = {};\n for (const rawLine of text.split(\"\\n\")) {\n const line = rawLine.trim();\n if (line.length === 0 || line.startsWith(\"#\")) continue;\n const eq = line.indexOf(\"=\");\n if (eq <= 0) {\n throw new Error(`[deploy/env] malformed env line: \"${rawLine}\"`);\n }\n const key = line.slice(0, eq).trim();\n const value = unquoteValue(line.slice(eq + 1).trim());\n validateKey(key);\n result[key] = value;\n }\n return result;\n};\n\n// =============================================================================\n// Deployment spec + sync primitive\n// =============================================================================\n\nexport type EnvDeployment = {\n /** The cloud Target whose remote filesystem we write to. */\n target: Target;\n /** Remote path for the env file (e.g. `/etc/myapp.env`). */\n remotePath: string;\n /**\n * Names of secrets this deployment consumes from the SecretSource.\n * Each name is resolved before the file is written.\n */\n secretNames?: ReadonlyArray<string>;\n /**\n * Non-secret env vars to merge in (NODE_ENV, PORT, LOG_LEVEL).\n * If an `extras` key collides with a `secretNames` entry, throws —\n * silent overrides are worse than a loud rejection.\n */\n extras?: Record<string, string>;\n /** chmod for the file. Default `'600'`. */\n mode?: string;\n /** chown for the file. Default unchanged. */\n owner?: string;\n /**\n * Command to run after the file changes. Skipped when the diff is\n * empty — bouncing a service for an identical file is the wrong\n * default.\n */\n reload?: string;\n};\n\nexport type EnvSyncResult = {\n /** Keys present in the new spec but not the existing file. */\n added: string[];\n /** Keys whose values changed (fingerprints, NOT plaintext, for safety). */\n changed: string[];\n /** Keys removed from the spec (still in the existing file). */\n removed: string[];\n /** Keys whose values matched — file wasn't rewritten for these. */\n unchanged: string[];\n /** True iff `deployment.reload` was invoked. */\n reloaded: boolean;\n /** True iff the file content changed (atomic-write happened). */\n wrote: boolean;\n /** Remote path that was synced. */\n remotePath: string;\n};\n\nconst shellQuote = (value: string): string =>\n `'${value.replaceAll(\"'\", \"'\\\\''\")}'`;\n\nconst tryReadRemoteFile = async (\n target: Target,\n remotePath: string,\n): Promise<string | undefined> => {\n const result = await target.exec(\n `if [ -f ${shellQuote(remotePath)} ]; then cat ${shellQuote(remotePath)}; else echo __ABS_DEPLOY_ENV_ABSENT__; fi`,\n );\n if (result.exitCode !== 0) {\n throw new Error(\n `[deploy/env] failed to read ${remotePath}: exit ${result.exitCode}: ${result.stderr || result.stdout}`,\n );\n }\n if (result.stdout.trim() === \"__ABS_DEPLOY_ENV_ABSENT__\") return undefined;\n return result.stdout;\n};\n\nconst mergeValues = (\n deployment: EnvDeployment,\n resolved: Record<string, string>,\n): Record<string, string> => {\n const out: Record<string, string> = {};\n for (const [key, value] of Object.entries(deployment.extras ?? {})) {\n validateKey(key);\n out[key] = value;\n }\n for (const [key, value] of Object.entries(resolved)) {\n if (out[key] !== undefined) {\n throw new Error(\n `[deploy/env] \"${key}\" defined in BOTH extras and secretNames for ${deployment.remotePath} — remove one`,\n );\n }\n out[key] = value;\n }\n return out;\n};\n\nconst diffEnv = (\n previous: Record<string, string>,\n next: Record<string, string>,\n): Pick<EnvSyncResult, \"added\" | \"changed\" | \"removed\" | \"unchanged\"> => {\n const added: string[] = [];\n const changed: string[] = [];\n const removed: string[] = [];\n const unchanged: string[] = [];\n const allKeys = new Set([...Object.keys(previous), ...Object.keys(next)]);\n for (const key of [...allKeys].sort()) {\n const prev = previous[key];\n const curr = next[key];\n if (curr === undefined) removed.push(key);\n else if (prev === undefined) added.push(key);\n else if (prev === curr) unchanged.push(key);\n else changed.push(key);\n }\n return { added, changed, removed, unchanged };\n};\n\nconst writeRemoteFileAtomically = async (\n target: Target,\n remotePath: string,\n contents: string,\n mode: string,\n owner?: string,\n): Promise<void> => {\n const tempPath = `${remotePath}.new.${Math.floor(Date.now() / 1000)}`;\n const dir = remotePath.split(\"/\").slice(0, -1).join(\"/\") || \"/\";\n const mkdir = await target.exec(`mkdir -p ${shellQuote(dir)}`);\n if (mkdir.exitCode !== 0) {\n throw new Error(\n `[deploy/env] mkdir ${dir} failed: ${mkdir.stderr || mkdir.stdout}`,\n );\n }\n // Write via stdin redirect — no shell escaping of the env file content.\n const writeResult = await target.exec(`cat > ${shellQuote(tempPath)}`, {\n stdin: contents,\n });\n if (writeResult.exitCode !== 0) {\n throw new Error(\n `[deploy/env] write to ${tempPath} failed: ${writeResult.stderr || writeResult.stdout}`,\n );\n }\n const chmod = await target.exec(\n `chmod ${shellQuote(mode)} ${shellQuote(tempPath)}`,\n );\n if (chmod.exitCode !== 0) {\n throw new Error(\n `[deploy/env] chmod failed: ${chmod.stderr || chmod.stdout}`,\n );\n }\n if (owner !== undefined) {\n const chown = await target.exec(\n `chown ${shellQuote(owner)} ${shellQuote(tempPath)}`,\n );\n if (chown.exitCode !== 0) {\n throw new Error(\n `[deploy/env] chown failed: ${chown.stderr || chown.stdout}`,\n );\n }\n }\n const mv = await target.exec(\n `mv ${shellQuote(tempPath)} ${shellQuote(remotePath)}`,\n );\n if (mv.exitCode !== 0) {\n throw new Error(`[deploy/env] mv failed: ${mv.stderr || mv.stdout}`);\n }\n};\n\n/**\n * Push an env file to a single target. Atomic: write to a `.new`\n * tempfile, chmod / chown, then `mv` into place. Diffs the merged\n * values against the existing file content and skips the\n * write+reload when nothing changed.\n */\nexport const syncEnvToTarget = async (\n deployment: EnvDeployment,\n values: Record<string, string>,\n): Promise<EnvSyncResult> => {\n const mode = deployment.mode ?? \"600\";\n const merged = mergeValues(deployment, values);\n const previousText = await tryReadRemoteFile(\n deployment.target,\n deployment.remotePath,\n );\n const previous = previousText === undefined ? {} : parseEnvFile(previousText);\n const diff = diffEnv(previous, merged);\n const changed = diff.added.length + diff.changed.length + diff.removed.length;\n\n if (changed === 0) {\n return {\n ...diff,\n reloaded: false,\n remotePath: deployment.remotePath,\n wrote: false,\n };\n }\n\n const nextText = serializeEnvFile(merged);\n await writeRemoteFileAtomically(\n deployment.target,\n deployment.remotePath,\n nextText,\n mode,\n deployment.owner,\n );\n\n let reloaded = false;\n if (deployment.reload !== undefined) {\n const reload = await deployment.target.exec(deployment.reload);\n if (reload.exitCode !== 0) {\n throw new Error(\n `[deploy/env] reload command failed: ${reload.stderr || reload.stdout}`,\n );\n }\n reloaded = true;\n }\n\n return {\n ...diff,\n reloaded,\n remotePath: deployment.remotePath,\n wrote: true,\n };\n};\n\n// =============================================================================\n// Multi-target fan-out via a SecretSource\n// =============================================================================\n\nconst resolveSecrets = async (\n source: SecretSource,\n names: ReadonlyArray<string>,\n): Promise<Record<string, string>> => {\n const out: Record<string, string> = {};\n for (const name of names) {\n const resolved = await source.resolve(name);\n if (resolved === null) {\n throw new Error(`[deploy/env] secret \"${name}\" not found in source`);\n }\n out[name] = resolved.value;\n }\n return out;\n};\n\nexport type DeploymentSyncResult = {\n deployment: EnvDeployment;\n result?: EnvSyncResult;\n error?: Error;\n};\n\n/**\n * Fan-out push: for each deployment, resolve its `secretNames` via\n * the source, merge with `extras`, and push to the target. Resolves\n * are fresh every call — so calling this again after\n * `broker.rotate(name)` propagates the new value to every\n * deployment that uses it.\n *\n * Best-effort across the fan-out: a per-target failure is captured\n * in the result, but doesn't stop the rest. Operator inspects the\n * returned array, fixes the broken target, re-runs.\n */\nexport const syncSecretsToDeployments = async (\n source: SecretSource,\n deployments: ReadonlyArray<EnvDeployment>,\n): Promise<DeploymentSyncResult[]> => {\n const results: DeploymentSyncResult[] = [];\n for (const deployment of deployments) {\n try {\n const resolved = await resolveSecrets(\n source,\n deployment.secretNames ?? [],\n );\n const result = await syncEnvToTarget(deployment, resolved);\n results.push({ deployment, result });\n } catch (error) {\n results.push({\n deployment,\n error: error instanceof Error ? error : new Error(String(error)),\n });\n }\n }\n return results;\n};\n\n/**\n * Returns just the deployments that use a given secret name. Useful\n * for \"rotate one secret and propagate ONLY to the consumers\" —\n * `syncSecretsToDeployments(source, deploymentsUsing(name, deployments))`.\n */\nexport const deploymentsUsing = (\n name: string,\n deployments: ReadonlyArray<EnvDeployment>,\n): EnvDeployment[] =>\n deployments.filter((d) => (d.secretNames ?? []).includes(name));\n"
6
6
  ],
7
- "mappings": "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",
7
+ "mappings": "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",
8
8
  "debugId": "17F0F914F6CFDD7764756E2164756E21",
9
9
  "names": []
10
10
  }
package/dist/gcp.js CHANGED
@@ -10456,7 +10456,11 @@ var createGcpInfrastructureProvider = (options) => {
10456
10456
  await request({
10457
10457
  data: {
10458
10458
  name: input.name,
10459
- ...input.userData ? { metadata: { items: [{ key: "user-data", value: input.userData }] } } : {}
10459
+ ...input.userData ? {
10460
+ metadata: {
10461
+ items: [{ key: "user-data", value: input.userData }]
10462
+ }
10463
+ } : {}
10460
10464
  },
10461
10465
  method: "POST",
10462
10466
  url: `${COMPUTE_BASE_URL}/projects/${encodeURIComponent(options.projectId)}/zones/${encodeURIComponent(zone)}/instances?requestId=${encodeURIComponent(input.idempotencyKey)}&sourceInstanceTemplate=${encodeURIComponent(normalizeTemplate(options.projectId, options.instanceTemplate))}`
@@ -10486,5 +10490,5 @@ export {
10486
10490
  GcpInfrastructureError
10487
10491
  };
10488
10492
 
10489
- //# debugId=C869CEEDA347495664756E2164756E21
10493
+ //# debugId=9081D37EB69FDF4B64756E2164756E21
10490
10494
  //# sourceMappingURL=gcp.js.map