163 lines · 5.2 KB · TypeScript
RawDownload
import { Effect, Layer, Schema, Struct } from "effect";import * as SqlClient from "effect/unstable/sql/SqlClient";import * as SqlSchema from "effect/unstable/sql/SqlSchema"; import { toPersistenceDecodeError, toPersistenceSqlError } from "../Errors.ts";import {  DeleteProjectionThreadWorkUnitsInput,  ListProjectionThreadWorkUnitsInput,  ProjectionThreadWorkUnit,  ProjectionThreadWorkUnitRepository,  type ProjectionThreadWorkUnitRepositoryShape,} from "../Services/ProjectionThreadWorkUnits.ts"; const ProjectionThreadWorkUnitDbRowSchema = ProjectionThreadWorkUnit.mapFields(  Struct.assign({    providerRefs: Schema.NullOr(      Schema.fromJsonString(ProjectionThreadWorkUnit.fields.providerRefs),    ),  }),); function toPersistenceSqlOrDecodeError(sqlOperation: string, decodeOperation: string) {  return (cause: unknown) =>    Schema.isSchemaError(cause)      ? toPersistenceDecodeError(decodeOperation)(cause)      : toPersistenceSqlError(sqlOperation)(cause);} const makeProjectionThreadWorkUnitRepository = Effect.gen(function* () {  const sql = yield* SqlClient.SqlClient;   const upsertProjectionThreadWorkUnitRow = SqlSchema.void({    Request: ProjectionThreadWorkUnit,    execute: (row) => sql`      INSERT INTO projection_thread_work_units (        work_unit_id,        thread_id,        turn_id,        parent_work_unit_id,        kind,        state,        title,        detail,        spawned_by_activity_id,        provider_refs_json,        started_at,        updated_at,        completed_at      )      VALUES (        ${row.workUnitId},        ${row.threadId},        ${row.turnId},        ${row.parentWorkUnitId},        ${row.kind},        ${row.state},        ${row.title},        ${row.detail},        ${row.spawnedByActivityId},        ${row.providerRefs ? JSON.stringify(row.providerRefs) : null},        ${row.startedAt},        ${row.updatedAt},        ${row.completedAt}      )      ON CONFLICT (work_unit_id)      DO UPDATE SET        thread_id = excluded.thread_id,        turn_id = excluded.turn_id,        parent_work_unit_id = excluded.parent_work_unit_id,        kind = excluded.kind,        state = excluded.state,        title = excluded.title,        detail = excluded.detail,        spawned_by_activity_id = excluded.spawned_by_activity_id,        provider_refs_json = excluded.provider_refs_json,        started_at = excluded.started_at,        updated_at = excluded.updated_at,        completed_at = excluded.completed_at    `,  });   const listProjectionThreadWorkUnitRows = SqlSchema.findAll({    Request: ListProjectionThreadWorkUnitsInput,    Result: ProjectionThreadWorkUnitDbRowSchema,    execute: ({ threadId }) => sql`      SELECT        work_unit_id AS "workUnitId",        thread_id AS "threadId",        turn_id AS "turnId",        parent_work_unit_id AS "parentWorkUnitId",        kind,        state,        title,        detail,        spawned_by_activity_id AS "spawnedByActivityId",        provider_refs_json AS "providerRefs",        started_at AS "startedAt",        updated_at AS "updatedAt",        completed_at AS "completedAt"      FROM projection_thread_work_units      WHERE thread_id = ${threadId}      ORDER BY started_at ASC, work_unit_id ASC    `,  });   const deleteProjectionThreadWorkUnitRows = SqlSchema.void({    Request: DeleteProjectionThreadWorkUnitsInput,    execute: ({ threadId }) => sql`      DELETE FROM projection_thread_work_units      WHERE thread_id = ${threadId}    `,  });   const upsert: ProjectionThreadWorkUnitRepositoryShape["upsert"] = (row) =>    upsertProjectionThreadWorkUnitRow(row).pipe(      Effect.mapError(toPersistenceSqlError("ProjectionThreadWorkUnitRepository.upsert:query")),    );   const listByThreadId: ProjectionThreadWorkUnitRepositoryShape["listByThreadId"] = (input) =>    listProjectionThreadWorkUnitRows(input).pipe(      Effect.mapError(        toPersistenceSqlOrDecodeError(          "ProjectionThreadWorkUnitRepository.listByThreadId:query",          "ProjectionThreadWorkUnitRepository.listByThreadId:decodeRows",        ),      ),      Effect.map((rows) =>        rows.map((row) => ({          workUnitId: row.workUnitId,          threadId: row.threadId,          turnId: row.turnId,          parentWorkUnitId: row.parentWorkUnitId,          kind: row.kind,          state: row.state,          title: row.title,          detail: row.detail,          spawnedByActivityId: row.spawnedByActivityId,          ...(row.providerRefs !== null ? { providerRefs: row.providerRefs } : {}),          startedAt: row.startedAt,          updatedAt: row.updatedAt,          completedAt: row.completedAt,        })),      ),    );   const deleteByThreadId: ProjectionThreadWorkUnitRepositoryShape["deleteByThreadId"] = (input) =>    deleteProjectionThreadWorkUnitRows(input).pipe(      Effect.mapError(        toPersistenceSqlError("ProjectionThreadWorkUnitRepository.deleteByThreadId:query"),      ),    );   return {    upsert,    listByThreadId,    deleteByThreadId,  } satisfies ProjectionThreadWorkUnitRepositoryShape;}); export const ProjectionThreadWorkUnitRepositoryLive = Layer.effect(  ProjectionThreadWorkUnitRepository,  makeProjectionThreadWorkUnitRepository,);