{"version":3,"file":"NodeSqliteClient-CJ_J62qy.mjs","names":[],"sources":["../src/persistence/NodeSqliteClient.ts"],"sourcesContent":["/**\n * Port of `@effect/sql-sqlite-node` that uses the native `node:sqlite`\n * bindings instead of `better-sqlite3`.\n *\n * @module SqliteClient\n */\nimport { DatabaseSync, type StatementSync } from \"node:sqlite\";\n\nimport * as Cache from \"effect/Cache\";\nimport * as Config from \"effect/Config\";\nimport * as Duration from \"effect/Duration\";\nimport * as Effect from \"effect/Effect\";\nimport * as Fiber from \"effect/Fiber\";\nimport { identity } from \"effect/Function\";\nimport * as Layer from \"effect/Layer\";\nimport * as Scope from \"effect/Scope\";\nimport * as Semaphore from \"effect/Semaphore\";\nimport * as Context from \"effect/Context\";\nimport * as Stream from \"effect/Stream\";\nimport * as Reactivity from \"effect/unstable/reactivity/Reactivity\";\nimport * as Client from \"effect/unstable/sql/SqlClient\";\nimport type { Connection } from \"effect/unstable/sql/SqlConnection\";\nimport { SqlError, classifySqliteError } from \"effect/unstable/sql/SqlError\";\nimport * as Statement from \"effect/unstable/sql/Statement\";\n\nconst ATTR_DB_SYSTEM_NAME = \"db.system.name\";\n\nexport const TypeId: TypeId = \"~local/sqlite-node/SqliteClient\";\n\nexport type TypeId = \"~local/sqlite-node/SqliteClient\";\n\n/**\n * SqliteClient - Effect service tag for the sqlite SQL client.\n */\nexport const SqliteClient = Context.Service<Client.SqlClient>(\n  \"salchi/persistence/NodeSqliteClient\",\n);\n\nexport interface SqliteClientConfig {\n  readonly filename: string;\n  readonly readonly?: boolean | undefined;\n  readonly allowExtension?: boolean | undefined;\n  readonly prepareCacheSize?: number | undefined;\n  readonly prepareCacheTTL?: Duration.Input | undefined;\n  readonly spanAttributes?: Record<string, unknown> | undefined;\n  readonly transformResultNames?: ((str: string) => string) | undefined;\n  readonly transformQueryNames?: ((str: string) => string) | undefined;\n}\n\nexport interface SqliteMemoryClientConfig extends Omit<\n  SqliteClientConfig,\n  \"filename\" | \"readonly\"\n> {}\n\n/**\n * Verify that the current Node.js version includes the `node:sqlite` APIs\n * used by `NodeSqliteClient` — specifically `StatementSync.columns()` (added\n * in Node 22.16.0 / 23.11.0).\n *\n * @see https://github.com/nodejs/node/pull/57490\n */\nconst checkNodeSqliteCompat = () => {\n  const parts = process.versions.node.split(\".\").map(Number);\n  const major = parts[0] ?? 0;\n  const minor = parts[1] ?? 0;\n  const supported = (major === 22 && minor >= 16) || (major === 23 && minor >= 11) || major >= 24;\n\n  if (!supported) {\n    return Effect.die(\n      `Node.js ${process.versions.node} is missing required node:sqlite APIs ` +\n        `(StatementSync.columns). Upgrade to Node.js >=22.16, >=23.11, or >=24.`,\n    );\n  }\n  return Effect.void;\n};\n\nconst makeWithDatabase = Effect.fn(\"makeWithDatabase\")(function* (\n  options: SqliteClientConfig,\n  openDatabase: () => DatabaseSync,\n): Effect.fn.Return<Client.SqlClient, never, Scope.Scope | Reactivity.Reactivity> {\n  yield* checkNodeSqliteCompat();\n\n  const compiler = Statement.makeCompilerSqlite(options.transformQueryNames);\n  const transformRows = options.transformResultNames\n    ? Statement.defaultTransforms(options.transformResultNames).array\n    : undefined;\n\n  const makeConnection = Effect.gen(function* () {\n    const scope = yield* Effect.scope;\n    const db = openDatabase();\n    yield* Scope.addFinalizer(\n      scope,\n      Effect.sync(() => db.close()),\n    );\n\n    const statementReaderCache = new WeakMap<StatementSync, boolean>();\n    const hasRows = (statement: StatementSync): boolean => {\n      const cached = statementReaderCache.get(statement);\n      if (cached !== undefined) {\n        return cached;\n      }\n      const value = statement.columns().length > 0;\n      statementReaderCache.set(statement, value);\n      return value;\n    };\n\n    const prepareCache = yield* Cache.make({\n      capacity: options.prepareCacheSize ?? 200,\n      timeToLive: options.prepareCacheTTL ?? Duration.minutes(10),\n      lookup: (sql: string) =>\n        Effect.try({\n          try: () => db.prepare(sql),\n          catch: (cause) =>\n            new SqlError({\n              reason: classifySqliteError(cause, {\n                message: \"Failed to prepare statement\",\n                operation: \"prepare\",\n              }),\n            }),\n        }),\n    });\n\n    const runStatement = (statement: StatementSync, params: ReadonlyArray<unknown>, raw: boolean) =>\n      Effect.withFiber<ReadonlyArray<any>, SqlError>((fiber) => {\n        statement.setReadBigInts(Boolean(Context.get(fiber.context, Client.SafeIntegers)));\n        try {\n          if (hasRows(statement)) {\n            return Effect.succeed(statement.all(...(params as any)));\n          }\n          const result = statement.run(...(params as any));\n          return Effect.succeed(raw ? (result as unknown as ReadonlyArray<any>) : []);\n        } catch (cause) {\n          return Effect.fail(\n            new SqlError({\n              reason: classifySqliteError(cause, {\n                message: \"Failed to execute statement\",\n                operation: \"execute\",\n              }),\n            }),\n          );\n        }\n      });\n\n    const run = (sql: string, params: ReadonlyArray<unknown>, raw = false) =>\n      Effect.flatMap(Cache.get(prepareCache, sql), (s) => runStatement(s, params, raw));\n\n    const runValues = (sql: string, params: ReadonlyArray<unknown>) =>\n      Effect.acquireUseRelease(\n        Cache.get(prepareCache, sql),\n        (statement) =>\n          Effect.try({\n            try: () => {\n              if (hasRows(statement)) {\n                statement.setReturnArrays(true);\n                // Safe to cast to array after we've setReturnArrays(true)\n                return statement.all(...(params as any)) as unknown as ReadonlyArray<\n                  ReadonlyArray<unknown>\n                >;\n              }\n              statement.run(...(params as any));\n              return [];\n            },\n            catch: (cause) =>\n              new SqlError({\n                reason: classifySqliteError(cause, {\n                  message: \"Failed to execute statement\",\n                  operation: \"execute\",\n                }),\n              }),\n          }),\n        (statement) =>\n          Effect.sync(() => {\n            if (hasRows(statement)) {\n              statement.setReturnArrays(false);\n            }\n          }),\n      );\n\n    return identity<Connection>({\n      execute(sql, params, rowTransform) {\n        return rowTransform ? Effect.map(run(sql, params), rowTransform) : run(sql, params);\n      },\n      executeRaw(sql, params) {\n        return run(sql, params, true);\n      },\n      executeValues(sql, params) {\n        return runValues(sql, params);\n      },\n      executeValuesUnprepared(sql, params) {\n        return Effect.try({\n          try: () => {\n            const statement = db.prepare(sql);\n            if (hasRows(statement)) {\n              statement.setReturnArrays(true);\n              return statement.all(...(params as any)) as unknown as ReadonlyArray<\n                ReadonlyArray<unknown>\n              >;\n            }\n            statement.run(...(params as any));\n            return [];\n          },\n          catch: (cause) =>\n            new SqlError({\n              reason: classifySqliteError(cause, {\n                message: \"Failed to execute statement\",\n                operation: \"execute\",\n              }),\n            }),\n        });\n      },\n      executeUnprepared(sql, params, rowTransform) {\n        const effect = runStatement(db.prepare(sql), params ?? [], false);\n        return rowTransform ? Effect.map(effect, rowTransform) : effect;\n      },\n      executeStream(_sql, _params) {\n        return Stream.die(\"executeStream not implemented\");\n      },\n    });\n  });\n\n  const semaphore = yield* Semaphore.make(1);\n  const connection = yield* makeConnection;\n\n  const acquirer = semaphore.withPermits(1)(Effect.succeed(connection));\n  const transactionAcquirer = Effect.uninterruptibleMask((restore) => {\n    const fiber = Fiber.getCurrent()!;\n    const scope = Context.getUnsafe(fiber.context, Scope.Scope);\n    return Effect.as(\n      Effect.tap(restore(semaphore.take(1)), () => Scope.addFinalizer(scope, semaphore.release(1))),\n      connection,\n    );\n  });\n\n  return yield* Client.make({\n    acquirer,\n    compiler,\n    transactionAcquirer,\n    spanAttributes: [\n      ...(options.spanAttributes ? Object.entries(options.spanAttributes) : []),\n      [ATTR_DB_SYSTEM_NAME, \"sqlite\"],\n    ],\n    transformRows,\n  });\n});\n\nconst make = (\n  options: SqliteClientConfig,\n): Effect.Effect<Client.SqlClient, never, Scope.Scope | Reactivity.Reactivity> =>\n  makeWithDatabase(\n    options,\n    () =>\n      new DatabaseSync(options.filename, {\n        readOnly: options.readonly ?? false,\n        allowExtension: options.allowExtension ?? false,\n      }),\n  );\n\nconst makeMemory = (\n  config: SqliteMemoryClientConfig = {},\n): Effect.Effect<Client.SqlClient, never, Scope.Scope | Reactivity.Reactivity> =>\n  makeWithDatabase(\n    {\n      ...config,\n      filename: \":memory:\",\n      readonly: false,\n    },\n    () => {\n      const database = new DatabaseSync(\":memory:\", {\n        allowExtension: config.allowExtension ?? false,\n      });\n      return database;\n    },\n  );\n\nexport const layerConfig = (\n  config: Config.Wrap<SqliteClientConfig>,\n): Layer.Layer<Client.SqlClient, Config.ConfigError> =>\n  Layer.effectContext(\n    Config.unwrap(config).pipe(\n      Effect.flatMap(make),\n      Effect.map((client) =>\n        Context.make(SqliteClient, client).pipe(Context.add(Client.SqlClient, client)),\n      ),\n    ),\n  ).pipe(Layer.provide(Reactivity.layer));\n\nexport const layer = (config: SqliteClientConfig): Layer.Layer<Client.SqlClient> =>\n  Layer.effectContext(\n    Effect.map(make(config), (client) =>\n      Context.make(SqliteClient, client).pipe(Context.add(Client.SqlClient, client)),\n    ),\n  ).pipe(Layer.provide(Reactivity.layer));\n\nexport const layerMemory = (config: SqliteMemoryClientConfig = {}): Layer.Layer<Client.SqlClient> =>\n  Layer.effectContext(\n    Effect.map(makeMemory(config), (client) =>\n      Context.make(SqliteClient, client).pipe(Context.add(Client.SqlClient, client)),\n    ),\n  ).pipe(Layer.provide(Reactivity.layer));\n"],"mappings":"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"}