import { EnvironmentId, type PreviewAutomationOwner, PreviewAutomationOperation, type PreviewAutomationRequest, type PreviewAutomationResponse, PreviewTabId, ThreadId, TrimmedNonEmptyString, } from "@t3tools/contracts"; import * as Schema from "effect/Schema"; export interface PreviewAutomationOperationContext { readonly requestId: PreviewAutomationRequest["requestId"]; readonly operation: PreviewAutomationRequest["operation"]; readonly environmentId: PreviewAutomationOwner["environmentId"]; readonly threadId: PreviewAutomationRequest["threadId"]; readonly tabId: Exclude | null; } export class PreviewAutomationOverlayTimeoutError extends Schema.TaggedErrorClass()( "PreviewAutomationOverlayTimeoutError", { requestId: TrimmedNonEmptyString, environmentId: EnvironmentId, threadId: ThreadId, timeoutMs: Schema.Int, }, ) { get responseTag() { return "PreviewAutomationTimeoutError" as const; } override get message(): string { return `Preview webview for request ${this.requestId} on environment ${this.environmentId} thread ${this.threadId} did not register within ${this.timeoutMs}ms.`; } } export class PreviewAutomationNavigationTimeoutError extends Schema.TaggedErrorClass()( "PreviewAutomationNavigationTimeoutError", { requestId: TrimmedNonEmptyString, environmentId: EnvironmentId, threadId: ThreadId, tabId: PreviewTabId, readiness: Schema.Literals(["domContentLoaded", "load"]), timeoutMs: Schema.Int, }, ) { get responseTag() { return "PreviewAutomationTimeoutError" as const; } override get message(): string { return `Preview navigation for request ${this.requestId} on environment ${this.environmentId} thread ${this.threadId} tab ${this.tabId} did not reach ${this.readiness} readiness within ${this.timeoutMs}ms.`; } } export class PreviewAutomationStaleOwnerError extends Schema.TaggedErrorClass()( "PreviewAutomationStaleOwnerError", { requestId: TrimmedNonEmptyString, environmentId: EnvironmentId, expectedThreadId: ThreadId, requestedThreadId: ThreadId, }, ) { get responseTag() { return "PreviewAutomationUnavailableError" as const; } override get message(): string { return `Preview automation request ${this.requestId} targeted thread ${this.requestedThreadId}, but the owner for environment ${this.environmentId} is attached to thread ${this.expectedThreadId}.`; } } export class PreviewAutomationTargetUnavailableError extends Schema.TaggedErrorClass()( "PreviewAutomationTargetUnavailableError", { requestId: TrimmedNonEmptyString, operation: PreviewAutomationOperation, environmentId: EnvironmentId, threadId: ThreadId, tabId: Schema.NullOr(PreviewTabId), bridgeAvailable: Schema.Boolean, }, ) { get responseTag() { return "PreviewAutomationTabNotFoundError" as const; } override get message(): string { return `Preview automation target for ${this.operation} request ${this.requestId} is unavailable on environment ${this.environmentId} thread ${this.threadId} (tab ${this.tabId ?? "unassigned"}, bridge ${this.bridgeAvailable ? "available" : "unavailable"}).`; } } export class PreviewAutomationRecordingNotActiveError extends Schema.TaggedErrorClass()( "PreviewAutomationRecordingNotActiveError", { requestId: TrimmedNonEmptyString, environmentId: EnvironmentId, threadId: ThreadId, tabId: PreviewTabId, }, ) { get responseTag() { return "PreviewAutomationExecutionError" as const; } override get message(): string { return `Preview automation request ${this.requestId} found no active recording for tab ${this.tabId} on environment ${this.environmentId} thread ${this.threadId}.`; } } export class PreviewAutomationOperationError extends Schema.TaggedErrorClass()( "PreviewAutomationOperationError", { requestId: TrimmedNonEmptyString, operation: PreviewAutomationOperation, environmentId: EnvironmentId, threadId: ThreadId, tabId: Schema.NullOr(PreviewTabId), cause: Schema.Defect(), }, ) { static fromCause( input: PreviewAutomationOperationContext & { readonly cause: unknown }, ): PreviewAutomationOwnerError { return isPreviewAutomationOwnerError(input.cause) ? input.cause : new PreviewAutomationOperationError(input); } get responseTag() { return "PreviewAutomationExecutionError" as const; } override get message(): string { return `Preview automation ${this.operation} request ${this.requestId} failed on environment ${this.environmentId} thread ${this.threadId} (tab ${this.tabId ?? "unassigned"}).`; } } export const PreviewAutomationOwnerError = Schema.Union([ PreviewAutomationOverlayTimeoutError, PreviewAutomationNavigationTimeoutError, PreviewAutomationStaleOwnerError, PreviewAutomationTargetUnavailableError, PreviewAutomationRecordingNotActiveError, PreviewAutomationOperationError, ]); export type PreviewAutomationOwnerError = typeof PreviewAutomationOwnerError.Type; export const isPreviewAutomationOwnerError = Schema.is(PreviewAutomationOwnerError); export function serializePreviewAutomationOwnerError( error: PreviewAutomationOwnerError, ): NonNullable { const detail = Object.fromEntries( Object.entries(error).filter( ([key]) => key !== "_tag" && key !== "cause" && key !== "name" && key !== "message" && key !== "stack", ), ); return { _tag: error.responseTag, message: error.message, ...(Object.keys(detail).length === 0 ? {} : { detail }), }; }