import * as Predicate from "effect/Predicate"; import * as Schema from "effect/Schema"; import * as SchemaIssue from "effect/SchemaIssue"; function summarizeSchemaIssue(issue: SchemaIssue.Issue): string { switch (issue._tag) { case "Filter": case "Encoding": case "Pointer": return `${issue._tag}(${summarizeSchemaIssue(issue.issue)})`; case "Composite": case "AnyOf": return `${issue._tag}(${issue.issues.map(summarizeSchemaIssue).join(",")})`; default: return issue._tag; } } // =============================== // Core Persistence Errors // =============================== export const PersistenceErrorCorrelation = Schema.Union([ Schema.Struct({ sessionId: Schema.String }), Schema.Struct({ currentSessionId: Schema.String }), Schema.Struct({ pairingLinkId: Schema.String }), Schema.Struct({ threadId: Schema.String }), ]); export type PersistenceErrorCorrelation = typeof PersistenceErrorCorrelation.Type; export class PersistenceSqlError extends Schema.TaggedErrorClass()( "PersistenceSqlError", { operation: Schema.String, detail: Schema.optional(Schema.String), correlation: Schema.optional(PersistenceErrorCorrelation), cause: Schema.optional(Schema.Defect()), }, ) { override get message(): string { return this.detail === undefined ? `SQL error in ${this.operation}` : `SQL error in ${this.operation}: ${this.detail}`; } } export class PersistenceDecodeError extends Schema.TaggedErrorClass()( "PersistenceDecodeError", { operation: Schema.String, issue: Schema.String, correlation: Schema.optional(PersistenceErrorCorrelation), cause: Schema.optional(Schema.Defect()), }, ) { static fromSchemaError( operation: string, cause: Schema.SchemaError, correlation?: PersistenceErrorCorrelation, ): PersistenceDecodeError { return new PersistenceDecodeError({ operation, issue: summarizeSchemaIssue(cause.issue), ...(correlation === undefined ? {} : { correlation }), cause, }); } override get message(): string { return `Decode error in ${this.operation}: ${this.issue}`; } } const isPersistenceSqlError = Schema.is(PersistenceSqlError); const isPersistenceDecodeError = Schema.is(PersistenceDecodeError); /** * Read a SQLite condition through SQL error wrappers. * Use Node's fixed description or Bun's numeric code, never the driver message. */ function sqliteCondition(cause: unknown): string | undefined { let value = cause; for (let depth = 0; depth < 4 && Predicate.isObject(value); depth += 1) { if ( "errcode" in value && typeof value.errcode === "number" && "errstr" in value && typeof value.errstr === "string" ) { return `SQLITE(${value.errcode}) ${value.errstr}`; } if ( "name" in value && value.name === "SQLiteError" && "errno" in value && typeof value.errno === "number" && Number.isInteger(value.errno) ) { return `SQLITE(${value.errno})`; } value = "cause" in value ? value.cause : undefined; } return undefined; } /** * A rejected payload must never reach diagnostics, so a schema failure * contributes only its issue tags, and a driver failure only its normalized * condition. Anything the mapper cannot categorize leaves the detail unset. */ function describeSqlCause(cause: unknown): string | undefined { if (Schema.isSchemaError(cause)) return summarizeSchemaIssue(cause.issue); return sqliteCondition(cause); } // Kept for orchestration/projection call sites, which are being revamped separately. export function toPersistenceSqlError(operation: string) { return (cause: unknown): PersistenceSqlError => { const detail = describeSqlCause(cause); return new PersistenceSqlError({ operation, ...(detail === undefined ? {} : { detail }), cause, }); }; } // Kept for orchestration/projection call sites, which are being revamped separately. export function toPersistenceDecodeError(operation: string) { return (cause: Schema.SchemaError): PersistenceDecodeError => PersistenceDecodeError.fromSchemaError(operation, cause); } export const isPersistenceError = (u: unknown) => isPersistenceSqlError(u) || isPersistenceDecodeError(u); // =============================== // Provider Session Repository Errors // =============================== export class ProviderSessionRepositoryValidationError extends Schema.TaggedErrorClass()( "ProviderSessionRepositoryValidationError", { operation: Schema.String, issue: Schema.String, cause: Schema.optional(Schema.Defect()), }, ) { override get message(): string { return `Provider session repository validation failed in ${this.operation}: ${this.issue}`; } } export class ProviderSessionRepositoryPersistenceError extends Schema.TaggedErrorClass()( "ProviderSessionRepositoryPersistenceError", { operation: Schema.String, detail: Schema.String, cause: Schema.optional(Schema.Defect()), }, ) { override get message(): string { return `Provider session repository persistence error in ${this.operation}: ${this.detail}`; } } export type OrchestrationEventStoreError = PersistenceSqlError | PersistenceDecodeError; export type ProviderSessionRepositoryError = | ProviderSessionRepositoryValidationError | ProviderSessionRepositoryPersistenceError; export type OrchestrationCommandReceiptRepositoryError = | PersistenceSqlError | PersistenceDecodeError; export type ProviderSessionRuntimeRepositoryError = PersistenceSqlError | PersistenceDecodeError; export type AuthPairingLinkRepositoryError = PersistenceSqlError | PersistenceDecodeError; export type AuthSessionRepositoryError = PersistenceSqlError | PersistenceDecodeError; export type ProjectionRepositoryError = PersistenceSqlError | PersistenceDecodeError;