209 lines · 7.1 KB · TypeScript
RawDownload
import * as SqlClient from "effect/unstable/sql/SqlClient";import * as SqlSchema from "effect/unstable/sql/SqlSchema";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as Schema from "effect/Schema";import * as Struct from "effect/Struct"; import { toPersistenceSqlError } from "../Errors.ts";import {  DeleteProjectionThreadInput,  GetProjectionThreadInput,  ListProjectionThreadsByProjectInput,  ProjectionThread,  ProjectionThreadRepository,  type ProjectionThreadRepositoryShape,} from "../Services/ProjectionThreads.ts";import { ModelSelection } from "@t3tools/contracts"; const ProjectionThreadDbRow = ProjectionThread.mapFields(  Struct.assign({    modelSelection: Schema.fromJsonString(ModelSelection),  }),);type ProjectionThreadDbRow = typeof ProjectionThreadDbRow.Type; const makeProjectionThreadRepository = Effect.gen(function* () {  const sql = yield* SqlClient.SqlClient;   const upsertProjectionThreadRow = SqlSchema.void({    Request: ProjectionThread,    execute: (row) =>      sql`        INSERT INTO projection_threads (          thread_id,          project_id,          title,          model_selection_json,          runtime_mode,          interaction_mode,          branch,          worktree_path,          latest_turn_id,          created_at,          updated_at,          archived_at,          settled_override,          settled_at,          snoozed_until,          snoozed_at,          latest_user_message_at,          pending_approval_count,          pending_user_input_count,          has_actionable_proposed_plan,          deleted_at        )        VALUES (          ${row.threadId},          ${row.projectId},          ${row.title},          ${JSON.stringify(row.modelSelection)},          ${row.runtimeMode},          ${row.interactionMode},          ${row.branch},          ${row.worktreePath},          ${row.latestTurnId},          ${row.createdAt},          ${row.updatedAt},          ${row.archivedAt},          ${row.settledOverride},          ${row.settledAt},          ${row.snoozedUntil},          ${row.snoozedAt},          ${row.latestUserMessageAt},          ${row.pendingApprovalCount},          ${row.pendingUserInputCount},          ${row.hasActionableProposedPlan},          ${row.deletedAt}        )        ON CONFLICT (thread_id)        DO UPDATE SET          project_id = excluded.project_id,          title = excluded.title,          model_selection_json = excluded.model_selection_json,          runtime_mode = excluded.runtime_mode,          interaction_mode = excluded.interaction_mode,          branch = excluded.branch,          worktree_path = excluded.worktree_path,          latest_turn_id = excluded.latest_turn_id,          created_at = excluded.created_at,          updated_at = excluded.updated_at,          archived_at = excluded.archived_at,          settled_override = excluded.settled_override,          settled_at = excluded.settled_at,          snoozed_until = excluded.snoozed_until,          snoozed_at = excluded.snoozed_at,          latest_user_message_at = excluded.latest_user_message_at,          pending_approval_count = excluded.pending_approval_count,          pending_user_input_count = excluded.pending_user_input_count,          has_actionable_proposed_plan = excluded.has_actionable_proposed_plan,          deleted_at = excluded.deleted_at      `,  });   const getProjectionThreadRow = SqlSchema.findOneOption({    Request: GetProjectionThreadInput,    Result: ProjectionThreadDbRow,    execute: ({ threadId }) =>      sql`        SELECT          thread_id AS "threadId",          project_id AS "projectId",          title,          model_selection_json AS "modelSelection",          runtime_mode AS "runtimeMode",          interaction_mode AS "interactionMode",          branch,          worktree_path AS "worktreePath",          latest_turn_id AS "latestTurnId",          created_at AS "createdAt",          updated_at AS "updatedAt",          archived_at AS "archivedAt",          settled_override AS "settledOverride",          settled_at AS "settledAt",          snoozed_until AS "snoozedUntil",          snoozed_at AS "snoozedAt",          latest_user_message_at AS "latestUserMessageAt",          pending_approval_count AS "pendingApprovalCount",          pending_user_input_count AS "pendingUserInputCount",          has_actionable_proposed_plan AS "hasActionableProposedPlan",          deleted_at AS "deletedAt"        FROM projection_threads        WHERE thread_id = ${threadId}      `,  });   const listProjectionThreadRows = SqlSchema.findAll({    Request: ListProjectionThreadsByProjectInput,    Result: ProjectionThreadDbRow,    execute: ({ projectId }) =>      sql`        SELECT          thread_id AS "threadId",          project_id AS "projectId",          title,          model_selection_json AS "modelSelection",          runtime_mode AS "runtimeMode",          interaction_mode AS "interactionMode",          branch,          worktree_path AS "worktreePath",          latest_turn_id AS "latestTurnId",          created_at AS "createdAt",          updated_at AS "updatedAt",          archived_at AS "archivedAt",          settled_override AS "settledOverride",          settled_at AS "settledAt",          snoozed_until AS "snoozedUntil",          snoozed_at AS "snoozedAt",          latest_user_message_at AS "latestUserMessageAt",          pending_approval_count AS "pendingApprovalCount",          pending_user_input_count AS "pendingUserInputCount",          has_actionable_proposed_plan AS "hasActionableProposedPlan",          deleted_at AS "deletedAt"        FROM projection_threads        WHERE project_id = ${projectId}        ORDER BY created_at ASC, thread_id ASC      `,  });   const deleteProjectionThreadRow = SqlSchema.void({    Request: DeleteProjectionThreadInput,    execute: ({ threadId }) =>      sql`        DELETE FROM projection_threads        WHERE thread_id = ${threadId}      `,  });   const upsert: ProjectionThreadRepositoryShape["upsert"] = (row) =>    upsertProjectionThreadRow(row).pipe(      Effect.mapError(toPersistenceSqlError("ProjectionThreadRepository.upsert:query")),    );   const getById: ProjectionThreadRepositoryShape["getById"] = (input) =>    getProjectionThreadRow(input).pipe(      Effect.mapError(toPersistenceSqlError("ProjectionThreadRepository.getById:query")),    );   const listByProjectId: ProjectionThreadRepositoryShape["listByProjectId"] = (input) =>    listProjectionThreadRows(input).pipe(      Effect.mapError(toPersistenceSqlError("ProjectionThreadRepository.listByProjectId:query")),    );   const deleteById: ProjectionThreadRepositoryShape["deleteById"] = (input) =>    deleteProjectionThreadRow(input).pipe(      Effect.mapError(toPersistenceSqlError("ProjectionThreadRepository.deleteById:query")),    );   return {    upsert,    getById,    listByProjectId,    deleteById,  } satisfies ProjectionThreadRepositoryShape;}); export const ProjectionThreadRepositoryLive = Layer.effect(  ProjectionThreadRepository,  makeProjectionThreadRepository,);