{"version":3,"file":"NodeSqliteClient-chi2rq9c.cjs","names":["Context","Effect","Statement","Scope","Cache","Duration","SqlError","Client","Stream","Semaphore","Fiber","DatabaseSync","Layer","Config","Reactivity"],"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>(\"t3/persistence/NodeSqliteClient\");\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      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)\n      .asEffect()\n      .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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"}