@ebay-dt/bonfire-node 0.1.0 → 0.2.0-alpha.10

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 (45) hide show
  1. package/README.md +10 -4
  2. package/dist/agent-service.d.ts +4 -1
  3. package/dist/agent-service.d.ts.map +1 -1
  4. package/dist/agent-service.js +18 -2
  5. package/dist/agent-service.js.map +1 -1
  6. package/dist/claude.d.ts +12 -11
  7. package/dist/claude.d.ts.map +1 -1
  8. package/dist/claude.js +166 -11
  9. package/dist/claude.js.map +1 -1
  10. package/dist/deployment-provider.d.ts +1 -0
  11. package/dist/deployment-provider.d.ts.map +1 -1
  12. package/dist/deployment-provider.js +1 -0
  13. package/dist/deployment-provider.js.map +1 -1
  14. package/dist/git-repository.d.ts +4 -2
  15. package/dist/git-repository.d.ts.map +1 -1
  16. package/dist/git-repository.js +35 -2
  17. package/dist/git-repository.js.map +1 -1
  18. package/dist/github-cli.d.ts.map +1 -1
  19. package/dist/github-cli.js +3 -1
  20. package/dist/github-cli.js.map +1 -1
  21. package/dist/index.d.ts +2 -0
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +2 -0
  24. package/dist/index.js.map +1 -1
  25. package/dist/lifecycle/deployment-worktree.d.ts +35 -0
  26. package/dist/lifecycle/deployment-worktree.d.ts.map +1 -0
  27. package/dist/lifecycle/deployment-worktree.js +302 -0
  28. package/dist/lifecycle/deployment-worktree.js.map +1 -0
  29. package/dist/lifecycle/local-prototype-lifecycle.d.ts +11 -1
  30. package/dist/lifecycle/local-prototype-lifecycle.d.ts.map +1 -1
  31. package/dist/lifecycle/local-prototype-lifecycle.js +272 -76
  32. package/dist/lifecycle/local-prototype-lifecycle.js.map +1 -1
  33. package/dist/lifecycle/repository-summary.d.ts +19 -0
  34. package/dist/lifecycle/repository-summary.d.ts.map +1 -0
  35. package/dist/lifecycle/repository-summary.js +191 -0
  36. package/dist/lifecycle/repository-summary.js.map +1 -0
  37. package/dist/lifecycle/repository-synchronization.d.ts +36 -0
  38. package/dist/lifecycle/repository-synchronization.d.ts.map +1 -0
  39. package/dist/lifecycle/repository-synchronization.js +314 -0
  40. package/dist/lifecycle/repository-synchronization.js.map +1 -0
  41. package/dist/lifecycle/runtime-context.d.ts +5 -1
  42. package/dist/lifecycle/runtime-context.d.ts.map +1 -1
  43. package/dist/lifecycle/runtime-context.js +7 -0
  44. package/dist/lifecycle/runtime-context.js.map +1 -1
  45. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"github-cli.js","sourceRoot":"","sources":["../src/github-cli.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n parseBonfireIdentity,\n type BonfireIdentity,\n} from \"@ebay-dt/bonfire-core\";\nimport {\n defaultGitHubCliProcessRunner,\n type GitHubCliChildProcess,\n type GitHubCliProcessRunner,\n} from \"./process-runner.js\";\n\nconst GH_EXECUTABLE = \"gh\";\nconst IDENTITY_TIMEOUT = 10_000;\nconst IDENTITY_MAX_BUFFER = 1024 * 1024;\nconst DEVICE_LOGIN_TIMEOUT = 10_000;\nconst ACTIVE_LOGIN_WINDOW = 10 * 60 * 1000;\nconst GH_LOGIN_CODE_LABEL_PATTERN = /\\bone[-\\s]time\\s+code\\b/i;\nconst GH_DEVICE_CODE_PATTERN = /\\b[A-Za-z0-9]+(?:-[A-Za-z0-9]+)+\\b/;\nconst ANSI_ESCAPE_PATTERN = new RegExp(\n `${String.fromCharCode(27)}\\\\[[0-?]*[ -/]*[@-~]`,\n \"g\",\n);\n\ntype GitHubCliFailure = {\n code?: unknown;\n killed?: unknown;\n signal?: unknown;\n stderr?: unknown;\n stdout?: unknown;\n};\n\nexport type GitHubCliErrorReason = \"missing\" | \"unauthenticated\" | \"unknown\";\n\nexport class GitHubCliError extends Error {\n readonly reason: GitHubCliErrorReason;\n\n constructor(message: string, reason: GitHubCliErrorReason = \"unknown\") {\n super(message);\n this.name = \"GitHubCliError\";\n this.reason = reason;\n }\n}\n\nexport type GitHubCliWebLogin = {\n code: string | null;\n deviceUrl: string;\n};\n\nexport type GitHubCliClientOptions = {\n hostname: string;\n process?: GitHubCliProcessRunner;\n};\n\nexport interface GitHubCliClientLike {\n getUser(): Promise<BonfireIdentity>;\n startWebLogin(): Promise<GitHubCliWebLogin>;\n}\n\ntype ActiveLogin = {\n child: GitHubCliChildProcess;\n code: string | null;\n settled: boolean;\n startedAt: number;\n promise: Promise<GitHubCliWebLogin>;\n};\n\nfunction asFailure(error: unknown): GitHubCliFailure {\n return error && typeof error === \"object\" ? (error as GitHubCliFailure) : {};\n}\n\nfunction outputFrom(value: unknown): string {\n return typeof value === \"string\" ? value : \"\";\n}\n\nfunction extractDeviceCode(output: string): string | null {\n const cleanOutput = output.replace(ANSI_ESCAPE_PATTERN, \"\").replace(/\\r/g, \"\");\n const lines = cleanOutput.split(\"\\n\");\n\n for (let index = 0; index < lines.length; index += 1) {\n const line = lines[index];\n if (line === undefined) continue;\n const label = line.match(GH_LOGIN_CODE_LABEL_PATTERN);\n if (!label || label.index === undefined) continue;\n\n const remainder = line.slice(label.index + label[0].length);\n const match =\n remainder.match(GH_DEVICE_CODE_PATTERN) ??\n lines[index + 1]?.match(GH_DEVICE_CODE_PATTERN);\n if (match) return match[0];\n }\n\n return null;\n}\n\nfunction projectIdentityFields(value: unknown) {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n return value;\n }\n\n const record = value as Record<string, unknown>;\n return {\n login: record.login,\n ...(Object.hasOwn(record, \"name\") ? { name: record.name } : {}),\n avatar_url: record.avatar_url,\n };\n}\n\nfunction isUnauthenticatedOutput(output: string): boolean {\n const normalized = output.toLowerCase();\n return (\n normalized.includes(\"gh auth login\") ||\n normalized.includes(\"not logged into\") ||\n normalized.includes(\"authentication required\") ||\n normalized.includes(\"must authenticate\") ||\n /\\b(?:http(?:\\s+status)?\\s*)?401\\b/.test(normalized)\n );\n}\n\nfunction describeFailure(failure: GitHubCliFailure): string {\n if (failure.code === \"ENOENT\") {\n return \"GitHub CLI (`gh`) is not installed or is not available on PATH.\";\n }\n\n if (\n failure.code === \"ETIMEDOUT\" ||\n failure.killed === true ||\n failure.signal === \"SIGTERM\"\n ) {\n return \"GitHub CLI command timed out.\";\n }\n\n if (failure.code === \"ERR_CHILD_PROCESS_STDIO_MAXBUFFER\") {\n return \"GitHub CLI output exceeded the allowed limit.\";\n }\n\n return \"GitHub CLI command failed.\";\n}\n\nfunction commandFailure(error: unknown, hostname: string): GitHubCliError {\n const failure = asFailure(error);\n const output = `${outputFrom(failure.stdout)}\\n${outputFrom(failure.stderr)}`;\n const guidance = ` Run: gh auth login --hostname ${hostname}`;\n\n if (failure.code === \"ENOENT\") {\n return new GitHubCliError(\n `GitHub CLI (\\`gh\\`) is not installed or is not available on PATH.${guidance}`,\n \"missing\",\n );\n }\n\n if (isUnauthenticatedOutput(output)) {\n return new GitHubCliError(\n `GitHub CLI is not authenticated for ${hostname}.${guidance}`,\n \"unauthenticated\",\n );\n }\n\n return new GitHubCliError(`${describeFailure(failure)}${guidance}`);\n}\n\nexport class GitHubCliClient implements GitHubCliClientLike {\n readonly hostname: string;\n readonly #process: GitHubCliProcessRunner;\n #activeLogin: ActiveLogin | null = null;\n\n constructor(options: GitHubCliClientOptions) {\n this.hostname = options.hostname;\n this.#process = options.process ?? defaultGitHubCliProcessRunner;\n }\n\n async getUser(): Promise<BonfireIdentity> {\n const args = [\"api\", \"--hostname\", this.hostname, \"user\"];\n let result;\n\n try {\n result = await this.#process.execFile(GH_EXECUTABLE, args, {\n timeout: IDENTITY_TIMEOUT,\n maxBuffer: IDENTITY_MAX_BUFFER,\n });\n } catch (error) {\n throw commandFailure(error, this.hostname);\n }\n\n if (isUnauthenticatedOutput(`${result.stdout}\\n${result.stderr}`)) {\n throw commandFailure(result, this.hostname);\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(result.stdout);\n } catch {\n throw new GitHubCliError(\n `GitHub CLI returned malformed user data. Run: gh auth login --hostname ${this.hostname}`,\n );\n }\n\n try {\n return parseBonfireIdentity(projectIdentityFields(parsed));\n } catch {\n throw new GitHubCliError(\"GitHub CLI returned an invalid user profile.\");\n }\n }\n\n async startWebLogin() {\n const activeLogin = this.#activeLogin;\n if (activeLogin) {\n const withinReuseWindow =\n Date.now() - activeLogin.startedAt < ACTIVE_LOGIN_WINDOW;\n if (\n !activeLogin.settled ||\n (activeLogin.code !== null && withinReuseWindow)\n ) {\n return activeLogin.promise;\n }\n\n try {\n activeLogin.child.kill(\"SIGTERM\");\n } catch {\n throw new GitHubCliError(\n \"The previous GitHub CLI login could not be terminated.\",\n );\n }\n this.#activeLogin = null;\n }\n\n const spawn = this.#process.spawn;\n if (!spawn) {\n throw new GitHubCliError(\"GitHub CLI device login is unavailable.\");\n }\n\n let child: GitHubCliChildProcess;\n try {\n child = spawn(\n GH_EXECUTABLE,\n [\n \"auth\",\n \"login\",\n \"--hostname\",\n this.hostname,\n \"--web\",\n \"--clipboard\",\n \"--git-protocol\",\n \"ssh\",\n \"--skip-ssh-key\",\n ],\n { stdio: [\"ignore\", \"pipe\", \"pipe\"] },\n );\n } catch (error) {\n throw commandFailure(error, this.hostname);\n }\n\n let resolveLogin!: (login: GitHubCliWebLogin) => void;\n let rejectLogin!: (error: GitHubCliError) => void;\n const promise = new Promise<GitHubCliWebLogin>((resolve, reject) => {\n resolveLogin = resolve;\n rejectLogin = reject;\n });\n const login: ActiveLogin = {\n child,\n code: null,\n settled: false,\n startedAt: Date.now(),\n promise,\n };\n this.#activeLogin = login;\n\n let output = \"\";\n let settled = false;\n let timer: ReturnType<typeof setTimeout>;\n\n const clearActiveLogin = () => {\n if (this.#activeLogin?.child === child) this.#activeLogin = null;\n };\n\n const settle = (code: string | null) => {\n if (settled) return;\n settled = true;\n login.code = code;\n login.settled = true;\n clearTimeout(timer);\n resolveLogin({\n code,\n deviceUrl: `https://${this.hostname}/login/device`,\n });\n };\n\n const fail = (error: unknown) => {\n clearActiveLogin();\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n rejectLogin(commandFailure(error, this.hostname));\n };\n\n const inspectOutput = (chunk: string | Uint8Array) => {\n output +=\n typeof chunk === \"string\" ? chunk : Buffer.from(chunk).toString(\"utf8\");\n const code = extractDeviceCode(output);\n if (code) settle(code);\n };\n\n timer = setTimeout(() => settle(null), DEVICE_LOGIN_TIMEOUT);\n timer.unref?.();\n child.stdout?.on(\"data\", inspectOutput);\n child.stderr?.on(\"data\", inspectOutput);\n child.once(\"error\", fail);\n child.once(\"close\", () => {\n if (!settled) settle(null);\n clearActiveLogin();\n });\n\n return promise;\n }\n}\n"]}
1
+ {"version":3,"file":"github-cli.js","sourceRoot":"","sources":["../src/github-cli.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n parseBonfireIdentity,\n type BonfireIdentity,\n} from \"@ebay-dt/bonfire-core\";\nimport {\n defaultGitHubCliProcessRunner,\n type GitHubCliChildProcess,\n type GitHubCliProcessRunner,\n} from \"./process-runner.js\";\n\nconst GH_EXECUTABLE = \"gh\";\nconst IDENTITY_TIMEOUT = 10_000;\nconst IDENTITY_MAX_BUFFER = 1024 * 1024;\nconst DEVICE_LOGIN_TIMEOUT = 10_000;\nconst ACTIVE_LOGIN_WINDOW = 10 * 60 * 1000;\nconst GH_LOGIN_CODE_LABEL_PATTERN = /\\bone[-\\s]time\\s+code\\b/i;\nconst GH_DEVICE_CODE_PATTERN = /\\b[A-Za-z0-9]+(?:-[A-Za-z0-9]+)+\\b/;\nconst ANSI_ESCAPE_PATTERN = new RegExp(\n `${String.fromCharCode(27)}\\\\[[0-?]*[ -/]*[@-~]`,\n \"g\",\n);\n\ntype GitHubCliFailure = {\n code?: unknown;\n killed?: unknown;\n signal?: unknown;\n stderr?: unknown;\n stdout?: unknown;\n};\n\nexport type GitHubCliErrorReason = \"missing\" | \"unauthenticated\" | \"unknown\";\n\nexport class GitHubCliError extends Error {\n readonly reason: GitHubCliErrorReason;\n\n constructor(message: string, reason: GitHubCliErrorReason = \"unknown\") {\n super(message);\n this.name = \"GitHubCliError\";\n this.reason = reason;\n }\n}\n\nexport type GitHubCliWebLogin = {\n code: string | null;\n deviceUrl: string;\n};\n\nexport type GitHubCliClientOptions = {\n hostname: string;\n process?: GitHubCliProcessRunner;\n};\n\nexport interface GitHubCliClientLike {\n getUser(): Promise<BonfireIdentity>;\n startWebLogin(): Promise<GitHubCliWebLogin>;\n}\n\ntype ActiveLogin = {\n child: GitHubCliChildProcess;\n code: string | null;\n settled: boolean;\n startedAt: number;\n promise: Promise<GitHubCliWebLogin>;\n};\n\nfunction asFailure(error: unknown): GitHubCliFailure {\n return error && typeof error === \"object\" ? (error as GitHubCliFailure) : {};\n}\n\nfunction outputFrom(value: unknown): string {\n return typeof value === \"string\" ? value : \"\";\n}\n\nfunction extractDeviceCode(output: string): string | null {\n const cleanOutput = output\n .replace(ANSI_ESCAPE_PATTERN, \"\")\n .replace(/\\r/g, \"\");\n const lines = cleanOutput.split(\"\\n\");\n\n for (let index = 0; index < lines.length; index += 1) {\n const line = lines[index];\n if (line === undefined) continue;\n const label = line.match(GH_LOGIN_CODE_LABEL_PATTERN);\n if (!label || label.index === undefined) continue;\n\n const remainder = line.slice(label.index + label[0].length);\n const match =\n remainder.match(GH_DEVICE_CODE_PATTERN) ??\n lines[index + 1]?.match(GH_DEVICE_CODE_PATTERN);\n if (match) return match[0];\n }\n\n return null;\n}\n\nfunction projectIdentityFields(value: unknown) {\n if (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n return value;\n }\n\n const record = value as Record<string, unknown>;\n return {\n login: record.login,\n ...(Object.hasOwn(record, \"name\") ? { name: record.name } : {}),\n avatar_url: record.avatar_url,\n };\n}\n\nfunction isUnauthenticatedOutput(output: string): boolean {\n const normalized = output.toLowerCase();\n return (\n normalized.includes(\"gh auth login\") ||\n normalized.includes(\"not logged into\") ||\n normalized.includes(\"authentication required\") ||\n normalized.includes(\"must authenticate\") ||\n /\\b(?:http(?:\\s+status)?\\s*)?401\\b/.test(normalized)\n );\n}\n\nfunction describeFailure(failure: GitHubCliFailure): string {\n if (failure.code === \"ENOENT\") {\n return \"GitHub CLI (`gh`) is not installed or is not available on PATH.\";\n }\n\n if (\n failure.code === \"ETIMEDOUT\" ||\n failure.killed === true ||\n failure.signal === \"SIGTERM\"\n ) {\n return \"GitHub CLI command timed out.\";\n }\n\n if (failure.code === \"ERR_CHILD_PROCESS_STDIO_MAXBUFFER\") {\n return \"GitHub CLI output exceeded the allowed limit.\";\n }\n\n return \"GitHub CLI command failed.\";\n}\n\nfunction commandFailure(error: unknown, hostname: string): GitHubCliError {\n const failure = asFailure(error);\n const output = `${outputFrom(failure.stdout)}\\n${outputFrom(failure.stderr)}`;\n const guidance = ` Run: gh auth login --hostname ${hostname}`;\n\n if (failure.code === \"ENOENT\") {\n return new GitHubCliError(\n `GitHub CLI (\\`gh\\`) is not installed or is not available on PATH.${guidance}`,\n \"missing\",\n );\n }\n\n if (isUnauthenticatedOutput(output)) {\n return new GitHubCliError(\n `GitHub CLI is not authenticated for ${hostname}.${guidance}`,\n \"unauthenticated\",\n );\n }\n\n return new GitHubCliError(`${describeFailure(failure)}${guidance}`);\n}\n\nexport class GitHubCliClient implements GitHubCliClientLike {\n readonly hostname: string;\n readonly #process: GitHubCliProcessRunner;\n #activeLogin: ActiveLogin | null = null;\n\n constructor(options: GitHubCliClientOptions) {\n this.hostname = options.hostname;\n this.#process = options.process ?? defaultGitHubCliProcessRunner;\n }\n\n async getUser(): Promise<BonfireIdentity> {\n const args = [\"api\", \"--hostname\", this.hostname, \"user\"];\n let result;\n\n try {\n result = await this.#process.execFile(GH_EXECUTABLE, args, {\n timeout: IDENTITY_TIMEOUT,\n maxBuffer: IDENTITY_MAX_BUFFER,\n });\n } catch (error) {\n throw commandFailure(error, this.hostname);\n }\n\n if (isUnauthenticatedOutput(`${result.stdout}\\n${result.stderr}`)) {\n throw commandFailure(result, this.hostname);\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(result.stdout);\n } catch {\n throw new GitHubCliError(\n `GitHub CLI returned malformed user data. Run: gh auth login --hostname ${this.hostname}`,\n );\n }\n\n try {\n return parseBonfireIdentity(projectIdentityFields(parsed));\n } catch {\n throw new GitHubCliError(\"GitHub CLI returned an invalid user profile.\");\n }\n }\n\n async startWebLogin() {\n const activeLogin = this.#activeLogin;\n if (activeLogin) {\n const withinReuseWindow =\n Date.now() - activeLogin.startedAt < ACTIVE_LOGIN_WINDOW;\n if (\n !activeLogin.settled ||\n (activeLogin.code !== null && withinReuseWindow)\n ) {\n return activeLogin.promise;\n }\n\n try {\n activeLogin.child.kill(\"SIGTERM\");\n } catch {\n throw new GitHubCliError(\n \"The previous GitHub CLI login could not be terminated.\",\n );\n }\n this.#activeLogin = null;\n }\n\n const spawn = this.#process.spawn;\n if (!spawn) {\n throw new GitHubCliError(\"GitHub CLI device login is unavailable.\");\n }\n\n let child: GitHubCliChildProcess;\n try {\n child = spawn(\n GH_EXECUTABLE,\n [\n \"auth\",\n \"login\",\n \"--hostname\",\n this.hostname,\n \"--web\",\n \"--clipboard\",\n \"--git-protocol\",\n \"ssh\",\n \"--skip-ssh-key\",\n ],\n { stdio: [\"ignore\", \"pipe\", \"pipe\"] },\n );\n } catch (error) {\n throw commandFailure(error, this.hostname);\n }\n\n let resolveLogin!: (login: GitHubCliWebLogin) => void;\n let rejectLogin!: (error: GitHubCliError) => void;\n const promise = new Promise<GitHubCliWebLogin>((resolve, reject) => {\n resolveLogin = resolve;\n rejectLogin = reject;\n });\n const login: ActiveLogin = {\n child,\n code: null,\n settled: false,\n startedAt: Date.now(),\n promise,\n };\n this.#activeLogin = login;\n\n let output = \"\";\n let settled = false;\n let timer: ReturnType<typeof setTimeout>;\n\n const clearActiveLogin = () => {\n if (this.#activeLogin?.child === child) this.#activeLogin = null;\n };\n\n const settle = (code: string | null) => {\n if (settled) return;\n settled = true;\n login.code = code;\n login.settled = true;\n clearTimeout(timer);\n resolveLogin({\n code,\n deviceUrl: `https://${this.hostname}/login/device`,\n });\n };\n\n const fail = (error: unknown) => {\n clearActiveLogin();\n if (settled) return;\n settled = true;\n clearTimeout(timer);\n rejectLogin(commandFailure(error, this.hostname));\n };\n\n const inspectOutput = (chunk: string | Uint8Array) => {\n output +=\n typeof chunk === \"string\" ? chunk : Buffer.from(chunk).toString(\"utf8\");\n const code = extractDeviceCode(output);\n if (code) settle(code);\n };\n\n timer = setTimeout(() => settle(null), DEVICE_LOGIN_TIMEOUT);\n timer.unref?.();\n child.stdout?.on(\"data\", inspectOutput);\n child.stderr?.on(\"data\", inspectOutput);\n child.once(\"error\", fail);\n child.once(\"close\", () => {\n if (!settled) settle(null);\n clearActiveLogin();\n });\n\n return promise;\n }\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -10,4 +10,6 @@ export { PathExecutableResolver, type ExecutableAccess, type ExecutableResolverL
10
10
  export { PrototypePreferences, PrototypePreferencesError, type PrototypePreferencesErrorCode, type PrototypePreferencesLike, type PrototypePreferencesOptions, } from "./preferences.js";
11
11
  export type { PreparePrototypeFiles, PreparePrototypeFilesRequest, } from "./prepare-prototype-files.js";
12
12
  export { LocalPrototypeLifecycle, type LocalPrototypeLifecycleOptions, type PrototypeLifecycle, } from "./lifecycle/local-prototype-lifecycle.js";
13
+ export { assertIdentityBranchReady, hasUnresolvedRepositoryOperation, synchronizeIdentityBranch, type RepositorySynchronizationResult, type SynchronizationDeferredReason, type SynchronizationUnsafeReason, type SynchronizeIdentityBranchOptions, } from "./lifecycle/repository-synchronization.js";
14
+ export { getRepositorySummary, type RepositorySummaryOptions, } from "./lifecycle/repository-summary.js";
13
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,EACxB,uBAAuB,EACvB,KAAK,+BAA+B,EACpC,KAAK,kBAAkB,EACvB,KAAK,2BAA2B,EAChC,KAAK,wBAAwB,GAC9B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,eAAe,EACf,cAAc,EACd,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,GACvB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,EACnB,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,gBAAgB,EAChB,KAAK,oBAAoB,GAC1B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,4BAA4B,EAC5B,KAAK,uBAAuB,EAC5B,KAAK,6BAA6B,GACnC,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,qBAAqB,EACrB,KAAK,yBAAyB,EAC9B,KAAK,4BAA4B,GAClC,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,kBAAkB,EAClB,0BAA0B,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,yBAAyB,EAC9B,KAAK,yBAAyB,EAC9B,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,GAC7B,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,cAAc,EACd,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,WAAW,EAChB,KAAK,kBAAkB,GACxB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,sBAAsB,EACtB,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,yBAAyB,GAC/B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,oBAAoB,EACpB,yBAAyB,EACzB,KAAK,6BAA6B,EAClC,KAAK,wBAAwB,EAC7B,KAAK,2BAA2B,GACjC,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,qBAAqB,EACrB,4BAA4B,GAC7B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,uBAAuB,EACvB,KAAK,8BAA8B,EACnC,KAAK,kBAAkB,GACxB,MAAM,0CAA0C,CAAC","sourcesContent":["export {\n createDeploymentProvider,\n DeploymentProviderError,\n type CreateDeploymentProviderOptions,\n type DeploymentProvider,\n type DeploymentProviderErrorKind,\n type DeploymentProviderResult,\n} from \"./deployment-provider.js\";\nexport {\n GitHubCliClient,\n GitHubCliError,\n type GitHubCliClientLike,\n type GitHubCliClientOptions,\n type GitHubCliErrorReason,\n type GitHubCliWebLogin,\n} from \"./github-cli.js\";\nexport type {\n GitHubCliChildProcess,\n GitHubCliExecOptions,\n GitHubCliExecResult,\n GitHubCliOutputStream,\n GitHubCliProcessRunner,\n GitHubCliSpawnOptions,\n} from \"./process-runner.js\";\nexport {\n PrototypeCatalog,\n type PrototypeCatalogLike,\n} from \"./prototype-catalog.js\";\nexport {\n resolvePrototypeCatalogPaths,\n type PrototypeCatalogOptions,\n type ResolvedPrototypeCatalogPaths,\n} from \"./prototype-paths.js\";\nexport {\n PrototypeAgentService,\n type PrototypeAgentServiceLike,\n type PrototypeAgentServiceOptions,\n} from \"./agent-service.js\";\nexport {\n ClaudeCodeLauncher,\n buildClaudeCodeAppleScript,\n type ClaudeCodeExecOptions,\n type ClaudeCodeExecResult,\n type ClaudeCodeLauncherLike,\n type ClaudeCodeLauncherOptions,\n type ClaudeCodePreflightResult,\n type ClaudeCodePreflightValue,\n type ClaudeCodeProcessRunner,\n} from \"./claude.js\";\nexport {\n CursorLauncher,\n type CursorChildProcess,\n type CursorLauncherLike,\n type CursorLauncherOptions,\n type CursorSpawn,\n type CursorSpawnOptions,\n} from \"./cursor.js\";\nexport {\n PathExecutableResolver,\n type ExecutableAccess,\n type ExecutableResolverLike,\n type ExecutableResolverOptions,\n} from \"./executable.js\";\nexport {\n PrototypePreferences,\n PrototypePreferencesError,\n type PrototypePreferencesErrorCode,\n type PrototypePreferencesLike,\n type PrototypePreferencesOptions,\n} from \"./preferences.js\";\nexport type {\n PreparePrototypeFiles,\n PreparePrototypeFilesRequest,\n} from \"./prepare-prototype-files.js\";\nexport {\n LocalPrototypeLifecycle,\n type LocalPrototypeLifecycleOptions,\n type PrototypeLifecycle,\n} from \"./lifecycle/local-prototype-lifecycle.js\";\n"]}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,EACxB,uBAAuB,EACvB,KAAK,+BAA+B,EACpC,KAAK,kBAAkB,EACvB,KAAK,2BAA2B,EAChC,KAAK,wBAAwB,GAC9B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,eAAe,EACf,cAAc,EACd,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,GACvB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,EACnB,qBAAqB,EACrB,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EACL,gBAAgB,EAChB,KAAK,oBAAoB,GAC1B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,4BAA4B,EAC5B,KAAK,uBAAuB,EAC5B,KAAK,6BAA6B,GACnC,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,qBAAqB,EACrB,KAAK,yBAAyB,EAC9B,KAAK,4BAA4B,GAClC,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,kBAAkB,EAClB,0BAA0B,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,yBAAyB,EAC9B,KAAK,yBAAyB,EAC9B,KAAK,wBAAwB,EAC7B,KAAK,uBAAuB,GAC7B,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,cAAc,EACd,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,qBAAqB,EAC1B,KAAK,WAAW,EAChB,KAAK,kBAAkB,GACxB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,sBAAsB,EACtB,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,yBAAyB,GAC/B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,oBAAoB,EACpB,yBAAyB,EACzB,KAAK,6BAA6B,EAClC,KAAK,wBAAwB,EAC7B,KAAK,2BAA2B,GACjC,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,qBAAqB,EACrB,4BAA4B,GAC7B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,uBAAuB,EACvB,KAAK,8BAA8B,EACnC,KAAK,kBAAkB,GACxB,MAAM,0CAA0C,CAAC;AAClD,OAAO,EACL,yBAAyB,EACzB,gCAAgC,EAChC,yBAAyB,EACzB,KAAK,+BAA+B,EACpC,KAAK,6BAA6B,EAClC,KAAK,2BAA2B,EAChC,KAAK,gCAAgC,GACtC,MAAM,2CAA2C,CAAC;AACnD,OAAO,EACL,oBAAoB,EACpB,KAAK,wBAAwB,GAC9B,MAAM,mCAAmC,CAAC","sourcesContent":["export {\n createDeploymentProvider,\n DeploymentProviderError,\n type CreateDeploymentProviderOptions,\n type DeploymentProvider,\n type DeploymentProviderErrorKind,\n type DeploymentProviderResult,\n} from \"./deployment-provider.js\";\nexport {\n GitHubCliClient,\n GitHubCliError,\n type GitHubCliClientLike,\n type GitHubCliClientOptions,\n type GitHubCliErrorReason,\n type GitHubCliWebLogin,\n} from \"./github-cli.js\";\nexport type {\n GitHubCliChildProcess,\n GitHubCliExecOptions,\n GitHubCliExecResult,\n GitHubCliOutputStream,\n GitHubCliProcessRunner,\n GitHubCliSpawnOptions,\n} from \"./process-runner.js\";\nexport {\n PrototypeCatalog,\n type PrototypeCatalogLike,\n} from \"./prototype-catalog.js\";\nexport {\n resolvePrototypeCatalogPaths,\n type PrototypeCatalogOptions,\n type ResolvedPrototypeCatalogPaths,\n} from \"./prototype-paths.js\";\nexport {\n PrototypeAgentService,\n type PrototypeAgentServiceLike,\n type PrototypeAgentServiceOptions,\n} from \"./agent-service.js\";\nexport {\n ClaudeCodeLauncher,\n buildClaudeCodeAppleScript,\n type ClaudeCodeExecOptions,\n type ClaudeCodeExecResult,\n type ClaudeCodeLauncherLike,\n type ClaudeCodeLauncherOptions,\n type ClaudeCodePreflightResult,\n type ClaudeCodePreflightValue,\n type ClaudeCodeProcessRunner,\n} from \"./claude.js\";\nexport {\n CursorLauncher,\n type CursorChildProcess,\n type CursorLauncherLike,\n type CursorLauncherOptions,\n type CursorSpawn,\n type CursorSpawnOptions,\n} from \"./cursor.js\";\nexport {\n PathExecutableResolver,\n type ExecutableAccess,\n type ExecutableResolverLike,\n type ExecutableResolverOptions,\n} from \"./executable.js\";\nexport {\n PrototypePreferences,\n PrototypePreferencesError,\n type PrototypePreferencesErrorCode,\n type PrototypePreferencesLike,\n type PrototypePreferencesOptions,\n} from \"./preferences.js\";\nexport type {\n PreparePrototypeFiles,\n PreparePrototypeFilesRequest,\n} from \"./prepare-prototype-files.js\";\nexport {\n LocalPrototypeLifecycle,\n type LocalPrototypeLifecycleOptions,\n type PrototypeLifecycle,\n} from \"./lifecycle/local-prototype-lifecycle.js\";\nexport {\n assertIdentityBranchReady,\n hasUnresolvedRepositoryOperation,\n synchronizeIdentityBranch,\n type RepositorySynchronizationResult,\n type SynchronizationDeferredReason,\n type SynchronizationUnsafeReason,\n type SynchronizeIdentityBranchOptions,\n} from \"./lifecycle/repository-synchronization.js\";\nexport {\n getRepositorySummary,\n type RepositorySummaryOptions,\n} from \"./lifecycle/repository-summary.js\";\n"]}
package/dist/index.js CHANGED
@@ -8,4 +8,6 @@ export { CursorLauncher, } from "./cursor.js";
8
8
  export { PathExecutableResolver, } from "./executable.js";
9
9
  export { PrototypePreferences, PrototypePreferencesError, } from "./preferences.js";
10
10
  export { LocalPrototypeLifecycle, } from "./lifecycle/local-prototype-lifecycle.js";
11
+ export { assertIdentityBranchReady, hasUnresolvedRepositoryOperation, synchronizeIdentityBranch, } from "./lifecycle/repository-synchronization.js";
12
+ export { getRepositorySummary, } from "./lifecycle/repository-summary.js";
11
13
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,EACxB,uBAAuB,GAKxB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,eAAe,EACf,cAAc,GAKf,MAAM,iBAAiB,CAAC;AASzB,OAAO,EACL,gBAAgB,GAEjB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,4BAA4B,GAG7B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,qBAAqB,GAGtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,kBAAkB,EAClB,0BAA0B,GAQ3B,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,cAAc,GAMf,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,sBAAsB,GAIvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,oBAAoB,EACpB,yBAAyB,GAI1B,MAAM,kBAAkB,CAAC;AAK1B,OAAO,EACL,uBAAuB,GAGxB,MAAM,0CAA0C,CAAC","sourcesContent":["export {\n createDeploymentProvider,\n DeploymentProviderError,\n type CreateDeploymentProviderOptions,\n type DeploymentProvider,\n type DeploymentProviderErrorKind,\n type DeploymentProviderResult,\n} from \"./deployment-provider.js\";\nexport {\n GitHubCliClient,\n GitHubCliError,\n type GitHubCliClientLike,\n type GitHubCliClientOptions,\n type GitHubCliErrorReason,\n type GitHubCliWebLogin,\n} from \"./github-cli.js\";\nexport type {\n GitHubCliChildProcess,\n GitHubCliExecOptions,\n GitHubCliExecResult,\n GitHubCliOutputStream,\n GitHubCliProcessRunner,\n GitHubCliSpawnOptions,\n} from \"./process-runner.js\";\nexport {\n PrototypeCatalog,\n type PrototypeCatalogLike,\n} from \"./prototype-catalog.js\";\nexport {\n resolvePrototypeCatalogPaths,\n type PrototypeCatalogOptions,\n type ResolvedPrototypeCatalogPaths,\n} from \"./prototype-paths.js\";\nexport {\n PrototypeAgentService,\n type PrototypeAgentServiceLike,\n type PrototypeAgentServiceOptions,\n} from \"./agent-service.js\";\nexport {\n ClaudeCodeLauncher,\n buildClaudeCodeAppleScript,\n type ClaudeCodeExecOptions,\n type ClaudeCodeExecResult,\n type ClaudeCodeLauncherLike,\n type ClaudeCodeLauncherOptions,\n type ClaudeCodePreflightResult,\n type ClaudeCodePreflightValue,\n type ClaudeCodeProcessRunner,\n} from \"./claude.js\";\nexport {\n CursorLauncher,\n type CursorChildProcess,\n type CursorLauncherLike,\n type CursorLauncherOptions,\n type CursorSpawn,\n type CursorSpawnOptions,\n} from \"./cursor.js\";\nexport {\n PathExecutableResolver,\n type ExecutableAccess,\n type ExecutableResolverLike,\n type ExecutableResolverOptions,\n} from \"./executable.js\";\nexport {\n PrototypePreferences,\n PrototypePreferencesError,\n type PrototypePreferencesErrorCode,\n type PrototypePreferencesLike,\n type PrototypePreferencesOptions,\n} from \"./preferences.js\";\nexport type {\n PreparePrototypeFiles,\n PreparePrototypeFilesRequest,\n} from \"./prepare-prototype-files.js\";\nexport {\n LocalPrototypeLifecycle,\n type LocalPrototypeLifecycleOptions,\n type PrototypeLifecycle,\n} from \"./lifecycle/local-prototype-lifecycle.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,EACxB,uBAAuB,GAKxB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,eAAe,EACf,cAAc,GAKf,MAAM,iBAAiB,CAAC;AASzB,OAAO,EACL,gBAAgB,GAEjB,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,4BAA4B,GAG7B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,qBAAqB,GAGtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,kBAAkB,EAClB,0BAA0B,GAQ3B,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,cAAc,GAMf,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,sBAAsB,GAIvB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,oBAAoB,EACpB,yBAAyB,GAI1B,MAAM,kBAAkB,CAAC;AAK1B,OAAO,EACL,uBAAuB,GAGxB,MAAM,0CAA0C,CAAC;AAClD,OAAO,EACL,yBAAyB,EACzB,gCAAgC,EAChC,yBAAyB,GAK1B,MAAM,2CAA2C,CAAC;AACnD,OAAO,EACL,oBAAoB,GAErB,MAAM,mCAAmC,CAAC","sourcesContent":["export {\n createDeploymentProvider,\n DeploymentProviderError,\n type CreateDeploymentProviderOptions,\n type DeploymentProvider,\n type DeploymentProviderErrorKind,\n type DeploymentProviderResult,\n} from \"./deployment-provider.js\";\nexport {\n GitHubCliClient,\n GitHubCliError,\n type GitHubCliClientLike,\n type GitHubCliClientOptions,\n type GitHubCliErrorReason,\n type GitHubCliWebLogin,\n} from \"./github-cli.js\";\nexport type {\n GitHubCliChildProcess,\n GitHubCliExecOptions,\n GitHubCliExecResult,\n GitHubCliOutputStream,\n GitHubCliProcessRunner,\n GitHubCliSpawnOptions,\n} from \"./process-runner.js\";\nexport {\n PrototypeCatalog,\n type PrototypeCatalogLike,\n} from \"./prototype-catalog.js\";\nexport {\n resolvePrototypeCatalogPaths,\n type PrototypeCatalogOptions,\n type ResolvedPrototypeCatalogPaths,\n} from \"./prototype-paths.js\";\nexport {\n PrototypeAgentService,\n type PrototypeAgentServiceLike,\n type PrototypeAgentServiceOptions,\n} from \"./agent-service.js\";\nexport {\n ClaudeCodeLauncher,\n buildClaudeCodeAppleScript,\n type ClaudeCodeExecOptions,\n type ClaudeCodeExecResult,\n type ClaudeCodeLauncherLike,\n type ClaudeCodeLauncherOptions,\n type ClaudeCodePreflightResult,\n type ClaudeCodePreflightValue,\n type ClaudeCodeProcessRunner,\n} from \"./claude.js\";\nexport {\n CursorLauncher,\n type CursorChildProcess,\n type CursorLauncherLike,\n type CursorLauncherOptions,\n type CursorSpawn,\n type CursorSpawnOptions,\n} from \"./cursor.js\";\nexport {\n PathExecutableResolver,\n type ExecutableAccess,\n type ExecutableResolverLike,\n type ExecutableResolverOptions,\n} from \"./executable.js\";\nexport {\n PrototypePreferences,\n PrototypePreferencesError,\n type PrototypePreferencesErrorCode,\n type PrototypePreferencesLike,\n type PrototypePreferencesOptions,\n} from \"./preferences.js\";\nexport type {\n PreparePrototypeFiles,\n PreparePrototypeFilesRequest,\n} from \"./prepare-prototype-files.js\";\nexport {\n LocalPrototypeLifecycle,\n type LocalPrototypeLifecycleOptions,\n type PrototypeLifecycle,\n} from \"./lifecycle/local-prototype-lifecycle.js\";\nexport {\n assertIdentityBranchReady,\n hasUnresolvedRepositoryOperation,\n synchronizeIdentityBranch,\n type RepositorySynchronizationResult,\n type SynchronizationDeferredReason,\n type SynchronizationUnsafeReason,\n type SynchronizeIdentityBranchOptions,\n} from \"./lifecycle/repository-synchronization.js\";\nexport {\n getRepositorySummary,\n type RepositorySummaryOptions,\n} from \"./lifecycle/repository-summary.js\";\n"]}
@@ -0,0 +1,35 @@
1
+ import { type GitCommitIdentity, type GitRepository } from "../git-repository.js";
2
+ export type DeploymentWorktreeOptions = {
3
+ remote: string;
4
+ primaryBranch: string;
5
+ sourceSha: string;
6
+ timeoutMs?: number;
7
+ commitIdentity?: GitCommitIdentity;
8
+ };
9
+ export type DeploymentWorktreeErrorKind = "invalid-source" | "preflight-conflict" | "preflight-failure" | "publication-conflict" | "publication-rejected" | "publication-failure" | "cleanup-failure";
10
+ type DeploymentPhase = "preflight" | "publication";
11
+ type DeploymentStep = "fetch" | "directory" | "marker" | "worktree-add" | "source-verify" | "merge" | "metadata-read" | "metadata-write" | "metadata-commit" | "push" | "cleanup";
12
+ /** Log only finite classifications; Git/OS messages may contain paths or credentials. */
13
+ export declare function reportDeploymentIssue(phase: DeploymentPhase, step: DeploymentStep, error: unknown): void;
14
+ export declare class DeploymentWorktreeError extends Error {
15
+ readonly kind: DeploymentWorktreeErrorKind;
16
+ readonly cleanupFailed: boolean;
17
+ readonly operationError: unknown;
18
+ readonly cause: unknown;
19
+ constructor(kind: DeploymentWorktreeErrorKind, options?: {
20
+ cleanupFailed?: boolean;
21
+ operationError?: unknown;
22
+ });
23
+ }
24
+ export declare function preflightDeploymentSource(repository: GitRepository, options: DeploymentWorktreeOptions): Promise<void>;
25
+ export type DeploymentPublicationResult = {
26
+ attempts: 1 | 2;
27
+ cleanupFailed: boolean;
28
+ };
29
+ export declare function publishDeploymentSource(repository: GitRepository, options: DeploymentWorktreeOptions, prepare: (workspace: {
30
+ path: string;
31
+ repository: GitRepository;
32
+ }) => Promise<void>): Promise<DeploymentPublicationResult>;
33
+ export declare function deploymentWorktreeRelativePath(repositoryRoot: string, workspacePath: string): string;
34
+ export {};
35
+ //# sourceMappingURL=deployment-worktree.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deployment-worktree.d.ts","sourceRoot":"","sources":["../../src/lifecycle/deployment-worktree.ts"],"names":[],"mappings":"AAGA,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,aAAa,EACnB,MAAM,sBAAsB,CAAC;AAK9B,MAAM,MAAM,yBAAyB,GAAG;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,iBAAiB,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,2BAA2B,GACnC,gBAAgB,GAChB,oBAAoB,GACpB,mBAAmB,GACnB,sBAAsB,GACtB,sBAAsB,GACtB,qBAAqB,GACrB,iBAAiB,CAAC;AAEtB,KAAK,eAAe,GAAG,WAAW,GAAG,aAAa,CAAC;AACnD,KAAK,cAAc,GACf,OAAO,GACP,WAAW,GACX,QAAQ,GACR,cAAc,GACd,eAAe,GACf,OAAO,GACP,eAAe,GACf,gBAAgB,GAChB,iBAAiB,GACjB,MAAM,GACN,SAAS,CAAC;AAEd,yFAAyF;AACzF,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,eAAe,EACtB,IAAI,EAAE,cAAc,EACpB,KAAK,EAAE,OAAO,QAiBf;AAED,qBAAa,uBAAwB,SAAQ,KAAK;IAChD,QAAQ,CAAC,IAAI,EAAE,2BAA2B,CAAC;IAC3C,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC,QAAQ,CAAC,cAAc,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IAExB,YACE,IAAI,EAAE,2BAA2B,EACjC,OAAO,GAAE;QAAE,aAAa,CAAC,EAAE,OAAO,CAAC;QAAC,cAAc,CAAC,EAAE,OAAO,CAAA;KAAO,EAQpE;CACF;AA+ND,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE,aAAa,EACzB,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,IAAI,CAAC,CAcf;AAED,MAAM,MAAM,2BAA2B,GAAG;IACxC,QAAQ,EAAE,CAAC,GAAG,CAAC,CAAC;IAChB,aAAa,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,aAAa,EACzB,OAAO,EAAE,yBAAyB,EAClC,OAAO,EAAE,CAAC,SAAS,EAAE;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,aAAa,CAAC;CAC3B,KAAK,OAAO,CAAC,IAAI,CAAC,GAClB,OAAO,CAAC,2BAA2B,CAAC,CAgEtC;AAED,wBAAgB,8BAA8B,CAC5C,cAAc,EAAE,MAAM,EACtB,aAAa,EAAE,MAAM,UAGtB","sourcesContent":["import { lstat, mkdtemp, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join, relative, sep } from \"node:path\";\nimport {\n createGitRepository,\n GitRepositoryError,\n type GitCommitIdentity,\n type GitRepository,\n} from \"../git-repository.js\";\n\nconst OWNERSHIP_MARKER = \"bonfire deployment workspace\\n\";\nconst SOURCE_SHA_PATTERN = /^[0-9a-f]{40,64}$/u;\n\nexport type DeploymentWorktreeOptions = {\n remote: string;\n primaryBranch: string;\n sourceSha: string;\n timeoutMs?: number;\n commitIdentity?: GitCommitIdentity;\n};\n\nexport type DeploymentWorktreeErrorKind =\n | \"invalid-source\"\n | \"preflight-conflict\"\n | \"preflight-failure\"\n | \"publication-conflict\"\n | \"publication-rejected\"\n | \"publication-failure\"\n | \"cleanup-failure\";\n\ntype DeploymentPhase = \"preflight\" | \"publication\";\ntype DeploymentStep =\n | \"fetch\"\n | \"directory\"\n | \"marker\"\n | \"worktree-add\"\n | \"source-verify\"\n | \"merge\"\n | \"metadata-read\"\n | \"metadata-write\"\n | \"metadata-commit\"\n | \"push\"\n | \"cleanup\";\n\n/** Log only finite classifications; Git/OS messages may contain paths or credentials. */\nexport function reportDeploymentIssue(\n phase: DeploymentPhase,\n step: DeploymentStep,\n error: unknown,\n) {\n const kind =\n error instanceof GitRepositoryError\n ? error.kind\n : error instanceof DeploymentWorktreeError\n ? error.kind\n : step.startsWith(\"metadata-\") ||\n step === \"directory\" ||\n step === \"marker\"\n ? \"filesystem-failure\"\n : \"unexpected\";\n console.warn(\"[bonfire] Deployment workspace attention\", {\n phase,\n step,\n kind,\n });\n}\n\nexport class DeploymentWorktreeError extends Error {\n readonly kind: DeploymentWorktreeErrorKind;\n readonly cleanupFailed: boolean;\n readonly operationError: unknown;\n readonly cause: unknown;\n\n constructor(\n kind: DeploymentWorktreeErrorKind,\n options: { cleanupFailed?: boolean; operationError?: unknown } = {},\n ) {\n super(`Deployment workspace operation failed: ${kind}`);\n this.name = \"DeploymentWorktreeError\";\n this.kind = kind;\n this.cleanupFailed = options.cleanupFailed ?? false;\n this.operationError = options.operationError;\n this.cause = options.operationError;\n }\n}\n\nfunction withCleanupFailure(error: unknown): DeploymentWorktreeError {\n if (error instanceof DeploymentWorktreeError) {\n return new DeploymentWorktreeError(error.kind, {\n cleanupFailed: true,\n operationError: error.operationError ?? error,\n });\n }\n return new DeploymentWorktreeError(\"cleanup-failure\", {\n cleanupFailed: true,\n operationError: error,\n });\n}\n\ntype DeploymentWorktree = {\n path: string;\n repository: GitRepository;\n cleanup(): Promise<void>;\n};\n\ntype DeploymentWorkspaceOperation<Value> = (\n workspace: DeploymentWorktree,\n) => Promise<Value>;\n\nfunction assertBranchName(value: string, option: string) {\n if (\n value.length === 0 ||\n value.startsWith(\"-\") ||\n value.includes(\"\\0\") ||\n /\\s/u.test(value) ||\n value.includes(\"@{\") ||\n value.split(/[\\\\/]/u).some((segment) => segment === \"..\")\n ) {\n throw new TypeError(`Invalid ${option}`);\n }\n}\n\nfunction assertSourceSha(sourceSha: string) {\n if (!SOURCE_SHA_PATTERN.test(sourceSha)) {\n throw new DeploymentWorktreeError(\"invalid-source\");\n }\n}\n\nfunction remotePrimaryRef(options: DeploymentWorktreeOptions) {\n return `refs/remotes/${options.remote}/${options.primaryBranch}`;\n}\n\nfunction mapMergeError(\n error: unknown,\n phase: \"preflight\" | \"publication\",\n): unknown {\n if (!(error instanceof GitRepositoryError)) return error;\n if (error.kind === \"conflict\") {\n return new DeploymentWorktreeError(`${phase}-conflict`);\n }\n if (phase === \"publication\") {\n if (error.kind === \"non-fast-forward\") {\n return new DeploymentWorktreeError(\"publication-rejected\");\n }\n return new DeploymentWorktreeError(\"publication-failure\");\n }\n return error;\n}\n\nasync function createDeploymentWorktree(\n repository: GitRepository,\n options: DeploymentWorktreeOptions,\n phase: DeploymentPhase,\n): Promise<DeploymentWorktree> {\n assertBranchName(options.remote, \"remote\");\n assertBranchName(options.primaryBranch, \"primary branch\");\n assertSourceSha(options.sourceSha);\n\n try {\n await repository.fetch(options.remote, options.primaryBranch);\n } catch (error) {\n reportDeploymentIssue(phase, \"fetch\", error);\n throw new DeploymentWorktreeError(`${phase}-failure`, {\n operationError: error,\n });\n }\n let operationDirectory: string;\n try {\n operationDirectory = await mkdtemp(join(tmpdir(), \"bonfire-deployment-\"));\n } catch (error) {\n reportDeploymentIssue(phase, \"directory\", error);\n throw new DeploymentWorktreeError(`${phase}-failure`, {\n operationError: error,\n });\n }\n const workspacePath = join(operationDirectory, \"workspace\");\n const markerPath = join(operationDirectory, \"OWNER\");\n\n let worktreeAdded = false;\n let step: DeploymentStep = \"marker\";\n try {\n await writeFile(markerPath, OWNERSHIP_MARKER, { flag: \"wx\" });\n step = \"worktree-add\";\n await repository.raw([\n \"worktree\",\n \"add\",\n \"--detach\",\n workspacePath,\n remotePrimaryRef(options),\n ]);\n worktreeAdded = true;\n\n const workspaceRepository = createGitRepository({\n repositoryRoot: workspacePath,\n timeoutMs: options.timeoutMs,\n commitIdentity: options.commitIdentity,\n });\n\n step = \"source-verify\";\n await workspaceRepository.raw([\n \"cat-file\",\n \"-e\",\n `${options.sourceSha}^{commit}`,\n ]);\n\n return {\n path: workspacePath,\n repository: workspaceRepository,\n async cleanup() {\n try {\n const marker = await readFile(markerPath, \"utf8\");\n const stats = await lstat(workspacePath);\n if (marker !== OWNERSHIP_MARKER || stats.isSymbolicLink()) {\n throw new DeploymentWorktreeError(\"cleanup-failure\");\n }\n await repository.raw([\n \"worktree\",\n \"remove\",\n \"--force\",\n workspacePath,\n ]);\n await rm(operationDirectory, { recursive: true, force: false });\n } catch (error) {\n reportDeploymentIssue(phase, \"cleanup\", error);\n if (error instanceof DeploymentWorktreeError) throw error;\n throw new DeploymentWorktreeError(\"cleanup-failure\", {\n operationError: error,\n });\n }\n },\n };\n } catch (error) {\n reportDeploymentIssue(phase, step, error);\n let cleanupFailed = false;\n if (worktreeAdded) {\n try {\n await repository.raw([\"worktree\", \"remove\", \"--force\", workspacePath]);\n } catch (cleanupError) {\n cleanupFailed = true;\n reportDeploymentIssue(phase, \"cleanup\", cleanupError);\n }\n }\n try {\n await rm(operationDirectory, { recursive: true, force: true });\n } catch (cleanupError) {\n cleanupFailed = true;\n reportDeploymentIssue(phase, \"cleanup\", cleanupError);\n }\n throw new DeploymentWorktreeError(`${phase}-failure`, {\n cleanupFailed,\n operationError: error,\n });\n }\n}\n\nasync function mergeSource(\n workspace: DeploymentWorktree,\n sourceSha: string,\n phase: \"preflight\" | \"publication\",\n) {\n try {\n await workspace.repository.merge([\n sourceSha,\n \"--no-ff\",\n \"-m\",\n `merge: deploy ${sourceSha.slice(0, 12)}`,\n ]);\n } catch (error) {\n reportDeploymentIssue(phase, \"merge\", error);\n const mapped = mapMergeError(error, phase);\n throw mapped instanceof DeploymentWorktreeError\n ? mapped\n : new DeploymentWorktreeError(`${phase}-failure`, {\n operationError: error,\n });\n }\n}\n\nasync function withDeploymentWorktree<Value>(\n repository: GitRepository,\n options: DeploymentWorktreeOptions,\n operation: DeploymentWorkspaceOperation<Value>,\n phase: DeploymentPhase,\n): Promise<{ value: Value; cleanupFailed: boolean }> {\n const workspace = await createDeploymentWorktree(repository, options, phase);\n let value: Value;\n try {\n value = await operation(workspace);\n } catch (error) {\n // Preserve the operation's failure while making cleanup attention\n // observable to callers.\n try {\n await workspace.cleanup();\n } catch {\n throw withCleanupFailure(error);\n }\n throw error;\n }\n\n try {\n await workspace.cleanup();\n return { value, cleanupFailed: false };\n } catch {\n return { value, cleanupFailed: true };\n }\n}\n\nexport async function preflightDeploymentSource(\n repository: GitRepository,\n options: DeploymentWorktreeOptions,\n): Promise<void> {\n const result = await withDeploymentWorktree(\n repository,\n options,\n async (workspace) => {\n await mergeSource(workspace, options.sourceSha, \"preflight\");\n },\n \"preflight\",\n );\n if (result.cleanupFailed) {\n throw new DeploymentWorktreeError(\"cleanup-failure\", {\n cleanupFailed: true,\n });\n }\n}\n\nexport type DeploymentPublicationResult = {\n attempts: 1 | 2;\n cleanupFailed: boolean;\n};\n\nexport async function publishDeploymentSource(\n repository: GitRepository,\n options: DeploymentWorktreeOptions,\n prepare: (workspace: {\n path: string;\n repository: GitRepository;\n }) => Promise<void>,\n): Promise<DeploymentPublicationResult> {\n let cleanupFailed = false;\n for (const attempt of [1, 2] as const) {\n try {\n const result = await withDeploymentWorktree(\n repository,\n options,\n async (workspace) => {\n await mergeSource(workspace, options.sourceSha, \"publication\");\n await prepare({\n path: workspace.path,\n repository: workspace.repository,\n });\n try {\n await workspace.repository.raw([\n \"push\",\n options.remote,\n `HEAD:refs/heads/${options.primaryBranch}`,\n ]);\n } catch (error) {\n reportDeploymentIssue(\"publication\", \"push\", error);\n const mapped = mapMergeError(error, \"publication\");\n throw mapped instanceof DeploymentWorktreeError\n ? mapped\n : new DeploymentWorktreeError(\"publication-failure\", {\n operationError: error,\n });\n }\n },\n \"publication\",\n );\n cleanupFailed ||= result.cleanupFailed;\n return { attempts: attempt, cleanupFailed };\n } catch (error) {\n cleanupFailed ||=\n error instanceof DeploymentWorktreeError && error.cleanupFailed;\n if (\n !(error instanceof DeploymentWorktreeError) ||\n error.kind !== \"publication-rejected\" ||\n attempt === 2\n ) {\n if (cleanupFailed) {\n if (error instanceof DeploymentWorktreeError) {\n if (!error.cleanupFailed) {\n throw new DeploymentWorktreeError(error.kind, {\n cleanupFailed: true,\n operationError: error.operationError ?? error,\n });\n }\n } else {\n throw new DeploymentWorktreeError(\"publication-failure\", {\n cleanupFailed: true,\n operationError: error,\n });\n }\n }\n throw error;\n }\n }\n }\n\n throw new DeploymentWorktreeError(\"publication-rejected\", {\n cleanupFailed,\n });\n}\n\nexport function deploymentWorktreeRelativePath(\n repositoryRoot: string,\n workspacePath: string,\n) {\n return relative(repositoryRoot, workspacePath).split(sep).join(\"/\");\n}\n"]}
@@ -0,0 +1,302 @@
1
+ import { lstat, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
2
+ import { tmpdir } from "node:os";
3
+ import { join, relative, sep } from "node:path";
4
+ import { createGitRepository, GitRepositoryError, } from "../git-repository.js";
5
+ const OWNERSHIP_MARKER = "bonfire deployment workspace\n";
6
+ const SOURCE_SHA_PATTERN = /^[0-9a-f]{40,64}$/u;
7
+ /** Log only finite classifications; Git/OS messages may contain paths or credentials. */
8
+ export function reportDeploymentIssue(phase, step, error) {
9
+ const kind = error instanceof GitRepositoryError
10
+ ? error.kind
11
+ : error instanceof DeploymentWorktreeError
12
+ ? error.kind
13
+ : step.startsWith("metadata-") ||
14
+ step === "directory" ||
15
+ step === "marker"
16
+ ? "filesystem-failure"
17
+ : "unexpected";
18
+ console.warn("[bonfire] Deployment workspace attention", {
19
+ phase,
20
+ step,
21
+ kind,
22
+ });
23
+ }
24
+ export class DeploymentWorktreeError extends Error {
25
+ kind;
26
+ cleanupFailed;
27
+ operationError;
28
+ cause;
29
+ constructor(kind, options = {}) {
30
+ super(`Deployment workspace operation failed: ${kind}`);
31
+ this.name = "DeploymentWorktreeError";
32
+ this.kind = kind;
33
+ this.cleanupFailed = options.cleanupFailed ?? false;
34
+ this.operationError = options.operationError;
35
+ this.cause = options.operationError;
36
+ }
37
+ }
38
+ function withCleanupFailure(error) {
39
+ if (error instanceof DeploymentWorktreeError) {
40
+ return new DeploymentWorktreeError(error.kind, {
41
+ cleanupFailed: true,
42
+ operationError: error.operationError ?? error,
43
+ });
44
+ }
45
+ return new DeploymentWorktreeError("cleanup-failure", {
46
+ cleanupFailed: true,
47
+ operationError: error,
48
+ });
49
+ }
50
+ function assertBranchName(value, option) {
51
+ if (value.length === 0 ||
52
+ value.startsWith("-") ||
53
+ value.includes("\0") ||
54
+ /\s/u.test(value) ||
55
+ value.includes("@{") ||
56
+ value.split(/[\\/]/u).some((segment) => segment === "..")) {
57
+ throw new TypeError(`Invalid ${option}`);
58
+ }
59
+ }
60
+ function assertSourceSha(sourceSha) {
61
+ if (!SOURCE_SHA_PATTERN.test(sourceSha)) {
62
+ throw new DeploymentWorktreeError("invalid-source");
63
+ }
64
+ }
65
+ function remotePrimaryRef(options) {
66
+ return `refs/remotes/${options.remote}/${options.primaryBranch}`;
67
+ }
68
+ function mapMergeError(error, phase) {
69
+ if (!(error instanceof GitRepositoryError))
70
+ return error;
71
+ if (error.kind === "conflict") {
72
+ return new DeploymentWorktreeError(`${phase}-conflict`);
73
+ }
74
+ if (phase === "publication") {
75
+ if (error.kind === "non-fast-forward") {
76
+ return new DeploymentWorktreeError("publication-rejected");
77
+ }
78
+ return new DeploymentWorktreeError("publication-failure");
79
+ }
80
+ return error;
81
+ }
82
+ async function createDeploymentWorktree(repository, options, phase) {
83
+ assertBranchName(options.remote, "remote");
84
+ assertBranchName(options.primaryBranch, "primary branch");
85
+ assertSourceSha(options.sourceSha);
86
+ try {
87
+ await repository.fetch(options.remote, options.primaryBranch);
88
+ }
89
+ catch (error) {
90
+ reportDeploymentIssue(phase, "fetch", error);
91
+ throw new DeploymentWorktreeError(`${phase}-failure`, {
92
+ operationError: error,
93
+ });
94
+ }
95
+ let operationDirectory;
96
+ try {
97
+ operationDirectory = await mkdtemp(join(tmpdir(), "bonfire-deployment-"));
98
+ }
99
+ catch (error) {
100
+ reportDeploymentIssue(phase, "directory", error);
101
+ throw new DeploymentWorktreeError(`${phase}-failure`, {
102
+ operationError: error,
103
+ });
104
+ }
105
+ const workspacePath = join(operationDirectory, "workspace");
106
+ const markerPath = join(operationDirectory, "OWNER");
107
+ let worktreeAdded = false;
108
+ let step = "marker";
109
+ try {
110
+ await writeFile(markerPath, OWNERSHIP_MARKER, { flag: "wx" });
111
+ step = "worktree-add";
112
+ await repository.raw([
113
+ "worktree",
114
+ "add",
115
+ "--detach",
116
+ workspacePath,
117
+ remotePrimaryRef(options),
118
+ ]);
119
+ worktreeAdded = true;
120
+ const workspaceRepository = createGitRepository({
121
+ repositoryRoot: workspacePath,
122
+ timeoutMs: options.timeoutMs,
123
+ commitIdentity: options.commitIdentity,
124
+ });
125
+ step = "source-verify";
126
+ await workspaceRepository.raw([
127
+ "cat-file",
128
+ "-e",
129
+ `${options.sourceSha}^{commit}`,
130
+ ]);
131
+ return {
132
+ path: workspacePath,
133
+ repository: workspaceRepository,
134
+ async cleanup() {
135
+ try {
136
+ const marker = await readFile(markerPath, "utf8");
137
+ const stats = await lstat(workspacePath);
138
+ if (marker !== OWNERSHIP_MARKER || stats.isSymbolicLink()) {
139
+ throw new DeploymentWorktreeError("cleanup-failure");
140
+ }
141
+ await repository.raw([
142
+ "worktree",
143
+ "remove",
144
+ "--force",
145
+ workspacePath,
146
+ ]);
147
+ await rm(operationDirectory, { recursive: true, force: false });
148
+ }
149
+ catch (error) {
150
+ reportDeploymentIssue(phase, "cleanup", error);
151
+ if (error instanceof DeploymentWorktreeError)
152
+ throw error;
153
+ throw new DeploymentWorktreeError("cleanup-failure", {
154
+ operationError: error,
155
+ });
156
+ }
157
+ },
158
+ };
159
+ }
160
+ catch (error) {
161
+ reportDeploymentIssue(phase, step, error);
162
+ let cleanupFailed = false;
163
+ if (worktreeAdded) {
164
+ try {
165
+ await repository.raw(["worktree", "remove", "--force", workspacePath]);
166
+ }
167
+ catch (cleanupError) {
168
+ cleanupFailed = true;
169
+ reportDeploymentIssue(phase, "cleanup", cleanupError);
170
+ }
171
+ }
172
+ try {
173
+ await rm(operationDirectory, { recursive: true, force: true });
174
+ }
175
+ catch (cleanupError) {
176
+ cleanupFailed = true;
177
+ reportDeploymentIssue(phase, "cleanup", cleanupError);
178
+ }
179
+ throw new DeploymentWorktreeError(`${phase}-failure`, {
180
+ cleanupFailed,
181
+ operationError: error,
182
+ });
183
+ }
184
+ }
185
+ async function mergeSource(workspace, sourceSha, phase) {
186
+ try {
187
+ await workspace.repository.merge([
188
+ sourceSha,
189
+ "--no-ff",
190
+ "-m",
191
+ `merge: deploy ${sourceSha.slice(0, 12)}`,
192
+ ]);
193
+ }
194
+ catch (error) {
195
+ reportDeploymentIssue(phase, "merge", error);
196
+ const mapped = mapMergeError(error, phase);
197
+ throw mapped instanceof DeploymentWorktreeError
198
+ ? mapped
199
+ : new DeploymentWorktreeError(`${phase}-failure`, {
200
+ operationError: error,
201
+ });
202
+ }
203
+ }
204
+ async function withDeploymentWorktree(repository, options, operation, phase) {
205
+ const workspace = await createDeploymentWorktree(repository, options, phase);
206
+ let value;
207
+ try {
208
+ value = await operation(workspace);
209
+ }
210
+ catch (error) {
211
+ // Preserve the operation's failure while making cleanup attention
212
+ // observable to callers.
213
+ try {
214
+ await workspace.cleanup();
215
+ }
216
+ catch {
217
+ throw withCleanupFailure(error);
218
+ }
219
+ throw error;
220
+ }
221
+ try {
222
+ await workspace.cleanup();
223
+ return { value, cleanupFailed: false };
224
+ }
225
+ catch {
226
+ return { value, cleanupFailed: true };
227
+ }
228
+ }
229
+ export async function preflightDeploymentSource(repository, options) {
230
+ const result = await withDeploymentWorktree(repository, options, async (workspace) => {
231
+ await mergeSource(workspace, options.sourceSha, "preflight");
232
+ }, "preflight");
233
+ if (result.cleanupFailed) {
234
+ throw new DeploymentWorktreeError("cleanup-failure", {
235
+ cleanupFailed: true,
236
+ });
237
+ }
238
+ }
239
+ export async function publishDeploymentSource(repository, options, prepare) {
240
+ let cleanupFailed = false;
241
+ for (const attempt of [1, 2]) {
242
+ try {
243
+ const result = await withDeploymentWorktree(repository, options, async (workspace) => {
244
+ await mergeSource(workspace, options.sourceSha, "publication");
245
+ await prepare({
246
+ path: workspace.path,
247
+ repository: workspace.repository,
248
+ });
249
+ try {
250
+ await workspace.repository.raw([
251
+ "push",
252
+ options.remote,
253
+ `HEAD:refs/heads/${options.primaryBranch}`,
254
+ ]);
255
+ }
256
+ catch (error) {
257
+ reportDeploymentIssue("publication", "push", error);
258
+ const mapped = mapMergeError(error, "publication");
259
+ throw mapped instanceof DeploymentWorktreeError
260
+ ? mapped
261
+ : new DeploymentWorktreeError("publication-failure", {
262
+ operationError: error,
263
+ });
264
+ }
265
+ }, "publication");
266
+ cleanupFailed ||= result.cleanupFailed;
267
+ return { attempts: attempt, cleanupFailed };
268
+ }
269
+ catch (error) {
270
+ cleanupFailed ||=
271
+ error instanceof DeploymentWorktreeError && error.cleanupFailed;
272
+ if (!(error instanceof DeploymentWorktreeError) ||
273
+ error.kind !== "publication-rejected" ||
274
+ attempt === 2) {
275
+ if (cleanupFailed) {
276
+ if (error instanceof DeploymentWorktreeError) {
277
+ if (!error.cleanupFailed) {
278
+ throw new DeploymentWorktreeError(error.kind, {
279
+ cleanupFailed: true,
280
+ operationError: error.operationError ?? error,
281
+ });
282
+ }
283
+ }
284
+ else {
285
+ throw new DeploymentWorktreeError("publication-failure", {
286
+ cleanupFailed: true,
287
+ operationError: error,
288
+ });
289
+ }
290
+ }
291
+ throw error;
292
+ }
293
+ }
294
+ }
295
+ throw new DeploymentWorktreeError("publication-rejected", {
296
+ cleanupFailed,
297
+ });
298
+ }
299
+ export function deploymentWorktreeRelativePath(repositoryRoot, workspacePath) {
300
+ return relative(repositoryRoot, workspacePath).split(sep).join("/");
301
+ }
302
+ //# sourceMappingURL=deployment-worktree.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deployment-worktree.js","sourceRoot":"","sources":["../../src/lifecycle/deployment-worktree.ts"],"names":[],"mappings":"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","sourcesContent":["import { lstat, mkdtemp, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join, relative, sep } from \"node:path\";\nimport {\n createGitRepository,\n GitRepositoryError,\n type GitCommitIdentity,\n type GitRepository,\n} from \"../git-repository.js\";\n\nconst OWNERSHIP_MARKER = \"bonfire deployment workspace\\n\";\nconst SOURCE_SHA_PATTERN = /^[0-9a-f]{40,64}$/u;\n\nexport type DeploymentWorktreeOptions = {\n remote: string;\n primaryBranch: string;\n sourceSha: string;\n timeoutMs?: number;\n commitIdentity?: GitCommitIdentity;\n};\n\nexport type DeploymentWorktreeErrorKind =\n | \"invalid-source\"\n | \"preflight-conflict\"\n | \"preflight-failure\"\n | \"publication-conflict\"\n | \"publication-rejected\"\n | \"publication-failure\"\n | \"cleanup-failure\";\n\ntype DeploymentPhase = \"preflight\" | \"publication\";\ntype DeploymentStep =\n | \"fetch\"\n | \"directory\"\n | \"marker\"\n | \"worktree-add\"\n | \"source-verify\"\n | \"merge\"\n | \"metadata-read\"\n | \"metadata-write\"\n | \"metadata-commit\"\n | \"push\"\n | \"cleanup\";\n\n/** Log only finite classifications; Git/OS messages may contain paths or credentials. */\nexport function reportDeploymentIssue(\n phase: DeploymentPhase,\n step: DeploymentStep,\n error: unknown,\n) {\n const kind =\n error instanceof GitRepositoryError\n ? error.kind\n : error instanceof DeploymentWorktreeError\n ? error.kind\n : step.startsWith(\"metadata-\") ||\n step === \"directory\" ||\n step === \"marker\"\n ? \"filesystem-failure\"\n : \"unexpected\";\n console.warn(\"[bonfire] Deployment workspace attention\", {\n phase,\n step,\n kind,\n });\n}\n\nexport class DeploymentWorktreeError extends Error {\n readonly kind: DeploymentWorktreeErrorKind;\n readonly cleanupFailed: boolean;\n readonly operationError: unknown;\n readonly cause: unknown;\n\n constructor(\n kind: DeploymentWorktreeErrorKind,\n options: { cleanupFailed?: boolean; operationError?: unknown } = {},\n ) {\n super(`Deployment workspace operation failed: ${kind}`);\n this.name = \"DeploymentWorktreeError\";\n this.kind = kind;\n this.cleanupFailed = options.cleanupFailed ?? false;\n this.operationError = options.operationError;\n this.cause = options.operationError;\n }\n}\n\nfunction withCleanupFailure(error: unknown): DeploymentWorktreeError {\n if (error instanceof DeploymentWorktreeError) {\n return new DeploymentWorktreeError(error.kind, {\n cleanupFailed: true,\n operationError: error.operationError ?? error,\n });\n }\n return new DeploymentWorktreeError(\"cleanup-failure\", {\n cleanupFailed: true,\n operationError: error,\n });\n}\n\ntype DeploymentWorktree = {\n path: string;\n repository: GitRepository;\n cleanup(): Promise<void>;\n};\n\ntype DeploymentWorkspaceOperation<Value> = (\n workspace: DeploymentWorktree,\n) => Promise<Value>;\n\nfunction assertBranchName(value: string, option: string) {\n if (\n value.length === 0 ||\n value.startsWith(\"-\") ||\n value.includes(\"\\0\") ||\n /\\s/u.test(value) ||\n value.includes(\"@{\") ||\n value.split(/[\\\\/]/u).some((segment) => segment === \"..\")\n ) {\n throw new TypeError(`Invalid ${option}`);\n }\n}\n\nfunction assertSourceSha(sourceSha: string) {\n if (!SOURCE_SHA_PATTERN.test(sourceSha)) {\n throw new DeploymentWorktreeError(\"invalid-source\");\n }\n}\n\nfunction remotePrimaryRef(options: DeploymentWorktreeOptions) {\n return `refs/remotes/${options.remote}/${options.primaryBranch}`;\n}\n\nfunction mapMergeError(\n error: unknown,\n phase: \"preflight\" | \"publication\",\n): unknown {\n if (!(error instanceof GitRepositoryError)) return error;\n if (error.kind === \"conflict\") {\n return new DeploymentWorktreeError(`${phase}-conflict`);\n }\n if (phase === \"publication\") {\n if (error.kind === \"non-fast-forward\") {\n return new DeploymentWorktreeError(\"publication-rejected\");\n }\n return new DeploymentWorktreeError(\"publication-failure\");\n }\n return error;\n}\n\nasync function createDeploymentWorktree(\n repository: GitRepository,\n options: DeploymentWorktreeOptions,\n phase: DeploymentPhase,\n): Promise<DeploymentWorktree> {\n assertBranchName(options.remote, \"remote\");\n assertBranchName(options.primaryBranch, \"primary branch\");\n assertSourceSha(options.sourceSha);\n\n try {\n await repository.fetch(options.remote, options.primaryBranch);\n } catch (error) {\n reportDeploymentIssue(phase, \"fetch\", error);\n throw new DeploymentWorktreeError(`${phase}-failure`, {\n operationError: error,\n });\n }\n let operationDirectory: string;\n try {\n operationDirectory = await mkdtemp(join(tmpdir(), \"bonfire-deployment-\"));\n } catch (error) {\n reportDeploymentIssue(phase, \"directory\", error);\n throw new DeploymentWorktreeError(`${phase}-failure`, {\n operationError: error,\n });\n }\n const workspacePath = join(operationDirectory, \"workspace\");\n const markerPath = join(operationDirectory, \"OWNER\");\n\n let worktreeAdded = false;\n let step: DeploymentStep = \"marker\";\n try {\n await writeFile(markerPath, OWNERSHIP_MARKER, { flag: \"wx\" });\n step = \"worktree-add\";\n await repository.raw([\n \"worktree\",\n \"add\",\n \"--detach\",\n workspacePath,\n remotePrimaryRef(options),\n ]);\n worktreeAdded = true;\n\n const workspaceRepository = createGitRepository({\n repositoryRoot: workspacePath,\n timeoutMs: options.timeoutMs,\n commitIdentity: options.commitIdentity,\n });\n\n step = \"source-verify\";\n await workspaceRepository.raw([\n \"cat-file\",\n \"-e\",\n `${options.sourceSha}^{commit}`,\n ]);\n\n return {\n path: workspacePath,\n repository: workspaceRepository,\n async cleanup() {\n try {\n const marker = await readFile(markerPath, \"utf8\");\n const stats = await lstat(workspacePath);\n if (marker !== OWNERSHIP_MARKER || stats.isSymbolicLink()) {\n throw new DeploymentWorktreeError(\"cleanup-failure\");\n }\n await repository.raw([\n \"worktree\",\n \"remove\",\n \"--force\",\n workspacePath,\n ]);\n await rm(operationDirectory, { recursive: true, force: false });\n } catch (error) {\n reportDeploymentIssue(phase, \"cleanup\", error);\n if (error instanceof DeploymentWorktreeError) throw error;\n throw new DeploymentWorktreeError(\"cleanup-failure\", {\n operationError: error,\n });\n }\n },\n };\n } catch (error) {\n reportDeploymentIssue(phase, step, error);\n let cleanupFailed = false;\n if (worktreeAdded) {\n try {\n await repository.raw([\"worktree\", \"remove\", \"--force\", workspacePath]);\n } catch (cleanupError) {\n cleanupFailed = true;\n reportDeploymentIssue(phase, \"cleanup\", cleanupError);\n }\n }\n try {\n await rm(operationDirectory, { recursive: true, force: true });\n } catch (cleanupError) {\n cleanupFailed = true;\n reportDeploymentIssue(phase, \"cleanup\", cleanupError);\n }\n throw new DeploymentWorktreeError(`${phase}-failure`, {\n cleanupFailed,\n operationError: error,\n });\n }\n}\n\nasync function mergeSource(\n workspace: DeploymentWorktree,\n sourceSha: string,\n phase: \"preflight\" | \"publication\",\n) {\n try {\n await workspace.repository.merge([\n sourceSha,\n \"--no-ff\",\n \"-m\",\n `merge: deploy ${sourceSha.slice(0, 12)}`,\n ]);\n } catch (error) {\n reportDeploymentIssue(phase, \"merge\", error);\n const mapped = mapMergeError(error, phase);\n throw mapped instanceof DeploymentWorktreeError\n ? mapped\n : new DeploymentWorktreeError(`${phase}-failure`, {\n operationError: error,\n });\n }\n}\n\nasync function withDeploymentWorktree<Value>(\n repository: GitRepository,\n options: DeploymentWorktreeOptions,\n operation: DeploymentWorkspaceOperation<Value>,\n phase: DeploymentPhase,\n): Promise<{ value: Value; cleanupFailed: boolean }> {\n const workspace = await createDeploymentWorktree(repository, options, phase);\n let value: Value;\n try {\n value = await operation(workspace);\n } catch (error) {\n // Preserve the operation's failure while making cleanup attention\n // observable to callers.\n try {\n await workspace.cleanup();\n } catch {\n throw withCleanupFailure(error);\n }\n throw error;\n }\n\n try {\n await workspace.cleanup();\n return { value, cleanupFailed: false };\n } catch {\n return { value, cleanupFailed: true };\n }\n}\n\nexport async function preflightDeploymentSource(\n repository: GitRepository,\n options: DeploymentWorktreeOptions,\n): Promise<void> {\n const result = await withDeploymentWorktree(\n repository,\n options,\n async (workspace) => {\n await mergeSource(workspace, options.sourceSha, \"preflight\");\n },\n \"preflight\",\n );\n if (result.cleanupFailed) {\n throw new DeploymentWorktreeError(\"cleanup-failure\", {\n cleanupFailed: true,\n });\n }\n}\n\nexport type DeploymentPublicationResult = {\n attempts: 1 | 2;\n cleanupFailed: boolean;\n};\n\nexport async function publishDeploymentSource(\n repository: GitRepository,\n options: DeploymentWorktreeOptions,\n prepare: (workspace: {\n path: string;\n repository: GitRepository;\n }) => Promise<void>,\n): Promise<DeploymentPublicationResult> {\n let cleanupFailed = false;\n for (const attempt of [1, 2] as const) {\n try {\n const result = await withDeploymentWorktree(\n repository,\n options,\n async (workspace) => {\n await mergeSource(workspace, options.sourceSha, \"publication\");\n await prepare({\n path: workspace.path,\n repository: workspace.repository,\n });\n try {\n await workspace.repository.raw([\n \"push\",\n options.remote,\n `HEAD:refs/heads/${options.primaryBranch}`,\n ]);\n } catch (error) {\n reportDeploymentIssue(\"publication\", \"push\", error);\n const mapped = mapMergeError(error, \"publication\");\n throw mapped instanceof DeploymentWorktreeError\n ? mapped\n : new DeploymentWorktreeError(\"publication-failure\", {\n operationError: error,\n });\n }\n },\n \"publication\",\n );\n cleanupFailed ||= result.cleanupFailed;\n return { attempts: attempt, cleanupFailed };\n } catch (error) {\n cleanupFailed ||=\n error instanceof DeploymentWorktreeError && error.cleanupFailed;\n if (\n !(error instanceof DeploymentWorktreeError) ||\n error.kind !== \"publication-rejected\" ||\n attempt === 2\n ) {\n if (cleanupFailed) {\n if (error instanceof DeploymentWorktreeError) {\n if (!error.cleanupFailed) {\n throw new DeploymentWorktreeError(error.kind, {\n cleanupFailed: true,\n operationError: error.operationError ?? error,\n });\n }\n } else {\n throw new DeploymentWorktreeError(\"publication-failure\", {\n cleanupFailed: true,\n operationError: error,\n });\n }\n }\n throw error;\n }\n }\n }\n\n throw new DeploymentWorktreeError(\"publication-rejected\", {\n cleanupFailed,\n });\n}\n\nexport function deploymentWorktreeRelativePath(\n repositoryRoot: string,\n workspacePath: string,\n) {\n return relative(repositoryRoot, workspacePath).split(sep).join(\"/\");\n}\n"]}
@@ -1,18 +1,23 @@
1
- import { type BranchPreparationValue, type CreatePrototypeInput, type CreatePrototypeValue, type DeletePrototypeValue, type DeploymentValue, type DirtyObservationValue, type DiscardPrototypeValue, type DuplicatePrototypeInput, type DuplicatePrototypeValue, type EditPrototypeInput, type EditPrototypeValue, type LifecycleResult, type BonfireIdentity, type PrototypeCommandInput, type SavePrototypeValue, type ThemeRegistry } from "@ebay-dt/bonfire-core";
1
+ import { type BranchPreparationValue, type CreatePrototypeInput, type CreatePrototypeValue, type DeletePrototypeValue, type DeploymentValue, type DirtyObservationValue, type DiscardPrototypeValue, type DuplicatePrototypeInput, type DuplicatePrototypeValue, type EditPrototypeInput, type EditPrototypeValue, type GitMode, type LifecycleResult, type BonfireIdentity, type PrototypeCommandInput, type SavePrototypeValue, type RepositorySummary, type ThemeRegistry } from "@ebay-dt/bonfire-core";
2
2
  import { type PrototypeCatalogLike } from "../prototype-catalog.js";
3
3
  import type { PreparePrototypeFiles } from "../prepare-prototype-files.js";
4
4
  import { type DeploymentProvider } from "../deployment-provider.js";
5
5
  type LifecycleMethod<Input, Value> = (identity: BonfireIdentity, input: Input) => Promise<LifecycleResult<Value>>;
6
6
  export type PrototypeLifecycle = {
7
+ /** Declares whether this lifecycle enforces the selected Git mode. */
8
+ readonly gitMode?: GitMode;
7
9
  prepareBranch(identity: BonfireIdentity): Promise<LifecycleResult<BranchPreparationValue>>;
8
10
  create: LifecycleMethod<CreatePrototypeInput, CreatePrototypeValue>;
9
11
  duplicate: LifecycleMethod<DuplicatePrototypeInput, DuplicatePrototypeValue>;
10
12
  edit: LifecycleMethod<EditPrototypeInput, EditPrototypeValue>;
11
13
  delete: LifecycleMethod<PrototypeCommandInput, DeletePrototypeValue>;
12
14
  getDirtyState: LifecycleMethod<PrototypeCommandInput, DirtyObservationValue>;
15
+ getRepositorySummary(identity: BonfireIdentity): Promise<LifecycleResult<RepositorySummary>>;
16
+ checkRepositorySummary(identity: BonfireIdentity): Promise<LifecycleResult<RepositorySummary>>;
13
17
  save: LifecycleMethod<PrototypeCommandInput, SavePrototypeValue>;
14
18
  discard: LifecycleMethod<PrototypeCommandInput, DiscardPrototypeValue>;
15
19
  deploy: LifecycleMethod<PrototypeCommandInput, DeploymentValue>;
20
+ retryPublish?: LifecycleMethod<PrototypeCommandInput, DeploymentValue>;
16
21
  };
17
22
  export type LocalPrototypeLifecycleOptions = {
18
23
  repositoryRoot: string;
@@ -20,6 +25,7 @@ export type LocalPrototypeLifecycleOptions = {
20
25
  remote?: string;
21
26
  primaryBranch?: string;
22
27
  automaticBranchSwitch?: "enabled" | "disabled";
28
+ gitMode?: GitMode;
23
29
  commitIdentity?: {
24
30
  name: string;
25
31
  email: string;
@@ -32,13 +38,17 @@ export type LocalPrototypeLifecycleOptions = {
32
38
  };
33
39
  export declare class LocalPrototypeLifecycle implements PrototypeLifecycle {
34
40
  #private;
41
+ readonly gitMode: GitMode;
35
42
  constructor(options: LocalPrototypeLifecycleOptions);
36
43
  prepareBranch(identity: BonfireIdentity): Promise<LifecycleResult<BranchPreparationValue>>;
37
44
  create(identity: BonfireIdentity, input: CreatePrototypeInput): Promise<LifecycleResult<CreatePrototypeValue>>;
38
45
  edit(identity: BonfireIdentity, input: EditPrototypeInput): Promise<LifecycleResult<EditPrototypeValue>>;
39
46
  delete(identity: BonfireIdentity, input: PrototypeCommandInput): Promise<LifecycleResult<DeletePrototypeValue>>;
40
47
  getDirtyState(identity: BonfireIdentity, input: PrototypeCommandInput): Promise<LifecycleResult<DirtyObservationValue>>;
48
+ getRepositorySummary(identity: BonfireIdentity): Promise<LifecycleResult<RepositorySummary>>;
49
+ checkRepositorySummary(identity: BonfireIdentity): Promise<LifecycleResult<RepositorySummary>>;
41
50
  deploy(identity: BonfireIdentity, input: PrototypeCommandInput): Promise<LifecycleResult<DeploymentValue>>;
51
+ retryPublish(identity: BonfireIdentity, input: PrototypeCommandInput): Promise<LifecycleResult<DeploymentValue>>;
42
52
  save(identity: BonfireIdentity, input: PrototypeCommandInput): Promise<LifecycleResult<SavePrototypeValue>>;
43
53
  discard(identity: BonfireIdentity, input: PrototypeCommandInput): Promise<LifecycleResult<DiscardPrototypeValue>>;
44
54
  duplicate(identity: BonfireIdentity, input: DuplicatePrototypeInput): Promise<LifecycleResult<DuplicatePrototypeValue>>;