@ebay-dt/bonfire-node 0.2.0-alpha.8 → 0.2.0-alpha.9

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.
@@ -6,7 +6,11 @@ export type DeploymentWorktreeOptions = {
6
6
  timeoutMs?: number;
7
7
  commitIdentity?: GitCommitIdentity;
8
8
  };
9
- export type DeploymentWorktreeErrorKind = "invalid-source" | "preflight-conflict" | "publication-conflict" | "publication-rejected" | "publication-failure" | "cleanup-failure";
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;
10
14
  export declare class DeploymentWorktreeError extends Error {
11
15
  readonly kind: DeploymentWorktreeErrorKind;
12
16
  readonly cleanupFailed: boolean;
@@ -27,4 +31,5 @@ export declare function publishDeploymentSource(repository: GitRepository, optio
27
31
  repository: GitRepository;
28
32
  }) => Promise<void>): Promise<DeploymentPublicationResult>;
29
33
  export declare function deploymentWorktreeRelativePath(repositoryRoot: string, workspacePath: string): string;
34
+ export {};
30
35
  //# sourceMappingURL=deployment-worktree.d.ts.map
@@ -1 +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,sBAAsB,GACtB,sBAAsB,GACtB,qBAAqB,GACrB,iBAAiB,CAAC;AAEtB,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;AAgLD,wBAAsB,yBAAyB,CAC7C,UAAU,EAAE,aAAa,EACzB,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,IAAI,CAAC,CAaf;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,CAsDtC;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 | \"publication-conflict\"\n | \"publication-rejected\"\n | \"publication-failure\"\n | \"cleanup-failure\";\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): Promise<DeploymentWorktree> {\n assertBranchName(options.remote, \"remote\");\n assertBranchName(options.primaryBranch, \"primary branch\");\n assertSourceSha(options.sourceSha);\n\n await repository.fetch(options.remote, options.primaryBranch);\n const operationDirectory = await mkdtemp(\n join(tmpdir(), \"bonfire-deployment-\"),\n );\n const workspacePath = join(operationDirectory, \"workspace\");\n const markerPath = join(operationDirectory, \"OWNER\");\n\n let worktreeAdded = false;\n try {\n await writeFile(markerPath, OWNERSHIP_MARKER, { flag: \"wx\" });\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 await workspaceRepository.raw([\"cat-file\", \"-e\", `${options.sourceSha}^{commit}`]);\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([\"worktree\", \"remove\", \"--force\", workspacePath]);\n await rm(operationDirectory, { recursive: true, force: false });\n } catch (error) {\n if (error instanceof DeploymentWorktreeError) throw error;\n throw new DeploymentWorktreeError(\"cleanup-failure\");\n }\n },\n };\n } catch (error) {\n if (worktreeAdded) {\n await repository\n .raw([\"worktree\", \"remove\", \"--force\", workspacePath])\n .catch(() => undefined);\n }\n await rm(operationDirectory, { recursive: true, force: true }).catch(\n () => undefined,\n );\n throw error;\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 throw mapMergeError(error, phase);\n }\n}\n\nasync function withDeploymentWorktree<Value>(\n repository: GitRepository,\n options: DeploymentWorktreeOptions,\n operation: DeploymentWorkspaceOperation<Value>,\n): Promise<{ value: Value; cleanupFailed: boolean }> {\n const workspace = await createDeploymentWorktree(repository, options);\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 );\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({ path: workspace.path, repository: workspace.repository });\n try {\n await workspace.repository.raw([\n \"push\",\n options.remote,\n `HEAD:refs/heads/${options.primaryBranch}`,\n ]);\n } catch (error) {\n throw mapMergeError(error, \"publication\");\n }\n },\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
+ {"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"]}
@@ -4,6 +4,23 @@ import { join, relative, sep } from "node:path";
4
4
  import { createGitRepository, GitRepositoryError, } from "../git-repository.js";
5
5
  const OWNERSHIP_MARKER = "bonfire deployment workspace\n";
6
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
+ }
7
24
  export class DeploymentWorktreeError extends Error {
8
25
  kind;
9
26
  cleanupFailed;
@@ -62,17 +79,36 @@ function mapMergeError(error, phase) {
62
79
  }
63
80
  return error;
64
81
  }
65
- async function createDeploymentWorktree(repository, options) {
82
+ async function createDeploymentWorktree(repository, options, phase) {
66
83
  assertBranchName(options.remote, "remote");
67
84
  assertBranchName(options.primaryBranch, "primary branch");
68
85
  assertSourceSha(options.sourceSha);
69
- await repository.fetch(options.remote, options.primaryBranch);
70
- const operationDirectory = await mkdtemp(join(tmpdir(), "bonfire-deployment-"));
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
+ }
71
105
  const workspacePath = join(operationDirectory, "workspace");
72
106
  const markerPath = join(operationDirectory, "OWNER");
73
107
  let worktreeAdded = false;
108
+ let step = "marker";
74
109
  try {
75
110
  await writeFile(markerPath, OWNERSHIP_MARKER, { flag: "wx" });
111
+ step = "worktree-add";
76
112
  await repository.raw([
77
113
  "worktree",
78
114
  "add",
@@ -86,7 +122,12 @@ async function createDeploymentWorktree(repository, options) {
86
122
  timeoutMs: options.timeoutMs,
87
123
  commitIdentity: options.commitIdentity,
88
124
  });
89
- await workspaceRepository.raw(["cat-file", "-e", `${options.sourceSha}^{commit}`]);
125
+ step = "source-verify";
126
+ await workspaceRepository.raw([
127
+ "cat-file",
128
+ "-e",
129
+ `${options.sourceSha}^{commit}`,
130
+ ]);
90
131
  return {
91
132
  path: workspacePath,
92
133
  repository: workspaceRepository,
@@ -97,25 +138,48 @@ async function createDeploymentWorktree(repository, options) {
97
138
  if (marker !== OWNERSHIP_MARKER || stats.isSymbolicLink()) {
98
139
  throw new DeploymentWorktreeError("cleanup-failure");
99
140
  }
100
- await repository.raw(["worktree", "remove", "--force", workspacePath]);
141
+ await repository.raw([
142
+ "worktree",
143
+ "remove",
144
+ "--force",
145
+ workspacePath,
146
+ ]);
101
147
  await rm(operationDirectory, { recursive: true, force: false });
102
148
  }
103
149
  catch (error) {
150
+ reportDeploymentIssue(phase, "cleanup", error);
104
151
  if (error instanceof DeploymentWorktreeError)
105
152
  throw error;
106
- throw new DeploymentWorktreeError("cleanup-failure");
153
+ throw new DeploymentWorktreeError("cleanup-failure", {
154
+ operationError: error,
155
+ });
107
156
  }
108
157
  },
109
158
  };
110
159
  }
111
160
  catch (error) {
161
+ reportDeploymentIssue(phase, step, error);
162
+ let cleanupFailed = false;
112
163
  if (worktreeAdded) {
113
- await repository
114
- .raw(["worktree", "remove", "--force", workspacePath])
115
- .catch(() => undefined);
164
+ try {
165
+ await repository.raw(["worktree", "remove", "--force", workspacePath]);
166
+ }
167
+ catch (cleanupError) {
168
+ cleanupFailed = true;
169
+ reportDeploymentIssue(phase, "cleanup", cleanupError);
170
+ }
116
171
  }
117
- await rm(operationDirectory, { recursive: true, force: true }).catch(() => undefined);
118
- throw error;
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
+ });
119
183
  }
120
184
  }
121
185
  async function mergeSource(workspace, sourceSha, phase) {
@@ -128,11 +192,17 @@ async function mergeSource(workspace, sourceSha, phase) {
128
192
  ]);
129
193
  }
130
194
  catch (error) {
131
- throw mapMergeError(error, phase);
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
+ });
132
202
  }
133
203
  }
134
- async function withDeploymentWorktree(repository, options, operation) {
135
- const workspace = await createDeploymentWorktree(repository, options);
204
+ async function withDeploymentWorktree(repository, options, operation, phase) {
205
+ const workspace = await createDeploymentWorktree(repository, options, phase);
136
206
  let value;
137
207
  try {
138
208
  value = await operation(workspace);
@@ -159,7 +229,7 @@ async function withDeploymentWorktree(repository, options, operation) {
159
229
  export async function preflightDeploymentSource(repository, options) {
160
230
  const result = await withDeploymentWorktree(repository, options, async (workspace) => {
161
231
  await mergeSource(workspace, options.sourceSha, "preflight");
162
- });
232
+ }, "preflight");
163
233
  if (result.cleanupFailed) {
164
234
  throw new DeploymentWorktreeError("cleanup-failure", {
165
235
  cleanupFailed: true,
@@ -172,7 +242,10 @@ export async function publishDeploymentSource(repository, options, prepare) {
172
242
  try {
173
243
  const result = await withDeploymentWorktree(repository, options, async (workspace) => {
174
244
  await mergeSource(workspace, options.sourceSha, "publication");
175
- await prepare({ path: workspace.path, repository: workspace.repository });
245
+ await prepare({
246
+ path: workspace.path,
247
+ repository: workspace.repository,
248
+ });
176
249
  try {
177
250
  await workspace.repository.raw([
178
251
  "push",
@@ -181,9 +254,15 @@ export async function publishDeploymentSource(repository, options, prepare) {
181
254
  ]);
182
255
  }
183
256
  catch (error) {
184
- throw mapMergeError(error, "publication");
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
+ });
185
264
  }
186
- });
265
+ }, "publication");
187
266
  cleanupFailed ||= result.cleanupFailed;
188
267
  return { attempts: attempt, cleanupFailed };
189
268
  }
@@ -1 +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 | \"publication-conflict\"\n | \"publication-rejected\"\n | \"publication-failure\"\n | \"cleanup-failure\";\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): Promise<DeploymentWorktree> {\n assertBranchName(options.remote, \"remote\");\n assertBranchName(options.primaryBranch, \"primary branch\");\n assertSourceSha(options.sourceSha);\n\n await repository.fetch(options.remote, options.primaryBranch);\n const operationDirectory = await mkdtemp(\n join(tmpdir(), \"bonfire-deployment-\"),\n );\n const workspacePath = join(operationDirectory, \"workspace\");\n const markerPath = join(operationDirectory, \"OWNER\");\n\n let worktreeAdded = false;\n try {\n await writeFile(markerPath, OWNERSHIP_MARKER, { flag: \"wx\" });\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 await workspaceRepository.raw([\"cat-file\", \"-e\", `${options.sourceSha}^{commit}`]);\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([\"worktree\", \"remove\", \"--force\", workspacePath]);\n await rm(operationDirectory, { recursive: true, force: false });\n } catch (error) {\n if (error instanceof DeploymentWorktreeError) throw error;\n throw new DeploymentWorktreeError(\"cleanup-failure\");\n }\n },\n };\n } catch (error) {\n if (worktreeAdded) {\n await repository\n .raw([\"worktree\", \"remove\", \"--force\", workspacePath])\n .catch(() => undefined);\n }\n await rm(operationDirectory, { recursive: true, force: true }).catch(\n () => undefined,\n );\n throw error;\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 throw mapMergeError(error, phase);\n }\n}\n\nasync function withDeploymentWorktree<Value>(\n repository: GitRepository,\n options: DeploymentWorktreeOptions,\n operation: DeploymentWorkspaceOperation<Value>,\n): Promise<{ value: Value; cleanupFailed: boolean }> {\n const workspace = await createDeploymentWorktree(repository, options);\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 );\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({ path: workspace.path, repository: workspace.repository });\n try {\n await workspace.repository.raw([\n \"push\",\n options.remote,\n `HEAD:refs/heads/${options.primaryBranch}`,\n ]);\n } catch (error) {\n throw mapMergeError(error, \"publication\");\n }\n },\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
+ {"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 +1 @@
1
- {"version":3,"file":"local-prototype-lifecycle.d.ts","sourceRoot":"","sources":["../../src/lifecycle/local-prototype-lifecycle.ts"],"names":[],"mappings":"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","sourcesContent":["import { constants } from \"node:fs\";\nimport { lstat, open, rename, rm, writeFile } from \"node:fs/promises\";\nimport { randomUUID } from \"node:crypto\";\nimport { join, relative, sep } from \"node:path\";\nimport {\n definePrototypeRef,\n getPrototypeUrl,\n LifecycleInputError,\n lifecycleFailure,\n lifecycleSuccess,\n parseCreatePrototypeInput,\n parseDuplicatePrototypeInput,\n parseEditPrototypeInput,\n parseBonfireIdentity,\n parsePrototypeCommandInput,\n parsePrototypeMetadata,\n slugifyPrototypeTitle,\n PrototypeRefError,\n type BranchPreparationValue,\n type CreatePrototypeInput,\n type CreatePrototypeValue,\n type DeletePrototypeValue,\n type DeploymentValue,\n type DeploymentWarningCode,\n type DirtyObservationValue,\n type DiscardPrototypeValue,\n type DuplicatePrototypeInput,\n type DuplicatePrototypeValue,\n type EditPrototypeInput,\n type EditPrototypeValue,\n parseGitModeSetting,\n type GitMode,\n type LifecycleErrorCode,\n type LifecycleResult,\n type BonfireIdentity,\n type PrototypeCommandInput,\n type SavePrototypeValue,\n type RepositorySummary,\n type ThemeRegistry,\n} from \"@ebay-dt/bonfire-core\";\nimport {\n commitPath,\n discardPath,\n GitRepositoryError,\n pushBranch,\n pushCommitToBranch,\n type GitRepository,\n} from \"../git-repository.js\";\nimport { duplicatePrototype, PrototypeDuplicateError } from \"../duplicate.js\";\nimport {\n PrototypeCatalog,\n type PrototypeCatalogLike,\n} from \"../prototype-catalog.js\";\nimport { PrototypeScaffoldError, scaffoldPrototype } from \"../scaffold.js\";\nimport {\n createPrototypeLifecycleRuntime,\n type PrototypeLifecycleRuntimeContext,\n} from \"./runtime-context.js\";\nimport type {\n PreparePrototypeFiles,\n PreparePrototypeFilesRequest,\n} from \"../prepare-prototype-files.js\";\nimport {\n cleanupOwnedDestination,\n validateOwnedDestination,\n type OwnedDestinationReceipt,\n} from \"../internal/owned-destination.js\";\nimport {\n RepositoryBusyError,\n withRepositoryOperation,\n} from \"./operation-gate.js\";\nimport {\n DeploymentProviderError,\n type DeploymentProvider,\n} from \"../deployment-provider.js\";\nimport {\n assertIdentityBranchReady,\n synchronizeIdentityBranch,\n} from \"./repository-synchronization.js\";\nimport { getRepositorySummary } from \"./repository-summary.js\";\nimport {\n DeploymentWorktreeError,\n preflightDeploymentSource,\n publishDeploymentSource,\n} from \"./deployment-worktree.js\";\n\ntype LifecycleMethod<Input, Value> = (\n identity: BonfireIdentity,\n input: Input,\n) => Promise<LifecycleResult<Value>>;\n\nexport type PrototypeLifecycle = {\n /** Declares whether this lifecycle enforces the selected Git mode. */\n readonly gitMode?: GitMode;\n prepareBranch(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<BranchPreparationValue>>;\n create: LifecycleMethod<CreatePrototypeInput, CreatePrototypeValue>;\n duplicate: LifecycleMethod<DuplicatePrototypeInput, DuplicatePrototypeValue>;\n edit: LifecycleMethod<EditPrototypeInput, EditPrototypeValue>;\n delete: LifecycleMethod<PrototypeCommandInput, DeletePrototypeValue>;\n getDirtyState: LifecycleMethod<PrototypeCommandInput, DirtyObservationValue>;\n getRepositorySummary(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<RepositorySummary>>;\n checkRepositorySummary(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<RepositorySummary>>;\n save: LifecycleMethod<PrototypeCommandInput, SavePrototypeValue>;\n discard: LifecycleMethod<PrototypeCommandInput, DiscardPrototypeValue>;\n deploy: LifecycleMethod<PrototypeCommandInput, DeploymentValue>;\n retryPublish?: LifecycleMethod<PrototypeCommandInput, DeploymentValue>;\n};\n\nexport type LocalPrototypeLifecycleOptions = {\n repositoryRoot: string;\n prototypeRoot?: string;\n remote?: string;\n primaryBranch?: string;\n automaticBranchSwitch?: \"enabled\" | \"disabled\";\n gitMode?: GitMode;\n commitIdentity?: {\n name: string;\n email: string;\n };\n catalog?: PrototypeCatalogLike;\n preparePrototypeFiles?: PreparePrototypeFiles;\n deploymentProvider?: DeploymentProvider;\n applicationId?: string;\n themeRegistry?: ThemeRegistry;\n};\n\ntype InternalLocalPrototypeLifecycleOptions = LocalPrototypeLifecycleOptions & {\n runtime?: PrototypeLifecycleRuntimeContext;\n};\n\nfunction repositoryRelativePath(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n) {\n const absolutePath = join(runtime.prototypeRoot, ref.login, ref.slug);\n return relative(runtime.repositoryRoot, absolutePath).split(sep).join(\"/\");\n}\n\nfunction destinationRef(identity: BonfireIdentity, title: string) {\n const slug = slugifyPrototypeTitle(title);\n if (slug === null)\n throw new PrototypeRefError(\"title\", \"must produce a slug\");\n return definePrototypeRef({ login: identity.login, slug });\n}\n\nclass PrototypeNotFoundError extends Error {}\nclass PrototypeOwnershipError extends Error {}\nclass PrototypeMetadataFileError extends Error {}\nclass DeploymentUnavailableError extends Error {}\nclass DeploymentSourcePushError extends Error {}\nclass DeploymentNotAcceptedError extends Error {}\n\ntype PendingPublication = {\n identity: BonfireIdentity;\n input: PrototypeCommandInput;\n ref: { login: string; slug: string };\n path: string;\n previewUrl: string;\n deploymentId: string;\n sourceSha: string;\n readyState: DeploymentValue[\"readyState\"];\n synchronizationWarning: DeploymentWarningCode | undefined;\n};\n\nfunction metadataPath(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n) {\n return join(runtime.prototypeRoot, ref.login, ref.slug, \"metadata.json\");\n}\n\nasync function assertPrototypeDirectory(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n) {\n const paths = [\n runtime.prototypeRoot,\n join(runtime.prototypeRoot, ref.login),\n join(runtime.prototypeRoot, ref.login, ref.slug),\n ];\n\n try {\n for (const path of paths) {\n const stats = await lstat(path);\n if (stats.isSymbolicLink() || !stats.isDirectory()) {\n throw new PrototypeMetadataFileError();\n }\n }\n } catch (error) {\n if (error instanceof PrototypeMetadataFileError) throw error;\n throw new PrototypeMetadataFileError();\n }\n}\n\nasync function readMetadataDocument(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n themeRegistry?: ThemeRegistry,\n) {\n const path = metadataPath(runtime, ref);\n try {\n const file = await open(\n path,\n constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0),\n );\n try {\n const source = await file.readFile(\"utf8\");\n const document: unknown = JSON.parse(source);\n parsePrototypeMetadata(document, { themeRegistry });\n if (\n typeof document !== \"object\" ||\n document === null ||\n Array.isArray(document)\n ) {\n throw new PrototypeMetadataFileError();\n }\n return { ...(document as Record<string, unknown>) };\n } finally {\n await file.close();\n }\n } catch (error) {\n if (error instanceof PrototypeMetadataFileError) throw error;\n throw new PrototypeMetadataFileError();\n }\n}\n\nasync function removePrototypeDirectory(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n) {\n await assertPrototypeDirectory(runtime, ref);\n try {\n await rm(join(runtime.prototypeRoot, ref.login, ref.slug), {\n force: false,\n recursive: true,\n });\n } catch {\n throw new PrototypeMetadataFileError();\n }\n}\n\nasync function writeMetadataDocument(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n document: Record<string, unknown>,\n) {\n const path = metadataPath(runtime, ref);\n const temporaryPath = `${path}.${randomUUID()}.tmp`;\n\n try {\n const stats = await lstat(path);\n if (stats.isSymbolicLink() || !stats.isFile()) {\n throw new PrototypeMetadataFileError();\n }\n await writeFile(temporaryPath, `${JSON.stringify(document, null, 2)}\\n`, {\n encoding: \"utf8\",\n flag: \"wx\",\n });\n await rename(temporaryPath, path);\n } catch (error) {\n if (error instanceof PrototypeMetadataFileError) throw error;\n throw new PrototypeMetadataFileError();\n } finally {\n await rm(temporaryPath, { force: true }).catch(() => undefined);\n }\n}\n\nfunction mapError(error: unknown): LifecycleErrorCode {\n if (error instanceof PrototypeNotFoundError) return \"not-found\";\n if (error instanceof PrototypeOwnershipError) return \"forbidden\";\n if (error instanceof PrototypeMetadataFileError) return \"filesystem-failure\";\n if (error instanceof RepositoryBusyError) return \"repository-busy\";\n if (\n error instanceof PrototypeRefError ||\n error instanceof LifecycleInputError\n ) {\n return \"invalid-input\";\n }\n if (error instanceof GitRepositoryError) {\n if (error.kind === \"timeout\") return \"timeout\";\n if (error.kind === \"conflict\") return \"working-tree-conflict\";\n if (error.kind === \"invalid-configuration\") return \"unexpected-failure\";\n return \"git-failure\";\n }\n if (error instanceof PrototypeScaffoldError) {\n if (error.kind === \"collision\") return \"already-exists\";\n if (error.kind === \"invalid-input\") return \"invalid-input\";\n return \"filesystem-failure\";\n }\n if (error instanceof PrototypeDuplicateError) {\n if (error.kind === \"source-not-found\") return \"not-found\";\n if (error.kind === \"collision\") return \"already-exists\";\n if (error.kind === \"invalid-source\") return \"invalid-input\";\n return \"filesystem-failure\";\n }\n if (error instanceof DeploymentUnavailableError) {\n return \"deployment-unavailable\";\n }\n if (\n error instanceof DeploymentSourcePushError ||\n error instanceof DeploymentNotAcceptedError ||\n error instanceof DeploymentWorktreeError\n ) {\n return \"deployment-failure\";\n }\n if (error instanceof DeploymentProviderError) {\n return error.kind === \"timeout\" ? \"timeout\" : \"deployment-failure\";\n }\n return \"unexpected-failure\";\n}\n\nfunction resultFrom<Value>(\n operation: () => Promise<Value>,\n): Promise<LifecycleResult<Value>> {\n return operation()\n .then(lifecycleSuccess)\n .catch((error: unknown) => lifecycleFailure(mapError(error)));\n}\n\nexport class LocalPrototypeLifecycle implements PrototypeLifecycle {\n readonly #runtime: PrototypeLifecycleRuntimeContext;\n readonly gitMode: GitMode;\n readonly #catalog: PrototypeCatalogLike;\n readonly #repository: GitRepository;\n readonly #preparePrototypeFiles: PreparePrototypeFiles;\n readonly #deploymentProvider?: DeploymentProvider;\n readonly #applicationId?: string;\n readonly #themeRegistry?: ThemeRegistry;\n #restartRequired = false;\n #pendingPublication?: PendingPublication;\n\n constructor(options: LocalPrototypeLifecycleOptions) {\n const internalOptions = options as InternalLocalPrototypeLifecycleOptions;\n const runtime =\n internalOptions.runtime ?? createPrototypeLifecycleRuntime(internalOptions);\n this.#runtime = {\n ...runtime,\n gitMode: parseGitModeSetting(runtime.gitMode),\n };\n this.gitMode = this.#runtime.gitMode;\n this.#themeRegistry = options.themeRegistry;\n this.#catalog =\n internalOptions.catalog ??\n new PrototypeCatalog({\n ...this.#runtime,\n themeRegistry: this.#themeRegistry,\n });\n this.#repository = this.#runtime.gitRepository;\n this.#preparePrototypeFiles =\n internalOptions.preparePrototypeFiles ?? (() => undefined);\n this.#deploymentProvider = internalOptions.deploymentProvider;\n this.#applicationId = internalOptions.applicationId;\n if (\n this.#deploymentProvider &&\n (typeof this.#applicationId !== \"string\" ||\n this.#applicationId.length === 0)\n ) {\n throw new TypeError(\n \"applicationId must be provided when deploymentProvider is configured\",\n );\n }\n }\n\n async #verifiedIdentity(identity: BonfireIdentity) {\n return parseBonfireIdentity(identity);\n }\n\n async #currentBranch() {\n const branch = await this.#repository.currentBranch();\n if (branch === \"HEAD\" || branch.length === 0) {\n throw new GitRepositoryError(\"conflict\", \"current-branch\");\n }\n return branch;\n }\n\n async #ownedPrototype(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ) {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parsePrototypeCommandInput(input);\n if (normalizedInput.ref.login !== verifiedIdentity.login) {\n throw new PrototypeOwnershipError();\n }\n const summary = await this.#catalog.getSummary(normalizedInput.ref);\n if (!summary) {\n throw new PrototypeNotFoundError();\n }\n\n return {\n ref: normalizedInput.ref,\n path: repositoryRelativePath(this.#runtime, normalizedInput.ref),\n summary,\n };\n }\n\n async #prepareMutationBranch(login: string) {\n await assertIdentityBranchReady(this.#repository, login);\n return login;\n }\n\n async #prepareDestination(\n receipt: OwnedDestinationReceipt,\n expectedFiles: readonly string[],\n request: PreparePrototypeFilesRequest,\n ) {\n try {\n const frozenRequest = Object.freeze({\n ...request,\n ref: Object.freeze({ ...request.ref }),\n identity: Object.freeze({ ...request.identity }),\n ...(request.operation === \"duplicate\"\n ? { source: Object.freeze({ ...request.source }) }\n : {}),\n }) as PreparePrototypeFilesRequest;\n await this.#preparePrototypeFiles(frozenRequest);\n await validateOwnedDestination(\n receipt,\n expectedFiles,\n request.ref,\n this.#themeRegistry,\n );\n } catch {\n try {\n await cleanupOwnedDestination(receipt);\n } catch {\n // The failure remains a finite filesystem failure without retrying cleanup.\n }\n throw new PrototypeScaffoldError(\"filesystem\");\n }\n }\n\n async prepareBranch(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<BranchPreparationValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n if (\n this.#runtime.gitMode === \"manual\" ||\n this.#runtime.automaticBranchSwitch === \"disabled\"\n ) {\n await this.#currentBranch();\n return { status: \"disabled\" } satisfies BranchPreparationValue;\n }\n\n const synchronization = await synchronizeIdentityBranch(\n this.#repository,\n {\n identityBranch: verifiedIdentity.login,\n primaryBranch: this.#runtime.primaryBranch,\n remote: this.#runtime.remote,\n managedPaths: this.#runtime.managedRepositoryPaths,\n },\n );\n if (synchronization.status !== \"synchronized\") {\n return {\n status: synchronization.status,\n reason: synchronization.reason,\n ...(synchronization.affectedPaths.length > 0\n ? { affectedPaths: synchronization.affectedPaths }\n : {}),\n } satisfies BranchPreparationValue;\n }\n if (\n synchronization.affectedPaths.some((path) =>\n this.#runtime.managedRepositoryPaths.includes(path),\n )\n ) {\n this.#restartRequired = true;\n }\n return {\n status: \"ready\",\n ...(synchronization.affectedPaths.length > 0\n ? { affectedPaths: synchronization.affectedPaths }\n : {}),\n } satisfies BranchPreparationValue;\n },\n );\n });\n }\n\n async create(\n identity: BonfireIdentity,\n input: CreatePrototypeInput,\n ): Promise<LifecycleResult<CreatePrototypeValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parseCreatePrototypeInput(\n input,\n this.#themeRegistry,\n );\n const ref = destinationRef(verifiedIdentity, normalizedInput.title);\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n if (await this.#catalog.getSummary(ref)) {\n throw new PrototypeScaffoldError(\"collision\");\n }\n\n const branch =\n this.#runtime.gitMode === \"managed\"\n ? await this.#prepareMutationBranch(verifiedIdentity.login)\n : undefined;\n const prepared = await scaffoldPrototype({\n paths: this.#runtime,\n ref,\n title: normalizedInput.title,\n description: normalizedInput.description,\n authorName: verifiedIdentity.name,\n theme: normalizedInput.theme,\n themeRegistry: this.#themeRegistry,\n });\n await this.#prepareDestination(\n prepared.receipt,\n prepared.expectedFiles,\n {\n operation: \"create\",\n repositoryRoot: this.#runtime.repositoryRoot,\n prototypeRoot: this.#runtime.prototypeRoot,\n prototypePath: prepared.destination,\n ref,\n identity: verifiedIdentity,\n title: normalizedInput.title,\n description: normalizedInput.description,\n },\n );\n if (branch === undefined) {\n return {\n ref,\n url: getPrototypeUrl(ref),\n remoteStatus: \"manual\",\n } satisfies CreatePrototypeValue;\n }\n\n await commitPath(\n this.#repository,\n repositoryRelativePath(this.#runtime, ref),\n `init: ${ref.slug}`,\n );\n const push = await pushBranch(\n this.#repository,\n this.#runtime.remote,\n branch,\n );\n\n return {\n ref,\n url: getPrototypeUrl(ref),\n remoteStatus: push.remoteStatus,\n } satisfies CreatePrototypeValue;\n },\n );\n });\n }\n\n async edit(\n identity: BonfireIdentity,\n input: EditPrototypeInput,\n ): Promise<LifecycleResult<EditPrototypeValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parseEditPrototypeInput(\n input,\n this.#themeRegistry,\n );\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n const { ref, path } = await this.#ownedPrototype(verifiedIdentity, {\n ref: normalizedInput.ref,\n });\n const branch =\n this.#runtime.gitMode === \"managed\"\n ? await this.#prepareMutationBranch(verifiedIdentity.login)\n : undefined;\n await assertPrototypeDirectory(this.#runtime, ref);\n const document = await readMetadataDocument(\n this.#runtime,\n ref,\n this.#themeRegistry,\n );\n const metadata = parsePrototypeMetadata(document, {\n themeRegistry: this.#themeRegistry,\n });\n if (metadata.authorLogin !== ref.login) {\n throw new PrototypeNotFoundError();\n }\n\n await writeMetadataDocument(this.#runtime, ref, {\n ...document,\n title: normalizedInput.title,\n description: normalizedInput.description,\n updatedAt: new Date().toISOString(),\n ...(normalizedInput.theme === undefined\n ? {}\n : { theme: normalizedInput.theme }),\n });\n if (branch !== undefined) {\n await commitPath(this.#repository, path, `edit: ${ref.slug}`);\n await pushBranch(this.#repository, this.#runtime.remote, branch);\n }\n\n return { edited: true, ref } satisfies EditPrototypeValue;\n },\n );\n });\n }\n\n async delete(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DeletePrototypeValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parsePrototypeCommandInput(input);\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n const { ref, path } = await this.#ownedPrototype(\n verifiedIdentity,\n normalizedInput,\n );\n const branch =\n this.#runtime.gitMode === \"managed\"\n ? await this.#prepareMutationBranch(verifiedIdentity.login)\n : undefined;\n await removePrototypeDirectory(this.#runtime, ref);\n if (branch !== undefined) {\n await commitPath(this.#repository, path, `delete: ${ref.slug}`);\n await pushBranch(this.#repository, this.#runtime.remote, branch);\n }\n\n return { deleted: true, ref } satisfies DeletePrototypeValue;\n },\n );\n });\n }\n\n async getDirtyState(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DirtyObservationValue>> {\n return resultFrom(async () => {\n const { path, summary } = await this.#ownedPrototype(identity, input);\n const status = await this.#repository.status([path]);\n return {\n status: status.files.length > 0 ? \"dirty\" : \"clean\",\n ...(summary.previewUrl !== undefined\n ? { previewUrl: summary.previewUrl }\n : {}),\n } satisfies DirtyObservationValue;\n });\n }\n\n async getRepositorySummary(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<RepositorySummary>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n return getRepositorySummary({\n repository: this.#repository,\n catalog: this.#catalog,\n repositoryRoot: this.#runtime.repositoryRoot,\n prototypeRoot: this.#runtime.prototypeRoot,\n identity: verifiedIdentity,\n mode: this.#runtime.gitMode,\n remote: this.#runtime.remote,\n primaryBranch: this.#runtime.primaryBranch,\n restartRequired: this.#restartRequired,\n });\n });\n }\n\n async checkRepositorySummary(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<RepositorySummary>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n return getRepositorySummary({\n repository: this.#repository,\n catalog: this.#catalog,\n repositoryRoot: this.#runtime.repositoryRoot,\n prototypeRoot: this.#runtime.prototypeRoot,\n identity: verifiedIdentity,\n mode: this.#runtime.gitMode,\n remote: this.#runtime.remote,\n primaryBranch: this.#runtime.primaryBranch,\n restartRequired: this.#restartRequired,\n refresh: true,\n });\n });\n }\n\n async #headCommit() {\n const commit = (await this.#repository.raw([\"rev-parse\", \"HEAD\"])).trim();\n if (!/^[0-9a-f]{40,64}$/iu.test(commit)) {\n throw new GitRepositoryError(\"malformed-output\", \"head-commit\");\n }\n return commit.toLowerCase();\n }\n\n async #prepareDeploymentBranch(login: string) {\n if (this.#runtime.automaticBranchSwitch === \"disabled\") {\n await assertIdentityBranchReady(this.#repository, login);\n return \"updates-not-synchronized\" as const;\n }\n\n const synchronization = await synchronizeIdentityBranch(this.#repository, {\n identityBranch: login,\n primaryBranch: this.#runtime.primaryBranch,\n remote: this.#runtime.remote,\n managedPaths: this.#runtime.managedRepositoryPaths,\n });\n if (synchronization.status === \"unsafe\") {\n throw new GitRepositoryError(\"conflict\", \"deployment-preparation\");\n }\n await assertIdentityBranchReady(this.#repository, login);\n return synchronization.status === \"deferred\"\n ? (\"updates-not-synchronized\" as const)\n : undefined;\n }\n\n #workspaceRuntime(\n workspacePath: string,\n workspaceRepository: GitRepository,\n ): PrototypeLifecycleRuntimeContext {\n return {\n ...this.#runtime,\n repositoryRoot: workspacePath,\n repositoryRealPath: workspacePath,\n prototypeRoot: join(\n workspacePath,\n relative(this.#runtime.repositoryRoot, this.#runtime.prototypeRoot),\n ),\n gitRepository: workspaceRepository,\n };\n }\n\n async #publishDeploymentMetadata(pending: PendingPublication) {\n return publishDeploymentSource(\n this.#repository,\n {\n remote: this.#runtime.remote,\n primaryBranch: this.#runtime.primaryBranch,\n sourceSha: pending.sourceSha,\n commitIdentity: this.#runtime.commitIdentity,\n },\n async ({ path, repository }) => {\n const workspaceRuntime = this.#workspaceRuntime(path, repository);\n const mergedDocument = await readMetadataDocument(\n workspaceRuntime,\n pending.ref,\n this.#themeRegistry,\n );\n await writeMetadataDocument(workspaceRuntime, pending.ref, {\n ...mergedDocument,\n previewUrl: pending.previewUrl,\n updatedAt: new Date().toISOString(),\n });\n await commitPath(\n repository,\n pending.path,\n `deploy: ${pending.ref.slug} (metadata)`,\n );\n },\n );\n }\n\n async #deploymentValue(\n pending: PendingPublication,\n publicationWarnings: readonly DeploymentWarningCode[] = [],\n ): Promise<DeploymentValue> {\n const warnings = [\n ...(pending.synchronizationWarning === undefined\n ? []\n : [pending.synchronizationWarning]),\n ...publicationWarnings,\n ];\n return {\n deployed: true,\n ref: pending.ref,\n previewUrl: pending.previewUrl,\n deploymentId: pending.deploymentId,\n sourceSha: pending.sourceSha,\n readyState: pending.readyState,\n warning: publicationWarnings[0] ?? pending.synchronizationWarning,\n ...(warnings.length === 0 ? {} : { warnings }),\n };\n }\n\n async deploy(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DeploymentValue>> {\n if (this.#runtime.gitMode === \"manual\") {\n return lifecycleFailure(\"forbidden\");\n }\n\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parsePrototypeCommandInput(input);\n if (!this.#deploymentProvider) {\n throw new DeploymentUnavailableError();\n }\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n const { ref, path } = await this.#ownedPrototype(\n verifiedIdentity,\n normalizedInput,\n );\n const synchronizationWarning = await this.#prepareDeploymentBranch(\n verifiedIdentity.login,\n );\n await assertPrototypeDirectory(this.#runtime, ref);\n const document = await readMetadataDocument(\n this.#runtime,\n ref,\n this.#themeRegistry,\n );\n const metadata = parsePrototypeMetadata(document, {\n themeRegistry: this.#themeRegistry,\n });\n if (metadata.authorLogin !== ref.login) {\n throw new PrototypeNotFoundError();\n }\n\n await commitPath(this.#repository, path, `deploy: ${ref.slug}`);\n const sourceSha = await this.#headCommit();\n const sourcePush = await pushCommitToBranch(\n this.#repository,\n this.#runtime.remote,\n sourceSha,\n verifiedIdentity.login,\n );\n if (sourcePush.remoteStatus !== \"pushed\") {\n throw new DeploymentSourcePushError();\n }\n\n await preflightDeploymentSource(this.#repository, {\n remote: this.#runtime.remote,\n primaryBranch: this.#runtime.primaryBranch,\n sourceSha,\n commitIdentity: this.#runtime.commitIdentity,\n });\n\n const deployment = await this.#deploymentProvider!.deploy({\n applicationId: this.#applicationId!,\n login: verifiedIdentity.login,\n });\n if (\n deployment.commitSha.toLowerCase() !== sourceSha ||\n ![\"READY\", \"QUEUED\", \"INITIALIZING\", \"BUILDING\"].includes(\n deployment.readyState,\n )\n ) {\n throw new DeploymentNotAcceptedError();\n }\n const previewUrl = new URL(\n getPrototypeUrl(ref).slice(1),\n `${deployment.url.replace(/\\/+$/, \"\")}/`,\n ).toString();\n const pending: PendingPublication = {\n identity: verifiedIdentity,\n input: normalizedInput,\n ref,\n path,\n previewUrl,\n deploymentId: deployment.deploymentId,\n sourceSha,\n readyState: deployment.readyState,\n synchronizationWarning,\n };\n\n try {\n const publication = await this.#publishDeploymentMetadata(pending);\n this.#pendingPublication = undefined;\n return this.#deploymentValue(\n pending,\n publication.cleanupFailed\n ? [\"publication-cleanup-failed\"]\n : [],\n );\n } catch (error) {\n // Keep the accepted deployment retryable for every publication\n // failure, including metadata preparation failures.\n this.#pendingPublication = pending;\n return this.#deploymentValue(pending, [\n error instanceof DeploymentWorktreeError &&\n error.kind !== \"cleanup-failure\"\n ? \"primary-merge-failed\"\n : \"metadata-persistence-failed\",\n ...(error instanceof DeploymentWorktreeError && error.cleanupFailed\n ? [\"publication-cleanup-failed\" as const]\n : []),\n ]);\n }\n },\n );\n });\n }\n\n async retryPublish(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DeploymentValue>> {\n if (this.#runtime.gitMode === \"manual\") {\n return lifecycleFailure(\"forbidden\");\n }\n\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parsePrototypeCommandInput(input);\n const pending = this.#pendingPublication;\n if (\n pending === undefined ||\n pending.identity.login !== verifiedIdentity.login ||\n pending.ref.login !== normalizedInput.ref.login ||\n pending.ref.slug !== normalizedInput.ref.slug\n ) {\n throw new DeploymentUnavailableError();\n }\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n await assertIdentityBranchReady(this.#repository, verifiedIdentity.login);\n const publication = await this.#publishDeploymentMetadata(pending);\n this.#pendingPublication = undefined;\n return this.#deploymentValue(\n pending,\n publication.cleanupFailed ? [\"publication-cleanup-failed\"] : [],\n );\n },\n );\n });\n }\n\n async save(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<SavePrototypeValue>> {\n if (this.#runtime.gitMode === \"manual\") {\n return lifecycleFailure(\"forbidden\");\n }\n\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const { ref, path } = await this.#ownedPrototype(\n verifiedIdentity,\n input,\n );\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n await this.#prepareMutationBranch(verifiedIdentity.login);\n await commitPath(this.#repository, path, `save: ${ref.slug}`);\n await pushBranch(\n this.#repository,\n this.#runtime.remote,\n verifiedIdentity.login,\n );\n return { saved: true } satisfies SavePrototypeValue;\n },\n );\n });\n }\n\n async discard(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DiscardPrototypeValue>> {\n if (this.#runtime.gitMode === \"manual\") {\n return lifecycleFailure(\"forbidden\");\n }\n\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const { path } = await this.#ownedPrototype(\n verifiedIdentity,\n input,\n );\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n await discardPath(this.#repository, path);\n return { discarded: true } satisfies DiscardPrototypeValue;\n },\n );\n });\n }\n\n async duplicate(\n identity: BonfireIdentity,\n input: DuplicatePrototypeInput,\n ): Promise<LifecycleResult<DuplicatePrototypeValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parseDuplicatePrototypeInput(input);\n if (!(await this.#catalog.getSummary(normalizedInput.source))) {\n throw new PrototypeDuplicateError(\"source-not-found\");\n }\n const ref = destinationRef(verifiedIdentity, normalizedInput.title);\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n if (await this.#catalog.getSummary(ref)) {\n throw new PrototypeDuplicateError(\"collision\");\n }\n\n const branch =\n this.#runtime.gitMode === \"managed\"\n ? await this.#prepareMutationBranch(verifiedIdentity.login)\n : undefined;\n const prepared = await duplicatePrototype({\n paths: this.#runtime,\n source: normalizedInput.source,\n destination: ref,\n title: normalizedInput.title,\n description: normalizedInput.description,\n authorName: verifiedIdentity.name,\n themeRegistry: this.#themeRegistry,\n });\n await this.#prepareDestination(\n prepared.receipt,\n prepared.expectedFiles,\n {\n operation: \"duplicate\",\n repositoryRoot: this.#runtime.repositoryRoot,\n prototypeRoot: this.#runtime.prototypeRoot,\n prototypePath: prepared.destination,\n ref,\n source: normalizedInput.source,\n identity: verifiedIdentity,\n title: normalizedInput.title,\n description: normalizedInput.description,\n },\n );\n if (branch === undefined) {\n return {\n ref,\n url: getPrototypeUrl(ref),\n remoteStatus: \"manual\",\n } satisfies DuplicatePrototypeValue;\n }\n\n await commitPath(\n this.#repository,\n repositoryRelativePath(this.#runtime, ref),\n `init: ${ref.slug}`,\n );\n const push = await pushBranch(\n this.#repository,\n this.#runtime.remote,\n branch,\n );\n\n return {\n ref,\n url: getPrototypeUrl(ref),\n remoteStatus: push.remoteStatus,\n } satisfies DuplicatePrototypeValue;\n },\n );\n });\n }\n}\n"]}
1
+ {"version":3,"file":"local-prototype-lifecycle.d.ts","sourceRoot":"","sources":["../../src/lifecycle/local-prototype-lifecycle.ts"],"names":[],"mappings":"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","sourcesContent":["import { constants } from \"node:fs\";\nimport { lstat, open, rename, rm, writeFile } from \"node:fs/promises\";\nimport { randomUUID } from \"node:crypto\";\nimport { join, relative, sep } from \"node:path\";\nimport {\n definePrototypeRef,\n getPrototypeUrl,\n LifecycleInputError,\n lifecycleFailure,\n lifecycleSuccess,\n parseCreatePrototypeInput,\n parseDuplicatePrototypeInput,\n parseEditPrototypeInput,\n parseBonfireIdentity,\n parsePrototypeCommandInput,\n parsePrototypeMetadata,\n slugifyPrototypeTitle,\n PrototypeRefError,\n type BranchPreparationValue,\n type CreatePrototypeInput,\n type CreatePrototypeValue,\n type DeletePrototypeValue,\n type DeploymentValue,\n type DeploymentWarningCode,\n type DirtyObservationValue,\n type DiscardPrototypeValue,\n type DuplicatePrototypeInput,\n type DuplicatePrototypeValue,\n type EditPrototypeInput,\n type EditPrototypeValue,\n parseGitModeSetting,\n type GitMode,\n type LifecycleErrorCode,\n type LifecycleResult,\n type BonfireIdentity,\n type PrototypeCommandInput,\n type SavePrototypeValue,\n type RepositorySummary,\n type ThemeRegistry,\n} from \"@ebay-dt/bonfire-core\";\nimport {\n commitPath,\n discardPath,\n GitRepositoryError,\n pushBranch,\n pushCommitToBranch,\n type GitRepository,\n} from \"../git-repository.js\";\nimport { duplicatePrototype, PrototypeDuplicateError } from \"../duplicate.js\";\nimport {\n PrototypeCatalog,\n type PrototypeCatalogLike,\n} from \"../prototype-catalog.js\";\nimport { PrototypeScaffoldError, scaffoldPrototype } from \"../scaffold.js\";\nimport {\n createPrototypeLifecycleRuntime,\n type PrototypeLifecycleRuntimeContext,\n} from \"./runtime-context.js\";\nimport type {\n PreparePrototypeFiles,\n PreparePrototypeFilesRequest,\n} from \"../prepare-prototype-files.js\";\nimport {\n cleanupOwnedDestination,\n validateOwnedDestination,\n type OwnedDestinationReceipt,\n} from \"../internal/owned-destination.js\";\nimport {\n RepositoryBusyError,\n withRepositoryOperation,\n} from \"./operation-gate.js\";\nimport {\n DeploymentProviderError,\n type DeploymentProvider,\n} from \"../deployment-provider.js\";\nimport {\n assertIdentityBranchReady,\n synchronizeIdentityBranch,\n} from \"./repository-synchronization.js\";\nimport { getRepositorySummary } from \"./repository-summary.js\";\nimport {\n DeploymentWorktreeError,\n preflightDeploymentSource,\n publishDeploymentSource,\n reportDeploymentIssue,\n} from \"./deployment-worktree.js\";\n\ntype LifecycleMethod<Input, Value> = (\n identity: BonfireIdentity,\n input: Input,\n) => Promise<LifecycleResult<Value>>;\n\nexport type PrototypeLifecycle = {\n /** Declares whether this lifecycle enforces the selected Git mode. */\n readonly gitMode?: GitMode;\n prepareBranch(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<BranchPreparationValue>>;\n create: LifecycleMethod<CreatePrototypeInput, CreatePrototypeValue>;\n duplicate: LifecycleMethod<DuplicatePrototypeInput, DuplicatePrototypeValue>;\n edit: LifecycleMethod<EditPrototypeInput, EditPrototypeValue>;\n delete: LifecycleMethod<PrototypeCommandInput, DeletePrototypeValue>;\n getDirtyState: LifecycleMethod<PrototypeCommandInput, DirtyObservationValue>;\n getRepositorySummary(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<RepositorySummary>>;\n checkRepositorySummary(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<RepositorySummary>>;\n save: LifecycleMethod<PrototypeCommandInput, SavePrototypeValue>;\n discard: LifecycleMethod<PrototypeCommandInput, DiscardPrototypeValue>;\n deploy: LifecycleMethod<PrototypeCommandInput, DeploymentValue>;\n retryPublish?: LifecycleMethod<PrototypeCommandInput, DeploymentValue>;\n};\n\nexport type LocalPrototypeLifecycleOptions = {\n repositoryRoot: string;\n prototypeRoot?: string;\n remote?: string;\n primaryBranch?: string;\n automaticBranchSwitch?: \"enabled\" | \"disabled\";\n gitMode?: GitMode;\n commitIdentity?: {\n name: string;\n email: string;\n };\n catalog?: PrototypeCatalogLike;\n preparePrototypeFiles?: PreparePrototypeFiles;\n deploymentProvider?: DeploymentProvider;\n applicationId?: string;\n themeRegistry?: ThemeRegistry;\n};\n\ntype InternalLocalPrototypeLifecycleOptions = LocalPrototypeLifecycleOptions & {\n runtime?: PrototypeLifecycleRuntimeContext;\n};\n\nfunction repositoryRelativePath(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n) {\n const absolutePath = join(runtime.prototypeRoot, ref.login, ref.slug);\n return relative(runtime.repositoryRoot, absolutePath).split(sep).join(\"/\");\n}\n\nfunction destinationRef(identity: BonfireIdentity, title: string) {\n const slug = slugifyPrototypeTitle(title);\n if (slug === null)\n throw new PrototypeRefError(\"title\", \"must produce a slug\");\n return definePrototypeRef({ login: identity.login, slug });\n}\n\nclass PrototypeNotFoundError extends Error {}\nclass PrototypeOwnershipError extends Error {}\nclass PrototypeMetadataFileError extends Error {}\nclass DeploymentUnavailableError extends Error {}\nclass DeploymentSourcePushError extends Error {}\nclass DeploymentNotAcceptedError extends Error {}\n\ntype PendingPublication = {\n identity: BonfireIdentity;\n input: PrototypeCommandInput;\n ref: { login: string; slug: string };\n path: string;\n previewUrl: string;\n deploymentId: string;\n sourceSha: string;\n readyState: DeploymentValue[\"readyState\"];\n synchronizationWarning: DeploymentWarningCode | undefined;\n};\n\nfunction metadataPath(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n) {\n return join(runtime.prototypeRoot, ref.login, ref.slug, \"metadata.json\");\n}\n\nasync function assertPrototypeDirectory(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n) {\n const paths = [\n runtime.prototypeRoot,\n join(runtime.prototypeRoot, ref.login),\n join(runtime.prototypeRoot, ref.login, ref.slug),\n ];\n\n try {\n for (const path of paths) {\n const stats = await lstat(path);\n if (stats.isSymbolicLink() || !stats.isDirectory()) {\n throw new PrototypeMetadataFileError();\n }\n }\n } catch (error) {\n if (error instanceof PrototypeMetadataFileError) throw error;\n throw new PrototypeMetadataFileError();\n }\n}\n\nasync function readMetadataDocument(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n themeRegistry?: ThemeRegistry,\n) {\n const path = metadataPath(runtime, ref);\n try {\n const file = await open(\n path,\n constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0),\n );\n try {\n const source = await file.readFile(\"utf8\");\n const document: unknown = JSON.parse(source);\n parsePrototypeMetadata(document, { themeRegistry });\n if (\n typeof document !== \"object\" ||\n document === null ||\n Array.isArray(document)\n ) {\n throw new PrototypeMetadataFileError();\n }\n return { ...(document as Record<string, unknown>) };\n } finally {\n await file.close();\n }\n } catch (error) {\n if (error instanceof PrototypeMetadataFileError) throw error;\n throw new PrototypeMetadataFileError();\n }\n}\n\nasync function removePrototypeDirectory(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n) {\n await assertPrototypeDirectory(runtime, ref);\n try {\n await rm(join(runtime.prototypeRoot, ref.login, ref.slug), {\n force: false,\n recursive: true,\n });\n } catch {\n throw new PrototypeMetadataFileError();\n }\n}\n\nasync function writeMetadataDocument(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n document: Record<string, unknown>,\n) {\n const path = metadataPath(runtime, ref);\n const temporaryPath = `${path}.${randomUUID()}.tmp`;\n\n try {\n const stats = await lstat(path);\n if (stats.isSymbolicLink() || !stats.isFile()) {\n throw new PrototypeMetadataFileError();\n }\n await writeFile(temporaryPath, `${JSON.stringify(document, null, 2)}\\n`, {\n encoding: \"utf8\",\n flag: \"wx\",\n });\n await rename(temporaryPath, path);\n } catch (error) {\n if (error instanceof PrototypeMetadataFileError) throw error;\n throw new PrototypeMetadataFileError();\n } finally {\n await rm(temporaryPath, { force: true }).catch(() => undefined);\n }\n}\n\nfunction mapError(error: unknown): LifecycleErrorCode {\n if (error instanceof PrototypeNotFoundError) return \"not-found\";\n if (error instanceof PrototypeOwnershipError) return \"forbidden\";\n if (error instanceof PrototypeMetadataFileError) return \"filesystem-failure\";\n if (error instanceof RepositoryBusyError) return \"repository-busy\";\n if (\n error instanceof PrototypeRefError ||\n error instanceof LifecycleInputError\n ) {\n return \"invalid-input\";\n }\n if (error instanceof GitRepositoryError) {\n if (error.kind === \"timeout\") return \"timeout\";\n if (error.kind === \"conflict\") return \"working-tree-conflict\";\n if (error.kind === \"invalid-configuration\") return \"unexpected-failure\";\n return \"git-failure\";\n }\n if (error instanceof PrototypeScaffoldError) {\n if (error.kind === \"collision\") return \"already-exists\";\n if (error.kind === \"invalid-input\") return \"invalid-input\";\n return \"filesystem-failure\";\n }\n if (error instanceof PrototypeDuplicateError) {\n if (error.kind === \"source-not-found\") return \"not-found\";\n if (error.kind === \"collision\") return \"already-exists\";\n if (error.kind === \"invalid-source\") return \"invalid-input\";\n return \"filesystem-failure\";\n }\n if (error instanceof DeploymentUnavailableError) {\n return \"deployment-unavailable\";\n }\n if (\n error instanceof DeploymentSourcePushError ||\n error instanceof DeploymentNotAcceptedError ||\n error instanceof DeploymentWorktreeError\n ) {\n return \"deployment-failure\";\n }\n if (error instanceof DeploymentProviderError) {\n return error.kind === \"timeout\" ? \"timeout\" : \"deployment-failure\";\n }\n return \"unexpected-failure\";\n}\n\nfunction resultFrom<Value>(\n operation: () => Promise<Value>,\n): Promise<LifecycleResult<Value>> {\n return operation()\n .then(lifecycleSuccess)\n .catch((error: unknown) => lifecycleFailure(mapError(error)));\n}\n\nasync function diagnoseMetadataStep<Value>(\n step: \"metadata-read\" | \"metadata-write\" | \"metadata-commit\",\n operation: () => Promise<Value>,\n): Promise<Value> {\n try {\n return await operation();\n } catch (error) {\n reportDeploymentIssue(\"publication\", step, error);\n throw error;\n }\n}\n\nexport class LocalPrototypeLifecycle implements PrototypeLifecycle {\n readonly #runtime: PrototypeLifecycleRuntimeContext;\n readonly gitMode: GitMode;\n readonly #catalog: PrototypeCatalogLike;\n readonly #repository: GitRepository;\n readonly #preparePrototypeFiles: PreparePrototypeFiles;\n readonly #deploymentProvider?: DeploymentProvider;\n readonly #applicationId?: string;\n readonly #themeRegistry?: ThemeRegistry;\n #restartRequired = false;\n #pendingPublication?: PendingPublication;\n\n constructor(options: LocalPrototypeLifecycleOptions) {\n const internalOptions = options as InternalLocalPrototypeLifecycleOptions;\n const runtime =\n internalOptions.runtime ??\n createPrototypeLifecycleRuntime(internalOptions);\n this.#runtime = {\n ...runtime,\n gitMode: parseGitModeSetting(runtime.gitMode),\n };\n this.gitMode = this.#runtime.gitMode;\n this.#themeRegistry = options.themeRegistry;\n this.#catalog =\n internalOptions.catalog ??\n new PrototypeCatalog({\n ...this.#runtime,\n themeRegistry: this.#themeRegistry,\n });\n this.#repository = this.#runtime.gitRepository;\n this.#preparePrototypeFiles =\n internalOptions.preparePrototypeFiles ?? (() => undefined);\n this.#deploymentProvider = internalOptions.deploymentProvider;\n this.#applicationId = internalOptions.applicationId;\n if (\n this.#deploymentProvider &&\n (typeof this.#applicationId !== \"string\" ||\n this.#applicationId.length === 0)\n ) {\n throw new TypeError(\n \"applicationId must be provided when deploymentProvider is configured\",\n );\n }\n }\n\n async #verifiedIdentity(identity: BonfireIdentity) {\n return parseBonfireIdentity(identity);\n }\n\n async #currentBranch() {\n const branch = await this.#repository.currentBranch();\n if (branch === \"HEAD\" || branch.length === 0) {\n throw new GitRepositoryError(\"conflict\", \"current-branch\");\n }\n return branch;\n }\n\n async #ownedPrototype(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ) {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parsePrototypeCommandInput(input);\n if (normalizedInput.ref.login !== verifiedIdentity.login) {\n throw new PrototypeOwnershipError();\n }\n const summary = await this.#catalog.getSummary(normalizedInput.ref);\n if (!summary) {\n throw new PrototypeNotFoundError();\n }\n\n return {\n ref: normalizedInput.ref,\n path: repositoryRelativePath(this.#runtime, normalizedInput.ref),\n summary,\n };\n }\n\n async #prepareMutationBranch(login: string) {\n await assertIdentityBranchReady(this.#repository, login);\n return login;\n }\n\n async #prepareDestination(\n receipt: OwnedDestinationReceipt,\n expectedFiles: readonly string[],\n request: PreparePrototypeFilesRequest,\n ) {\n try {\n const frozenRequest = Object.freeze({\n ...request,\n ref: Object.freeze({ ...request.ref }),\n identity: Object.freeze({ ...request.identity }),\n ...(request.operation === \"duplicate\"\n ? { source: Object.freeze({ ...request.source }) }\n : {}),\n }) as PreparePrototypeFilesRequest;\n await this.#preparePrototypeFiles(frozenRequest);\n await validateOwnedDestination(\n receipt,\n expectedFiles,\n request.ref,\n this.#themeRegistry,\n );\n } catch {\n try {\n await cleanupOwnedDestination(receipt);\n } catch {\n // The failure remains a finite filesystem failure without retrying cleanup.\n }\n throw new PrototypeScaffoldError(\"filesystem\");\n }\n }\n\n async prepareBranch(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<BranchPreparationValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n if (\n this.#runtime.gitMode === \"manual\" ||\n this.#runtime.automaticBranchSwitch === \"disabled\"\n ) {\n await this.#currentBranch();\n return { status: \"disabled\" } satisfies BranchPreparationValue;\n }\n\n const synchronization = await synchronizeIdentityBranch(\n this.#repository,\n {\n identityBranch: verifiedIdentity.login,\n primaryBranch: this.#runtime.primaryBranch,\n remote: this.#runtime.remote,\n managedPaths: this.#runtime.managedRepositoryPaths,\n },\n );\n if (synchronization.status !== \"synchronized\") {\n return {\n status: synchronization.status,\n reason: synchronization.reason,\n ...(synchronization.affectedPaths.length > 0\n ? { affectedPaths: synchronization.affectedPaths }\n : {}),\n } satisfies BranchPreparationValue;\n }\n if (\n synchronization.affectedPaths.some((path) =>\n this.#runtime.managedRepositoryPaths.includes(path),\n )\n ) {\n this.#restartRequired = true;\n }\n return {\n status: \"ready\",\n ...(synchronization.affectedPaths.length > 0\n ? { affectedPaths: synchronization.affectedPaths }\n : {}),\n } satisfies BranchPreparationValue;\n },\n );\n });\n }\n\n async create(\n identity: BonfireIdentity,\n input: CreatePrototypeInput,\n ): Promise<LifecycleResult<CreatePrototypeValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parseCreatePrototypeInput(\n input,\n this.#themeRegistry,\n );\n const ref = destinationRef(verifiedIdentity, normalizedInput.title);\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n if (await this.#catalog.getSummary(ref)) {\n throw new PrototypeScaffoldError(\"collision\");\n }\n\n const branch =\n this.#runtime.gitMode === \"managed\"\n ? await this.#prepareMutationBranch(verifiedIdentity.login)\n : undefined;\n const prepared = await scaffoldPrototype({\n paths: this.#runtime,\n ref,\n title: normalizedInput.title,\n description: normalizedInput.description,\n authorName: verifiedIdentity.name,\n theme: normalizedInput.theme,\n themeRegistry: this.#themeRegistry,\n });\n await this.#prepareDestination(\n prepared.receipt,\n prepared.expectedFiles,\n {\n operation: \"create\",\n repositoryRoot: this.#runtime.repositoryRoot,\n prototypeRoot: this.#runtime.prototypeRoot,\n prototypePath: prepared.destination,\n ref,\n identity: verifiedIdentity,\n title: normalizedInput.title,\n description: normalizedInput.description,\n },\n );\n if (branch === undefined) {\n return {\n ref,\n url: getPrototypeUrl(ref),\n remoteStatus: \"manual\",\n } satisfies CreatePrototypeValue;\n }\n\n await commitPath(\n this.#repository,\n repositoryRelativePath(this.#runtime, ref),\n `init: ${ref.slug}`,\n );\n const push = await pushBranch(\n this.#repository,\n this.#runtime.remote,\n branch,\n );\n\n return {\n ref,\n url: getPrototypeUrl(ref),\n remoteStatus: push.remoteStatus,\n } satisfies CreatePrototypeValue;\n },\n );\n });\n }\n\n async edit(\n identity: BonfireIdentity,\n input: EditPrototypeInput,\n ): Promise<LifecycleResult<EditPrototypeValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parseEditPrototypeInput(\n input,\n this.#themeRegistry,\n );\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n const { ref, path } = await this.#ownedPrototype(verifiedIdentity, {\n ref: normalizedInput.ref,\n });\n const branch =\n this.#runtime.gitMode === \"managed\"\n ? await this.#prepareMutationBranch(verifiedIdentity.login)\n : undefined;\n await assertPrototypeDirectory(this.#runtime, ref);\n const document = await readMetadataDocument(\n this.#runtime,\n ref,\n this.#themeRegistry,\n );\n const metadata = parsePrototypeMetadata(document, {\n themeRegistry: this.#themeRegistry,\n });\n if (metadata.authorLogin !== ref.login) {\n throw new PrototypeNotFoundError();\n }\n\n await writeMetadataDocument(this.#runtime, ref, {\n ...document,\n title: normalizedInput.title,\n description: normalizedInput.description,\n updatedAt: new Date().toISOString(),\n ...(normalizedInput.theme === undefined\n ? {}\n : { theme: normalizedInput.theme }),\n });\n if (branch !== undefined) {\n await commitPath(this.#repository, path, `edit: ${ref.slug}`);\n await pushBranch(this.#repository, this.#runtime.remote, branch);\n }\n\n return { edited: true, ref } satisfies EditPrototypeValue;\n },\n );\n });\n }\n\n async delete(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DeletePrototypeValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parsePrototypeCommandInput(input);\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n const { ref, path } = await this.#ownedPrototype(\n verifiedIdentity,\n normalizedInput,\n );\n const branch =\n this.#runtime.gitMode === \"managed\"\n ? await this.#prepareMutationBranch(verifiedIdentity.login)\n : undefined;\n await removePrototypeDirectory(this.#runtime, ref);\n if (branch !== undefined) {\n await commitPath(this.#repository, path, `delete: ${ref.slug}`);\n await pushBranch(this.#repository, this.#runtime.remote, branch);\n }\n\n return { deleted: true, ref } satisfies DeletePrototypeValue;\n },\n );\n });\n }\n\n async getDirtyState(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DirtyObservationValue>> {\n return resultFrom(async () => {\n const { path, summary } = await this.#ownedPrototype(identity, input);\n const status = await this.#repository.status([path]);\n return {\n status: status.files.length > 0 ? \"dirty\" : \"clean\",\n ...(summary.previewUrl !== undefined\n ? { previewUrl: summary.previewUrl }\n : {}),\n } satisfies DirtyObservationValue;\n });\n }\n\n async getRepositorySummary(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<RepositorySummary>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n return getRepositorySummary({\n repository: this.#repository,\n catalog: this.#catalog,\n repositoryRoot: this.#runtime.repositoryRoot,\n prototypeRoot: this.#runtime.prototypeRoot,\n identity: verifiedIdentity,\n mode: this.#runtime.gitMode,\n remote: this.#runtime.remote,\n primaryBranch: this.#runtime.primaryBranch,\n restartRequired: this.#restartRequired,\n });\n });\n }\n\n async checkRepositorySummary(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<RepositorySummary>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n return getRepositorySummary({\n repository: this.#repository,\n catalog: this.#catalog,\n repositoryRoot: this.#runtime.repositoryRoot,\n prototypeRoot: this.#runtime.prototypeRoot,\n identity: verifiedIdentity,\n mode: this.#runtime.gitMode,\n remote: this.#runtime.remote,\n primaryBranch: this.#runtime.primaryBranch,\n restartRequired: this.#restartRequired,\n refresh: true,\n });\n });\n }\n\n async #headCommit() {\n const commit = (await this.#repository.raw([\"rev-parse\", \"HEAD\"])).trim();\n if (!/^[0-9a-f]{40,64}$/iu.test(commit)) {\n throw new GitRepositoryError(\"malformed-output\", \"head-commit\");\n }\n return commit.toLowerCase();\n }\n\n async #prepareDeploymentBranch(login: string) {\n if (this.#runtime.automaticBranchSwitch === \"disabled\") {\n await assertIdentityBranchReady(this.#repository, login);\n return \"updates-not-synchronized\" as const;\n }\n\n const synchronization = await synchronizeIdentityBranch(this.#repository, {\n identityBranch: login,\n primaryBranch: this.#runtime.primaryBranch,\n remote: this.#runtime.remote,\n managedPaths: this.#runtime.managedRepositoryPaths,\n });\n if (synchronization.status === \"unsafe\") {\n throw new GitRepositoryError(\"conflict\", \"deployment-preparation\");\n }\n await assertIdentityBranchReady(this.#repository, login);\n return synchronization.status === \"deferred\"\n ? (\"updates-not-synchronized\" as const)\n : undefined;\n }\n\n #workspaceRuntime(\n workspacePath: string,\n workspaceRepository: GitRepository,\n ): PrototypeLifecycleRuntimeContext {\n return {\n ...this.#runtime,\n repositoryRoot: workspacePath,\n repositoryRealPath: workspacePath,\n prototypeRoot: join(\n workspacePath,\n relative(this.#runtime.repositoryRoot, this.#runtime.prototypeRoot),\n ),\n gitRepository: workspaceRepository,\n };\n }\n\n async #publishDeploymentMetadata(pending: PendingPublication) {\n return publishDeploymentSource(\n this.#repository,\n {\n remote: this.#runtime.remote,\n primaryBranch: this.#runtime.primaryBranch,\n sourceSha: pending.sourceSha,\n commitIdentity: this.#runtime.commitIdentity,\n },\n async ({ path, repository }) => {\n const workspaceRuntime = this.#workspaceRuntime(path, repository);\n const mergedDocument = await diagnoseMetadataStep(\"metadata-read\", () =>\n readMetadataDocument(\n workspaceRuntime,\n pending.ref,\n this.#themeRegistry,\n ),\n );\n await diagnoseMetadataStep(\"metadata-write\", () =>\n writeMetadataDocument(workspaceRuntime, pending.ref, {\n ...mergedDocument,\n previewUrl: pending.previewUrl,\n updatedAt: new Date().toISOString(),\n }),\n );\n await diagnoseMetadataStep(\"metadata-commit\", () =>\n commitPath(\n repository,\n pending.path,\n `deploy: ${pending.ref.slug} (metadata)`,\n ),\n );\n },\n );\n }\n\n async #deploymentValue(\n pending: PendingPublication,\n publicationWarnings: readonly DeploymentWarningCode[] = [],\n ): Promise<DeploymentValue> {\n const warnings = [\n ...(pending.synchronizationWarning === undefined\n ? []\n : [pending.synchronizationWarning]),\n ...publicationWarnings,\n ];\n return {\n deployed: true,\n ref: pending.ref,\n previewUrl: pending.previewUrl,\n deploymentId: pending.deploymentId,\n sourceSha: pending.sourceSha,\n readyState: pending.readyState,\n warning: publicationWarnings[0] ?? pending.synchronizationWarning,\n ...(warnings.length === 0 ? {} : { warnings }),\n };\n }\n\n async deploy(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DeploymentValue>> {\n if (this.#runtime.gitMode === \"manual\") {\n return lifecycleFailure(\"forbidden\");\n }\n\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parsePrototypeCommandInput(input);\n if (!this.#deploymentProvider) {\n throw new DeploymentUnavailableError();\n }\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n const { ref, path } = await this.#ownedPrototype(\n verifiedIdentity,\n normalizedInput,\n );\n const synchronizationWarning = await this.#prepareDeploymentBranch(\n verifiedIdentity.login,\n );\n await assertPrototypeDirectory(this.#runtime, ref);\n const document = await readMetadataDocument(\n this.#runtime,\n ref,\n this.#themeRegistry,\n );\n const metadata = parsePrototypeMetadata(document, {\n themeRegistry: this.#themeRegistry,\n });\n if (metadata.authorLogin !== ref.login) {\n throw new PrototypeNotFoundError();\n }\n\n await commitPath(this.#repository, path, `deploy: ${ref.slug}`);\n const sourceSha = await this.#headCommit();\n const sourcePush = await pushCommitToBranch(\n this.#repository,\n this.#runtime.remote,\n sourceSha,\n verifiedIdentity.login,\n );\n if (sourcePush.remoteStatus !== \"pushed\") {\n throw new DeploymentSourcePushError();\n }\n\n await preflightDeploymentSource(this.#repository, {\n remote: this.#runtime.remote,\n primaryBranch: this.#runtime.primaryBranch,\n sourceSha,\n commitIdentity: this.#runtime.commitIdentity,\n });\n\n const deployment = await this.#deploymentProvider!.deploy({\n applicationId: this.#applicationId!,\n login: verifiedIdentity.login,\n });\n if (\n deployment.commitSha.toLowerCase() !== sourceSha ||\n ![\"READY\", \"QUEUED\", \"INITIALIZING\", \"BUILDING\"].includes(\n deployment.readyState,\n )\n ) {\n throw new DeploymentNotAcceptedError();\n }\n const previewUrl = new URL(\n getPrototypeUrl(ref).slice(1),\n `${deployment.url.replace(/\\/+$/, \"\")}/`,\n ).toString();\n const pending: PendingPublication = {\n identity: verifiedIdentity,\n input: normalizedInput,\n ref,\n path,\n previewUrl,\n deploymentId: deployment.deploymentId,\n sourceSha,\n readyState: deployment.readyState,\n synchronizationWarning,\n };\n\n try {\n const publication = await this.#publishDeploymentMetadata(pending);\n this.#pendingPublication = undefined;\n return this.#deploymentValue(\n pending,\n publication.cleanupFailed ? [\"publication-cleanup-failed\"] : [],\n );\n } catch (error) {\n // Keep the accepted deployment retryable for every publication\n // failure, including metadata preparation failures.\n this.#pendingPublication = pending;\n return this.#deploymentValue(pending, [\n error instanceof DeploymentWorktreeError &&\n error.kind !== \"cleanup-failure\"\n ? \"primary-merge-failed\"\n : \"metadata-persistence-failed\",\n ...(error instanceof DeploymentWorktreeError &&\n error.cleanupFailed\n ? [\"publication-cleanup-failed\" as const]\n : []),\n ]);\n }\n },\n );\n });\n }\n\n async retryPublish(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DeploymentValue>> {\n if (this.#runtime.gitMode === \"manual\") {\n return lifecycleFailure(\"forbidden\");\n }\n\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parsePrototypeCommandInput(input);\n const pending = this.#pendingPublication;\n if (\n pending === undefined ||\n pending.identity.login !== verifiedIdentity.login ||\n pending.ref.login !== normalizedInput.ref.login ||\n pending.ref.slug !== normalizedInput.ref.slug\n ) {\n throw new DeploymentUnavailableError();\n }\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n await assertIdentityBranchReady(\n this.#repository,\n verifiedIdentity.login,\n );\n const publication = await this.#publishDeploymentMetadata(pending);\n this.#pendingPublication = undefined;\n return this.#deploymentValue(\n pending,\n publication.cleanupFailed ? [\"publication-cleanup-failed\"] : [],\n );\n },\n );\n });\n }\n\n async save(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<SavePrototypeValue>> {\n if (this.#runtime.gitMode === \"manual\") {\n return lifecycleFailure(\"forbidden\");\n }\n\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const { ref, path } = await this.#ownedPrototype(verifiedIdentity, input);\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n await this.#prepareMutationBranch(verifiedIdentity.login);\n await commitPath(this.#repository, path, `save: ${ref.slug}`);\n await pushBranch(\n this.#repository,\n this.#runtime.remote,\n verifiedIdentity.login,\n );\n return { saved: true } satisfies SavePrototypeValue;\n },\n );\n });\n }\n\n async discard(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DiscardPrototypeValue>> {\n if (this.#runtime.gitMode === \"manual\") {\n return lifecycleFailure(\"forbidden\");\n }\n\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const { path } = await this.#ownedPrototype(verifiedIdentity, input);\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n await discardPath(this.#repository, path);\n return { discarded: true } satisfies DiscardPrototypeValue;\n },\n );\n });\n }\n\n async duplicate(\n identity: BonfireIdentity,\n input: DuplicatePrototypeInput,\n ): Promise<LifecycleResult<DuplicatePrototypeValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parseDuplicatePrototypeInput(input);\n if (!(await this.#catalog.getSummary(normalizedInput.source))) {\n throw new PrototypeDuplicateError(\"source-not-found\");\n }\n const ref = destinationRef(verifiedIdentity, normalizedInput.title);\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n if (await this.#catalog.getSummary(ref)) {\n throw new PrototypeDuplicateError(\"collision\");\n }\n\n const branch =\n this.#runtime.gitMode === \"managed\"\n ? await this.#prepareMutationBranch(verifiedIdentity.login)\n : undefined;\n const prepared = await duplicatePrototype({\n paths: this.#runtime,\n source: normalizedInput.source,\n destination: ref,\n title: normalizedInput.title,\n description: normalizedInput.description,\n authorName: verifiedIdentity.name,\n themeRegistry: this.#themeRegistry,\n });\n await this.#prepareDestination(\n prepared.receipt,\n prepared.expectedFiles,\n {\n operation: \"duplicate\",\n repositoryRoot: this.#runtime.repositoryRoot,\n prototypeRoot: this.#runtime.prototypeRoot,\n prototypePath: prepared.destination,\n ref,\n source: normalizedInput.source,\n identity: verifiedIdentity,\n title: normalizedInput.title,\n description: normalizedInput.description,\n },\n );\n if (branch === undefined) {\n return {\n ref,\n url: getPrototypeUrl(ref),\n remoteStatus: \"manual\",\n } satisfies DuplicatePrototypeValue;\n }\n\n await commitPath(\n this.#repository,\n repositoryRelativePath(this.#runtime, ref),\n `init: ${ref.slug}`,\n );\n const push = await pushBranch(\n this.#repository,\n this.#runtime.remote,\n branch,\n );\n\n return {\n ref,\n url: getPrototypeUrl(ref),\n remoteStatus: push.remoteStatus,\n } satisfies DuplicatePrototypeValue;\n },\n );\n });\n }\n}\n"]}
@@ -13,7 +13,7 @@ import { RepositoryBusyError, withRepositoryOperation, } from "./operation-gate.
13
13
  import { DeploymentProviderError, } from "../deployment-provider.js";
14
14
  import { assertIdentityBranchReady, synchronizeIdentityBranch, } from "./repository-synchronization.js";
15
15
  import { getRepositorySummary } from "./repository-summary.js";
16
- import { DeploymentWorktreeError, preflightDeploymentSource, publishDeploymentSource, } from "./deployment-worktree.js";
16
+ import { DeploymentWorktreeError, preflightDeploymentSource, publishDeploymentSource, reportDeploymentIssue, } from "./deployment-worktree.js";
17
17
  function repositoryRelativePath(runtime, ref) {
18
18
  const absolutePath = join(runtime.prototypeRoot, ref.login, ref.slug);
19
19
  return relative(runtime.repositoryRoot, absolutePath).split(sep).join("/");
@@ -175,6 +175,15 @@ function resultFrom(operation) {
175
175
  .then(lifecycleSuccess)
176
176
  .catch((error) => lifecycleFailure(mapError(error)));
177
177
  }
178
+ async function diagnoseMetadataStep(step, operation) {
179
+ try {
180
+ return await operation();
181
+ }
182
+ catch (error) {
183
+ reportDeploymentIssue("publication", step, error);
184
+ throw error;
185
+ }
186
+ }
178
187
  export class LocalPrototypeLifecycle {
179
188
  #runtime;
180
189
  gitMode;
@@ -188,7 +197,8 @@ export class LocalPrototypeLifecycle {
188
197
  #pendingPublication;
189
198
  constructor(options) {
190
199
  const internalOptions = options;
191
- const runtime = internalOptions.runtime ?? createPrototypeLifecycleRuntime(internalOptions);
200
+ const runtime = internalOptions.runtime ??
201
+ createPrototypeLifecycleRuntime(internalOptions);
192
202
  this.#runtime = {
193
203
  ...runtime,
194
204
  gitMode: parseGitModeSetting(runtime.gitMode),
@@ -491,13 +501,13 @@ export class LocalPrototypeLifecycle {
491
501
  commitIdentity: this.#runtime.commitIdentity,
492
502
  }, async ({ path, repository }) => {
493
503
  const workspaceRuntime = this.#workspaceRuntime(path, repository);
494
- const mergedDocument = await readMetadataDocument(workspaceRuntime, pending.ref, this.#themeRegistry);
495
- await writeMetadataDocument(workspaceRuntime, pending.ref, {
504
+ const mergedDocument = await diagnoseMetadataStep("metadata-read", () => readMetadataDocument(workspaceRuntime, pending.ref, this.#themeRegistry));
505
+ await diagnoseMetadataStep("metadata-write", () => writeMetadataDocument(workspaceRuntime, pending.ref, {
496
506
  ...mergedDocument,
497
507
  previewUrl: pending.previewUrl,
498
508
  updatedAt: new Date().toISOString(),
499
- });
500
- await commitPath(repository, pending.path, `deploy: ${pending.ref.slug} (metadata)`);
509
+ }));
510
+ await diagnoseMetadataStep("metadata-commit", () => commitPath(repository, pending.path, `deploy: ${pending.ref.slug} (metadata)`));
501
511
  });
502
512
  }
503
513
  async #deploymentValue(pending, publicationWarnings = []) {
@@ -574,9 +584,7 @@ export class LocalPrototypeLifecycle {
574
584
  try {
575
585
  const publication = await this.#publishDeploymentMetadata(pending);
576
586
  this.#pendingPublication = undefined;
577
- return this.#deploymentValue(pending, publication.cleanupFailed
578
- ? ["publication-cleanup-failed"]
579
- : []);
587
+ return this.#deploymentValue(pending, publication.cleanupFailed ? ["publication-cleanup-failed"] : []);
580
588
  }
581
589
  catch (error) {
582
590
  // Keep the accepted deployment retryable for every publication
@@ -587,7 +595,8 @@ export class LocalPrototypeLifecycle {
587
595
  error.kind !== "cleanup-failure"
588
596
  ? "primary-merge-failed"
589
597
  : "metadata-persistence-failed",
590
- ...(error instanceof DeploymentWorktreeError && error.cleanupFailed
598
+ ...(error instanceof DeploymentWorktreeError &&
599
+ error.cleanupFailed
591
600
  ? ["publication-cleanup-failed"]
592
601
  : []),
593
602
  ]);
@@ -1 +1 @@
1
- {"version":3,"file":"local-prototype-lifecycle.js","sourceRoot":"","sources":["../../src/lifecycle/local-prototype-lifecycle.ts"],"names":[],"mappings":"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","sourcesContent":["import { constants } from \"node:fs\";\nimport { lstat, open, rename, rm, writeFile } from \"node:fs/promises\";\nimport { randomUUID } from \"node:crypto\";\nimport { join, relative, sep } from \"node:path\";\nimport {\n definePrototypeRef,\n getPrototypeUrl,\n LifecycleInputError,\n lifecycleFailure,\n lifecycleSuccess,\n parseCreatePrototypeInput,\n parseDuplicatePrototypeInput,\n parseEditPrototypeInput,\n parseBonfireIdentity,\n parsePrototypeCommandInput,\n parsePrototypeMetadata,\n slugifyPrototypeTitle,\n PrototypeRefError,\n type BranchPreparationValue,\n type CreatePrototypeInput,\n type CreatePrototypeValue,\n type DeletePrototypeValue,\n type DeploymentValue,\n type DeploymentWarningCode,\n type DirtyObservationValue,\n type DiscardPrototypeValue,\n type DuplicatePrototypeInput,\n type DuplicatePrototypeValue,\n type EditPrototypeInput,\n type EditPrototypeValue,\n parseGitModeSetting,\n type GitMode,\n type LifecycleErrorCode,\n type LifecycleResult,\n type BonfireIdentity,\n type PrototypeCommandInput,\n type SavePrototypeValue,\n type RepositorySummary,\n type ThemeRegistry,\n} from \"@ebay-dt/bonfire-core\";\nimport {\n commitPath,\n discardPath,\n GitRepositoryError,\n pushBranch,\n pushCommitToBranch,\n type GitRepository,\n} from \"../git-repository.js\";\nimport { duplicatePrototype, PrototypeDuplicateError } from \"../duplicate.js\";\nimport {\n PrototypeCatalog,\n type PrototypeCatalogLike,\n} from \"../prototype-catalog.js\";\nimport { PrototypeScaffoldError, scaffoldPrototype } from \"../scaffold.js\";\nimport {\n createPrototypeLifecycleRuntime,\n type PrototypeLifecycleRuntimeContext,\n} from \"./runtime-context.js\";\nimport type {\n PreparePrototypeFiles,\n PreparePrototypeFilesRequest,\n} from \"../prepare-prototype-files.js\";\nimport {\n cleanupOwnedDestination,\n validateOwnedDestination,\n type OwnedDestinationReceipt,\n} from \"../internal/owned-destination.js\";\nimport {\n RepositoryBusyError,\n withRepositoryOperation,\n} from \"./operation-gate.js\";\nimport {\n DeploymentProviderError,\n type DeploymentProvider,\n} from \"../deployment-provider.js\";\nimport {\n assertIdentityBranchReady,\n synchronizeIdentityBranch,\n} from \"./repository-synchronization.js\";\nimport { getRepositorySummary } from \"./repository-summary.js\";\nimport {\n DeploymentWorktreeError,\n preflightDeploymentSource,\n publishDeploymentSource,\n} from \"./deployment-worktree.js\";\n\ntype LifecycleMethod<Input, Value> = (\n identity: BonfireIdentity,\n input: Input,\n) => Promise<LifecycleResult<Value>>;\n\nexport type PrototypeLifecycle = {\n /** Declares whether this lifecycle enforces the selected Git mode. */\n readonly gitMode?: GitMode;\n prepareBranch(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<BranchPreparationValue>>;\n create: LifecycleMethod<CreatePrototypeInput, CreatePrototypeValue>;\n duplicate: LifecycleMethod<DuplicatePrototypeInput, DuplicatePrototypeValue>;\n edit: LifecycleMethod<EditPrototypeInput, EditPrototypeValue>;\n delete: LifecycleMethod<PrototypeCommandInput, DeletePrototypeValue>;\n getDirtyState: LifecycleMethod<PrototypeCommandInput, DirtyObservationValue>;\n getRepositorySummary(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<RepositorySummary>>;\n checkRepositorySummary(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<RepositorySummary>>;\n save: LifecycleMethod<PrototypeCommandInput, SavePrototypeValue>;\n discard: LifecycleMethod<PrototypeCommandInput, DiscardPrototypeValue>;\n deploy: LifecycleMethod<PrototypeCommandInput, DeploymentValue>;\n retryPublish?: LifecycleMethod<PrototypeCommandInput, DeploymentValue>;\n};\n\nexport type LocalPrototypeLifecycleOptions = {\n repositoryRoot: string;\n prototypeRoot?: string;\n remote?: string;\n primaryBranch?: string;\n automaticBranchSwitch?: \"enabled\" | \"disabled\";\n gitMode?: GitMode;\n commitIdentity?: {\n name: string;\n email: string;\n };\n catalog?: PrototypeCatalogLike;\n preparePrototypeFiles?: PreparePrototypeFiles;\n deploymentProvider?: DeploymentProvider;\n applicationId?: string;\n themeRegistry?: ThemeRegistry;\n};\n\ntype InternalLocalPrototypeLifecycleOptions = LocalPrototypeLifecycleOptions & {\n runtime?: PrototypeLifecycleRuntimeContext;\n};\n\nfunction repositoryRelativePath(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n) {\n const absolutePath = join(runtime.prototypeRoot, ref.login, ref.slug);\n return relative(runtime.repositoryRoot, absolutePath).split(sep).join(\"/\");\n}\n\nfunction destinationRef(identity: BonfireIdentity, title: string) {\n const slug = slugifyPrototypeTitle(title);\n if (slug === null)\n throw new PrototypeRefError(\"title\", \"must produce a slug\");\n return definePrototypeRef({ login: identity.login, slug });\n}\n\nclass PrototypeNotFoundError extends Error {}\nclass PrototypeOwnershipError extends Error {}\nclass PrototypeMetadataFileError extends Error {}\nclass DeploymentUnavailableError extends Error {}\nclass DeploymentSourcePushError extends Error {}\nclass DeploymentNotAcceptedError extends Error {}\n\ntype PendingPublication = {\n identity: BonfireIdentity;\n input: PrototypeCommandInput;\n ref: { login: string; slug: string };\n path: string;\n previewUrl: string;\n deploymentId: string;\n sourceSha: string;\n readyState: DeploymentValue[\"readyState\"];\n synchronizationWarning: DeploymentWarningCode | undefined;\n};\n\nfunction metadataPath(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n) {\n return join(runtime.prototypeRoot, ref.login, ref.slug, \"metadata.json\");\n}\n\nasync function assertPrototypeDirectory(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n) {\n const paths = [\n runtime.prototypeRoot,\n join(runtime.prototypeRoot, ref.login),\n join(runtime.prototypeRoot, ref.login, ref.slug),\n ];\n\n try {\n for (const path of paths) {\n const stats = await lstat(path);\n if (stats.isSymbolicLink() || !stats.isDirectory()) {\n throw new PrototypeMetadataFileError();\n }\n }\n } catch (error) {\n if (error instanceof PrototypeMetadataFileError) throw error;\n throw new PrototypeMetadataFileError();\n }\n}\n\nasync function readMetadataDocument(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n themeRegistry?: ThemeRegistry,\n) {\n const path = metadataPath(runtime, ref);\n try {\n const file = await open(\n path,\n constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0),\n );\n try {\n const source = await file.readFile(\"utf8\");\n const document: unknown = JSON.parse(source);\n parsePrototypeMetadata(document, { themeRegistry });\n if (\n typeof document !== \"object\" ||\n document === null ||\n Array.isArray(document)\n ) {\n throw new PrototypeMetadataFileError();\n }\n return { ...(document as Record<string, unknown>) };\n } finally {\n await file.close();\n }\n } catch (error) {\n if (error instanceof PrototypeMetadataFileError) throw error;\n throw new PrototypeMetadataFileError();\n }\n}\n\nasync function removePrototypeDirectory(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n) {\n await assertPrototypeDirectory(runtime, ref);\n try {\n await rm(join(runtime.prototypeRoot, ref.login, ref.slug), {\n force: false,\n recursive: true,\n });\n } catch {\n throw new PrototypeMetadataFileError();\n }\n}\n\nasync function writeMetadataDocument(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n document: Record<string, unknown>,\n) {\n const path = metadataPath(runtime, ref);\n const temporaryPath = `${path}.${randomUUID()}.tmp`;\n\n try {\n const stats = await lstat(path);\n if (stats.isSymbolicLink() || !stats.isFile()) {\n throw new PrototypeMetadataFileError();\n }\n await writeFile(temporaryPath, `${JSON.stringify(document, null, 2)}\\n`, {\n encoding: \"utf8\",\n flag: \"wx\",\n });\n await rename(temporaryPath, path);\n } catch (error) {\n if (error instanceof PrototypeMetadataFileError) throw error;\n throw new PrototypeMetadataFileError();\n } finally {\n await rm(temporaryPath, { force: true }).catch(() => undefined);\n }\n}\n\nfunction mapError(error: unknown): LifecycleErrorCode {\n if (error instanceof PrototypeNotFoundError) return \"not-found\";\n if (error instanceof PrototypeOwnershipError) return \"forbidden\";\n if (error instanceof PrototypeMetadataFileError) return \"filesystem-failure\";\n if (error instanceof RepositoryBusyError) return \"repository-busy\";\n if (\n error instanceof PrototypeRefError ||\n error instanceof LifecycleInputError\n ) {\n return \"invalid-input\";\n }\n if (error instanceof GitRepositoryError) {\n if (error.kind === \"timeout\") return \"timeout\";\n if (error.kind === \"conflict\") return \"working-tree-conflict\";\n if (error.kind === \"invalid-configuration\") return \"unexpected-failure\";\n return \"git-failure\";\n }\n if (error instanceof PrototypeScaffoldError) {\n if (error.kind === \"collision\") return \"already-exists\";\n if (error.kind === \"invalid-input\") return \"invalid-input\";\n return \"filesystem-failure\";\n }\n if (error instanceof PrototypeDuplicateError) {\n if (error.kind === \"source-not-found\") return \"not-found\";\n if (error.kind === \"collision\") return \"already-exists\";\n if (error.kind === \"invalid-source\") return \"invalid-input\";\n return \"filesystem-failure\";\n }\n if (error instanceof DeploymentUnavailableError) {\n return \"deployment-unavailable\";\n }\n if (\n error instanceof DeploymentSourcePushError ||\n error instanceof DeploymentNotAcceptedError ||\n error instanceof DeploymentWorktreeError\n ) {\n return \"deployment-failure\";\n }\n if (error instanceof DeploymentProviderError) {\n return error.kind === \"timeout\" ? \"timeout\" : \"deployment-failure\";\n }\n return \"unexpected-failure\";\n}\n\nfunction resultFrom<Value>(\n operation: () => Promise<Value>,\n): Promise<LifecycleResult<Value>> {\n return operation()\n .then(lifecycleSuccess)\n .catch((error: unknown) => lifecycleFailure(mapError(error)));\n}\n\nexport class LocalPrototypeLifecycle implements PrototypeLifecycle {\n readonly #runtime: PrototypeLifecycleRuntimeContext;\n readonly gitMode: GitMode;\n readonly #catalog: PrototypeCatalogLike;\n readonly #repository: GitRepository;\n readonly #preparePrototypeFiles: PreparePrototypeFiles;\n readonly #deploymentProvider?: DeploymentProvider;\n readonly #applicationId?: string;\n readonly #themeRegistry?: ThemeRegistry;\n #restartRequired = false;\n #pendingPublication?: PendingPublication;\n\n constructor(options: LocalPrototypeLifecycleOptions) {\n const internalOptions = options as InternalLocalPrototypeLifecycleOptions;\n const runtime =\n internalOptions.runtime ?? createPrototypeLifecycleRuntime(internalOptions);\n this.#runtime = {\n ...runtime,\n gitMode: parseGitModeSetting(runtime.gitMode),\n };\n this.gitMode = this.#runtime.gitMode;\n this.#themeRegistry = options.themeRegistry;\n this.#catalog =\n internalOptions.catalog ??\n new PrototypeCatalog({\n ...this.#runtime,\n themeRegistry: this.#themeRegistry,\n });\n this.#repository = this.#runtime.gitRepository;\n this.#preparePrototypeFiles =\n internalOptions.preparePrototypeFiles ?? (() => undefined);\n this.#deploymentProvider = internalOptions.deploymentProvider;\n this.#applicationId = internalOptions.applicationId;\n if (\n this.#deploymentProvider &&\n (typeof this.#applicationId !== \"string\" ||\n this.#applicationId.length === 0)\n ) {\n throw new TypeError(\n \"applicationId must be provided when deploymentProvider is configured\",\n );\n }\n }\n\n async #verifiedIdentity(identity: BonfireIdentity) {\n return parseBonfireIdentity(identity);\n }\n\n async #currentBranch() {\n const branch = await this.#repository.currentBranch();\n if (branch === \"HEAD\" || branch.length === 0) {\n throw new GitRepositoryError(\"conflict\", \"current-branch\");\n }\n return branch;\n }\n\n async #ownedPrototype(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ) {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parsePrototypeCommandInput(input);\n if (normalizedInput.ref.login !== verifiedIdentity.login) {\n throw new PrototypeOwnershipError();\n }\n const summary = await this.#catalog.getSummary(normalizedInput.ref);\n if (!summary) {\n throw new PrototypeNotFoundError();\n }\n\n return {\n ref: normalizedInput.ref,\n path: repositoryRelativePath(this.#runtime, normalizedInput.ref),\n summary,\n };\n }\n\n async #prepareMutationBranch(login: string) {\n await assertIdentityBranchReady(this.#repository, login);\n return login;\n }\n\n async #prepareDestination(\n receipt: OwnedDestinationReceipt,\n expectedFiles: readonly string[],\n request: PreparePrototypeFilesRequest,\n ) {\n try {\n const frozenRequest = Object.freeze({\n ...request,\n ref: Object.freeze({ ...request.ref }),\n identity: Object.freeze({ ...request.identity }),\n ...(request.operation === \"duplicate\"\n ? { source: Object.freeze({ ...request.source }) }\n : {}),\n }) as PreparePrototypeFilesRequest;\n await this.#preparePrototypeFiles(frozenRequest);\n await validateOwnedDestination(\n receipt,\n expectedFiles,\n request.ref,\n this.#themeRegistry,\n );\n } catch {\n try {\n await cleanupOwnedDestination(receipt);\n } catch {\n // The failure remains a finite filesystem failure without retrying cleanup.\n }\n throw new PrototypeScaffoldError(\"filesystem\");\n }\n }\n\n async prepareBranch(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<BranchPreparationValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n if (\n this.#runtime.gitMode === \"manual\" ||\n this.#runtime.automaticBranchSwitch === \"disabled\"\n ) {\n await this.#currentBranch();\n return { status: \"disabled\" } satisfies BranchPreparationValue;\n }\n\n const synchronization = await synchronizeIdentityBranch(\n this.#repository,\n {\n identityBranch: verifiedIdentity.login,\n primaryBranch: this.#runtime.primaryBranch,\n remote: this.#runtime.remote,\n managedPaths: this.#runtime.managedRepositoryPaths,\n },\n );\n if (synchronization.status !== \"synchronized\") {\n return {\n status: synchronization.status,\n reason: synchronization.reason,\n ...(synchronization.affectedPaths.length > 0\n ? { affectedPaths: synchronization.affectedPaths }\n : {}),\n } satisfies BranchPreparationValue;\n }\n if (\n synchronization.affectedPaths.some((path) =>\n this.#runtime.managedRepositoryPaths.includes(path),\n )\n ) {\n this.#restartRequired = true;\n }\n return {\n status: \"ready\",\n ...(synchronization.affectedPaths.length > 0\n ? { affectedPaths: synchronization.affectedPaths }\n : {}),\n } satisfies BranchPreparationValue;\n },\n );\n });\n }\n\n async create(\n identity: BonfireIdentity,\n input: CreatePrototypeInput,\n ): Promise<LifecycleResult<CreatePrototypeValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parseCreatePrototypeInput(\n input,\n this.#themeRegistry,\n );\n const ref = destinationRef(verifiedIdentity, normalizedInput.title);\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n if (await this.#catalog.getSummary(ref)) {\n throw new PrototypeScaffoldError(\"collision\");\n }\n\n const branch =\n this.#runtime.gitMode === \"managed\"\n ? await this.#prepareMutationBranch(verifiedIdentity.login)\n : undefined;\n const prepared = await scaffoldPrototype({\n paths: this.#runtime,\n ref,\n title: normalizedInput.title,\n description: normalizedInput.description,\n authorName: verifiedIdentity.name,\n theme: normalizedInput.theme,\n themeRegistry: this.#themeRegistry,\n });\n await this.#prepareDestination(\n prepared.receipt,\n prepared.expectedFiles,\n {\n operation: \"create\",\n repositoryRoot: this.#runtime.repositoryRoot,\n prototypeRoot: this.#runtime.prototypeRoot,\n prototypePath: prepared.destination,\n ref,\n identity: verifiedIdentity,\n title: normalizedInput.title,\n description: normalizedInput.description,\n },\n );\n if (branch === undefined) {\n return {\n ref,\n url: getPrototypeUrl(ref),\n remoteStatus: \"manual\",\n } satisfies CreatePrototypeValue;\n }\n\n await commitPath(\n this.#repository,\n repositoryRelativePath(this.#runtime, ref),\n `init: ${ref.slug}`,\n );\n const push = await pushBranch(\n this.#repository,\n this.#runtime.remote,\n branch,\n );\n\n return {\n ref,\n url: getPrototypeUrl(ref),\n remoteStatus: push.remoteStatus,\n } satisfies CreatePrototypeValue;\n },\n );\n });\n }\n\n async edit(\n identity: BonfireIdentity,\n input: EditPrototypeInput,\n ): Promise<LifecycleResult<EditPrototypeValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parseEditPrototypeInput(\n input,\n this.#themeRegistry,\n );\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n const { ref, path } = await this.#ownedPrototype(verifiedIdentity, {\n ref: normalizedInput.ref,\n });\n const branch =\n this.#runtime.gitMode === \"managed\"\n ? await this.#prepareMutationBranch(verifiedIdentity.login)\n : undefined;\n await assertPrototypeDirectory(this.#runtime, ref);\n const document = await readMetadataDocument(\n this.#runtime,\n ref,\n this.#themeRegistry,\n );\n const metadata = parsePrototypeMetadata(document, {\n themeRegistry: this.#themeRegistry,\n });\n if (metadata.authorLogin !== ref.login) {\n throw new PrototypeNotFoundError();\n }\n\n await writeMetadataDocument(this.#runtime, ref, {\n ...document,\n title: normalizedInput.title,\n description: normalizedInput.description,\n updatedAt: new Date().toISOString(),\n ...(normalizedInput.theme === undefined\n ? {}\n : { theme: normalizedInput.theme }),\n });\n if (branch !== undefined) {\n await commitPath(this.#repository, path, `edit: ${ref.slug}`);\n await pushBranch(this.#repository, this.#runtime.remote, branch);\n }\n\n return { edited: true, ref } satisfies EditPrototypeValue;\n },\n );\n });\n }\n\n async delete(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DeletePrototypeValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parsePrototypeCommandInput(input);\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n const { ref, path } = await this.#ownedPrototype(\n verifiedIdentity,\n normalizedInput,\n );\n const branch =\n this.#runtime.gitMode === \"managed\"\n ? await this.#prepareMutationBranch(verifiedIdentity.login)\n : undefined;\n await removePrototypeDirectory(this.#runtime, ref);\n if (branch !== undefined) {\n await commitPath(this.#repository, path, `delete: ${ref.slug}`);\n await pushBranch(this.#repository, this.#runtime.remote, branch);\n }\n\n return { deleted: true, ref } satisfies DeletePrototypeValue;\n },\n );\n });\n }\n\n async getDirtyState(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DirtyObservationValue>> {\n return resultFrom(async () => {\n const { path, summary } = await this.#ownedPrototype(identity, input);\n const status = await this.#repository.status([path]);\n return {\n status: status.files.length > 0 ? \"dirty\" : \"clean\",\n ...(summary.previewUrl !== undefined\n ? { previewUrl: summary.previewUrl }\n : {}),\n } satisfies DirtyObservationValue;\n });\n }\n\n async getRepositorySummary(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<RepositorySummary>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n return getRepositorySummary({\n repository: this.#repository,\n catalog: this.#catalog,\n repositoryRoot: this.#runtime.repositoryRoot,\n prototypeRoot: this.#runtime.prototypeRoot,\n identity: verifiedIdentity,\n mode: this.#runtime.gitMode,\n remote: this.#runtime.remote,\n primaryBranch: this.#runtime.primaryBranch,\n restartRequired: this.#restartRequired,\n });\n });\n }\n\n async checkRepositorySummary(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<RepositorySummary>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n return getRepositorySummary({\n repository: this.#repository,\n catalog: this.#catalog,\n repositoryRoot: this.#runtime.repositoryRoot,\n prototypeRoot: this.#runtime.prototypeRoot,\n identity: verifiedIdentity,\n mode: this.#runtime.gitMode,\n remote: this.#runtime.remote,\n primaryBranch: this.#runtime.primaryBranch,\n restartRequired: this.#restartRequired,\n refresh: true,\n });\n });\n }\n\n async #headCommit() {\n const commit = (await this.#repository.raw([\"rev-parse\", \"HEAD\"])).trim();\n if (!/^[0-9a-f]{40,64}$/iu.test(commit)) {\n throw new GitRepositoryError(\"malformed-output\", \"head-commit\");\n }\n return commit.toLowerCase();\n }\n\n async #prepareDeploymentBranch(login: string) {\n if (this.#runtime.automaticBranchSwitch === \"disabled\") {\n await assertIdentityBranchReady(this.#repository, login);\n return \"updates-not-synchronized\" as const;\n }\n\n const synchronization = await synchronizeIdentityBranch(this.#repository, {\n identityBranch: login,\n primaryBranch: this.#runtime.primaryBranch,\n remote: this.#runtime.remote,\n managedPaths: this.#runtime.managedRepositoryPaths,\n });\n if (synchronization.status === \"unsafe\") {\n throw new GitRepositoryError(\"conflict\", \"deployment-preparation\");\n }\n await assertIdentityBranchReady(this.#repository, login);\n return synchronization.status === \"deferred\"\n ? (\"updates-not-synchronized\" as const)\n : undefined;\n }\n\n #workspaceRuntime(\n workspacePath: string,\n workspaceRepository: GitRepository,\n ): PrototypeLifecycleRuntimeContext {\n return {\n ...this.#runtime,\n repositoryRoot: workspacePath,\n repositoryRealPath: workspacePath,\n prototypeRoot: join(\n workspacePath,\n relative(this.#runtime.repositoryRoot, this.#runtime.prototypeRoot),\n ),\n gitRepository: workspaceRepository,\n };\n }\n\n async #publishDeploymentMetadata(pending: PendingPublication) {\n return publishDeploymentSource(\n this.#repository,\n {\n remote: this.#runtime.remote,\n primaryBranch: this.#runtime.primaryBranch,\n sourceSha: pending.sourceSha,\n commitIdentity: this.#runtime.commitIdentity,\n },\n async ({ path, repository }) => {\n const workspaceRuntime = this.#workspaceRuntime(path, repository);\n const mergedDocument = await readMetadataDocument(\n workspaceRuntime,\n pending.ref,\n this.#themeRegistry,\n );\n await writeMetadataDocument(workspaceRuntime, pending.ref, {\n ...mergedDocument,\n previewUrl: pending.previewUrl,\n updatedAt: new Date().toISOString(),\n });\n await commitPath(\n repository,\n pending.path,\n `deploy: ${pending.ref.slug} (metadata)`,\n );\n },\n );\n }\n\n async #deploymentValue(\n pending: PendingPublication,\n publicationWarnings: readonly DeploymentWarningCode[] = [],\n ): Promise<DeploymentValue> {\n const warnings = [\n ...(pending.synchronizationWarning === undefined\n ? []\n : [pending.synchronizationWarning]),\n ...publicationWarnings,\n ];\n return {\n deployed: true,\n ref: pending.ref,\n previewUrl: pending.previewUrl,\n deploymentId: pending.deploymentId,\n sourceSha: pending.sourceSha,\n readyState: pending.readyState,\n warning: publicationWarnings[0] ?? pending.synchronizationWarning,\n ...(warnings.length === 0 ? {} : { warnings }),\n };\n }\n\n async deploy(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DeploymentValue>> {\n if (this.#runtime.gitMode === \"manual\") {\n return lifecycleFailure(\"forbidden\");\n }\n\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parsePrototypeCommandInput(input);\n if (!this.#deploymentProvider) {\n throw new DeploymentUnavailableError();\n }\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n const { ref, path } = await this.#ownedPrototype(\n verifiedIdentity,\n normalizedInput,\n );\n const synchronizationWarning = await this.#prepareDeploymentBranch(\n verifiedIdentity.login,\n );\n await assertPrototypeDirectory(this.#runtime, ref);\n const document = await readMetadataDocument(\n this.#runtime,\n ref,\n this.#themeRegistry,\n );\n const metadata = parsePrototypeMetadata(document, {\n themeRegistry: this.#themeRegistry,\n });\n if (metadata.authorLogin !== ref.login) {\n throw new PrototypeNotFoundError();\n }\n\n await commitPath(this.#repository, path, `deploy: ${ref.slug}`);\n const sourceSha = await this.#headCommit();\n const sourcePush = await pushCommitToBranch(\n this.#repository,\n this.#runtime.remote,\n sourceSha,\n verifiedIdentity.login,\n );\n if (sourcePush.remoteStatus !== \"pushed\") {\n throw new DeploymentSourcePushError();\n }\n\n await preflightDeploymentSource(this.#repository, {\n remote: this.#runtime.remote,\n primaryBranch: this.#runtime.primaryBranch,\n sourceSha,\n commitIdentity: this.#runtime.commitIdentity,\n });\n\n const deployment = await this.#deploymentProvider!.deploy({\n applicationId: this.#applicationId!,\n login: verifiedIdentity.login,\n });\n if (\n deployment.commitSha.toLowerCase() !== sourceSha ||\n ![\"READY\", \"QUEUED\", \"INITIALIZING\", \"BUILDING\"].includes(\n deployment.readyState,\n )\n ) {\n throw new DeploymentNotAcceptedError();\n }\n const previewUrl = new URL(\n getPrototypeUrl(ref).slice(1),\n `${deployment.url.replace(/\\/+$/, \"\")}/`,\n ).toString();\n const pending: PendingPublication = {\n identity: verifiedIdentity,\n input: normalizedInput,\n ref,\n path,\n previewUrl,\n deploymentId: deployment.deploymentId,\n sourceSha,\n readyState: deployment.readyState,\n synchronizationWarning,\n };\n\n try {\n const publication = await this.#publishDeploymentMetadata(pending);\n this.#pendingPublication = undefined;\n return this.#deploymentValue(\n pending,\n publication.cleanupFailed\n ? [\"publication-cleanup-failed\"]\n : [],\n );\n } catch (error) {\n // Keep the accepted deployment retryable for every publication\n // failure, including metadata preparation failures.\n this.#pendingPublication = pending;\n return this.#deploymentValue(pending, [\n error instanceof DeploymentWorktreeError &&\n error.kind !== \"cleanup-failure\"\n ? \"primary-merge-failed\"\n : \"metadata-persistence-failed\",\n ...(error instanceof DeploymentWorktreeError && error.cleanupFailed\n ? [\"publication-cleanup-failed\" as const]\n : []),\n ]);\n }\n },\n );\n });\n }\n\n async retryPublish(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DeploymentValue>> {\n if (this.#runtime.gitMode === \"manual\") {\n return lifecycleFailure(\"forbidden\");\n }\n\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parsePrototypeCommandInput(input);\n const pending = this.#pendingPublication;\n if (\n pending === undefined ||\n pending.identity.login !== verifiedIdentity.login ||\n pending.ref.login !== normalizedInput.ref.login ||\n pending.ref.slug !== normalizedInput.ref.slug\n ) {\n throw new DeploymentUnavailableError();\n }\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n await assertIdentityBranchReady(this.#repository, verifiedIdentity.login);\n const publication = await this.#publishDeploymentMetadata(pending);\n this.#pendingPublication = undefined;\n return this.#deploymentValue(\n pending,\n publication.cleanupFailed ? [\"publication-cleanup-failed\"] : [],\n );\n },\n );\n });\n }\n\n async save(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<SavePrototypeValue>> {\n if (this.#runtime.gitMode === \"manual\") {\n return lifecycleFailure(\"forbidden\");\n }\n\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const { ref, path } = await this.#ownedPrototype(\n verifiedIdentity,\n input,\n );\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n await this.#prepareMutationBranch(verifiedIdentity.login);\n await commitPath(this.#repository, path, `save: ${ref.slug}`);\n await pushBranch(\n this.#repository,\n this.#runtime.remote,\n verifiedIdentity.login,\n );\n return { saved: true } satisfies SavePrototypeValue;\n },\n );\n });\n }\n\n async discard(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DiscardPrototypeValue>> {\n if (this.#runtime.gitMode === \"manual\") {\n return lifecycleFailure(\"forbidden\");\n }\n\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const { path } = await this.#ownedPrototype(\n verifiedIdentity,\n input,\n );\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n await discardPath(this.#repository, path);\n return { discarded: true } satisfies DiscardPrototypeValue;\n },\n );\n });\n }\n\n async duplicate(\n identity: BonfireIdentity,\n input: DuplicatePrototypeInput,\n ): Promise<LifecycleResult<DuplicatePrototypeValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parseDuplicatePrototypeInput(input);\n if (!(await this.#catalog.getSummary(normalizedInput.source))) {\n throw new PrototypeDuplicateError(\"source-not-found\");\n }\n const ref = destinationRef(verifiedIdentity, normalizedInput.title);\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n if (await this.#catalog.getSummary(ref)) {\n throw new PrototypeDuplicateError(\"collision\");\n }\n\n const branch =\n this.#runtime.gitMode === \"managed\"\n ? await this.#prepareMutationBranch(verifiedIdentity.login)\n : undefined;\n const prepared = await duplicatePrototype({\n paths: this.#runtime,\n source: normalizedInput.source,\n destination: ref,\n title: normalizedInput.title,\n description: normalizedInput.description,\n authorName: verifiedIdentity.name,\n themeRegistry: this.#themeRegistry,\n });\n await this.#prepareDestination(\n prepared.receipt,\n prepared.expectedFiles,\n {\n operation: \"duplicate\",\n repositoryRoot: this.#runtime.repositoryRoot,\n prototypeRoot: this.#runtime.prototypeRoot,\n prototypePath: prepared.destination,\n ref,\n source: normalizedInput.source,\n identity: verifiedIdentity,\n title: normalizedInput.title,\n description: normalizedInput.description,\n },\n );\n if (branch === undefined) {\n return {\n ref,\n url: getPrototypeUrl(ref),\n remoteStatus: \"manual\",\n } satisfies DuplicatePrototypeValue;\n }\n\n await commitPath(\n this.#repository,\n repositoryRelativePath(this.#runtime, ref),\n `init: ${ref.slug}`,\n );\n const push = await pushBranch(\n this.#repository,\n this.#runtime.remote,\n branch,\n );\n\n return {\n ref,\n url: getPrototypeUrl(ref),\n remoteStatus: push.remoteStatus,\n } satisfies DuplicatePrototypeValue;\n },\n );\n });\n }\n}\n"]}
1
+ {"version":3,"file":"local-prototype-lifecycle.js","sourceRoot":"","sources":["../../src/lifecycle/local-prototype-lifecycle.ts"],"names":[],"mappings":"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","sourcesContent":["import { constants } from \"node:fs\";\nimport { lstat, open, rename, rm, writeFile } from \"node:fs/promises\";\nimport { randomUUID } from \"node:crypto\";\nimport { join, relative, sep } from \"node:path\";\nimport {\n definePrototypeRef,\n getPrototypeUrl,\n LifecycleInputError,\n lifecycleFailure,\n lifecycleSuccess,\n parseCreatePrototypeInput,\n parseDuplicatePrototypeInput,\n parseEditPrototypeInput,\n parseBonfireIdentity,\n parsePrototypeCommandInput,\n parsePrototypeMetadata,\n slugifyPrototypeTitle,\n PrototypeRefError,\n type BranchPreparationValue,\n type CreatePrototypeInput,\n type CreatePrototypeValue,\n type DeletePrototypeValue,\n type DeploymentValue,\n type DeploymentWarningCode,\n type DirtyObservationValue,\n type DiscardPrototypeValue,\n type DuplicatePrototypeInput,\n type DuplicatePrototypeValue,\n type EditPrototypeInput,\n type EditPrototypeValue,\n parseGitModeSetting,\n type GitMode,\n type LifecycleErrorCode,\n type LifecycleResult,\n type BonfireIdentity,\n type PrototypeCommandInput,\n type SavePrototypeValue,\n type RepositorySummary,\n type ThemeRegistry,\n} from \"@ebay-dt/bonfire-core\";\nimport {\n commitPath,\n discardPath,\n GitRepositoryError,\n pushBranch,\n pushCommitToBranch,\n type GitRepository,\n} from \"../git-repository.js\";\nimport { duplicatePrototype, PrototypeDuplicateError } from \"../duplicate.js\";\nimport {\n PrototypeCatalog,\n type PrototypeCatalogLike,\n} from \"../prototype-catalog.js\";\nimport { PrototypeScaffoldError, scaffoldPrototype } from \"../scaffold.js\";\nimport {\n createPrototypeLifecycleRuntime,\n type PrototypeLifecycleRuntimeContext,\n} from \"./runtime-context.js\";\nimport type {\n PreparePrototypeFiles,\n PreparePrototypeFilesRequest,\n} from \"../prepare-prototype-files.js\";\nimport {\n cleanupOwnedDestination,\n validateOwnedDestination,\n type OwnedDestinationReceipt,\n} from \"../internal/owned-destination.js\";\nimport {\n RepositoryBusyError,\n withRepositoryOperation,\n} from \"./operation-gate.js\";\nimport {\n DeploymentProviderError,\n type DeploymentProvider,\n} from \"../deployment-provider.js\";\nimport {\n assertIdentityBranchReady,\n synchronizeIdentityBranch,\n} from \"./repository-synchronization.js\";\nimport { getRepositorySummary } from \"./repository-summary.js\";\nimport {\n DeploymentWorktreeError,\n preflightDeploymentSource,\n publishDeploymentSource,\n reportDeploymentIssue,\n} from \"./deployment-worktree.js\";\n\ntype LifecycleMethod<Input, Value> = (\n identity: BonfireIdentity,\n input: Input,\n) => Promise<LifecycleResult<Value>>;\n\nexport type PrototypeLifecycle = {\n /** Declares whether this lifecycle enforces the selected Git mode. */\n readonly gitMode?: GitMode;\n prepareBranch(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<BranchPreparationValue>>;\n create: LifecycleMethod<CreatePrototypeInput, CreatePrototypeValue>;\n duplicate: LifecycleMethod<DuplicatePrototypeInput, DuplicatePrototypeValue>;\n edit: LifecycleMethod<EditPrototypeInput, EditPrototypeValue>;\n delete: LifecycleMethod<PrototypeCommandInput, DeletePrototypeValue>;\n getDirtyState: LifecycleMethod<PrototypeCommandInput, DirtyObservationValue>;\n getRepositorySummary(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<RepositorySummary>>;\n checkRepositorySummary(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<RepositorySummary>>;\n save: LifecycleMethod<PrototypeCommandInput, SavePrototypeValue>;\n discard: LifecycleMethod<PrototypeCommandInput, DiscardPrototypeValue>;\n deploy: LifecycleMethod<PrototypeCommandInput, DeploymentValue>;\n retryPublish?: LifecycleMethod<PrototypeCommandInput, DeploymentValue>;\n};\n\nexport type LocalPrototypeLifecycleOptions = {\n repositoryRoot: string;\n prototypeRoot?: string;\n remote?: string;\n primaryBranch?: string;\n automaticBranchSwitch?: \"enabled\" | \"disabled\";\n gitMode?: GitMode;\n commitIdentity?: {\n name: string;\n email: string;\n };\n catalog?: PrototypeCatalogLike;\n preparePrototypeFiles?: PreparePrototypeFiles;\n deploymentProvider?: DeploymentProvider;\n applicationId?: string;\n themeRegistry?: ThemeRegistry;\n};\n\ntype InternalLocalPrototypeLifecycleOptions = LocalPrototypeLifecycleOptions & {\n runtime?: PrototypeLifecycleRuntimeContext;\n};\n\nfunction repositoryRelativePath(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n) {\n const absolutePath = join(runtime.prototypeRoot, ref.login, ref.slug);\n return relative(runtime.repositoryRoot, absolutePath).split(sep).join(\"/\");\n}\n\nfunction destinationRef(identity: BonfireIdentity, title: string) {\n const slug = slugifyPrototypeTitle(title);\n if (slug === null)\n throw new PrototypeRefError(\"title\", \"must produce a slug\");\n return definePrototypeRef({ login: identity.login, slug });\n}\n\nclass PrototypeNotFoundError extends Error {}\nclass PrototypeOwnershipError extends Error {}\nclass PrototypeMetadataFileError extends Error {}\nclass DeploymentUnavailableError extends Error {}\nclass DeploymentSourcePushError extends Error {}\nclass DeploymentNotAcceptedError extends Error {}\n\ntype PendingPublication = {\n identity: BonfireIdentity;\n input: PrototypeCommandInput;\n ref: { login: string; slug: string };\n path: string;\n previewUrl: string;\n deploymentId: string;\n sourceSha: string;\n readyState: DeploymentValue[\"readyState\"];\n synchronizationWarning: DeploymentWarningCode | undefined;\n};\n\nfunction metadataPath(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n) {\n return join(runtime.prototypeRoot, ref.login, ref.slug, \"metadata.json\");\n}\n\nasync function assertPrototypeDirectory(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n) {\n const paths = [\n runtime.prototypeRoot,\n join(runtime.prototypeRoot, ref.login),\n join(runtime.prototypeRoot, ref.login, ref.slug),\n ];\n\n try {\n for (const path of paths) {\n const stats = await lstat(path);\n if (stats.isSymbolicLink() || !stats.isDirectory()) {\n throw new PrototypeMetadataFileError();\n }\n }\n } catch (error) {\n if (error instanceof PrototypeMetadataFileError) throw error;\n throw new PrototypeMetadataFileError();\n }\n}\n\nasync function readMetadataDocument(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n themeRegistry?: ThemeRegistry,\n) {\n const path = metadataPath(runtime, ref);\n try {\n const file = await open(\n path,\n constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0),\n );\n try {\n const source = await file.readFile(\"utf8\");\n const document: unknown = JSON.parse(source);\n parsePrototypeMetadata(document, { themeRegistry });\n if (\n typeof document !== \"object\" ||\n document === null ||\n Array.isArray(document)\n ) {\n throw new PrototypeMetadataFileError();\n }\n return { ...(document as Record<string, unknown>) };\n } finally {\n await file.close();\n }\n } catch (error) {\n if (error instanceof PrototypeMetadataFileError) throw error;\n throw new PrototypeMetadataFileError();\n }\n}\n\nasync function removePrototypeDirectory(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n) {\n await assertPrototypeDirectory(runtime, ref);\n try {\n await rm(join(runtime.prototypeRoot, ref.login, ref.slug), {\n force: false,\n recursive: true,\n });\n } catch {\n throw new PrototypeMetadataFileError();\n }\n}\n\nasync function writeMetadataDocument(\n runtime: PrototypeLifecycleRuntimeContext,\n ref: { login: string; slug: string },\n document: Record<string, unknown>,\n) {\n const path = metadataPath(runtime, ref);\n const temporaryPath = `${path}.${randomUUID()}.tmp`;\n\n try {\n const stats = await lstat(path);\n if (stats.isSymbolicLink() || !stats.isFile()) {\n throw new PrototypeMetadataFileError();\n }\n await writeFile(temporaryPath, `${JSON.stringify(document, null, 2)}\\n`, {\n encoding: \"utf8\",\n flag: \"wx\",\n });\n await rename(temporaryPath, path);\n } catch (error) {\n if (error instanceof PrototypeMetadataFileError) throw error;\n throw new PrototypeMetadataFileError();\n } finally {\n await rm(temporaryPath, { force: true }).catch(() => undefined);\n }\n}\n\nfunction mapError(error: unknown): LifecycleErrorCode {\n if (error instanceof PrototypeNotFoundError) return \"not-found\";\n if (error instanceof PrototypeOwnershipError) return \"forbidden\";\n if (error instanceof PrototypeMetadataFileError) return \"filesystem-failure\";\n if (error instanceof RepositoryBusyError) return \"repository-busy\";\n if (\n error instanceof PrototypeRefError ||\n error instanceof LifecycleInputError\n ) {\n return \"invalid-input\";\n }\n if (error instanceof GitRepositoryError) {\n if (error.kind === \"timeout\") return \"timeout\";\n if (error.kind === \"conflict\") return \"working-tree-conflict\";\n if (error.kind === \"invalid-configuration\") return \"unexpected-failure\";\n return \"git-failure\";\n }\n if (error instanceof PrototypeScaffoldError) {\n if (error.kind === \"collision\") return \"already-exists\";\n if (error.kind === \"invalid-input\") return \"invalid-input\";\n return \"filesystem-failure\";\n }\n if (error instanceof PrototypeDuplicateError) {\n if (error.kind === \"source-not-found\") return \"not-found\";\n if (error.kind === \"collision\") return \"already-exists\";\n if (error.kind === \"invalid-source\") return \"invalid-input\";\n return \"filesystem-failure\";\n }\n if (error instanceof DeploymentUnavailableError) {\n return \"deployment-unavailable\";\n }\n if (\n error instanceof DeploymentSourcePushError ||\n error instanceof DeploymentNotAcceptedError ||\n error instanceof DeploymentWorktreeError\n ) {\n return \"deployment-failure\";\n }\n if (error instanceof DeploymentProviderError) {\n return error.kind === \"timeout\" ? \"timeout\" : \"deployment-failure\";\n }\n return \"unexpected-failure\";\n}\n\nfunction resultFrom<Value>(\n operation: () => Promise<Value>,\n): Promise<LifecycleResult<Value>> {\n return operation()\n .then(lifecycleSuccess)\n .catch((error: unknown) => lifecycleFailure(mapError(error)));\n}\n\nasync function diagnoseMetadataStep<Value>(\n step: \"metadata-read\" | \"metadata-write\" | \"metadata-commit\",\n operation: () => Promise<Value>,\n): Promise<Value> {\n try {\n return await operation();\n } catch (error) {\n reportDeploymentIssue(\"publication\", step, error);\n throw error;\n }\n}\n\nexport class LocalPrototypeLifecycle implements PrototypeLifecycle {\n readonly #runtime: PrototypeLifecycleRuntimeContext;\n readonly gitMode: GitMode;\n readonly #catalog: PrototypeCatalogLike;\n readonly #repository: GitRepository;\n readonly #preparePrototypeFiles: PreparePrototypeFiles;\n readonly #deploymentProvider?: DeploymentProvider;\n readonly #applicationId?: string;\n readonly #themeRegistry?: ThemeRegistry;\n #restartRequired = false;\n #pendingPublication?: PendingPublication;\n\n constructor(options: LocalPrototypeLifecycleOptions) {\n const internalOptions = options as InternalLocalPrototypeLifecycleOptions;\n const runtime =\n internalOptions.runtime ??\n createPrototypeLifecycleRuntime(internalOptions);\n this.#runtime = {\n ...runtime,\n gitMode: parseGitModeSetting(runtime.gitMode),\n };\n this.gitMode = this.#runtime.gitMode;\n this.#themeRegistry = options.themeRegistry;\n this.#catalog =\n internalOptions.catalog ??\n new PrototypeCatalog({\n ...this.#runtime,\n themeRegistry: this.#themeRegistry,\n });\n this.#repository = this.#runtime.gitRepository;\n this.#preparePrototypeFiles =\n internalOptions.preparePrototypeFiles ?? (() => undefined);\n this.#deploymentProvider = internalOptions.deploymentProvider;\n this.#applicationId = internalOptions.applicationId;\n if (\n this.#deploymentProvider &&\n (typeof this.#applicationId !== \"string\" ||\n this.#applicationId.length === 0)\n ) {\n throw new TypeError(\n \"applicationId must be provided when deploymentProvider is configured\",\n );\n }\n }\n\n async #verifiedIdentity(identity: BonfireIdentity) {\n return parseBonfireIdentity(identity);\n }\n\n async #currentBranch() {\n const branch = await this.#repository.currentBranch();\n if (branch === \"HEAD\" || branch.length === 0) {\n throw new GitRepositoryError(\"conflict\", \"current-branch\");\n }\n return branch;\n }\n\n async #ownedPrototype(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ) {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parsePrototypeCommandInput(input);\n if (normalizedInput.ref.login !== verifiedIdentity.login) {\n throw new PrototypeOwnershipError();\n }\n const summary = await this.#catalog.getSummary(normalizedInput.ref);\n if (!summary) {\n throw new PrototypeNotFoundError();\n }\n\n return {\n ref: normalizedInput.ref,\n path: repositoryRelativePath(this.#runtime, normalizedInput.ref),\n summary,\n };\n }\n\n async #prepareMutationBranch(login: string) {\n await assertIdentityBranchReady(this.#repository, login);\n return login;\n }\n\n async #prepareDestination(\n receipt: OwnedDestinationReceipt,\n expectedFiles: readonly string[],\n request: PreparePrototypeFilesRequest,\n ) {\n try {\n const frozenRequest = Object.freeze({\n ...request,\n ref: Object.freeze({ ...request.ref }),\n identity: Object.freeze({ ...request.identity }),\n ...(request.operation === \"duplicate\"\n ? { source: Object.freeze({ ...request.source }) }\n : {}),\n }) as PreparePrototypeFilesRequest;\n await this.#preparePrototypeFiles(frozenRequest);\n await validateOwnedDestination(\n receipt,\n expectedFiles,\n request.ref,\n this.#themeRegistry,\n );\n } catch {\n try {\n await cleanupOwnedDestination(receipt);\n } catch {\n // The failure remains a finite filesystem failure without retrying cleanup.\n }\n throw new PrototypeScaffoldError(\"filesystem\");\n }\n }\n\n async prepareBranch(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<BranchPreparationValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n if (\n this.#runtime.gitMode === \"manual\" ||\n this.#runtime.automaticBranchSwitch === \"disabled\"\n ) {\n await this.#currentBranch();\n return { status: \"disabled\" } satisfies BranchPreparationValue;\n }\n\n const synchronization = await synchronizeIdentityBranch(\n this.#repository,\n {\n identityBranch: verifiedIdentity.login,\n primaryBranch: this.#runtime.primaryBranch,\n remote: this.#runtime.remote,\n managedPaths: this.#runtime.managedRepositoryPaths,\n },\n );\n if (synchronization.status !== \"synchronized\") {\n return {\n status: synchronization.status,\n reason: synchronization.reason,\n ...(synchronization.affectedPaths.length > 0\n ? { affectedPaths: synchronization.affectedPaths }\n : {}),\n } satisfies BranchPreparationValue;\n }\n if (\n synchronization.affectedPaths.some((path) =>\n this.#runtime.managedRepositoryPaths.includes(path),\n )\n ) {\n this.#restartRequired = true;\n }\n return {\n status: \"ready\",\n ...(synchronization.affectedPaths.length > 0\n ? { affectedPaths: synchronization.affectedPaths }\n : {}),\n } satisfies BranchPreparationValue;\n },\n );\n });\n }\n\n async create(\n identity: BonfireIdentity,\n input: CreatePrototypeInput,\n ): Promise<LifecycleResult<CreatePrototypeValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parseCreatePrototypeInput(\n input,\n this.#themeRegistry,\n );\n const ref = destinationRef(verifiedIdentity, normalizedInput.title);\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n if (await this.#catalog.getSummary(ref)) {\n throw new PrototypeScaffoldError(\"collision\");\n }\n\n const branch =\n this.#runtime.gitMode === \"managed\"\n ? await this.#prepareMutationBranch(verifiedIdentity.login)\n : undefined;\n const prepared = await scaffoldPrototype({\n paths: this.#runtime,\n ref,\n title: normalizedInput.title,\n description: normalizedInput.description,\n authorName: verifiedIdentity.name,\n theme: normalizedInput.theme,\n themeRegistry: this.#themeRegistry,\n });\n await this.#prepareDestination(\n prepared.receipt,\n prepared.expectedFiles,\n {\n operation: \"create\",\n repositoryRoot: this.#runtime.repositoryRoot,\n prototypeRoot: this.#runtime.prototypeRoot,\n prototypePath: prepared.destination,\n ref,\n identity: verifiedIdentity,\n title: normalizedInput.title,\n description: normalizedInput.description,\n },\n );\n if (branch === undefined) {\n return {\n ref,\n url: getPrototypeUrl(ref),\n remoteStatus: \"manual\",\n } satisfies CreatePrototypeValue;\n }\n\n await commitPath(\n this.#repository,\n repositoryRelativePath(this.#runtime, ref),\n `init: ${ref.slug}`,\n );\n const push = await pushBranch(\n this.#repository,\n this.#runtime.remote,\n branch,\n );\n\n return {\n ref,\n url: getPrototypeUrl(ref),\n remoteStatus: push.remoteStatus,\n } satisfies CreatePrototypeValue;\n },\n );\n });\n }\n\n async edit(\n identity: BonfireIdentity,\n input: EditPrototypeInput,\n ): Promise<LifecycleResult<EditPrototypeValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parseEditPrototypeInput(\n input,\n this.#themeRegistry,\n );\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n const { ref, path } = await this.#ownedPrototype(verifiedIdentity, {\n ref: normalizedInput.ref,\n });\n const branch =\n this.#runtime.gitMode === \"managed\"\n ? await this.#prepareMutationBranch(verifiedIdentity.login)\n : undefined;\n await assertPrototypeDirectory(this.#runtime, ref);\n const document = await readMetadataDocument(\n this.#runtime,\n ref,\n this.#themeRegistry,\n );\n const metadata = parsePrototypeMetadata(document, {\n themeRegistry: this.#themeRegistry,\n });\n if (metadata.authorLogin !== ref.login) {\n throw new PrototypeNotFoundError();\n }\n\n await writeMetadataDocument(this.#runtime, ref, {\n ...document,\n title: normalizedInput.title,\n description: normalizedInput.description,\n updatedAt: new Date().toISOString(),\n ...(normalizedInput.theme === undefined\n ? {}\n : { theme: normalizedInput.theme }),\n });\n if (branch !== undefined) {\n await commitPath(this.#repository, path, `edit: ${ref.slug}`);\n await pushBranch(this.#repository, this.#runtime.remote, branch);\n }\n\n return { edited: true, ref } satisfies EditPrototypeValue;\n },\n );\n });\n }\n\n async delete(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DeletePrototypeValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parsePrototypeCommandInput(input);\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n const { ref, path } = await this.#ownedPrototype(\n verifiedIdentity,\n normalizedInput,\n );\n const branch =\n this.#runtime.gitMode === \"managed\"\n ? await this.#prepareMutationBranch(verifiedIdentity.login)\n : undefined;\n await removePrototypeDirectory(this.#runtime, ref);\n if (branch !== undefined) {\n await commitPath(this.#repository, path, `delete: ${ref.slug}`);\n await pushBranch(this.#repository, this.#runtime.remote, branch);\n }\n\n return { deleted: true, ref } satisfies DeletePrototypeValue;\n },\n );\n });\n }\n\n async getDirtyState(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DirtyObservationValue>> {\n return resultFrom(async () => {\n const { path, summary } = await this.#ownedPrototype(identity, input);\n const status = await this.#repository.status([path]);\n return {\n status: status.files.length > 0 ? \"dirty\" : \"clean\",\n ...(summary.previewUrl !== undefined\n ? { previewUrl: summary.previewUrl }\n : {}),\n } satisfies DirtyObservationValue;\n });\n }\n\n async getRepositorySummary(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<RepositorySummary>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n return getRepositorySummary({\n repository: this.#repository,\n catalog: this.#catalog,\n repositoryRoot: this.#runtime.repositoryRoot,\n prototypeRoot: this.#runtime.prototypeRoot,\n identity: verifiedIdentity,\n mode: this.#runtime.gitMode,\n remote: this.#runtime.remote,\n primaryBranch: this.#runtime.primaryBranch,\n restartRequired: this.#restartRequired,\n });\n });\n }\n\n async checkRepositorySummary(\n identity: BonfireIdentity,\n ): Promise<LifecycleResult<RepositorySummary>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n return getRepositorySummary({\n repository: this.#repository,\n catalog: this.#catalog,\n repositoryRoot: this.#runtime.repositoryRoot,\n prototypeRoot: this.#runtime.prototypeRoot,\n identity: verifiedIdentity,\n mode: this.#runtime.gitMode,\n remote: this.#runtime.remote,\n primaryBranch: this.#runtime.primaryBranch,\n restartRequired: this.#restartRequired,\n refresh: true,\n });\n });\n }\n\n async #headCommit() {\n const commit = (await this.#repository.raw([\"rev-parse\", \"HEAD\"])).trim();\n if (!/^[0-9a-f]{40,64}$/iu.test(commit)) {\n throw new GitRepositoryError(\"malformed-output\", \"head-commit\");\n }\n return commit.toLowerCase();\n }\n\n async #prepareDeploymentBranch(login: string) {\n if (this.#runtime.automaticBranchSwitch === \"disabled\") {\n await assertIdentityBranchReady(this.#repository, login);\n return \"updates-not-synchronized\" as const;\n }\n\n const synchronization = await synchronizeIdentityBranch(this.#repository, {\n identityBranch: login,\n primaryBranch: this.#runtime.primaryBranch,\n remote: this.#runtime.remote,\n managedPaths: this.#runtime.managedRepositoryPaths,\n });\n if (synchronization.status === \"unsafe\") {\n throw new GitRepositoryError(\"conflict\", \"deployment-preparation\");\n }\n await assertIdentityBranchReady(this.#repository, login);\n return synchronization.status === \"deferred\"\n ? (\"updates-not-synchronized\" as const)\n : undefined;\n }\n\n #workspaceRuntime(\n workspacePath: string,\n workspaceRepository: GitRepository,\n ): PrototypeLifecycleRuntimeContext {\n return {\n ...this.#runtime,\n repositoryRoot: workspacePath,\n repositoryRealPath: workspacePath,\n prototypeRoot: join(\n workspacePath,\n relative(this.#runtime.repositoryRoot, this.#runtime.prototypeRoot),\n ),\n gitRepository: workspaceRepository,\n };\n }\n\n async #publishDeploymentMetadata(pending: PendingPublication) {\n return publishDeploymentSource(\n this.#repository,\n {\n remote: this.#runtime.remote,\n primaryBranch: this.#runtime.primaryBranch,\n sourceSha: pending.sourceSha,\n commitIdentity: this.#runtime.commitIdentity,\n },\n async ({ path, repository }) => {\n const workspaceRuntime = this.#workspaceRuntime(path, repository);\n const mergedDocument = await diagnoseMetadataStep(\"metadata-read\", () =>\n readMetadataDocument(\n workspaceRuntime,\n pending.ref,\n this.#themeRegistry,\n ),\n );\n await diagnoseMetadataStep(\"metadata-write\", () =>\n writeMetadataDocument(workspaceRuntime, pending.ref, {\n ...mergedDocument,\n previewUrl: pending.previewUrl,\n updatedAt: new Date().toISOString(),\n }),\n );\n await diagnoseMetadataStep(\"metadata-commit\", () =>\n commitPath(\n repository,\n pending.path,\n `deploy: ${pending.ref.slug} (metadata)`,\n ),\n );\n },\n );\n }\n\n async #deploymentValue(\n pending: PendingPublication,\n publicationWarnings: readonly DeploymentWarningCode[] = [],\n ): Promise<DeploymentValue> {\n const warnings = [\n ...(pending.synchronizationWarning === undefined\n ? []\n : [pending.synchronizationWarning]),\n ...publicationWarnings,\n ];\n return {\n deployed: true,\n ref: pending.ref,\n previewUrl: pending.previewUrl,\n deploymentId: pending.deploymentId,\n sourceSha: pending.sourceSha,\n readyState: pending.readyState,\n warning: publicationWarnings[0] ?? pending.synchronizationWarning,\n ...(warnings.length === 0 ? {} : { warnings }),\n };\n }\n\n async deploy(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DeploymentValue>> {\n if (this.#runtime.gitMode === \"manual\") {\n return lifecycleFailure(\"forbidden\");\n }\n\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parsePrototypeCommandInput(input);\n if (!this.#deploymentProvider) {\n throw new DeploymentUnavailableError();\n }\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n const { ref, path } = await this.#ownedPrototype(\n verifiedIdentity,\n normalizedInput,\n );\n const synchronizationWarning = await this.#prepareDeploymentBranch(\n verifiedIdentity.login,\n );\n await assertPrototypeDirectory(this.#runtime, ref);\n const document = await readMetadataDocument(\n this.#runtime,\n ref,\n this.#themeRegistry,\n );\n const metadata = parsePrototypeMetadata(document, {\n themeRegistry: this.#themeRegistry,\n });\n if (metadata.authorLogin !== ref.login) {\n throw new PrototypeNotFoundError();\n }\n\n await commitPath(this.#repository, path, `deploy: ${ref.slug}`);\n const sourceSha = await this.#headCommit();\n const sourcePush = await pushCommitToBranch(\n this.#repository,\n this.#runtime.remote,\n sourceSha,\n verifiedIdentity.login,\n );\n if (sourcePush.remoteStatus !== \"pushed\") {\n throw new DeploymentSourcePushError();\n }\n\n await preflightDeploymentSource(this.#repository, {\n remote: this.#runtime.remote,\n primaryBranch: this.#runtime.primaryBranch,\n sourceSha,\n commitIdentity: this.#runtime.commitIdentity,\n });\n\n const deployment = await this.#deploymentProvider!.deploy({\n applicationId: this.#applicationId!,\n login: verifiedIdentity.login,\n });\n if (\n deployment.commitSha.toLowerCase() !== sourceSha ||\n ![\"READY\", \"QUEUED\", \"INITIALIZING\", \"BUILDING\"].includes(\n deployment.readyState,\n )\n ) {\n throw new DeploymentNotAcceptedError();\n }\n const previewUrl = new URL(\n getPrototypeUrl(ref).slice(1),\n `${deployment.url.replace(/\\/+$/, \"\")}/`,\n ).toString();\n const pending: PendingPublication = {\n identity: verifiedIdentity,\n input: normalizedInput,\n ref,\n path,\n previewUrl,\n deploymentId: deployment.deploymentId,\n sourceSha,\n readyState: deployment.readyState,\n synchronizationWarning,\n };\n\n try {\n const publication = await this.#publishDeploymentMetadata(pending);\n this.#pendingPublication = undefined;\n return this.#deploymentValue(\n pending,\n publication.cleanupFailed ? [\"publication-cleanup-failed\"] : [],\n );\n } catch (error) {\n // Keep the accepted deployment retryable for every publication\n // failure, including metadata preparation failures.\n this.#pendingPublication = pending;\n return this.#deploymentValue(pending, [\n error instanceof DeploymentWorktreeError &&\n error.kind !== \"cleanup-failure\"\n ? \"primary-merge-failed\"\n : \"metadata-persistence-failed\",\n ...(error instanceof DeploymentWorktreeError &&\n error.cleanupFailed\n ? [\"publication-cleanup-failed\" as const]\n : []),\n ]);\n }\n },\n );\n });\n }\n\n async retryPublish(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DeploymentValue>> {\n if (this.#runtime.gitMode === \"manual\") {\n return lifecycleFailure(\"forbidden\");\n }\n\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parsePrototypeCommandInput(input);\n const pending = this.#pendingPublication;\n if (\n pending === undefined ||\n pending.identity.login !== verifiedIdentity.login ||\n pending.ref.login !== normalizedInput.ref.login ||\n pending.ref.slug !== normalizedInput.ref.slug\n ) {\n throw new DeploymentUnavailableError();\n }\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n await assertIdentityBranchReady(\n this.#repository,\n verifiedIdentity.login,\n );\n const publication = await this.#publishDeploymentMetadata(pending);\n this.#pendingPublication = undefined;\n return this.#deploymentValue(\n pending,\n publication.cleanupFailed ? [\"publication-cleanup-failed\"] : [],\n );\n },\n );\n });\n }\n\n async save(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<SavePrototypeValue>> {\n if (this.#runtime.gitMode === \"manual\") {\n return lifecycleFailure(\"forbidden\");\n }\n\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const { ref, path } = await this.#ownedPrototype(verifiedIdentity, input);\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n await this.#prepareMutationBranch(verifiedIdentity.login);\n await commitPath(this.#repository, path, `save: ${ref.slug}`);\n await pushBranch(\n this.#repository,\n this.#runtime.remote,\n verifiedIdentity.login,\n );\n return { saved: true } satisfies SavePrototypeValue;\n },\n );\n });\n }\n\n async discard(\n identity: BonfireIdentity,\n input: PrototypeCommandInput,\n ): Promise<LifecycleResult<DiscardPrototypeValue>> {\n if (this.#runtime.gitMode === \"manual\") {\n return lifecycleFailure(\"forbidden\");\n }\n\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const { path } = await this.#ownedPrototype(verifiedIdentity, input);\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n await discardPath(this.#repository, path);\n return { discarded: true } satisfies DiscardPrototypeValue;\n },\n );\n });\n }\n\n async duplicate(\n identity: BonfireIdentity,\n input: DuplicatePrototypeInput,\n ): Promise<LifecycleResult<DuplicatePrototypeValue>> {\n return resultFrom(async () => {\n const verifiedIdentity = await this.#verifiedIdentity(identity);\n const normalizedInput = parseDuplicatePrototypeInput(input);\n if (!(await this.#catalog.getSummary(normalizedInput.source))) {\n throw new PrototypeDuplicateError(\"source-not-found\");\n }\n const ref = destinationRef(verifiedIdentity, normalizedInput.title);\n\n return withRepositoryOperation(\n this.#runtime.repositoryRealPath,\n async () => {\n if (await this.#catalog.getSummary(ref)) {\n throw new PrototypeDuplicateError(\"collision\");\n }\n\n const branch =\n this.#runtime.gitMode === \"managed\"\n ? await this.#prepareMutationBranch(verifiedIdentity.login)\n : undefined;\n const prepared = await duplicatePrototype({\n paths: this.#runtime,\n source: normalizedInput.source,\n destination: ref,\n title: normalizedInput.title,\n description: normalizedInput.description,\n authorName: verifiedIdentity.name,\n themeRegistry: this.#themeRegistry,\n });\n await this.#prepareDestination(\n prepared.receipt,\n prepared.expectedFiles,\n {\n operation: \"duplicate\",\n repositoryRoot: this.#runtime.repositoryRoot,\n prototypeRoot: this.#runtime.prototypeRoot,\n prototypePath: prepared.destination,\n ref,\n source: normalizedInput.source,\n identity: verifiedIdentity,\n title: normalizedInput.title,\n description: normalizedInput.description,\n },\n );\n if (branch === undefined) {\n return {\n ref,\n url: getPrototypeUrl(ref),\n remoteStatus: \"manual\",\n } satisfies DuplicatePrototypeValue;\n }\n\n await commitPath(\n this.#repository,\n repositoryRelativePath(this.#runtime, ref),\n `init: ${ref.slug}`,\n );\n const push = await pushBranch(\n this.#repository,\n this.#runtime.remote,\n branch,\n );\n\n return {\n ref,\n url: getPrototypeUrl(ref),\n remoteStatus: push.remoteStatus,\n } satisfies DuplicatePrototypeValue;\n },\n );\n });\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ebay-dt/bonfire-node",
3
- "version": "0.2.0-alpha.8",
3
+ "version": "0.2.0-alpha.9",
4
4
  "description": "Node.js adapters for Bonfire.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -31,7 +31,7 @@
31
31
  "access": "public"
32
32
  },
33
33
  "dependencies": {
34
- "@ebay-dt/bonfire-core": "0.2.0-alpha.8",
34
+ "@ebay-dt/bonfire-core": "0.2.0-alpha.9",
35
35
  "chokidar": "^5.0.0",
36
36
  "simple-git": "^3.36.0"
37
37
  },