function createMockDeploymentBridge(context) { const { wait, clone, iso, storage, repositoryListeners, operationListeners, updateListeners, emitRepositories, emitOperations, randomSha, now, profile, state, repositories, recompute, snapshot, commitHistory, advanceOperation, syncState, diffs, findRepo, findProfileRepo, branchesByRepo, stashesByRepo, updateOperation } = context; return { async saveDeploymentProfile(fullName, input) { const repo = repositories.find((item) => item.fullName === fullName); const existing = repo.deploymentProfiles.find( (item) => item.id === input.id, ); const saved = { ...(existing || profile( input.id || `profile-${Date.now()}`, input.name || input.environment, input.environment || "production", )), ...input, id: input.id || `profile-${Date.now()}`, provider: input.provider || existing?.provider || "gitea-actions", inputs: existing?.inputs || {}, state: existing?.state || { liveSha: null, previousSha: null, healthy: null, healthConfigured: Boolean(input.healthcheckUrl), statusConfigured: Boolean(input.statusUrl), checkedAt: null, }, }; repo.deploymentProfiles = [ ...repo.deploymentProfiles.filter((item) => item.id !== saved.id), saved, ]; snapshot(); return { profile: clone(saved), state: clone(state) }; }, async deleteDeploymentProfile(fullName, profileId) { const repo = repositories.find((item) => item.fullName === fullName); repo.deploymentProfiles = repo.deploymentProfiles.filter( (item) => item.id !== profileId, ); snapshot(); return { profiles: clone(repo.deploymentProfiles), state: clone(state) }; }, async deploymentPreflight(repository, profileId) { await wait(280); const profile = repository.deploymentProfiles.find( (item) => item.id === profileId, ); const status = repository.localStatus; const checks = [ { id: "repository.linked", label: "Local repository link", status: repository.localPath ? "pass" : "fail", detail: repository.localPath || "No local folder linked.", required: true, }, { id: "git.branch", label: "Allowed branch", status: status?.branch.head === profile?.branch ? "pass" : "fail", detail: `Current: ${status?.branch.head || "unknown"}; required: ${profile?.branch || "unknown"}.`, required: true, }, { id: "git.clean", label: "Clean working tree", status: status?.clean ? "pass" : "fail", detail: status?.clean ? "No uncommitted changes." : `${status?.counts.changed || 0} changed file(s).`, required: true, }, { id: "git.sync", label: "Local and Gitea synchronized", status: !status?.branch.ahead && !status?.branch.behind ? "pass" : "fail", detail: `${status?.branch.ahead || 0} ahead, ${status?.branch.behind || 0} behind.`, required: true, }, { id: "workflow.deploy.remote", label: "Deploy workflow on Gitea branch", status: "pass", detail: `${profile?.workflowFile || "deploy.yml"} exists on ${profile?.branch || "main"}.`, required: true, }, { id: "gitea.actions", label: "Gitea Actions API", status: "pass", detail: "The Actions runs endpoint is accessible.", required: true, }, { id: "server.status", label: "Server version endpoint", status: profile?.statusUrl ? "pass" : "warning", detail: profile?.statusUrl ? `Endpoint reachable; live ${profile.state?.liveSha?.slice(0, 7) || "unknown"}.` : "No status URL configured.", required: false, }, { id: "server.health", label: "Application healthcheck", status: profile?.healthcheckUrl ? "pass" : "warning", detail: profile?.healthcheckUrl ? "HTTP 200 in 42 ms." : "No healthcheck URL configured.", required: false, }, ]; const blocking = checks .filter((i) => i.required && i.status === "fail") .map((i) => i.id); return { kind: "deployment", repository: repository.fullName, profileId, startedAt: iso(-100), completedAt: iso(), checks, summary: { counts: { pass: checks.filter((i) => i.status === "pass").length, warning: checks.filter((i) => i.status === "warning").length, fail: checks.filter((i) => i.status === "fail").length, skipped: 0, }, blocking, ready: blocking.length === 0, }, head: status?.head || null, }; }, async deploy(repository, profileId, sha) { await wait(320); const selected = repository.deploymentProfiles.find( (item) => item.id === profileId, ); const operation = { id: `deploy-${Date.now()}`, type: "deployment", action: "deploy", status: "queued", repository: repository.fullName, profileId, profileName: selected.name, environment: selected.environment, workflowFile: selected.workflowFile, branch: selected.branch, sha, shortSha: sha.slice(0, 7), dispatchedAt: iso(), createdAt: iso(), updatedAt: iso(), demoPolls: 0, stages: [ { id: "requested", label: "Requested", status: "complete" }, { id: "verified", label: "Verified", status: "complete" }, { id: "queued", label: "Workflow queued", status: "active" }, { id: "runner", label: "Runner execution", status: "pending" }, { id: "healthcheck", label: "Healthcheck", status: "pending" }, { id: "complete", label: "Complete", status: "pending" }, ], logs: [ `[info] Verified clean ${selected.branch} at ${sha}`, `[ok] Gitea accepted ${selected.workflowFile}.`, ], }; return updateOperation(operation); }, async rollback(repository, profileId, targetSha) { await wait(320); const selected = repository.deploymentProfiles.find( (item) => item.id === profileId, ); const operation = { id: `rollback-${Date.now()}`, type: "deployment", action: "rollback", status: "queued", repository: repository.fullName, profileId, profileName: selected.name, environment: selected.environment, workflowFile: selected.rollbackWorkflowFile, branch: selected.branch, sha: targetSha, shortSha: targetSha.slice(0, 7), dispatchedAt: iso(), createdAt: iso(), updatedAt: iso(), demoPolls: 0, stages: [ { id: "requested", label: "Requested", status: "complete" }, { id: "verified", label: "Verified", status: "complete" }, { id: "queued", label: "Workflow queued", status: "active" }, { id: "runner", label: "Runner execution", status: "pending" }, { id: "healthcheck", label: "Healthcheck", status: "pending" }, { id: "complete", label: "Complete", status: "pending" }, ], logs: [ `[warning] Rollback target verified: ${targetSha}`, `[ok] Gitea accepted ${selected.rollbackWorkflowFile}.`, ], }; return updateOperation(operation); }, async healthcheck() { await wait(160); return { configured: true, healthy: true, status: 200, latencyMs: 42 }; }, async refreshProfileState(fullName, profileId) { await wait(240); const repo = repositories.find((item) => item.fullName === fullName) || findProfileRepo(profileId); const target = repo?.deploymentProfiles.find( (item) => item.id === profileId, ); if (!target) throw new Error("Deployment profile not found."); target.state = { ...target.state, checkedAt: iso(), healthy: target.state.healthy !== false, healthConfigured: Boolean(target.healthcheckUrl), statusConfigured: Boolean(target.statusUrl), }; syncState(); return clone(target.state); }, async discoverServerDeployments() { await wait(80); return [ { serverId: "server-unraid", serverName: "Unraid", detected: 3, adopted: 0, verified: 1, refreshedProfiles: 1, refreshedProfileIds: ["profile-portfolio"], linked: 2, unmatched: 0, needsReview: 2, running: 3, stopped: 0, capabilities: { docker: true, dockerReady: true, compose: true, git: false, tar: true, checksum: true, }, warnings: [], workloads: [ { workloadId: "workload-demo-linked", displayName: "Portfolio", status: "linked", runtime: { running: true, health: "healthy" }, compose: { project: "portfolio", workingDir: "/mnt/user/appdata/portfolio", configFiles: ["/mnt/user/appdata/portfolio/docker-compose.yml"], services: ["web"], }, containers: [{ name: "Portfolio", running: true }], candidates: [], link: { profileId: "profile-portfolio", repositoryFullName: "jens/portfolio", source: "manual", }, }, { workloadId: "workload-demo-review", displayName: "OmniRoute", status: "suggested", runtime: { running: true, health: "unverified" }, compose: { project: "omniroute", workingDir: "/mnt/user/appdata/OmniRoute", configFiles: ["/mnt/user/appdata/OmniRoute/docker-compose.yml"], services: ["omniroute"], }, containers: [{ name: "omniroute", running: true }], remoteFolderCandidate: "OmniRoute", candidates: repositories.slice(0, 1).map((repository) => ({ repositoryFullName: repository.fullName, repositoryName: repository.name, score: 55, exact: false, reasons: ["container and repository names are similar"], })), }, { workloadId: "workload-demo-unresolved", displayName: "Legacy Worker", status: "linked", runtime: { running: true, health: "healthy" }, containers: [{ name: "legacy-worker", running: true }], candidates: [], link: { profileId: "profile-that-no-longer-exists", repositoryFullName: "jens/removed-repository", source: "manual", }, }, ], }, ]; }, async planServerReconciliation(serverId) { await wait(90); const id = "a".repeat(64); return { inventory: (await this.discoverServerDeployments()).find((item) => item.serverId === serverId), plan: { id, serverId, summary: { additions: 0, updates: 1, stale: 0, conflicts: 1 }, additions: [], updates: [{ workloadId: "workload-demo-linked", profileId: "profile-portfolio", repositoryFullName: "jens/portfolio", impact: "Refresh detected Compose identity and observed deployment state" }], stale: [], conflicts: [{ workloadId: "workload-demo-review", displayName: "OmniRoute", status: "suggested", candidates: [{ repositoryFullName: repositories[0].fullName, score: 55, exact: false }] }], }, }; }, async applyServerReconciliation(serverId, planId) { await wait(120); if (serverId !== "server-unraid" || planId !== "a".repeat(64)) throw new Error("The reconciliation plan is stale."); return { adopted: 0, refreshed: 1, retired: 0, state: clone(state) }; }, async planInventoryReview(serverId, workloadId, action, reason = "", repositoryFullName = null) { if (["ignore", "manual-exclude", "exclude-scan-root"].includes(action) && reason.length < 5) throw new Error("A meaningful review reason is required."); return { id: "b".repeat(64), serverId, workloadId, action, reason, repositoryFullName, evidenceHash: "c".repeat(64), classification: "ambiguous", containersUnaffected: true, configurationChanges: [`Persist review decision ${action}`], recovery: "Remove the decision or rescan after evidence changes." }; }, async applyInventoryReview(serverId, workloadId, action, reason, repositoryFullName, planId) { if (planId !== "b".repeat(64)) throw new Error("The inventory review plan is stale."); const inventory = (await this.discoverServerDeployments()).find((item) => item.serverId === serverId); const workload = inventory.workloads.find((item) => item.workloadId === workloadId); if (workload) workload.reviewDecision = { action, reason, repositoryFullName, evidenceHash: "c".repeat(64) }; return { decision: workload?.reviewDecision, inventory, state: clone(state) }; }, async linkServerWorkload(repository, serverId, workloadId, deploymentMode = "server-git", remoteFolder = "") { await wait(120); const repo = repositories.find((item) => item.fullName === repository.fullName); if (!repo) throw new Error("Repository not found."); const id = `profile-${workloadId}`; const saved = { id, name: `Unraid · ${remoteFolder || repo.name}`, environment: "production", provider: "ssh-unraid", branch: repo.defaultBranch || "main", serverId, remoteFolder: remoteFolder || repo.name, deploymentMode, composeFile: "docker-compose.yml", composeFiles: ["docker-compose.yml"], composeProject: String(remoteFolder || repo.name).toLowerCase(), composeService: String(remoteFolder || repo.name).toLowerCase(), composeServices: [String(remoteFolder || repo.name).toLowerCase()], containerName: remoteFolder || repo.name, preservePaths: [".env", "appdata", "data", "logs", "config"], generatedCompose: false, adoptedFromServer: true, serverSourceOfTruth: true, manageDockerMan: false, forceRecreate: false, removeOrphans: false, workloadIdentity: { workloadId, linkSource: "manual", linkedAt: iso() }, confirmationRequired: true, state: { liveSha: null, healthy: null, containerRunning: true, runtimeVerification: "running-unverified", checkedAt: iso(), }, }; repo.deploymentProfiles = [ ...repo.deploymentProfiles.filter((item) => item.id !== id), saved, ]; syncState(); return { profile: clone(saved), state: clone(state) }; }, async configureServerGitAccess(repository, profileId) { const repo = repositories.find((item) => item.fullName === repository.fullName); const target = repo?.deploymentProfiles.find((item) => item.id === profileId); if (!target) throw new Error("Deployment profile not found."); target.deploymentMode = "server-git"; target.serverGitAccess = { configured: true, keyFingerprint: "SHA256:demo", hostFingerprint: "SHA256:gitea", configuredAt: iso() }; syncState(); return { profile: clone(target), created: true, remoteSha: target.state?.giteaSha || repo.localStatus?.head }; }, async verifyServerGitProfile(repository, profileId) { const repo = repositories.find((item) => item.fullName === repository.fullName); const target = repo?.deploymentProfiles.find((item) => item.id === profileId); if (!target) throw new Error("Deployment profile not found."); const branchSha = target.state?.giteaSha || repo.localStatus?.head || null; const liveSha = target.state?.liveSha || null; return { readiness: branchSha && liveSha === branchSha ? "Ready" : "Commit mismatch", ready: true, checkedAt: iso(), repository: repo.fullName, profileId, branchSha, liveSha, checks: [ { id: "remote-branch", label: "Gitea branch", status: "pass", detail: "Exact branch resolved." }, { id: "deploy-key-scope", label: "Repository deploy key", status: "pass", detail: "Repository-scoped and read-only." }, { id: "server-git-access", label: "Unraid to Gitea", status: "pass", detail: "Pinned SSH access verified." }, ], }; }, async deployKeyInventory(repository, profileId) { const repo = repositories.find((item) => item.fullName === repository.fullName); const profile = repo?.deploymentProfiles.find((item) => item.id === profileId); return { repository: repo.fullName, profileId, server: { id: profile.serverId, name: "Unraid" }, configuredKey: { id: profile.serverGitAccess?.deployKeyId || 17, readOnly: true }, serverKey: { privateKeyPresent: true, fingerprint: profile.serverGitAccess?.keyFingerprint || "SHA256:demo" }, stale: false, orphaned: [], shared: [], conflicts: [], ready: true, checkedAt: iso() }; }, async planDeployKeyRotation(repository, profileId) { const evidence = await this.deployKeyInventory(repository, profileId); return { id: `rotation-${profileId}`, operation: "rotate-deploy-key", impact: ["Generate a new server-side key", "Verify read-only access", "Switch atomically", "Revoke the previous key"], recovery: "Previous access remains recoverable until verification succeeds.", evidence }; }, async applyDeployKeyRotation(repository, profileId) { const repo = repositories.find((item) => item.fullName === repository.fullName); const profile = repo.deploymentProfiles.find((item) => item.id === profileId); profile.serverGitAccess = { ...profile.serverGitAccess, configured: true, deployKeyId: 18, keyFingerprint: "SHA256:rotated", rotatedAt: iso() }; syncState(); return { profile: clone(profile), state: clone(state) }; }, async planDeployKeyRevocation(repository, profileId) { const evidence = await this.deployKeyInventory(repository, profileId); return { id: `revocation-${profileId}`, operation: "revoke-deploy-key", impact: ["Remove the repository key", "Disable server pull", "Preserve recovery material"], containersUnaffected: true, evidence }; }, async applyDeployKeyRevocation(repository, profileId) { const repo = repositories.find((item) => item.fullName === repository.fullName); const profile = repo.deploymentProfiles.find((item) => item.id === profileId); profile.deploymentMode = "monitor-only"; profile.serverGitAccess = { ...profile.serverGitAccess, configured: false, revokedAt: iso(), recoveryAvailable: true }; syncState(); return { profile: clone(profile), state: clone(state) }; }, async restoreDeployKey(repository, profileId) { const repo = repositories.find((item) => item.fullName === repository.fullName); const profile = repo.deploymentProfiles.find((item) => item.id === profileId); profile.deploymentMode = "server-git"; profile.serverGitAccess = { ...profile.serverGitAccess, configured: true, deployKeyId: 19, keyFingerprint: "SHA256:restored", restoredAt: iso() }; syncState(); return { profile: clone(profile), state: clone(state), proof: { ready: true } }; }, async refreshOperations(operationId = null) { await wait(300); if (operationId) { const operation = state.operations.find( (item) => item.id === operationId, ); if (!operation) throw new Error("Operation not found."); return updateOperation(advanceOperation(operation)); } const active = state.operations .filter( (item) => !["success", "failed", "cancelled", "rolled-back"].includes( item.status, ), ) .map(advanceOperation); if (active.length) emitOperations(active); state.operations = state.operations.map( (item) => active.find((entry) => entry.id === item.id) || item, ); return clone(active); }, async getOperation(operationId) { return clone( state.operations.find((item) => item.id === operationId) || null, ); }, async gitValidatorScan(fullName) { await wait(260); const policy = state.gitValidatorPolicy || { id: "standard", label: "Standard", requiredScore: 70 }; const activeWarnings = 3; return { repository: fullName, checkedAt: iso(), score: 78, grade: "Good", policy, ready: 78 >= policy.requiredScore && (policy.id === "minimal" || activeWarnings === 0), commitSha: "8cbaf303aa3bb9b4023a7c89aa13fb70ce612847", trend: { newlyFound: ["working-tree"], resolved: ["editorconfig"], regressions: [], suppressions: [] }, expiredSuppressions: [], summary: { passed: 7, warnings: 3, errors: 0, repairable: 2 }, checks: [ { id: "origin", category: "Repository identity", title: "Origin matches Gitea", status: "pass", detail: "The local origin resolves to this Gitea repository.", weight: 15, }, { id: "default-branch-protection", category: "Gitea governance", title: "Default branch protection", status: "warning", detail: "main accepts unprotected direct changes.", weight: 18, fixAction: "protect-default-branch", safe: false, confirmation: "Protect main on Gitea and block direct and force pushes?", }, { id: "force-push", category: "Gitea governance", title: "Force-push protection", status: "pass", detail: "Force pushes are blocked.", weight: 8, }, { id: "upstream", category: "Branch hygiene", title: "Current branch has an upstream", status: "pass", detail: "main tracks origin/main.", weight: 8, }, { id: "working-tree", category: "Branch hygiene", title: "Working tree is intentional", status: "warning", detail: "3 changed files require review, commit or stash.", weight: 5, }, { id: "identity", category: "Commit integrity", title: "Repository author identity", status: "pass", detail: "Jens ", weight: 7, }, { id: "local-safety", category: "Local configuration", title: "Safe synchronization defaults", status: "warning", detail: "Recommended repository-local safeguards are incomplete.", weight: 10, fixAction: "configure-local-safety", safe: true, }, { id: "readme", category: "Repository documentation", title: "README is versioned", status: "pass", detail: "Repository documentation is tracked.", weight: 7, }, { id: "gitignore", category: "Repository hygiene", title: ".gitignore is versioned", status: "pass", detail: "Generated files are excluded centrally.", weight: 8, }, { id: "tracked-secrets", category: "Security", title: "No secret-shaped files are tracked", status: "pass", detail: "No tracked environment, key or credential filenames detected.", weight: 22, }, { id: "large-files", category: "Repository performance", title: "No oversized tracked files", status: "pass", detail: "No tracked files above 10 MB were found.", weight: 7, }, ], }; }, async gitValidatorSetPolicy(_fullName, policy) { const requiredScores = { minimal: 55, standard: 70, strict: 82, production: 90 }; state.gitValidatorPolicy = { id: policy.id, label: policy.id[0].toUpperCase() + policy.id.slice(1), requiredScore: requiredScores[policy.id] || 70, }; return clone(state.gitValidatorPolicy); }, async gitValidatorSuppress(_fullName, suppression) { return { ...suppression, id: `suppression-${Date.now()}`, createdAt: iso() }; }, async gitValidatorPreviewRepair(_fullName, check) { return { checkId: check.id, action: check.fixAction, files: [".git/config"], diff: "+ reviewed configuration change\n", remoteMutation: check.fixAction === "protect-default-branch" }; }, async gitValidatorExport(fullName, format) { return { extension: format === "markdown" ? "md" : format, mimeType: "text/plain", content: `# Git assurance — ${fullName}\n` }; }, async gitValidatorRepair() { await wait(180); return { repaired: true }; }, async diagnosticsStatus() { return { enabled: state.preferences.diagnosticsEnabled !== false, level: state.preferences.diagnosticLevel, retentionDays: state.preferences.logRetentionDays, maxFileMb: state.preferences.maxLogFileMb, directory: "/AppData/Roaming/ForgeFlow/diagnostics", fileCount: 2, totalBytes: 18432, totalSize: "18.0 KB", latestAt: iso(-2000), lastWriteError: null, }; }, async exportConfigurationBackup() { return { filePath: "C:\\Downloads\\ForgeFlow-Configuration-demo.ffbackup", }; }, async importConfigurationBackup() { return { state: clone(state), exportedAt: iso(-86400000) }; }, async listAuditEvents() { return [ { id: "audit-1", timestamp: iso(-60000), event: "deployment.completed", details: { repository: "Jens/ForgeFlow", result: "success" }, }, ]; }, async exportAuditLog() { return { filePath: "C:\\Downloads\\ForgeFlow-Audit-demo.json", count: 1 }; }, async clearDiagnostics() { return { enabled: true, level: state.preferences.diagnosticLevel, retentionDays: state.preferences.logRetentionDays, maxFileMb: state.preferences.maxLogFileMb, directory: "/AppData/Roaming/ForgeFlow/diagnostics", fileCount: 1, totalBytes: 256, totalSize: "256 B", latestAt: iso(), lastWriteError: null, }; }, async openDiagnosticsFolder() { return true; }, async exportDiagnostics(privacyMode = "standard") { await wait(500); return { path: `C:\Users\your-name\Downloads\ForgeFlow-Diagnostics-demo.zip`, bytes: 38221, size: "37.3 KB", sha256: "b".repeat(64), privacyMode, generatedAt: iso(), }; }, async showDiagnosticBundle() { return true; }, async reportRendererEvent() { return true; }, onRepositoriesChanged(listener) { repositoryListeners.add(listener); return () => repositoryListeners.delete(listener); }, onOperationsChanged(listener) { operationListeners.add(listener); return () => operationListeners.delete(listener); }, onUpdatesChanged(listener) { updateListeners.add(listener); return () => updateListeners.delete(listener); }, async reset() { state.setupComplete = false; storage.set("forgeflow-demo-setup", "false"); return clone(state); }, }; }