import type { GenericDatabaseReader } from "convex/server"; import * as Context from "effect/Context"; import * as Layer from "effect/Layer"; import type { BaseDatabaseReader } from "@confect/core/Types"; import * as DatabaseSchema from "./DatabaseSchema"; import type * as DataModel from "./DataModel"; import * as QueryInitializer from "./QueryInitializer"; import * as Table from "./Table"; type IncludedTables = | DatabaseSchema.Tables | Table.SystemTables; type IncludedDataModel = DataModel.FromTables>; export interface DatabaseReaderService< DatabaseSchema_ extends DatabaseSchema.AnyWithProps, Docs = {}, > { readonly table: < const TableName extends Table.Name>, >( tableName: TableName, ) => QueryInitializer.QueryInitializer< IncludedDataModel, TableName, TableName extends keyof Docs ? Docs[TableName] : DataModel.DocumentWithName< IncludedDataModel, TableName > >; } export const make = ( databaseSchema: DatabaseSchema_, convexDatabaseReader: GenericDatabaseReader< DataModel.ToConvex> >, ): DatabaseReaderService => { return { table: < const TableName extends Table.Name>, >( tableName: TableName, ) => { const isSystem = Object.hasOwn(Table.systemTables, tableName); const baseDatabaseReader: BaseDatabaseReader = isSystem ? { get: convexDatabaseReader.system.get.bind( convexDatabaseReader.system, ), query: convexDatabaseReader.system.query.bind( convexDatabaseReader.system, ), } : { get: convexDatabaseReader.get.bind(convexDatabaseReader), query: convexDatabaseReader.query.bind(convexDatabaseReader), }; const table = ( isSystem ? (Table.systemTables as Record)[ tableName ] : DatabaseSchema.tables(databaseSchema)[tableName] ) as Table.WithName, TableName>; return QueryInitializer.make, TableName>( baseDatabaseReader, table, ); }, } as DatabaseReaderService; }; /** * The tag's _Identifier_ (the Effect requirements-channel type) is * `Docs`-independent so a helper's `R` channel is the same whether or not a * codegen document registry is supplied—this keeps it identical to what * `Handler`/runtime provisioning provide. The tag's _Service_ (what `yield*` * produces) carries `Docs`, so queries resolve to the named doc interfaces. */ export type DatabaseReaderTag< DatabaseSchema_ extends DatabaseSchema.AnyWithProps, Docs = {}, > = Context.Service< DatabaseReaderService, DatabaseReaderService >; export const DatabaseReader = < DatabaseSchema_ extends DatabaseSchema.AnyWithProps, Docs = {}, >(): DatabaseReaderTag => Context.Service< DatabaseReaderService, DatabaseReaderService >("@confect/server/DatabaseReader"); export type DatabaseReader< DatabaseSchema_ extends DatabaseSchema.AnyWithProps, > = DatabaseReaderService; export const layer = ( databaseSchema: DatabaseSchema_, convexDatabaseReader: GenericDatabaseReader< DataModel.ToConvex> >, ) => Layer.succeed( DatabaseReader(), make(databaseSchema, convexDatabaseReader), );