import { env } from "cloudflare:workers"; import { Effect, Layer } from "effect"; import { type FumaDB } from "@executor-js/fumadb"; import { type DrizzleConfig } from "@executor-js/fumadb/adapters/drizzle"; import { type schema as fumaSchema, type RelationsMap } from "@executor-js/fumadb/schema"; import { createExecutorFumaDb, DbProvider, type ExecutorDbHandle, type ExecutorDbProvider, } from "@executor-js/api/server"; import { makeR2BlobStore } from "@executor-js/cloudflare/blob-store"; import type { FumaDb, FumaTables } from "@executor-js/sdk"; import { DbService } from "./db"; type DrizzleFumaSchema = ReturnType< typeof fumaSchema> >; export interface DrizzleFumaDb { readonly db: FumaDb>; readonly fuma: FumaDB[]>; } export interface CreateDrizzleFumaDbOptions { readonly db: DrizzleConfig["db"]; readonly tables: TTables; readonly namespace: string; readonly version?: string; readonly provider: ExecutorDbProvider; } // Cloud opens its own postgres-js drizzle handle (see ./db.ts) and runs // migrations out-of-band, so this is the pure FumaDB assembly over an // already-opened `db`. Delegates to the shared factory; the local wrapper // keeps the cloud-specific default version + option names. export const createDrizzleFumaDb = ( options: CreateDrizzleFumaDbOptions, ): DrizzleFumaDb => createExecutorFumaDb(options.db, { tables: options.tables, namespace: options.namespace, version: options.version ?? "1.0.0", provider: options.provider, }); export const CLOUD_NAMESPACE = "executor_cloud"; // Shared DbProvider seam (P2a). Cloud opens a fresh postgres-js connection per // request (Cloudflare forbids sharing I/O across handlers); this assembles the // FumaDB handle over the request-scoped `DbService.db`. Migrations run // out-of-band, so there is no schema bring-up here, and `close` is a no-op — // `DbService.Live` owns the postgres connection lifecycle. export const cloudDbProviderLayer = ( tables: FumaTables, ): Layer.Layer => Layer.effect(DbProvider)( Effect.map(DbService.asEffect(), ({ db }): ExecutorDbHandle => { const fuma = createDrizzleFumaDb({ db, tables, namespace: CLOUD_NAMESPACE, provider: "postgresql", }); return { db: fuma.db, fuma: fuma.fuma, close: async () => {}, // Plugin blobs (multi-MB resolved specs) live in R2, not Postgres. // Guarded because test workers / local dev may run without the // binding — the executor then falls back to the FumaDB `blob` table. blobs: env.BLOBS ? makeR2BlobStore(env.BLOBS) : undefined, }; }), );