@shivaedev/sql 0.25.0 → 0.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +28 -7
  3. package/dist/PostgresListenerError.d.ts +10 -0
  4. package/dist/PostgresListenerError.d.ts.map +1 -0
  5. package/dist/PostgresListenerError.js +7 -0
  6. package/dist/PostgresListenerError.js.map +1 -0
  7. package/dist/SchemaComponent.d.ts +5 -5
  8. package/dist/SchemaComponent.d.ts.map +1 -1
  9. package/dist/SchemaComponent.js +1 -2
  10. package/dist/SchemaComponent.js.map +1 -1
  11. package/dist/WorkerRelease.d.ts +7 -8
  12. package/dist/WorkerRelease.d.ts.map +1 -1
  13. package/dist/WorkerRelease.js +10 -4
  14. package/dist/WorkerRelease.js.map +1 -1
  15. package/dist/adapters/makePostgresClient.d.ts +14 -0
  16. package/dist/adapters/makePostgresClient.d.ts.map +1 -0
  17. package/dist/adapters/makePostgresClient.js +63 -0
  18. package/dist/adapters/makePostgresClient.js.map +1 -0
  19. package/dist/adapters/postgresStream.d.ts +4 -0
  20. package/dist/adapters/postgresStream.d.ts.map +1 -0
  21. package/dist/adapters/postgresStream.js +20 -0
  22. package/dist/adapters/postgresStream.js.map +1 -0
  23. package/dist/assertPostgresMigrations.d.ts +1 -1
  24. package/dist/assertPostgresMigrations.d.ts.map +1 -1
  25. package/dist/assertPostgresMigrations.js.map +1 -1
  26. package/dist/coordinateWorkerRelease.d.ts +3 -3
  27. package/dist/coordinateWorkerRelease.d.ts.map +1 -1
  28. package/dist/coordinateWorkerRelease.js +14 -5
  29. package/dist/coordinateWorkerRelease.js.map +1 -1
  30. package/dist/databaseAddress.d.ts +4 -0
  31. package/dist/databaseAddress.d.ts.map +1 -0
  32. package/dist/databaseAddress.js +25 -0
  33. package/dist/databaseAddress.js.map +1 -0
  34. package/dist/internal/CommandRecovery.d.ts +3 -0
  35. package/dist/internal/CommandRecovery.d.ts.map +1 -0
  36. package/dist/internal/CommandRecovery.js +5 -0
  37. package/dist/internal/CommandRecovery.js.map +1 -0
  38. package/dist/listenForChanges.d.ts +3 -1
  39. package/dist/listenForChanges.d.ts.map +1 -1
  40. package/dist/listenForChanges.js +31 -34
  41. package/dist/listenForChanges.js.map +1 -1
  42. package/dist/migrations.d.ts +5 -4
  43. package/dist/migrations.d.ts.map +1 -1
  44. package/dist/migrations.js +7 -3
  45. package/dist/migrations.js.map +1 -1
  46. package/dist/notifyPayload.d.ts +1 -0
  47. package/dist/notifyPayload.d.ts.map +1 -1
  48. package/dist/notifyPayload.js +2 -0
  49. package/dist/notifyPayload.js.map +1 -1
  50. package/dist/pendingPostgresMigrations.d.ts +4 -3
  51. package/dist/pendingPostgresMigrations.d.ts.map +1 -1
  52. package/dist/pendingPostgresMigrations.js +8 -4
  53. package/dist/pendingPostgresMigrations.js.map +1 -1
  54. package/dist/postgresChanges.d.ts +3 -1
  55. package/dist/postgresChanges.d.ts.map +1 -1
  56. package/dist/postgresChanges.js +4 -2
  57. package/dist/postgresChanges.js.map +1 -1
  58. package/dist/postgresLayer.d.ts +11 -0
  59. package/dist/postgresLayer.d.ts.map +1 -0
  60. package/dist/postgresLayer.js +19 -0
  61. package/dist/postgresLayer.js.map +1 -0
  62. package/dist/postgresRelease.d.ts +9 -6
  63. package/dist/postgresRelease.d.ts.map +1 -1
  64. package/dist/postgresRelease.js +3 -3
  65. package/dist/postgresRelease.js.map +1 -1
  66. package/dist/releaseWorkers.d.ts +2 -2
  67. package/dist/releaseWorkers.d.ts.map +1 -1
  68. package/dist/releaseWorkers.js +3 -3
  69. package/dist/releaseWorkers.js.map +1 -1
  70. package/dist/testing.d.ts +2 -2
  71. package/dist/testing.d.ts.map +1 -1
  72. package/dist/testing.js +2 -2
  73. package/dist/testing.js.map +1 -1
  74. package/package.json +12 -7
  75. package/src/PostgresListenerError.ts +9 -0
  76. package/src/SchemaComponent.ts +6 -6
  77. package/src/WorkerRelease.ts +11 -9
  78. package/src/adapters/makePostgresClient.ts +72 -0
  79. package/src/adapters/postgresStream.ts +26 -0
  80. package/src/assertPostgresMigrations.ts +2 -2
  81. package/src/coordinateWorkerRelease.ts +31 -5
  82. package/src/databaseAddress.ts +25 -0
  83. package/src/internal/CommandRecovery.ts +5 -0
  84. package/src/listenForChanges.ts +41 -39
  85. package/src/migrations.ts +14 -7
  86. package/src/notifyPayload.ts +2 -0
  87. package/src/pendingPostgresMigrations.ts +16 -7
  88. package/src/postgresChanges.ts +10 -2
  89. package/src/postgresLayer.ts +27 -0
  90. package/src/postgresRelease.ts +9 -5
  91. package/src/releaseWorkers.ts +5 -3
  92. package/src/testing.ts +4 -8
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.26.0 - 2026-10-30
4
+
5
+ ### Added
6
+
7
+ - `@shivaedev/server`: Configure a direct or session-mode change listener with `config.listenDatabaseUrl` or `LISTEN_DATABASE_URL`, falling back to the application connection when unset.
8
+ - `@shivaedev/sql`: Supply `listenerClient` separately from the writing `client` to `postgresChanges`; set `connectionSetting` to identify that connection in diagnostics.
9
+ - `@shivaedev/sql/postgresLayer.ts` provides `postgresLayer(url, options)` with one native `PgPool` shared by `PgClient` and `SqlClient`. Reserved connections and client queries share `maxConnections`, and Layer shutdown waits for every socket to close.
10
+ - `appDatabase` and database-backed `appHost` expose the application's native `PgPool` service for packages that need reserved PostgreSQL connections.
11
+
12
+ ### Fixed
13
+
14
+ - Retry command-declared invalidation publication after the handler returns, with two bounded exponential backoffs. Exhausted retries preserve the committed result and resynchronize local live streams, then broadcast Resync to other PostgreSQL hosts when the change channel is reachable.
15
+ - Clarify that atomic record and bucket invalidation comes from the stored model and repository; command `invalidates` keys remain after-commit. No consumer API changes or migration are required.
16
+ - Retry worker-release heartbeat and poll failures on a bounded schedule, log their causes, and close the worker scope with the original cause when recovery fails. Heartbeats continue while jobs drain after a connection blip.
17
+ - Log change-listener loss and reconnect causes, and bound listener sub-scope cleanup without rewriting scope finalizers.
18
+ - Fail application database startup when its first PostgreSQL LISTEN or heartbeat fails, instead of waiting indefinitely.
19
+ - SQL decodes migration ledger, release ownership and change-coverage rows before using them. Malformed rows fail with `SchemaError` instead of being treated as current, pending or a different owner.
20
+ - SQL generates worker identities and change-listener heartbeat tokens through Effect's `Random` service.
21
+
22
+ ### Breaking
23
+
24
+ - Listener startup deadlines now fail with `PostgresListenerError`, naming the connection setting, instead of `SqlError`. Consumers handling startup failures must also handle `PostgresListenerError` from `@shivaedev/sql/PostgresListenerError.ts`. Hosts using transaction-pooled application URLs must set `LISTEN_DATABASE_URL` or `config.listenDatabaseUrl` to a direct or session-mode connection to the same database. Native SQL startup failures still use `SqlError`.
25
+ - Server's PostgreSQL construction and address helpers move to sql. Consumers of `@shivaedev/server/adapters/postgresClient.ts` must compose `postgresLayer` from `@shivaedev/sql/postgresLayer.ts` and read the native `PgPool` or `PgClient` service instead. Address-only consumers import `databaseAddress` from `@shivaedev/sql/databaseAddress.ts` instead of `@shivaedev/server/databaseAddress.ts`.
26
+ - `postgresChanges` and `listenForChanges` now expose `SqlError` during acquisition. Consumers that required an infallible Layer or Effect must handle or propagate this error. `heartbeatTimeout` bounds the first LISTEN acquisition and its first heartbeat; retry startup at the application boundary if desired. Established listeners continue to reconnect.
27
+ - Schema component failures now reach SQL migration and readiness callers, server startup and test-sql migration helpers with their original error, tag and cause, and log the component name. Handle the component's own error instead of a wrapping `MigrationError` with kind `Failed`. When annotating migration options with fallible components, supply their error type as the second generic parameter; the default describes components that cannot fail.
28
+ - Declare `WorkerRelease` through `defineService`. Consumers using the old class as a type must use `Context.Service.Identifier<typeof WorkerRelease>`; yielding the service and providing its value stay the same.
29
+
3
30
  ## 0.25.0 - 2026-10-30
4
31
 
5
32
  ### Changed
package/README.md CHANGED
@@ -21,7 +21,7 @@ const rows = yield* lines.findMany({
21
21
 
22
22
  ### How to think about it
23
23
 
24
- The application owns the database schema and its native Effect `SqlClient` layer. Configure new clients with `clientOptions` to map camelCase model fields to snake_case PostgreSQL columns. A **model** describes decoded row fields and the variants used for insertion, updating and JSON. A **repository** combines native `SqlModel` CRUD with a small `findMany` query whose filters and selected results use those fields.
24
+ The application owns the database schema. For PostgreSQL, `postgresLayer` provides one native pool and the Effect SQL clients that use it. Configure new clients with `clientOptions` to map camelCase model fields to snake_case PostgreSQL columns. A **model** describes decoded row fields and the variants used for insertion, updating and JSON. A **repository** combines native `SqlModel` CRUD with a small `findMany` query whose filters and selected results use those fields.
25
25
 
26
26
  Repository writes announce their table and returned row keys automatically. Every written row keeps its record key, including in bulk writes. Fields marked with `bucket` also announce their old and new bucket keys. Each write uses `transact`; start a surrounding multi-write boundary with `transact` too, so keys leave only after its owning database commits. Bare native transactions are refused because their commit cannot be observed. The change ownership comes from `changes`; this package supplies the SQL boundary and the Reactivity publisher.
27
27
 
@@ -29,6 +29,25 @@ Migrations are ordinary Effects loaded by native Migrator. `migratePostgres` add
29
29
 
30
30
  The work has three parts: describe the model and provide its database once, choose repository operations per feature, and put each changed write inside the transaction that owns its commit.
31
31
 
32
+ ### One PostgreSQL pool per application
33
+
34
+ `postgresLayer(url, options)` from `@shivaedev/sql/postgresLayer.ts` provides the native `PgPool` service from `@effect/sql-pg/PgPool`, `PgClient` and `SqlClient` together. `options` accepts `applicationName`, `maxConnections`, `transformQueryNames` and `transformResultNames`. Queries, transactions and raw connections reserved with `yield* pool.reserve` share the same `maxConnections` budget. A reservation holds its connection until its scope closes; release it before waiting for work that needs that slot.
35
+
36
+ ```ts
37
+ import * as PgPool from "@effect/sql-pg/PgPool";
38
+ import { Effect, Redacted } from "effect";
39
+ import { postgresLayer } from "@shivaedev/sql/postgresLayer.ts";
40
+
41
+ const database = postgresLayer(Redacted.make("postgresql://localhost/example"), { maxConnections: 4 });
42
+ const backend = Effect.gen(function* () {
43
+ const pool = yield* PgPool.PgPool;
44
+ const connection = yield* pool.reserve;
45
+ return yield* connection.query("select pg_backend_pid() as pid");
46
+ }).pipe(Effect.scoped, Effect.provide(database));
47
+ ```
48
+
49
+ The Layer owns the pool and waits for every socket to close during shutdown. A reserved connection sees committed writes from client transactions in the same database; it does not implicitly join a client's open transaction. `appDatabase` and database-backed `appHost` from `@shivaedev/server/appHost.ts` compose this Layer and expose the pool to application services.
50
+
32
51
  ### SQL names: configure the client once
33
52
 
34
53
  ```ts
@@ -381,7 +400,7 @@ const makeDocuments = makeRepository(Document, {
381
400
 
382
401
  const reviseDocument = Effect.gen(function* () {
383
402
  const documents = yield* makeDocuments;
384
- const key = { id: crypto.randomUUID(), ownerId: "current-owner" };
403
+ const key = { id: `${yield* Random.nextInt}:${yield* Random.nextInt}`, ownerId: "current-owner" };
385
404
  yield* documents.insertOrIgnore({ ...key, name: "Draft" });
386
405
  const original = yield* documents.findById(key);
387
406
  const saved = yield* documents.update({ ...original, name: "Published" });
@@ -441,6 +460,8 @@ Use the repository's table name as the collection name in query read keys. Every
441
460
 
442
461
  Bucket writes require PostgreSQL 18 or later: native `RETURNING old` supplies old values atomically, including for conflict updates. Repositories without bucket fields keep their existing SQL dialect support. Bulk writes keep every record and bucket key; the PostgreSQL publisher splits large batches into NOTIFY payloads.
443
462
 
463
+ Record and bucket keys that must commit atomically with a write come from this model and repository path. A command's `invalidates(payload, result, viewer)` runs after its handler returns and uses a separate publication transaction. Failed publication retries twice with 50 ms and 100 ms backoffs. If those attempts fail, `postgresChanges` resynchronizes local live streams and broadcasts Resync on its change channel. If PostgreSQL also rejects the broadcast or cannot be reached, local streams still resynchronize, but healthy listeners on other hosts can miss the write until they reconnect or otherwise reconcile. The failure is logged; NOTIFY provides no durable replay.
464
+
444
465
  The transaction rules follow the database owner:
445
466
  - A typed failure or interruption before commit rolls back and announces nothing. A failed deferred `COMMIT` fails through `onSqlError` and also announces nothing.
446
467
  - A nested `transact` on the same client uses a native savepoint. Its successful keys join the outer set; a rolled-back savepoint's keys are discarded.
@@ -515,15 +536,15 @@ Use sequential numbers, not timestamps. Effect's Migrator skips any migration wh
515
536
 
516
537
  A failed batch rolls back its DDL, data and ledger entries, retaining previously committed migrations. A failed first batch also rolls back the bootstrapped ledger. A lock timeout fails with typed `SqlError` and reason `LockTimeoutError`; a migration-body SQL failure is a typed native `MigrationError`. Inspect startup completion with `Effect.exit` when both typed failures and defects matter. Keep nontransactional DDL in schema components; external side effects, crash recovery and deployment compatibility need their own migration policy.
517
538
 
518
- Schema components describe storage owned by another engine alongside the numbered `loader`: pass `components: [{ name, apply, pending }]` to `migratePostgres` and `assertPostgresMigrations`. `apply` runs after the ledger transaction commits, outside that transaction, on every migration invocation; it must support repeated execution. `pending` returns a description when work remains and `undefined` when current. Components are never recorded as numbered ledger entries. Component failures become `MigrationError` (`kind: "Failed"`) with the component name and original cause; their failure leaves the application ledger committed.
539
+ Schema components describe storage owned by another engine alongside the numbered `loader`: pass `components: [{ name, apply, pending }]` to `migratePostgres` and `assertPostgresMigrations`. `apply` runs after the ledger transaction commits, outside that transaction, on every migration invocation; it must support repeated execution. `pending` returns a description when work remains and `undefined` when current. Components are never recorded as numbered ledger entries. Component failures reach callers unchanged, including their original tags and causes, and are logged with the component name; their failure leaves the application ledger committed. Migration and readiness functions infer component error types. An explicitly annotated `PostgresMigrationOptions<R, E>` uses its second parameter for those errors; omitting it describes components that cannot fail.
519
540
 
520
541
  For processes whose deployment owns migration execution, call `assertPostgresMigrations({ loader, table?, components? })` from `@shivaedev/sql/assertPostgresMigrations.ts` before starting services. It checks the native loader’s required IDs and names against the ledger without running migration bodies or creating a ledger. A missing ledger, pending migration or mismatched name fails with native `MigrationError` (`kind: "BadState"`); database failures stay typed `SqlError`. Later ledger entries are allowed so an older application version can start during a rolling deployment. Pending ledger IDs and component names and details appear together in the error. This checks recorded migrations and component readouts, not physical schema drift.
521
542
 
522
- For a structured readiness readout, use `pendingPostgresMigrations({ loader, table?, components? })` from `@shivaedev/sql/pendingPostgresMigrations.ts`. It returns `{ migrations: [{ id, name }], components: [{ name, detail }] }` for pending work, with both arrays empty when current. It reads the same ledger and component checks as `assertPostgresMigrations` without running migrations.
543
+ For a structured readiness readout, use `pendingPostgresMigrations({ loader, table?, components? })` from `@shivaedev/sql/pendingPostgresMigrations.ts`. It returns `{ migrations: [{ id, name }], components: [{ name, detail }] }` for pending work, with both arrays empty when current. It reads the same ledger and component checks as `assertPostgresMigrations` without running migrations. Malformed ledger and release rows fail with `SchemaError`.
523
544
 
524
545
  Pass an optional `release` string to `migratePostgres` to record the owning release after all ledger migrations and schema components succeed. The final `postgresRelease(release, table?)` schema component owns a one-row `<ledger>_release` table with typed `release` and UTC `recorded_at timestamptz` columns, plus a `<ledger>_workers` table with typed process identity, release and UTC heartbeat columns. `readPostgresRelease(table?)` from `postgresRelease.ts` returns the recorded release and `recordedAt` Date, or `undefined` for a missing marker. `pendingPostgresMigrations({ loader, release, table? })` reports a missing or different release as a pending component. Without `release`, migration and readiness calls neither write nor read these tables.
525
546
 
526
- `appDatabase` from `@shivaedev/server/appHost.ts` supplies the optional `WorkerRelease` Context service in migration mode `"wait"` with a release. Startup waits for that marker within `waitTimeout`; subsequent checks run every `pollInterval`. Each worker database Layer claims a process row only when its release owns the marker and no live row of another release remains. Heartbeats continue every poll while jobs drain; completed drain and clean shutdown remove the row. Rows older than five polling intervals count as gone, bounding takeover after a crash (five seconds at the default one-second poll). Reserve SQL pool capacity for heartbeats alongside active jobs and the change listener. Release equality decides ownership, so rolling back the marker resumes the earlier worker. `@shivaedev/jobs` reads the service automatically; applications do not connect a signal themselves. Web's `"check"` mode ignores the marker.
547
+ `appDatabase` from `@shivaedev/server/appHost.ts` supplies the optional `WorkerRelease` Context service in migration mode `"wait"` with a release. Startup waits for that marker within `waitTimeout`; subsequent checks run every `pollInterval`. Each worker database Layer claims a process row only when its release owns the marker and no live row of another release remains. Heartbeats continue every poll while jobs drain, including after a connection blip. Each failed heartbeat or ownership poll logs its cause and retries three times, one quarter of a polling interval apart; exhausted retries close the worker scope with the failure cause. Completed drain and clean shutdown remove the row. Rows older than five polling intervals count as gone, bounding takeover after a crash (five seconds at the default one-second poll). Reserve SQL pool capacity for heartbeats alongside active jobs and the change listener. Release equality decides ownership, so rolling back the marker resumes the earlier worker. `@shivaedev/jobs` reads the service automatically; applications do not connect a signal themselves. Web's `"check"` mode ignores the marker.
527
548
 
528
549
  ### PostgreSQL delivery between processes
529
550
 
@@ -537,9 +558,9 @@ const delivery = postgresChanges({
537
558
  });
538
559
  ```
539
560
 
540
- `pg` is the application's `@effect/sql-pg` client; the binding accepts its native SQL and `listen` methods without creating another pool. Reserve one session connection for LISTEN and another for writes and heartbeats; use a pool client with at least two connections. Compose `delivery` into the Layer that provides feature services using `transact`, alongside the existing SQL and Reactivity services. The [reference server](https://github.com/ShivaeDev/platform/blob/main/packages/react/src/test-support/postgres-example/server.ts) shows the complete composition. The binding sends `select pg_notify(...)` through the writing transaction's `SqlClient`, so a rollback wakes no process. Its transaction sink leaves delivery to the listener, including in the writing process. Unframed `invalidateOnCommit` calls retain local invalidation; cross-process writes must use `transact`.
561
+ `pg` is the application's `@effect/sql-pg` client; the binding accepts its native SQL and `listen` methods without creating another pool. Optional `listenerClient` supplies a different native client for LISTEN; the application client still owns write notifications and heartbeat publication. Both clients must reach the same database. LISTEN requires a direct or session-mode connection. With one shared client, reserve one session connection for LISTEN and another for writes and heartbeats; use a pool client with at least two connections. With separate clients, the listener needs one connection of its own. Compose `delivery` into the Layer that provides feature services using `transact`, alongside the existing SQL and Reactivity services. The [reference server](https://github.com/ShivaeDev/platform/blob/main/packages/react/src/test-support/postgres-example/server.ts) shows the complete composition. The binding sends `select pg_notify(...)` through the writing transaction's `SqlClient`, so a rollback wakes no process. Its transaction sink leaves delivery to the listener, including in the writing process. Unframed `invalidateOnCommit` calls retain local invalidation; cross-process writes must use `transact`.
541
562
 
542
- `PostgresChangesOptions` takes `channel`, `client`, `resync`, and optional `heartbeatInterval`, `heartbeatTimeout` and `retryDelay` Effect durations. The defaults are 5 seconds, 10 seconds and 1 second. Acquisition waits for a heartbeat received through LISTEN. A missing heartbeat or a stream failure resyncs streams before listener cleanup; a successful reconnection resyncs again. Each listener finalizer has the same `heartbeatTimeout` deadline, so a stalled UNLISTEN cannot prevent the following native connection release or a fresh subscription. The heartbeat detects a listener that stops delivering without failing. `resync` is an infallible `Effect.Effect<number>` returning the number of open live streams it marked for reconciliation. `makeLiveServer.resync` provides this count and stops counting a stream when its scope closes. Direct bindings with another stream owner must return that owner's count. Provide the Layer for the process lifetime so scope release closes the listener.
563
+ `PostgresChangesOptions` takes `channel`, `client`, `resync`, optional `listenerClient` and `connectionSetting` (a diagnostic label, default `"client"`), and optional `heartbeatInterval`, `heartbeatTimeout` and `retryDelay` Effect durations. The defaults are 5 seconds, 10 seconds and 1 second. Acquisition waits for LISTEN and then a heartbeat received through it, each within `heartbeatTimeout`. A deadline without a heartbeat fails startup with `PostgresListenerError` from `@shivaedev/sql/PostgresListenerError.ts`, whose `connectionSetting` and message identify the connection to fix. Native SQL failures still fail startup with `SqlError`. Established listeners reconnect after a loss. Loss and reconnect failures log their causes. A missing heartbeat or a stream failure resyncs streams before listener cleanup; a successful reconnection resyncs again. Listener cleanup runs in a parallel sub-scope bounded by `heartbeatTimeout`, so stalled listener cleanup cannot delay native connection release, a fresh subscription or scope shutdown. The heartbeat detects a listener that stops delivering without failing. `resync` is an infallible `Effect.Effect<number>` returning the number of open live streams it marked for reconciliation. `makeLiveServer.resync` provides this count and stops counting a stream when its scope closes. Direct bindings with another stream owner must return that owner's count. Provide the Layer for the process lifetime so scope release closes the listener.
543
564
 
544
565
  NOTIFY is a wake-up signal, not a durable log. Transaction payloads contain only string or finite-number invalidation keys, with no row values. PostgreSQL requires each payload to be smaller than 8,000 bytes in the default configuration. The binding splits JSON arrays by their UTF-8 byte length; one key that cannot fit, or a nonserializable key type, fails with `SqlError` before commit and rolls back the write. Heartbeats use a separate control payload on the same channel. Applications choose a channel shared only by their cooperating processes. LISTEN requires a session connection; PgBouncer transaction pooling needs another delivery adapter. Missed notifications are reconciled by querying again on resync, with no replay guarantee.
545
566
 
@@ -0,0 +1,10 @@
1
+ declare const PostgresListenerError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
2
+ readonly _tag: "PostgresListenerError";
3
+ } & Readonly<A>;
4
+ export declare class PostgresListenerError extends PostgresListenerError_base<{
5
+ readonly connectionSetting: string;
6
+ }> {
7
+ get message(): string;
8
+ }
9
+ export {};
10
+ //# sourceMappingURL=PostgresListenerError.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PostgresListenerError.d.ts","sourceRoot":"","sources":["../src/PostgresListenerError.ts"],"names":[],"mappings":";;;AAEA,qBAAa,qBAAsB,SAAQ,2BAA0C;IACpF,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;CACnC,CAAC;IACD,IAAa,OAAO,WAEnB;CACD"}
@@ -0,0 +1,7 @@
1
+ import { Data } from "effect";
2
+ export class PostgresListenerError extends Data.TaggedError("PostgresListenerError") {
3
+ get message() {
4
+ return `PostgreSQL change listener heartbeat timed out without receiving its own notification. Set ${this.connectionSetting} to a direct or session-mode connection to the application database.`;
5
+ }
6
+ }
7
+ //# sourceMappingURL=PostgresListenerError.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PostgresListenerError.js","sourceRoot":"","sources":["../src/PostgresListenerError.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAE9B,MAAM,OAAO,qBAAsB,SAAQ,IAAI,CAAC,WAAW,CAAC,uBAAuB,CAEjF;IACD,IAAa,OAAO;QACnB,OAAO,8FAA8F,IAAI,CAAC,iBAAiB,sEAAsE,CAAC;IACnM,CAAC;CACD"}
@@ -1,9 +1,9 @@
1
1
  import { Effect } from "effect";
2
- import { Migrator, type SqlClient } from "effect/sql";
3
- export interface SchemaComponent<R = never> {
4
- readonly apply: Effect.Effect<unknown, unknown, R | SqlClient.SqlClient>;
2
+ import type { SqlClient } from "effect/sql";
3
+ export interface SchemaComponent<R = never, E = unknown> {
4
+ readonly apply: Effect.Effect<unknown, E, R | SqlClient.SqlClient>;
5
5
  readonly name: string;
6
- readonly pending: Effect.Effect<string | undefined, unknown, R | SqlClient.SqlClient>;
6
+ readonly pending: Effect.Effect<string | undefined, E, R | SqlClient.SqlClient>;
7
7
  }
8
- export declare function runSchemaComponent<A, R>(name: string, effect: Effect.Effect<A, unknown, R>): Effect.Effect<A, Migrator.MigrationError, R>;
8
+ export declare function runSchemaComponent<A, E, R>(name: string, effect: Effect.Effect<A, E, R>): Effect.Effect<A, E, R>;
9
9
  //# sourceMappingURL=SchemaComponent.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SchemaComponent.d.ts","sourceRoot":"","sources":["../src/SchemaComponent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,YAAY,CAAC;AAEtD,MAAM,WAAW,eAAe,CAAC,CAAC,GAAG,KAAK;IACzC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,CAAC,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;IACzE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,CAAC,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;CACtF;AAED,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,gDAE1F"}
1
+ {"version":3,"file":"SchemaComponent.d.ts","sourceRoot":"","sources":["../src/SchemaComponent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,MAAM,WAAW,eAAe,CAAC,CAAC,GAAG,KAAK,EAAE,CAAC,GAAG,OAAO;IACtD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;IACnE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,GAAG,SAAS,EAAE,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC;CAChF;AAED,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,0BAEvF"}
@@ -1,6 +1,5 @@
1
1
  import { Effect } from "effect";
2
- import { Migrator } from "effect/sql";
3
2
  export function runSchemaComponent(name, effect) {
4
- return effect.pipe(Effect.mapError((cause) => new Migrator.MigrationError({ cause, kind: "Failed", message: `Schema component ${name} failed.` })));
3
+ return effect.pipe(Effect.tapCause((cause) => Effect.logError(`Schema component ${name} failed.`, cause)));
5
4
  }
6
5
  //# sourceMappingURL=SchemaComponent.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SchemaComponent.js","sourceRoot":"","sources":["../src/SchemaComponent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAkB,MAAM,YAAY,CAAC;AAQtD,MAAM,UAAU,kBAAkB,CAAO,IAAY,EAAE,MAAoC;IAC1F,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,QAAQ,CAAC,cAAc,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,oBAAoB,IAAI,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC;AACrJ,CAAC"}
1
+ {"version":3,"file":"SchemaComponent.js","sourceRoot":"","sources":["../src/SchemaComponent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAShC,MAAM,UAAU,kBAAkB,CAAU,IAAY,EAAE,MAA8B;IACvF,OAAO,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,oBAAoB,IAAI,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;AAC5G,CAAC"}
@@ -1,14 +1,13 @@
1
- import { Context, Effect, type Scope } from "effect";
2
- declare const WorkerRelease_base: Context.ServiceClass<WorkerRelease, "@shivaedev/sql/WorkerRelease", {
3
- readonly register: <E>(control: (active: boolean) => Effect.Effect<void, E>) => Effect.Effect<void, E, Scope.Scope>;
4
- }>;
5
- export declare class WorkerRelease extends WorkerRelease_base {
6
- }
1
+ import { Effect } from "effect";
7
2
  export declare const makeWorkerRelease: () => Effect.Effect<{
8
3
  service: {
9
- readonly register: <E>(control: (active: boolean) => Effect.Effect<void, E>) => Effect.Effect<void, E, Scope.Scope>;
4
+ register: <E>(control: (active: boolean) => Effect.Effect<void, E>) => Effect.Effect<void, E, import("effect/Scope").Scope>;
10
5
  };
11
6
  setActive: (next: boolean) => Effect.Effect<void, unknown, never>;
12
7
  }, never, never>;
13
- export {};
8
+ export declare const WorkerRelease: import("effect/Context").Service<"@shivaedev/sql/WorkerRelease", Readonly<{
9
+ readonly register: <E>(control: (active: boolean) => Effect.Effect<void, E>) => Effect.Effect<void, E, import("effect/Scope").Scope>;
10
+ }>> & {
11
+ readonly layer: import("effect/Layer").Layer<"@shivaedev/sql/WorkerRelease", never, never>;
12
+ };
14
13
  //# sourceMappingURL=WorkerRelease.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"WorkerRelease.d.ts","sourceRoot":"","sources":["../src/WorkerRelease.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,KAAK,EAAa,MAAM,QAAQ,CAAC;;uBAK3C,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC;;AAHrH,qBAAa,aAAc,SAAQ,kBAKA;CAAG;AAEtC,eAAO,MAAM,iBAAiB;;2BAJT,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC;;sBAqBjG,OAAO;gBAWzB,CAAC"}
1
+ {"version":3,"file":"WorkerRelease.d.ts","sourceRoot":"","sources":["../src/WorkerRelease.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAa,MAAM,QAAQ,CAAC;AAI3C,eAAO,MAAM,iBAAiB;;mBAKX,CAAC,WAAW,CAAC,MAAM,EAAE,OAAO,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;;sBAYtD,OAAO;gBAWzB,CAAC;AAEH,eAAO,MAAM,aAAa;wBAzBP,CAAC,WAAW,CAAC,MAAM,EAAE,OAAO,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;;;CA8BxE,CAAC"}
@@ -1,6 +1,6 @@
1
- import { Context, Effect, Semaphore } from "effect";
2
- export class WorkerRelease extends Context.Service()("@shivaedev/sql/WorkerRelease") {
3
- }
1
+ import { Effect, Semaphore } from "effect";
2
+ import { defineService } from "@shivaedev/service/defineService.ts";
3
+ import { genericMethod } from "@shivaedev/service/genericMethod.ts";
4
4
  export const makeWorkerRelease = Effect.fn("EffectSql.makeWorkerRelease")(function* () {
5
5
  const mutex = Semaphore.makeUnsafe(1);
6
6
  const controls = new Set();
@@ -17,7 +17,7 @@ export const makeWorkerRelease = Effect.fn("EffectSql.makeWorkerRelease")(functi
17
17
  return Effect.acquireRelease(locked(attach), () => locked(detach));
18
18
  }
19
19
  return {
20
- service: WorkerRelease.of({ register }),
20
+ service: { register },
21
21
  setActive: (next) => locked(Effect.gen(function* () {
22
22
  if (next === active) {
23
23
  return;
@@ -27,4 +27,10 @@ export const makeWorkerRelease = Effect.fn("EffectSql.makeWorkerRelease")(functi
27
27
  })).pipe(Effect.uninterruptible),
28
28
  };
29
29
  });
30
+ export const WorkerRelease = defineService({
31
+ id: "@shivaedev/sql/WorkerRelease",
32
+ initialize: makeWorkerRelease(),
33
+ methods: ({ service }) => ({ register: genericMethod(service.register) }),
34
+ requires: [],
35
+ });
30
36
  //# sourceMappingURL=WorkerRelease.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"WorkerRelease.js","sourceRoot":"","sources":["../src/WorkerRelease.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAc,SAAS,EAAE,MAAM,QAAQ,CAAC;AAEhE,MAAM,OAAO,aAAc,SAAQ,OAAO,CAAC,OAAO,EAK/C,CAAC,8BAA8B,CAAC;CAAG;AAEtC,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC,EAAE,CAAC,6BAA6B,CAAC,CAAC,QAAQ,CAAC;IAClF,MAAM,KAAK,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IACtC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAqD,CAAC;IAC9E,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,MAAM,MAAM,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IACpC,SAAS,QAAQ,CAAI,OAAoD;QACxE,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAClC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACtB,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACxB,CAAC,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;YAC/B,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC1B,CAAC,CAAC,CAAC;QACH,OAAO,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IACpE,CAAC;IACD,OAAO;QACN,OAAO,EAAE,aAAa,CAAC,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC;QACvC,SAAS,EAAE,CAAC,IAAa,EAAE,EAAE,CAC5B,MAAM,CACL,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YACnB,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;gBACrB,OAAO;YACR,CAAC;YACD,MAAM,GAAG,IAAI,CAAC;YACd,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5G,CAAC,CAAC,CACF,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC;KAC/B,CAAC;AACH,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"WorkerRelease.js","sourceRoot":"","sources":["../src/WorkerRelease.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AAEpE,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC,EAAE,CAAC,6BAA6B,CAAC,CAAC,QAAQ,CAAC;IAClF,MAAM,KAAK,GAAG,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IACtC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAqD,CAAC;IAC9E,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,MAAM,MAAM,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;IACpC,SAAS,QAAQ,CAAI,OAAoD;QACxE,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAClC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACtB,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QACxB,CAAC,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;YAC/B,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC1B,CAAC,CAAC,CAAC;QACH,OAAO,MAAM,CAAC,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;IACpE,CAAC;IACD,OAAO;QACN,OAAO,EAAE,EAAE,QAAQ,EAAE;QACrB,SAAS,EAAE,CAAC,IAAa,EAAE,EAAE,CAC5B,MAAM,CACL,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YACnB,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;gBACrB,OAAO;YACR,CAAC;YACD,MAAM,GAAG,IAAI,CAAC;YACd,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAC5G,CAAC,CAAC,CACF,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC;KAC/B,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,aAAa,GAAG,aAAa,CAAC;IAC1C,EAAE,EAAE,8BAA8B;IAClC,UAAU,EAAE,iBAAiB,EAAE;IAC/B,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,aAAa,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;IACzE,QAAQ,EAAE,EAAE;CACZ,CAAC,CAAC"}
@@ -0,0 +1,14 @@
1
+ import * as PgClient from "@effect/sql-pg/PgClient";
2
+ import type * as PgConnection from "@effect/sql-pg/PgConnection";
3
+ import * as PgPool from "@effect/sql-pg/PgPool";
4
+ import { Effect } from "effect";
5
+ import { SqlClient, Statement } from "effect/sql";
6
+ import { SqlError } from "effect/sql/SqlError";
7
+ export declare const makePostgresClient: (config: PgClient.PgClientConfig<never>) => Effect.Effect<SqlClient.SqlClient & {
8
+ "~@effect/sql-pg/PgClient": "~@effect/sql-pg/PgClient";
9
+ config: PgClient.PgClientConfig<never>;
10
+ json: (value: unknown) => Statement.Fragment;
11
+ listen: (channel: string) => Effect.Effect<import("effect/Queue").Dequeue<PgConnection.Notification, SqlError>, SqlError, import("effect/Scope").Scope>;
12
+ notify: (channel: string, payload: string) => Effect.Effect<void, SqlError, never>;
13
+ }, never, PgPool.PgPool | import("effect/reactivity/Reactivity").Reactivity>;
14
+ //# sourceMappingURL=makePostgresClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"makePostgresClient.d.ts","sourceRoot":"","sources":["../../src/adapters/makePostgresClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,yBAAyB,CAAC;AACpD,OAAO,KAAK,KAAK,YAAY,MAAM,6BAA6B,CAAC;AACjE,OAAO,KAAK,MAAM,MAAM,uBAAuB,CAAC;AAChD,OAAO,EAAE,MAAM,EAAqB,MAAM,QAAQ,CAAC;AACnD,OAAO,EAAE,SAAS,EAAsB,SAAS,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,EAAE,QAAQ,EAAgB,MAAM,qBAAqB,CAAC;AAI7D,eAAO,MAAM,kBAAkB;;;kBAuBf,OAAO;sBACH,MAAM;sBACN,MAAM,WAAW,MAAM;4EAGzC,CAAC"}
@@ -0,0 +1,63 @@
1
+ import * as PgClient from "@effect/sql-pg/PgClient";
2
+ import * as PgPool from "@effect/sql-pg/PgPool";
3
+ import { Effect, Predicate, Stream } from "effect";
4
+ import { SqlClient, Statement } from "effect/sql";
5
+ import { SqlError, UnknownError } from "effect/sql/SqlError";
6
+ const json = Statement.custom("PgJson");
7
+ export const makePostgresClient = Effect.fn("Sql.makePostgresClient")(function* (config) {
8
+ const pool = yield* PgPool.PgPool;
9
+ const acquirer = Effect.map(pool.get, postgresConnection);
10
+ const client = yield* SqlClient.make({
11
+ acquirer,
12
+ borrower: (use) => pool.use((connection) => use(postgresConnection(connection))),
13
+ commit,
14
+ compiler: PgClient.makeCompiler(config.transformQueryNames),
15
+ driver: "pg",
16
+ prepareTransactionControls: true,
17
+ releaseSavepoint: (name) => `RELEASE SAVEPOINT ${name}`,
18
+ spanAttributes: [
19
+ ["db.system.name", "postgresql"],
20
+ ["db.namespace", config.database ?? config.username ?? "postgres"],
21
+ ["server.address", config.host ?? "localhost"],
22
+ ["server.port", config.port ?? 5432],
23
+ ],
24
+ transactionAcquirer: Effect.map(pool.reserve, postgresConnection),
25
+ transformRows: config.transformResultNames === undefined ? undefined : Statement.defaultTransforms(config.transformResultNames).array,
26
+ });
27
+ return Object.assign(client, {
28
+ [PgClient.TypeId]: PgClient.TypeId,
29
+ config,
30
+ json: (value) => Statement.fragment([json(value)]),
31
+ listen: (channel) => Effect.flatMap(pool.reserve, (connection) => connection.listen(channel)),
32
+ notify: (channel, payload) => pool.use((connection) => connection.query("SELECT pg_notify($1, $2)", [channel, payload])).pipe(Effect.asVoid),
33
+ });
34
+ });
35
+ function postgresConnection(connection) {
36
+ function execute(sql, params, transformRows, prepare = true) {
37
+ const rows = Effect.map(connection.query(sql, params, prepare), (result) => result.rows);
38
+ return transformRows === undefined ? rows : Effect.map(rows, transformRows);
39
+ }
40
+ return {
41
+ execute,
42
+ executeRaw: (sql, params) => connection.query(sql, params),
43
+ executeStream: (sql, params, transformRows) => {
44
+ const stream = connection.stream(sql, params);
45
+ return transformRows === undefined ? stream : Stream.mapArray(stream, (rows) => [transformRows(rows)]).pipe(Stream.flattenIterable);
46
+ },
47
+ executeUnprepared: (sql, params, transformRows) => execute(sql, params, transformRows, false),
48
+ executeValues: (sql, params) => connection.queryValues(sql, params),
49
+ executeValuesUnprepared: (sql, params) => connection.queryValues(sql, params, false),
50
+ };
51
+ }
52
+ function commit(connection) {
53
+ return Effect.flatMap(connection.executeRaw("COMMIT", []), (result) => Predicate.hasProperty(result, "command") && result.command === "ROLLBACK"
54
+ ? Effect.fail(new SqlError({
55
+ reason: new UnknownError({
56
+ cause: new Error("COMMIT rolled back an aborted transaction"),
57
+ message: "PgClient: COMMIT rolled back an aborted transaction",
58
+ operation: "commit",
59
+ }),
60
+ }))
61
+ : Effect.void);
62
+ }
63
+ //# sourceMappingURL=makePostgresClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"makePostgresClient.js","sourceRoot":"","sources":["../../src/adapters/makePostgresClient.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { PgPoolConfig } from "@effect/sql-pg/PgClient";
2
+ import { Effect, Redacted } from "effect";
3
+ export declare const postgresStream: (url: Redacted.Redacted<string>, address: Pick<PgPoolConfig<never>, "host" | "port">) => Effect.Effect<() => import("net").Socket, never, import("effect/Scope").Scope>;
4
+ //# sourceMappingURL=postgresStream.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgresStream.d.ts","sourceRoot":"","sources":["../../src/adapters/postgresStream.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAE1C,eAAO,MAAM,cAAc,yKAqBzB,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { createConnection } from "node:net";
2
+ import { Effect, Redacted } from "effect";
3
+ export const postgresStream = Effect.fn("Sql.postgresStream")(function* (url, address) {
4
+ const disconnected = new Set();
5
+ // Native pool shutdown sends Terminate without waiting for its sockets to close.
6
+ yield* Effect.addFinalizer(() => Effect.promise(() => Promise.all([...disconnected])));
7
+ return () => {
8
+ const target = new URL(Redacted.value(url));
9
+ const host = address.host ?? target.searchParams.getAll("host").at(-1) ?? (decodeURIComponent(target.hostname) || "localhost");
10
+ const port = address.port ?? 5432;
11
+ const socket = createConnection(host.startsWith("/") ? { path: `${host}/.s.PGSQL.${port}` } : { host, noDelay: true, port });
12
+ const closed = new Promise((resolve) => socket.once("close", () => {
13
+ disconnected.delete(closed);
14
+ resolve();
15
+ }));
16
+ disconnected.add(closed);
17
+ return socket;
18
+ };
19
+ });
20
+ //# sourceMappingURL=postgresStream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgresStream.js","sourceRoot":"","sources":["../../src/adapters/postgresStream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAE5C,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAE1C,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,EAAE,CAAC,oBAAoB,CAAC,CAAC,QAAQ,CAAC,EACtE,GAA8B,EAC9B,OAA4C;IAE5C,MAAM,YAAY,GAAG,IAAI,GAAG,EAAiB,CAAC;IAC9C,iFAAiF;IACjF,KAAK,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IACvF,OAAO,GAAG,EAAE;QACX,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;QAC5C,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,WAAW,CAAC,CAAC;QAC/H,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC;QAClC,MAAM,MAAM,GAAG,gBAAgB,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,GAAG,IAAI,aAAa,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7H,MAAM,MAAM,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAC5C,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,EAAE;YACzB,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC5B,OAAO,EAAE,CAAC;QACX,CAAC,CAAC,CACF,CAAC;QACF,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACzB,OAAO,MAAM,CAAC;IACf,CAAC,CAAC;AACH,CAAC,CAAC,CAAC"}
@@ -2,6 +2,6 @@ import { Effect } from "effect";
2
2
  import { Migrator } from "effect/sql";
3
3
  import type { PostgresMigrationOptions } from "./migrations.ts";
4
4
  import { type PendingPostgresMigrations } from "./pendingPostgresMigrations.ts";
5
- export declare const assertPostgresMigrations: <R>(options: Pick<PostgresMigrationOptions<R>, "components" | "loader" | "release" | "table">) => Effect.Effect<undefined, Migrator.MigrationError | import("effect/sql/SqlError").SqlError, R | import("effect/sql/SqlClient").SqlClient>;
5
+ export declare const assertPostgresMigrations: <R, E = never>(options: Pick<PostgresMigrationOptions<R, E>, "components" | "loader" | "release" | "table">) => Effect.Effect<undefined, E | Migrator.MigrationError | import("effect/Schema").SchemaError | import("effect/sql/SqlError").SqlError, R | import("effect/sql/SqlClient").SqlClient>;
6
6
  export declare function postgresMigrationError(pending: PendingPostgresMigrations): Migrator.MigrationError;
7
7
  //# sourceMappingURL=assertPostgresMigrations.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"assertPostgresMigrations.d.ts","sourceRoot":"","sources":["../src/assertPostgresMigrations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,EAAE,KAAK,yBAAyB,EAA6B,MAAM,gCAAgC,CAAC;AAE3G,eAAO,MAAM,wBAAwB,GAA8D,CAAC,wOAOlG,CAAC;AAEH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,yBAAyB,2BASxE"}
1
+ {"version":3,"file":"assertPostgresMigrations.d.ts","sourceRoot":"","sources":["../src/assertPostgresMigrations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAChE,OAAO,EAAE,KAAK,yBAAyB,EAA6B,MAAM,gCAAgC,CAAC;AAE3G,eAAO,MAAM,wBAAwB,GAA8D,CAAC,EAAE,CAAC,6RAOrG,CAAC;AAEH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,yBAAyB,2BASxE"}
@@ -1 +1 @@
1
- {"version":3,"file":"assertPostgresMigrations.js","sourceRoot":"","sources":["../src/assertPostgresMigrations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,OAAO,EAAkC,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAE3G,MAAM,CAAC,MAAM,wBAAwB,GAAG,MAAM,CAAC,EAAE,CAAC,oCAAoC,CAAC,CAAC,QAAQ,CAAC,EAChG,OAAyF;IAEzF,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,yBAAyB,CAAC,OAAO,CAAC,CAAC;IAC1D,IAAI,OAAO,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpE,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC,CAAC;IAC5D,CAAC;AACF,CAAC,CAAC,CAAC;AAEH,MAAM,UAAU,sBAAsB,CAAC,OAAkC;IACxE,MAAM,OAAO,GAAG;QACf,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,aAAa,EAAE,KAAK,IAAI,GAAG,CAAC;QACxE,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,aAAa,IAAI,KAAK,MAAM,EAAE,CAAC;KAC/E,CAAC;IACF,OAAO,IAAI,QAAQ,CAAC,cAAc,CAAC;QAClC,IAAI,EAAE,UAAU;QAChB,OAAO,EAAE,wCAAwC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,yCAAyC;KAC5G,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"assertPostgresMigrations.js","sourceRoot":"","sources":["../src/assertPostgresMigrations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,OAAO,EAAkC,yBAAyB,EAAE,MAAM,gCAAgC,CAAC;AAE3G,MAAM,CAAC,MAAM,wBAAwB,GAAG,MAAM,CAAC,EAAE,CAAC,oCAAoC,CAAC,CAAC,QAAQ,CAAC,EAChG,OAA4F;IAE5F,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,yBAAyB,CAAC,OAAO,CAAC,CAAC;IAC1D,IAAI,OAAO,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpE,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC,CAAC;IAC5D,CAAC;AACF,CAAC,CAAC,CAAC;AAEH,MAAM,UAAU,sBAAsB,CAAC,OAAkC;IACxE,MAAM,OAAO,GAAG;QACf,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,aAAa,EAAE,KAAK,IAAI,GAAG,CAAC;QACxE,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,aAAa,IAAI,KAAK,MAAM,EAAE,CAAC;KAC/E,CAAC;IACF,OAAO,IAAI,QAAQ,CAAC,cAAc,CAAC;QAClC,IAAI,EAAE,UAAU;QAChB,OAAO,EAAE,wCAAwC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,yCAAyC;KAC5G,CAAC,CAAC;AACJ,CAAC"}
@@ -1,5 +1,5 @@
1
- import { Duration, Effect } from "effect";
1
+ import { Duration, Effect, Scope } from "effect";
2
2
  export declare const coordinateWorkerRelease: (release: string, pollInterval?: Duration.Input | undefined, table?: any) => Effect.Effect<{
3
- readonly register: <E>(control: (active: boolean) => Effect.Effect<void, E>) => Effect.Effect<void, E, import("effect/Scope").Scope>;
4
- }, import("effect/sql/SqlError").SqlError, import("effect/Scope").Scope | import("effect/sql/SqlClient").SqlClient>;
3
+ register: <E>(control: (active: boolean) => Effect.Effect<void, E>) => Effect.Effect<void, E, Scope.Scope>;
4
+ }, import("effect/Schema").SchemaError | import("effect/sql/SqlError").SqlError, Scope.Scope | import("effect/sql/SqlClient").SqlClient>;
5
5
  //# sourceMappingURL=coordinateWorkerRelease.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"coordinateWorkerRelease.d.ts","sourceRoot":"","sources":["../src/coordinateWorkerRelease.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAK1C,eAAO,MAAM,uBAAuB;;mHA2BlC,CAAC"}
1
+ {"version":3,"file":"coordinateWorkerRelease.d.ts","sourceRoot":"","sources":["../src/coordinateWorkerRelease.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAkB,KAAK,EAAE,MAAM,QAAQ,CAAC;AAMjE,eAAO,MAAM,uBAAuB;;wIAoDlC,CAAC"}
@@ -1,9 +1,12 @@
1
- import { Duration, Effect } from "effect";
1
+ import { Duration, Effect, Exit, Schedule, Scope } from "effect";
2
+ import { isSqlError } from "effect/sql/SqlError";
2
3
  import { readPostgresRelease } from "./postgresRelease.js";
3
4
  import { releaseWorkers } from "./releaseWorkers.js";
4
5
  import { makeWorkerRelease } from "./WorkerRelease.js";
5
6
  export const coordinateWorkerRelease = Effect.fn("EffectSql.coordinateWorkerRelease")(function* (release, pollInterval = "1 second", table = "effect_sql_migrations") {
6
7
  const gate = yield* makeWorkerRelease();
8
+ const scope = yield* Scope.fork(yield* Effect.scope);
9
+ const retries = Schedule.spaced(Duration.toMillis(pollInterval) / 4).pipe(Schedule.upTo({ times: 3 }));
7
10
  const rows = yield* releaseWorkers(release, Duration.toMillis(pollInterval) * 5, table);
8
11
  let claimed = false;
9
12
  const poll = Effect.gen(function* () {
@@ -20,11 +23,17 @@ export const coordinateWorkerRelease = Effect.fn("EffectSql.coordinateWorkerRele
20
23
  }
21
24
  }
22
25
  });
23
- yield* Effect.addFinalizer(() => rows.remove.pipe(Effect.orDie));
26
+ function tick(name, effect) {
27
+ return effect.pipe(Effect.tapCause((cause) => Effect.logWarning(`Worker release ${name} failed`, cause)), Effect.retry({ schedule: retries, while: isSqlError }));
28
+ }
29
+ function loop(name, effect) {
30
+ return Effect.forever(Effect.sleep(pollInterval).pipe(Effect.andThen(tick(name, effect)))).pipe(Effect.catchCause((cause) => Effect.logError(`Worker release ${name} stopped; closing worker scope`, cause).pipe(Effect.andThen(Scope.close(scope, Exit.failCause(cause))))), Scope.provide(scope), Effect.forkIn(scope));
31
+ }
32
+ yield* Scope.addFinalizer(scope, rows.remove.pipe(Effect.tapCause((cause) => Effect.logError("Worker release cleanup failed", cause)), Effect.orDie));
24
33
  // Heartbeats continue while offWork waits for active jobs to finish.
25
- yield* Effect.forever(Effect.sleep(pollInterval).pipe(Effect.andThen(rows.heartbeat))).pipe(Effect.orDie, Effect.forkScoped);
26
- yield* poll;
27
- yield* Effect.forever(Effect.sleep(pollInterval).pipe(Effect.andThen(poll))).pipe(Effect.orDie, Effect.forkScoped);
34
+ yield* loop("heartbeat", rows.heartbeat);
35
+ yield* tick("poll", poll).pipe(Scope.provide(scope));
36
+ yield* loop("poll", poll);
28
37
  return gate.service;
29
38
  });
30
39
  //# sourceMappingURL=coordinateWorkerRelease.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"coordinateWorkerRelease.js","sourceRoot":"","sources":["../src/coordinateWorkerRelease.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC1C,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAEvD,MAAM,CAAC,MAAM,uBAAuB,GAAG,MAAM,CAAC,EAAE,CAAC,mCAAmC,CAAC,CAAC,QAAQ,CAAC,EAC9F,OAAe,EACf,YAAY,GAAmB,UAAU,EACzC,KAAK,GAAG,uBAAuB;IAE/B,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,iBAAiB,EAAE,CAAC;IACxC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC,OAAO,EAAE,QAAQ,CAAC,QAAQ,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;IACxF,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAChC,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;QAChD,IAAI,KAAK,EAAE,OAAO,KAAK,OAAO,EAAE,CAAC;YAChC,KAAK,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAChD,KAAK,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC;YACnB,OAAO,GAAG,KAAK,CAAC;QACjB,CAAC;aAAM,IAAI,CAAC,OAAO,EAAE,CAAC;YACrB,OAAO,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;YAC5B,IAAI,OAAO,EAAE,CAAC;gBACb,KAAK,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAChD,CAAC;QACF,CAAC;IACF,CAAC,CAAC,CAAC;IACH,KAAK,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACjE,qEAAqE;IACrE,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;IAC7H,KAAK,CAAC,CAAC,IAAI,CAAC;IACZ,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC;IACnH,OAAO,IAAI,CAAC,OAAO,CAAC;AACrB,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"coordinateWorkerRelease.js","sourceRoot":"","sources":["../src/coordinateWorkerRelease.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { PgPoolConfig } from "@effect/sql-pg/PgClient";
2
+ import { Redacted } from "effect";
3
+ export declare function databaseAddress(value: Redacted.Redacted<string>): Pick<PgPoolConfig, "host" | "port">;
4
+ //# sourceMappingURL=databaseAddress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"databaseAddress.d.ts","sourceRoot":"","sources":["../src/databaseAddress.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAElC,wBAAgB,eAAe,CAAC,KAAK,EAAE,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAAC,CAqBrG"}
@@ -0,0 +1,25 @@
1
+ import { Redacted } from "effect";
2
+ export function databaseAddress(value) {
3
+ try {
4
+ const url = new URL(Redacted.value(value));
5
+ if (url.protocol !== "postgres:" && url.protocol !== "postgresql:") {
6
+ return {};
7
+ }
8
+ const hostname = url.hostname.startsWith("[") ? url.hostname.slice(1, -1) : decodeURIComponent(url.hostname);
9
+ // PgConnection applies query parameters in order, after the authority.
10
+ const host = url.searchParams.getAll("host").at(-1) ?? hostname;
11
+ const rawPort = url.searchParams.getAll("port").at(-1) ?? url.port;
12
+ const port = rawPort === "" ? undefined : Number(rawPort);
13
+ if (host.includes(",") || (port !== undefined && (!Number.isInteger(port) || port < 1 || port > 65_535))) {
14
+ return {};
15
+ }
16
+ return {
17
+ ...(host === "" && !url.searchParams.has("host") ? {} : { host }),
18
+ ...(port === undefined ? {} : { port }),
19
+ };
20
+ }
21
+ catch {
22
+ return {};
23
+ }
24
+ }
25
+ //# sourceMappingURL=databaseAddress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"databaseAddress.js","sourceRoot":"","sources":["../src/databaseAddress.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAElC,MAAM,UAAU,eAAe,CAAC,KAAgC;IAC/D,IAAI,CAAC;QACJ,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;QAC3C,IAAI,GAAG,CAAC,QAAQ,KAAK,WAAW,IAAI,GAAG,CAAC,QAAQ,KAAK,aAAa,EAAE,CAAC;YACpE,OAAO,EAAE,CAAC;QACX,CAAC;QACD,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC7G,uEAAuE;QACvE,MAAM,IAAI,GAAG,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC;QAChE,MAAM,OAAO,GAAG,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC;QACnE,MAAM,IAAI,GAAG,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC1D,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,MAAM,CAAC,CAAC,EAAE,CAAC;YAC1G,OAAO,EAAE,CAAC;QACX,CAAC;QACD,OAAO;YACN,GAAG,CAAC,IAAI,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;YACjE,GAAG,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;SACvC,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,EAAE,CAAC;IACX,CAAC;AACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Context, Effect } from "effect";
2
+ export declare const CommandRecovery: Context.Reference<Effect.Effect<void, never, never>>;
3
+ //# sourceMappingURL=CommandRecovery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommandRecovery.d.ts","sourceRoot":"","sources":["../../src/internal/CommandRecovery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEzC,eAAO,MAAM,eAAe,sDAE1B,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { Context, Effect } from "effect";
2
+ export const CommandRecovery = Context.Reference("@shivaedev/sql/CommandRecovery", {
3
+ defaultValue: () => Effect.void,
4
+ });
5
+ //# sourceMappingURL=CommandRecovery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommandRecovery.js","sourceRoot":"","sources":["../../src/internal/CommandRecovery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEzC,MAAM,CAAC,MAAM,eAAe,GAAG,OAAO,CAAC,SAAS,CAAsB,gCAAgC,EAAE;IACvG,YAAY,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI;CAC/B,CAAC,CAAC"}
@@ -1,5 +1,7 @@
1
1
  import { Effect, Scope } from "effect";
2
2
  import * as Reactivity from "effect/reactivity/Reactivity";
3
+ import type { SqlError } from "effect/sql/SqlError";
4
+ import { PostgresListenerError } from "./PostgresListenerError.ts";
3
5
  import type { PostgresChangesOptions } from "./postgresChanges.ts";
4
- export declare const listenForChanges: (options: PostgresChangesOptions) => Effect.Effect<void, never, Reactivity.Reactivity | Scope.Scope>;
6
+ export declare const listenForChanges: (options: PostgresChangesOptions) => Effect.Effect<void, PostgresListenerError | SqlError, Reactivity.Reactivity | Scope.Scope>;
5
7
  //# sourceMappingURL=listenForChanges.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"listenForChanges.d.ts","sourceRoot":"","sources":["../src/listenForChanges.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkC,MAAM,EAAoB,KAAK,EAAE,MAAM,QAAQ,CAAC;AACzF,OAAO,KAAK,UAAU,MAAM,8BAA8B,CAAC;AAE3D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAuBnE,eAAO,MAAM,gBAAgB,sGAsE3B,CAAC"}
1
+ {"version":3,"file":"listenForChanges.d.ts","sourceRoot":"","sources":["../src/listenForChanges.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkC,MAAM,EAAiB,KAAK,EAAE,MAAM,QAAQ,CAAC;AACtF,OAAO,KAAK,UAAU,MAAM,8BAA8B,CAAC;AAC3D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AASnE,eAAO,MAAM,gBAAgB,iIAoF3B,CAAC"}