From a3a2f3ab20d101ea550661bee3135e4f1aceaa2e Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Wed, 1 Apr 2026 00:27:20 -0700 Subject: [PATCH 1/2] migrate Effect.fn in apps/server/src/persistence/NodeSqliteClient.ts Co-authored-by: codex --- apps/server/src/persistence/NodeSqliteClient.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/server/src/persistence/NodeSqliteClient.ts b/apps/server/src/persistence/NodeSqliteClient.ts index 78dbe8a1a9..e95602c18b 100644 --- a/apps/server/src/persistence/NodeSqliteClient.ts +++ b/apps/server/src/persistence/NodeSqliteClient.ts @@ -76,7 +76,11 @@ const makeWithDatabase = ( options: SqliteClientConfig, openDatabase: () => DatabaseSync, ): Effect.Effect => - Effect.gen(function* () { + Effect.fn("makeWithDatabase")(function* (): Effect.fn.Return< + Client.SqlClient, + never, + Scope.Scope | Reactivity.Reactivity + > { yield* checkNodeSqliteCompat(); const compiler = Statement.makeCompilerSqlite(options.transformQueryNames); @@ -224,7 +228,7 @@ const makeWithDatabase = ( ], transformRows, }); - }); + })(); const make = ( options: SqliteClientConfig, From 446372a2545aa54e7b8bb7a359be57119ee92b30 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Wed, 1 Apr 2026 08:52:56 -0700 Subject: [PATCH 2/2] bad codex --- .../src/persistence/NodeSqliteClient.ts | 261 +++++++++--------- 1 file changed, 125 insertions(+), 136 deletions(-) diff --git a/apps/server/src/persistence/NodeSqliteClient.ts b/apps/server/src/persistence/NodeSqliteClient.ts index e95602c18b..77c316e8ca 100644 --- a/apps/server/src/persistence/NodeSqliteClient.ts +++ b/apps/server/src/persistence/NodeSqliteClient.ts @@ -72,163 +72,152 @@ const checkNodeSqliteCompat = () => { return Effect.void; }; -const makeWithDatabase = ( +const makeWithDatabase = Effect.fn("makeWithDatabase")(function* ( options: SqliteClientConfig, openDatabase: () => DatabaseSync, -): Effect.Effect => - Effect.fn("makeWithDatabase")(function* (): Effect.fn.Return< - Client.SqlClient, - never, - Scope.Scope | Reactivity.Reactivity - > { - yield* checkNodeSqliteCompat(); +): Effect.fn.Return { + yield* checkNodeSqliteCompat(); - const compiler = Statement.makeCompilerSqlite(options.transformQueryNames); - const transformRows = options.transformResultNames - ? Statement.defaultTransforms(options.transformResultNames).array - : undefined; + const compiler = Statement.makeCompilerSqlite(options.transformQueryNames); + const transformRows = options.transformResultNames + ? Statement.defaultTransforms(options.transformResultNames).array + : undefined; - const makeConnection = Effect.gen(function* () { - const scope = yield* Effect.scope; - const db = openDatabase(); - yield* Scope.addFinalizer( - scope, - Effect.sync(() => db.close()), - ); + const makeConnection = Effect.gen(function* () { + const scope = yield* Effect.scope; + const db = openDatabase(); + yield* Scope.addFinalizer( + scope, + Effect.sync(() => db.close()), + ); - const statementReaderCache = new WeakMap(); - const hasRows = (statement: StatementSync): boolean => { - const cached = statementReaderCache.get(statement); - if (cached !== undefined) { - return cached; + const statementReaderCache = new WeakMap(); + const hasRows = (statement: StatementSync): boolean => { + const cached = statementReaderCache.get(statement); + if (cached !== undefined) { + return cached; + } + const value = statement.columns().length > 0; + statementReaderCache.set(statement, value); + return value; + }; + + const prepareCache = yield* Cache.make({ + capacity: options.prepareCacheSize ?? 200, + timeToLive: options.prepareCacheTTL ?? Duration.minutes(10), + lookup: (sql: string) => + Effect.try({ + try: () => db.prepare(sql), + catch: (cause) => + new SqlError({ + reason: classifySqliteError(cause, { + message: "Failed to prepare statement", + operation: "prepare", + }), + }), + }), + }); + + const runStatement = (statement: StatementSync, params: ReadonlyArray, raw: boolean) => + Effect.withFiber, SqlError>((fiber) => { + statement.setReadBigInts(Boolean(ServiceMap.get(fiber.services, Client.SafeIntegers))); + try { + if (hasRows(statement)) { + return Effect.succeed(statement.all(...(params as any))); + } + const result = statement.run(...(params as any)); + return Effect.succeed(raw ? (result as unknown as ReadonlyArray) : []); + } catch (cause) { + return Effect.fail( + new SqlError({ + reason: classifySqliteError(cause, { + message: "Failed to execute statement", + operation: "execute", + }), + }), + ); } - const value = statement.columns().length > 0; - statementReaderCache.set(statement, value); - return value; - }; + }); + + const run = (sql: string, params: ReadonlyArray, raw = false) => + Effect.flatMap(Cache.get(prepareCache, sql), (s) => runStatement(s, params, raw)); - const prepareCache = yield* Cache.make({ - capacity: options.prepareCacheSize ?? 200, - timeToLive: options.prepareCacheTTL ?? Duration.minutes(10), - lookup: (sql: string) => + const runValues = (sql: string, params: ReadonlyArray) => + Effect.acquireUseRelease( + Cache.get(prepareCache, sql), + (statement) => Effect.try({ - try: () => db.prepare(sql), + try: () => { + if (hasRows(statement)) { + statement.setReturnArrays(true); + // Safe to cast to array after we've setReturnArrays(true) + return statement.all(...(params as any)) as unknown as ReadonlyArray< + ReadonlyArray + >; + } + statement.run(...(params as any)); + return []; + }, catch: (cause) => new SqlError({ reason: classifySqliteError(cause, { - message: "Failed to prepare statement", - operation: "prepare", + message: "Failed to execute statement", + operation: "execute", }), }), }), - }); - - const runStatement = ( - statement: StatementSync, - params: ReadonlyArray, - raw: boolean, - ) => - Effect.withFiber, SqlError>((fiber) => { - statement.setReadBigInts(Boolean(ServiceMap.get(fiber.services, Client.SafeIntegers))); - try { + (statement) => + Effect.sync(() => { if (hasRows(statement)) { - return Effect.succeed(statement.all(...(params as any))); + statement.setReturnArrays(false); } - const result = statement.run(...(params as any)); - return Effect.succeed(raw ? (result as unknown as ReadonlyArray) : []); - } catch (cause) { - return Effect.fail( - new SqlError({ - reason: classifySqliteError(cause, { - message: "Failed to execute statement", - operation: "execute", - }), - }), - ); - } - }); - - const run = (sql: string, params: ReadonlyArray, raw = false) => - Effect.flatMap(Cache.get(prepareCache, sql), (s) => runStatement(s, params, raw)); - - const runValues = (sql: string, params: ReadonlyArray) => - Effect.acquireUseRelease( - Cache.get(prepareCache, sql), - (statement) => - Effect.try({ - try: () => { - if (hasRows(statement)) { - statement.setReturnArrays(true); - // Safe to cast to array after we've setReturnArrays(true) - return statement.all(...(params as any)) as unknown as ReadonlyArray< - ReadonlyArray - >; - } - statement.run(...(params as any)); - return []; - }, - catch: (cause) => - new SqlError({ - reason: classifySqliteError(cause, { - message: "Failed to execute statement", - operation: "execute", - }), - }), - }), - (statement) => - Effect.sync(() => { - if (hasRows(statement)) { - statement.setReturnArrays(false); - } - }), - ); + }), + ); - return identity({ - execute(sql, params, rowTransform) { - return rowTransform ? Effect.map(run(sql, params), rowTransform) : run(sql, params); - }, - executeRaw(sql, params) { - return run(sql, params, true); - }, - executeValues(sql, params) { - return runValues(sql, params); - }, - executeUnprepared(sql, params, rowTransform) { - const effect = runStatement(db.prepare(sql), params ?? [], false); - return rowTransform ? Effect.map(effect, rowTransform) : effect; - }, - executeStream(_sql, _params) { - return Stream.die("executeStream not implemented"); - }, - }); + return identity({ + execute(sql, params, rowTransform) { + return rowTransform ? Effect.map(run(sql, params), rowTransform) : run(sql, params); + }, + executeRaw(sql, params) { + return run(sql, params, true); + }, + executeValues(sql, params) { + return runValues(sql, params); + }, + executeUnprepared(sql, params, rowTransform) { + const effect = runStatement(db.prepare(sql), params ?? [], false); + return rowTransform ? Effect.map(effect, rowTransform) : effect; + }, + executeStream(_sql, _params) { + return Stream.die("executeStream not implemented"); + }, }); + }); - const semaphore = yield* Semaphore.make(1); - const connection = yield* makeConnection; + const semaphore = yield* Semaphore.make(1); + const connection = yield* makeConnection; - const acquirer = semaphore.withPermits(1)(Effect.succeed(connection)); - const transactionAcquirer = Effect.uninterruptibleMask((restore) => { - const fiber = Fiber.getCurrent()!; - const scope = ServiceMap.getUnsafe(fiber.services, Scope.Scope); - return Effect.as( - Effect.tap(restore(semaphore.take(1)), () => - Scope.addFinalizer(scope, semaphore.release(1)), - ), - connection, - ); - }); + const acquirer = semaphore.withPermits(1)(Effect.succeed(connection)); + const transactionAcquirer = Effect.uninterruptibleMask((restore) => { + const fiber = Fiber.getCurrent()!; + const scope = ServiceMap.getUnsafe(fiber.services, Scope.Scope); + return Effect.as( + Effect.tap(restore(semaphore.take(1)), () => Scope.addFinalizer(scope, semaphore.release(1))), + connection, + ); + }); - return yield* Client.make({ - acquirer, - compiler, - transactionAcquirer, - spanAttributes: [ - ...(options.spanAttributes ? Object.entries(options.spanAttributes) : []), - [ATTR_DB_SYSTEM_NAME, "sqlite"], - ], - transformRows, - }); - })(); + return yield* Client.make({ + acquirer, + compiler, + transactionAcquirer, + spanAttributes: [ + ...(options.spanAttributes ? Object.entries(options.spanAttributes) : []), + [ATTR_DB_SYSTEM_NAME, "sqlite"], + ], + transformRows, + }); +}); const make = ( options: SqliteClientConfig,