import { Context, Effect, Layer } from "effect"; import { makeUserStore } from "../auth/user-store"; import { DbService } from "../db/db"; import { UserStoreError, tryPromiseService, withServiceLogging } from "./errors"; // --------------------------------------------------------------------------- // UserStoreService — wraps the Drizzle-backed user store with Effect // --------------------------------------------------------------------------- type RawStore = ReturnType; const makeService = (store: RawStore) => ({ use: (fn: (s: RawStore) => Promise) => withServiceLogging( "user_store", () => new UserStoreError(), tryPromiseService(() => fn(store)), ), }); type UserStoreServiceType = ReturnType; export class UserStoreService extends Context.Service()( "@executor-js/cloud/UserStoreService", ) { static Live = Layer.effect(this)( Effect.map(DbService.asEffect(), ({ db }) => makeService(makeUserStore(db))), ); } /** * A FRESH `UserStoreService` layer (new layer value per call). `UserStoreService.Live` * captures its `db` at build time; when a long-lived runtime's shared memo map * memoizes that const layer once, it pins the first request's postgres socket and * reuses it on later requests — illegal under Cloudflare's per-request I/O. Build * this (over a fresh `DbService` layer) anywhere a service is constructed once but * invoked across many requests (the MCP org-authorization seam). See [[makeDbLayer]]. */ export const makeUserStoreLayer = (): Layer.Layer => Layer.effect(UserStoreService)( Effect.map(DbService.asEffect(), ({ db }) => makeService(makeUserStore(db))), );