@shivaedev/sql 0.31.0 → 0.33.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.
- package/CHANGELOG.md +29 -0
- package/README.md +58 -19
- package/dist/conditionalUpsert.d.ts +4 -3
- package/dist/conditionalUpsert.d.ts.map +1 -1
- package/dist/conditionalUpsert.js +1 -1
- package/dist/conditionalUpsert.js.map +1 -1
- package/dist/conflictWrites.d.ts +6 -5
- package/dist/conflictWrites.d.ts.map +1 -1
- package/dist/conflictWrites.js +1 -1
- package/dist/conflictWrites.js.map +1 -1
- package/dist/coordinateWorkerRelease.d.ts.map +1 -1
- package/dist/coordinateWorkerRelease.js +10 -3
- package/dist/coordinateWorkerRelease.js.map +1 -1
- package/dist/inValues.d.ts +5 -0
- package/dist/inValues.d.ts.map +1 -0
- package/dist/inValues.js +10 -0
- package/dist/inValues.js.map +1 -0
- package/dist/int4Array.d.ts +5 -0
- package/dist/int4Array.d.ts.map +1 -0
- package/dist/int4Array.js +10 -0
- package/dist/int4Array.js.map +1 -0
- package/dist/int8.d.ts +5 -0
- package/dist/int8.d.ts.map +1 -0
- package/dist/int8.js +9 -0
- package/dist/int8.js.map +1 -0
- package/dist/internal/MigrationStartedAt.d.ts +3 -0
- package/dist/internal/MigrationStartedAt.d.ts.map +1 -0
- package/dist/internal/MigrationStartedAt.js +5 -0
- package/dist/internal/MigrationStartedAt.js.map +1 -0
- package/dist/internal/migrationLock.d.ts +4 -0
- package/dist/internal/migrationLock.d.ts.map +1 -0
- package/dist/internal/migrationLock.js +17 -0
- package/dist/internal/migrationLock.js.map +1 -0
- package/dist/listenForChanges.js +2 -2
- package/dist/listenForChanges.js.map +1 -1
- package/dist/makeConflictClause.d.ts +2 -1
- package/dist/makeConflictClause.d.ts.map +1 -1
- package/dist/makeConflictClause.js +12 -4
- package/dist/makeConflictClause.js.map +1 -1
- package/dist/migrations.d.ts +1 -1
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +17 -6
- package/dist/migrations.js.map +1 -1
- package/dist/postgresRelease.d.ts +1 -1
- package/dist/postgresRelease.d.ts.map +1 -1
- package/dist/postgresRelease.js +7 -2
- package/dist/postgresRelease.js.map +1 -1
- package/dist/repositoryCollection.d.ts +1 -0
- package/dist/repositoryCollection.d.ts.map +1 -1
- package/dist/repositoryCollection.js +19 -6
- package/dist/repositoryCollection.js.map +1 -1
- package/dist/repositoryFilter.d.ts.map +1 -1
- package/dist/repositoryFilter.js +9 -2
- package/dist/repositoryFilter.js.map +1 -1
- package/dist/transact.d.ts.map +1 -1
- package/dist/transact.js +4 -1
- package/dist/transact.js.map +1 -1
- package/dist/upsertColumns.d.ts +16 -0
- package/dist/upsertColumns.d.ts.map +1 -0
- package/dist/upsertColumns.js +2 -0
- package/dist/upsertColumns.js.map +1 -0
- package/package.json +7 -6
- package/src/conditionalUpsert.ts +4 -3
- package/src/conflictWrites.ts +11 -7
- package/src/coordinateWorkerRelease.ts +13 -3
- package/src/inValues.ts +11 -0
- package/src/int4Array.ts +14 -0
- package/src/int8.ts +14 -0
- package/src/internal/MigrationStartedAt.ts +5 -0
- package/src/internal/migrationLock.ts +20 -0
- package/src/listenForChanges.ts +2 -2
- package/src/makeConflictClause.ts +22 -6
- package/src/migrations.ts +20 -6
- package/src/postgresRelease.ts +9 -2
- package/src/repositoryCollection.ts +20 -6
- package/src/repositoryFilter.ts +11 -2
- package/src/transact.ts +6 -1
- package/src/upsertColumns.ts +16 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.33.0 - 2026-11-02
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Repository `upsert` and `upsertMany` accept per-field `add` and `keepOnNull` modes in `updateColumns`, including conditional upserts. Counter additions run atomically in SQL, while null incoming values can preserve stored values; record and bucket changes still publish after commit.
|
|
8
|
+
- `inValues(column, values)` supplies a native SQL predicate that matches nothing for an empty list, including qualified columns in `SqlSchema` queries.
|
|
9
|
+
- `int8(field)` and `int4Array(field)` preserve field schemas while encoding native PostgreSQL storage parameters for bigint values and integer arrays, including empty arrays.
|
|
10
|
+
|
|
11
|
+
## 0.32.0 - 2026-11-02
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- `@shivaedev/changes/keys.ts` exports `isBucketSchema` to check whether a schema decodes to string or number keys, including nullable and optional model fields. `declaredBuckets` includes this check as `supported`.
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- Exceptions in change observers are logged without stopping sink delivery or changing transaction outcomes, including SQL change observations.
|
|
20
|
+
- Match SQL NULL for null equality filters on JSONB fields in reads, counts and deletes. Values accepted by the column schema compare through its codec before filter operators are considered, including JSON objects with `gt` or `in` keys.
|
|
21
|
+
- Fail the scope coordinating a worker release with the original cause when heartbeat or ownership polling retries are exhausted, and stop registered jobs workers before removing the release lease. Application-host workers can no longer keep fetching after their release coordinator fails.
|
|
22
|
+
- Publish a release only after every component succeeds, and reject marker writes from invocations older than the recorded one. `recordedAt` identifies the invocation's PostgreSQL start time; a new invocation can still deploy an earlier release label.
|
|
23
|
+
- Allow interrupted transactions to stop waiting for a pooled connection while preserving announcements for commits that land during interruption.
|
|
24
|
+
|
|
25
|
+
### Breaking
|
|
26
|
+
|
|
27
|
+
- Transactions inside `batch` now publish at their outermost transaction's commit, honoring the scoped `CommittedSink`, instead of waiting for the batch to end. Use an enclosing transaction when writes and their changes must commit together, or coalesce in the sink when independent transactions need grouped delivery. Nontransactional batch changes still wait for the batch to end, and rolled-back transactions publish nothing.
|
|
28
|
+
- SQL repository registration rejects annotated bucket fields whose decoded schemas cannot be represented by change keys, with `BucketRegistrationError.invalidFields` naming the rejected fields. Use string or number schemas, optionally nullable or optional, for bucket values. Codecs such as `Schema.NumberFromString` remain supported. Replace opaque declarations with an explicit string or number schema and refinements. When constructing `BucketRegistrationError` directly, supply `invalidFields` (an empty array for field-name mismatches).
|
|
29
|
+
- Migration runners with schema components or a release now hold a session advisory lock through components and release publication. Allocate at least two pool connections for an active runner plus capacity for waiting runners and other work, and migrate transaction-pooled connections to direct connections or session pooling. Components continue to run outside the ledger transaction. Runs with only numbered migrations retain their transaction lock and one-connection pool support.
|
|
30
|
+
- Direct callers of `postgresRelease(...).apply` must handle `SchemaError` alongside `SqlError` because the component now decodes its PostgreSQL clock row.
|
|
31
|
+
|
|
3
32
|
## 0.31.0 - 2026-11-01
|
|
4
33
|
|
|
5
34
|
### Added
|
package/README.md
CHANGED
|
@@ -129,27 +129,24 @@ const createdAt = Model.Field({
|
|
|
129
129
|
|
|
130
130
|
This field is absent from insert and update. A PostgreSQL `timestamptz` default supplies a `Date` on the database boundary, while the JSON codec encodes a string. Choose each field's schema for the values its driver or transport actually returns.
|
|
131
131
|
|
|
132
|
-
For
|
|
132
|
+
For PostgreSQL `int8` and one-dimensional `int4[]` columns, wrap the field's value schema with a storage codec:
|
|
133
133
|
|
|
134
134
|
```ts
|
|
135
|
-
import
|
|
136
|
-
import {
|
|
135
|
+
import { int8 } from "@shivaedev/sql/int8.ts";
|
|
136
|
+
import { int4Array } from "@shivaedev/sql/int4Array.ts";
|
|
137
137
|
|
|
138
138
|
const Integers = Schema.Array(Schema.Int);
|
|
139
|
-
const StoredIntegers =
|
|
140
|
-
Schema.decodeTo(Integers, {
|
|
141
|
-
decode: SchemaGetter.transformEffect((value) => SchemaParser.decodeUnknownEffect(Integers)(PgTypes.isParameter(value) ? value.value : value)),
|
|
142
|
-
encode: SchemaGetter.transform((values) => Result.getOrThrow(PgTypes.array(values, PgTypes.OID.int4))),
|
|
143
|
-
}),
|
|
144
|
-
);
|
|
139
|
+
const StoredIntegers = int4Array(Integers);
|
|
145
140
|
|
|
146
141
|
class Sample extends Model.Class<Sample>("Sample")({
|
|
147
|
-
id: Schema.
|
|
142
|
+
id: int8(Schema.BigInt),
|
|
148
143
|
values: Model.Field({ insert: StoredIntegers, select: StoredIntegers, update: StoredIntegers, json: Integers }),
|
|
149
144
|
}) {}
|
|
150
145
|
```
|
|
151
146
|
|
|
152
|
-
|
|
147
|
+
`int8(field)` keeps bigint values exact beyond `Number.MAX_SAFE_INTEGER` and encodes native `PgTypes.int8` parameters. The native driver returns `bigint`; no conversion through a JS number occurs. `int4Array(field)` encodes a native array parameter with the `int4` element OID, including for an empty array. Both take the field schema, preserve its validation, and unwrap their native parameters when repository writes decode returned rows. PostgreSQL still enforces its integer ranges.
|
|
148
|
+
|
|
149
|
+
Use the array storage codec on select, insert and update so equality filters encode empty arrays too. Keep an ordinary array schema for JSON. A bigint field transported as JSON likewise needs an explicit transport codec in its JSON variant; these helpers describe database storage.
|
|
153
150
|
|
|
154
151
|
For a PostgreSQL `jsonb` column, wrap the value schema with `jsonb`:
|
|
155
152
|
|
|
@@ -240,7 +237,9 @@ Use `{ in: values }` to match any value in a list, alongside equality entries:
|
|
|
240
237
|
yield* lines.findMany({ where: { id: { in: [1, 3, 5] }, amount: 52 } });
|
|
241
238
|
```
|
|
242
239
|
|
|
243
|
-
Each list value uses the field's codec and decoded type. An empty list matches no rows. A plain array remains an equality value for an array-typed field
|
|
240
|
+
Each list value uses the field's codec and decoded type. An empty list matches no rows. A plain array remains an equality value for an array-typed field.
|
|
241
|
+
|
|
242
|
+
A value accepted by the field's decoded schema uses equality through its codec before filter operators are considered. For a JSONB field whose schema accepts `{ gt: 7 }` or `{ in: [7] }`, those objects compare as JSON values. An object is an operator filter only when the field's decoded schema does not accept it. A nullable field's `null` equality matches SQL NULL, including on JSONB columns; it does not match JSON null. This rule applies to reads, counts and deletes.
|
|
244
243
|
|
|
245
244
|
Use `count` with the same `where` shape as `findMany` to return the matching row count decoded as a number:
|
|
246
245
|
|
|
@@ -344,6 +343,29 @@ Here `archive` is another unversioned `InvoiceLine` repository with its own tabl
|
|
|
344
343
|
|
|
345
344
|
A repository configured with `conflictColumns` also exposes `insertFromSelectOrIgnore(source, columns)`. It uses the same conflict columns and optional `conflictWhere` predicate as `insertOrIgnore`, skips existing keys, and returns only inserted rows. An empty source returns `[]`. Both writes join `transact` and roll back with it. Source filter encoding services and target result decoding services remain in the write's Effect environment.
|
|
346
345
|
|
|
346
|
+
### Native SQL list predicates
|
|
347
|
+
|
|
348
|
+
`inValues(column, values)` from `@shivaedev/sql/inValues.ts` returns an Effect requiring the native `SqlClient`. It builds a native SQL fragment: an empty list matches nothing, and a non-empty list binds each value as a parameter. Pass a column name or a native fragment for a qualified column. Keep request encoding, result decoding and the SQL itself in native `SqlSchema`:
|
|
349
|
+
|
|
350
|
+
```ts
|
|
351
|
+
import { SqlClient, SqlSchema } from "effect/sql";
|
|
352
|
+
import { inValues } from "@shivaedev/sql/inValues.ts";
|
|
353
|
+
|
|
354
|
+
const sql = yield* SqlClient.SqlClient;
|
|
355
|
+
const paymentsBySequence = SqlSchema.findAll({
|
|
356
|
+
Request: Schema.Array(Payment.fields.sequence),
|
|
357
|
+
Result: Schema.Struct({ sequence: Payment.fields.sequence }),
|
|
358
|
+
execute: (sequences) => Effect.gen(function* () {
|
|
359
|
+
const predicate = yield* inValues(sql`p.${sql("sequence")}`, sequences);
|
|
360
|
+
return yield* sql`select p.sequence from payments p where ${predicate}`;
|
|
361
|
+
}),
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
const rows = yield* paymentsBySequence([]);
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
`rows` is empty even when the table contains payments. The helper takes its column explicitly and leaves native `SqlSchema` visible. Use native SQL with composed schemas for joins; this helper adds no query layer or typed join.
|
|
368
|
+
|
|
347
369
|
### Composite keys and idempotent writes
|
|
348
370
|
|
|
349
371
|
A generated app can ingest the same reading more than once. Declare the table's primary key with an `idColumn` tuple and its unique conflict key with `conflictColumns`:
|
|
@@ -382,7 +404,7 @@ The application creates `readings` with `PRIMARY KEY (source, sequence)`; `seque
|
|
|
382
404
|
| `insertManyOrIgnore` | Readonly array of model insert inputs | Decoded inserted models; conflicting rows are omitted. |
|
|
383
405
|
| `upsertMany` | Readonly array of model insert inputs and `{ updateColumns: [...] }` | An array of `{ row, inserted }` results. |
|
|
384
406
|
|
|
385
|
-
All four methods encode with the model's insert schema and retain its codec services. `upsert` and `upsertMany` require a nonempty list of inserted, updateable fields; primary-key, conflict-key and generated fields are excluded.
|
|
407
|
+
All four methods encode with the model's insert schema and retain its codec services. `upsert` and `upsertMany` require a nonempty list of inserted, updateable fields; primary-key, conflict-key and generated fields are excluded. String entries replace those columns with their incoming encoded values on conflict. Other stored values stay intact. A selected optional field must be present in every encoded input. Unknown, empty, protected or absent update columns fail with `SchemaError`. A violation of another unique constraint remains a native `SqlError`.
|
|
386
408
|
|
|
387
409
|
For a partial unique index such as `CREATE UNIQUE INDEX ON entries (a, b) WHERE kind = 'x'`, add `conflictWhere` beside `conflictColumns`:
|
|
388
410
|
|
|
@@ -399,6 +421,20 @@ const inserted = yield* entries.insertManyOrIgnore(inputs);
|
|
|
399
421
|
|
|
400
422
|
All four conflict methods use this target, producing `ON CONFLICT (a, b) WHERE kind = $n` before `DO NOTHING` or `DO UPDATE SET ...`, followed by `RETURNING`. `conflictWhere` uses typed equality entries like `findMany.where`: decoded field values pass through their field codecs and remain bound parameters. Rows outside the index predicate can insert with the same key. A target that matches no unique index fails with native `SqlError` in the error channel. Use native `SqlClient` and `SqlSchema` for SQL expressions or other conflict policies.
|
|
401
423
|
|
|
424
|
+
Declare conflict behavior per field in the same `updateColumns` list:
|
|
425
|
+
|
|
426
|
+
```ts
|
|
427
|
+
const saved = yield* counters.upsert(incoming, {
|
|
428
|
+
updateColumns: [
|
|
429
|
+
{ column: "count", mode: "add" },
|
|
430
|
+
{ column: "label", mode: "keepOnNull" },
|
|
431
|
+
"note",
|
|
432
|
+
],
|
|
433
|
+
});
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
`add` computes the stored column plus its incoming encoded value inside the conflict update, so concurrent increments from separate PostgreSQL connections all count. Its model field must decode to a number, bigint or `BigDecimal`; choose a storage codec and SQL column that support native addition. `keepOnNull` uses the incoming value when it is non-null and keeps the stored value when it is SQL `NULL`. A string entry still replaces the field. Inserts use the incoming values directly. These entries also work in `upsertMany` and with `updateWhere`, and publish the same record and old/new bucket keys as other repository writes.
|
|
437
|
+
|
|
402
438
|
On PostgreSQL, pass `updateWhere` to conditionally apply the update branch:
|
|
403
439
|
|
|
404
440
|
```ts
|
|
@@ -562,7 +598,7 @@ const makeNotes = makeRepository(Note, {
|
|
|
562
598
|
});
|
|
563
599
|
```
|
|
564
600
|
|
|
565
|
-
Pass the contract collection as the repository's `collection` option when the model has buckets. Registration fails with `BucketRegistrationError` if the collection is missing, its name differs from `tableName`, or its bucket field names differ from the model's annotated fields. The error names both declarations. Repositories without buckets may omit the collection. Every inserted, updated or deleted row announces its record key and each declared bucket from the old and new stored values. An insert belongs only to its new buckets; a move announces both sides; a delete announces the old buckets. Bucket codecs decode stored values before producing keys, so a `bucket(Schema.NumberFromString)` field matches a numeric query bucket even when stored as text. Nullable and optional bucket values contribute no key when absent. Bucket codecs must need no services and decode to strings, numbers or absent values.
|
|
601
|
+
Pass the contract collection as the repository's `collection` option when the model has buckets. Registration fails with `BucketRegistrationError` if the collection is missing, its name differs from `tableName`, or its bucket field names differ from the model's annotated fields. The error names both declarations. Registration also rejects annotated fields whose decoded schemas cannot produce key values, naming them in `invalidFields`, before any write. Use explicit string or number schemas and refinements instead of opaque declarations; nullable and optional wrappers remain supported. Repositories without buckets may omit the collection. Every inserted, updated or deleted row announces its record key and each declared bucket from the old and new stored values. An insert belongs only to its new buckets; a move announces both sides; a delete announces the old buckets. Bucket codecs decode stored values before producing keys, so a `bucket(Schema.NumberFromString)` field matches a numeric query bucket even when stored as text. Nullable and optional bucket values contribute no key when absent. Bucket codecs must need no services and decode to strings, numbers or absent values.
|
|
566
602
|
|
|
567
603
|
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.
|
|
568
604
|
|
|
@@ -638,7 +674,7 @@ Use sequential numbers, not timestamps. Effect's Migrator skips any migration wh
|
|
|
638
674
|
|
|
639
675
|
`migratePostgres` rejects loaded migration IDs at or below the latest applied ID that are missing from the ledger with a typed native `MigrationError` (`kind: "BadState"`) naming those IDs.
|
|
640
676
|
|
|
641
|
-
`migratePostgres` takes
|
|
677
|
+
`migratePostgres` discovers migrations, then takes an advisory lock before creating the ledger and running native Migrator. Runs with only numbered migrations use a transaction lock and work with a one-connection pool. Runs with schema components or a `release` use a session lock that stays held through the ledger transaction, all schema components and release publication, and releases on completion, failure or interruption. Both lock modes coordinate on the same ledger key. Concurrent runners on a fresh or existing ledger wait, then see the committed ledger and avoid repeating migrations; their schema components never overlap. Every runner of that ledger must use this helper and the same ledger name. The session lock reserves one pool connection while migration and component SQL use other connections: allow at least two connections for an active runner with components or a release, plus room for waiting runners and other work. Those runs require a direct PostgreSQL connection or session pooling. The required `lockTimeout` bounds acquisition of the advisory lock and lock waits in the ledger transaction; `Duration.infinity` disables those lock timeouts. Choose deployment and execution timeouts in the application.
|
|
642
678
|
|
|
643
679
|
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.
|
|
644
680
|
|
|
@@ -648,9 +684,9 @@ For processes whose deployment owns migration execution, call `assertPostgresMig
|
|
|
648
684
|
|
|
649
685
|
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`.
|
|
650
686
|
|
|
651
|
-
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.
|
|
687
|
+
Pass an optional `release` string to `migratePostgres` to record the owning release after all ledger migrations and schema components succeed. A failed component publishes no release and preserves any existing marker. PostgreSQL timestamps each invocation before discovery and locking, and the marker accepts only a strictly newer timestamp, so an older invocation finishing later cannot replace a newer release. Release labels are opaque strings; they are not sorted. To deploy an earlier release again, start a new invocation. 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 of the invocation's start, or `undefined` for a missing marker. A standalone `postgresRelease` component timestamps the start of its `apply` effect. `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.
|
|
652
688
|
|
|
653
|
-
`appDatabase` from `@shivaedev/server/appDatabase.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
|
|
689
|
+
`appDatabase` from `@shivaedev/server/appDatabase.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 a new migration invocation publishing an earlier release resumes that release's worker. `@shivaedev/jobs` reads the service automatically; applications do not connect a signal themselves. Web's `"check"` mode ignores the marker.
|
|
654
690
|
|
|
655
691
|
### PostgreSQL delivery between processes
|
|
656
692
|
|
|
@@ -666,7 +702,7 @@ const delivery = postgresChanges({
|
|
|
666
702
|
|
|
667
703
|
`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`.
|
|
668
704
|
|
|
669
|
-
`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
|
|
705
|
+
`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 warnings and reconnect debug logs omit database 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.
|
|
670
706
|
|
|
671
707
|
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.
|
|
672
708
|
|
|
@@ -674,6 +710,9 @@ NOTIFY is a wake-up signal, not a durable log. Transaction payloads contain only
|
|
|
674
710
|
|
|
675
711
|
Import the module that defines the name; there is no root entry.
|
|
676
712
|
- `@shivaedev/sql/clientOptions.ts`: `clientOptions(options, { snakeCase? })`, `SqlNameTransforms` — Add default native name transforms to driver or app host options; opt out with `snakeCase: false`.
|
|
713
|
+
- `@shivaedev/sql/inValues.ts`: `inValues(column, values)` — Native list predicate with match-nothing semantics for an empty list; requires `SqlClient`.
|
|
714
|
+
- `@shivaedev/sql/int8.ts`: `int8(field)` — PostgreSQL bigint storage codec using the supplied field schema.
|
|
715
|
+
- `@shivaedev/sql/int4Array.ts`: `int4Array(field)` — PostgreSQL integer-array storage codec, including typed empty arrays.
|
|
677
716
|
- `@shivaedev/sql/repository.ts`: `makeRepository(model, options)` — `tableName`, scalar or composite `idColumn`, `spanPrefix`, optional `versionColumn`, `conflictColumns` and `conflictWhere`, and `collection` required for bucket models; requires native `SqlClient`.
|
|
678
717
|
- `@shivaedev/sql/repository.ts`: `OptimisticConcurrencyError` — Typed stale or missing-row failure for versioned writes, with `tableName`, `id`, `expectedVersion`, `reason: "missing" | "stale"`.
|
|
679
718
|
- `@shivaedev/sql/tupleIn.ts`: `tupleIn(model, fields, tuples, types)` — Filter entry matching two or more fields together against a list of tuples, for `where` in reads and unversioned deletes.
|
|
@@ -745,7 +784,7 @@ The mapper handles named unique violations and PostgreSQL foreign-key and check
|
|
|
745
784
|
|
|
746
785
|
Recover outside `transact` so earlier writes roll back before handling a mapped domain error. A losing unique-key insert can then catch `NameTaken` and read the winner's row without a private transaction error. See the [constraint-failures guide](https://github.com/ShivaeDev/platform/blob/main/docs/framework/constraint-errors.md).
|
|
747
786
|
|
|
748
|
-
For the native PostgreSQL driver's default representations, use precise numeric strings with `Schema.BigDecimalFromString`, `int8` values with `Schema.BigInt`, `date` values as `YYYY-MM-DD` strings with `Schema.String`, instants with `Schema.Date`, nullable text and generated values. A persisted value that violates its schema fails decoding; a write and read inside the enclosing transaction roll back together when that failure escapes.
|
|
787
|
+
For the native PostgreSQL driver's default representations, use precise numeric strings with `Schema.BigDecimalFromString`, `int8` values with `int8(Schema.BigInt)`, `date` values as `YYYY-MM-DD` strings with `Schema.String`, instants with `Schema.Date`, nullable text and generated values. A persisted value that violates its schema fails decoding; a write and read inside the enclosing transaction roll back together when that failure escapes.
|
|
749
788
|
|
|
750
789
|
JSON and JSONB columns are not supported. A repository write of a plain object to one fails with `SqlError` and writes nothing. Give structured data its own columns, or its own table when it repeats, so PostgreSQL constraints and the model both describe it.
|
|
751
790
|
|
|
@@ -2,9 +2,10 @@ import { Effect, Schema } from "effect";
|
|
|
2
2
|
import type { Model } from "effect/schema";
|
|
3
3
|
import type { SqlClient } from "effect/sql";
|
|
4
4
|
import type { SqlError } from "effect/sql/SqlError";
|
|
5
|
+
import type { RepositoryChanges } from "#repositoryChanges.ts";
|
|
5
6
|
import type { Comparisons, FindManyFilter } from "#repositoryFilter.ts";
|
|
6
|
-
import type {
|
|
7
|
-
import { type UpsertRow } from "
|
|
7
|
+
import type { UpsertAssignment } from "#upsertColumns.ts";
|
|
8
|
+
import { type UpsertRow } from "#upsertResult.ts";
|
|
8
9
|
type Fields<TModel extends Model.Any> = Schema.Struct.Type<TModel["fields"]>;
|
|
9
10
|
interface Incoming<TModel extends Model.Any, TValue> {
|
|
10
11
|
readonly excluded: {
|
|
@@ -49,7 +50,7 @@ export declare function makeConditionalUpsert(model: Model.Any, sql: SqlClient.S
|
|
|
49
50
|
readonly conflictWhere?: Readonly<Record<string, unknown>>;
|
|
50
51
|
readonly idColumns: readonly string[];
|
|
51
52
|
}): (inputs: readonly unknown[], settings: {
|
|
52
|
-
readonly updateColumns: readonly
|
|
53
|
+
readonly updateColumns: readonly UpsertAssignment[];
|
|
53
54
|
readonly updateWhere: Readonly<Record<string, unknown>>;
|
|
54
55
|
}) => Effect.Effect<({
|
|
55
56
|
readonly _tag: "Unchanged";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conditionalUpsert.d.ts","sourceRoot":"","sources":["../src/conditionalUpsert.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACxE,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"conditionalUpsert.d.ts","sourceRoot":"","sources":["../src/conditionalUpsert.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Effect, Schema } from "effect";
|
|
2
2
|
import { makeConflictClause } from "#makeConflictClause.ts";
|
|
3
|
-
import { makeUpsertDecoder, upsertReturning } from "
|
|
3
|
+
import { makeUpsertDecoder, upsertReturning } from "#upsertResult.ts";
|
|
4
4
|
export function makeConditionalUpsert(model, sql, options) {
|
|
5
5
|
const encode = Schema.encodeEffect(model.insert);
|
|
6
6
|
const record = Schema.decodeUnknownEffect(Schema.Record(Schema.String, Schema.Unknown));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conditionalUpsert.js","sourceRoot":"","sources":["../src/conditionalUpsert.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIxC,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"conditionalUpsert.js","sourceRoot":"","sources":["../src/conditionalUpsert.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIxC,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAI5D,OAAO,EAAE,iBAAiB,EAAkB,eAAe,EAAE,MAAM,kBAAkB,CAAC;AA+CtF,MAAM,UAAU,qBAAqB,CACpC,KAAgB,EAChB,GAAwB,EACxB,OAOC;IAED,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACjD,MAAM,MAAM,GAAG,MAAM,CAAC,mBAAmB,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IACxF,MAAM,MAAM,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;IACxC,MAAM,MAAM,GAAG,kBAAkB,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;IACvD,OAAO,CACN,MAA0B,EAC1B,QAA0H,EACzH,EAAE,CACH,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QACnB,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAC1D,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YACnB,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;YAChD,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,aAAa,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;YAChF,OAAO,GAAG,CAAA,eAAe,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,MAAM,cAAc,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QAC7I,CAAC,CAAC,CACF,CAAC;QACF,MAAM,WAAW,GAAwC,EAAE,CAAC;QAC5D,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,SAAS,EAAE,EAAE,CAC/D,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YACnB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,SAAS,CAAC;YAC9B,WAAW,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;YAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;YACpB,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;gBACvB,OAAO,EAAE,IAAI,EAAE,WAAW,EAAW,CAAC;YACvC,CAAC;YACD,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAClC,OAAO,MAAM,CAAC,QAAQ;gBACrB,CAAC,CAAE,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,EAAY;gBAC5D,CAAC,CAAE,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAY,CAAC;QAC/D,CAAC,CAAC,CACF,CAAC;QACF,KAAK,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QAC3C,OAAO,OAAO,CAAC;IAChB,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,GAAG,OAAO,CAAC,UAAU,oBAAoB,CAAC,CAAC,CAAC;AAC7F,CAAC"}
|
package/dist/conflictWrites.d.ts
CHANGED
|
@@ -5,13 +5,14 @@ import type { SqlError } from "effect/sql/SqlError";
|
|
|
5
5
|
import { type InsertMany } from "#batchWrites.ts";
|
|
6
6
|
import { type ConditionalUpsert, type ConditionalUpsertMany } from "#conditionalUpsert.ts";
|
|
7
7
|
import { type InsertFromSelect } from "#insertFromSelect.ts";
|
|
8
|
-
import type { RepositoryChanges } from "
|
|
8
|
+
import type { RepositoryChanges } from "#repositoryChanges.ts";
|
|
9
|
+
import type { UpsertAssignment, UpsertColumn } from "#upsertColumns.ts";
|
|
9
10
|
export type InsertColumn<TModel extends Model.Any> = Extract<keyof TModel["insert"]["Type"] & keyof TModel["fields"], string>;
|
|
10
11
|
export type ConflictColumns<TModel extends Model.Any> = readonly [InsertColumn<TModel>, ...InsertColumn<TModel>[]];
|
|
11
12
|
interface UpdateOptions<TModel extends Model.Any, TExcluded extends PropertyKey> {
|
|
12
13
|
readonly updateColumns: readonly [
|
|
13
|
-
Exclude<InsertColumn<TModel> & keyof TModel["update"]["Type"], TExcluded
|
|
14
|
-
...Exclude<InsertColumn<TModel> & keyof TModel["update"]["Type"], TExcluded
|
|
14
|
+
UpsertColumn<TModel, Exclude<InsertColumn<TModel> & keyof TModel["update"]["Type"], TExcluded>>,
|
|
15
|
+
...UpsertColumn<TModel, Exclude<InsertColumn<TModel> & keyof TModel["update"]["Type"], TExcluded>>[]
|
|
15
16
|
];
|
|
16
17
|
}
|
|
17
18
|
export interface ConflictWrites<TModel extends Model.Any, TExcluded extends PropertyKey, TFilterServices = never> {
|
|
@@ -33,7 +34,7 @@ export declare function makeConflictWrites(model: Model.Any, sql: SqlClient.SqlC
|
|
|
33
34
|
insertManyOrIgnore: (inputs: readonly unknown[]) => Effect.Effect<unknown[], Schema.SchemaError | SqlError, unknown>;
|
|
34
35
|
insertOrIgnore: (input: unknown) => Effect.Effect<Option.None<unknown> | Option.Some<unknown>, Schema.SchemaError | SqlError, unknown>;
|
|
35
36
|
upsert: (input: unknown, settings: {
|
|
36
|
-
readonly updateColumns: readonly
|
|
37
|
+
readonly updateColumns: readonly UpsertAssignment[];
|
|
37
38
|
readonly updateWhere?: Readonly<Record<string, unknown>>;
|
|
38
39
|
}) => Effect.Effect<{
|
|
39
40
|
inserted: boolean;
|
|
@@ -50,7 +51,7 @@ export declare function makeConflictWrites(model: Model.Any, sql: SqlClient.SqlC
|
|
|
50
51
|
readonly inserted: false;
|
|
51
52
|
} | undefined, Schema.SchemaError | SqlError, unknown>;
|
|
52
53
|
upsertMany: (inputs: readonly unknown[], settings: {
|
|
53
|
-
readonly updateColumns: readonly
|
|
54
|
+
readonly updateColumns: readonly UpsertAssignment[];
|
|
54
55
|
readonly updateWhere?: Readonly<Record<string, unknown>>;
|
|
55
56
|
}) => Effect.Effect<unknown[], Schema.SchemaError | SqlError, unknown>;
|
|
56
57
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conflictWrites.d.ts","sourceRoot":"","sources":["../src/conflictWrites.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,KAAK,UAAU,EAAkB,MAAM,iBAAiB,CAAC;AAClE,OAAO,EAAE,KAAK,iBAAiB,EAAE,KAAK,qBAAqB,EAAyB,MAAM,uBAAuB,CAAC;AAClH,OAAO,EAAE,KAAK,gBAAgB,EAAwB,MAAM,sBAAsB,CAAC;AAEnF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"conflictWrites.d.ts","sourceRoot":"","sources":["../src/conflictWrites.ts"],"names":[],"mappings":"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"}
|
package/dist/conflictWrites.js
CHANGED
|
@@ -3,7 +3,7 @@ import { makeInsertMany } from "#batchWrites.ts";
|
|
|
3
3
|
import { makeConditionalUpsert } from "#conditionalUpsert.ts";
|
|
4
4
|
import { makeInsertFromSelect } from "#insertFromSelect.ts";
|
|
5
5
|
import { makeConflictClause } from "#makeConflictClause.ts";
|
|
6
|
-
import { makeUpsertDecoder, upsertReturning } from "
|
|
6
|
+
import { makeUpsertDecoder, upsertReturning } from "#upsertResult.ts";
|
|
7
7
|
export function makeConflictWrites(model, sql, options) {
|
|
8
8
|
const { tableName, spanPrefix } = options;
|
|
9
9
|
const encode = Schema.encodeEffect(model.insert);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conflictWrites.js","sourceRoot":"","sources":["../src/conflictWrites.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIhD,OAAO,EAAmB,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAClE,OAAO,EAAsD,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAClH,OAAO,EAAyB,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACnF,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"conflictWrites.js","sourceRoot":"","sources":["../src/conflictWrites.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coordinateWorkerRelease.d.ts","sourceRoot":"","sources":["../src/coordinateWorkerRelease.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"coordinateWorkerRelease.d.ts","sourceRoot":"","sources":["../src/coordinateWorkerRelease.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAA6B,KAAK,EAAE,MAAM,QAAQ,CAAC;AAU5E,eAAO,MAAM,uBAAuB;;wIA0DlC,CAAC"}
|
|
@@ -1,11 +1,18 @@
|
|
|
1
|
-
import { Duration, Effect, Exit, Schedule, Scope } from "effect";
|
|
1
|
+
import { Duration, Effect, Exit, Predicate, Schedule, Scope } from "effect";
|
|
2
2
|
import { isSqlError } from "effect/sql/SqlError";
|
|
3
3
|
import { readPostgresRelease } from "./postgresRelease.js";
|
|
4
4
|
import { releaseWorkers } from "./releaseWorkers.js";
|
|
5
5
|
import { makeWorkerRelease } from "./WorkerRelease.js";
|
|
6
|
+
function isCloseable(scope) {
|
|
7
|
+
return Predicate.hasProperty(scope, "~effect/Scope/Closeable") && scope["~effect/Scope/Closeable"] === "~effect/Scope/Closeable";
|
|
8
|
+
}
|
|
6
9
|
export const coordinateWorkerRelease = Effect.fn("EffectSql.coordinateWorkerRelease")(function* (release, pollInterval = "1 second", table = "effect_sql_migrations") {
|
|
7
10
|
const gate = yield* makeWorkerRelease();
|
|
8
|
-
const
|
|
11
|
+
const owningScope = yield* Effect.scope;
|
|
12
|
+
if (!isCloseable(owningScope)) {
|
|
13
|
+
return yield* Effect.die(new Error("Worker release coordination requires a closeable owning scope"));
|
|
14
|
+
}
|
|
15
|
+
const scope = owningScope;
|
|
9
16
|
const retries = Schedule.spaced(Duration.toMillis(pollInterval) / 4).pipe(Schedule.upTo({ times: 3 }));
|
|
10
17
|
const rows = yield* releaseWorkers(release, Duration.toMillis(pollInterval) * 5, table);
|
|
11
18
|
let claimed = false;
|
|
@@ -29,7 +36,7 @@ export const coordinateWorkerRelease = Effect.fn("EffectSql.coordinateWorkerRele
|
|
|
29
36
|
function loop(name, effect) {
|
|
30
37
|
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
38
|
}
|
|
32
|
-
yield* Scope.addFinalizer(scope,
|
|
39
|
+
yield* Scope.addFinalizer(scope, gate.setActive(false).pipe(Effect.orDie, Effect.andThen(rows.remove), Effect.tapCause((cause) => Effect.logError("Worker release cleanup failed", cause)), Effect.orDie));
|
|
33
40
|
// Heartbeats continue while offWork waits for active jobs to finish.
|
|
34
41
|
yield* loop("heartbeat", rows.heartbeat);
|
|
35
42
|
yield* tick("poll", poll).pipe(Scope.provide(scope));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"coordinateWorkerRelease.js","sourceRoot":"","sources":["../src/coordinateWorkerRelease.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"coordinateWorkerRelease.js","sourceRoot":"","sources":["../src/coordinateWorkerRelease.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { SqlClient } from "effect/sql";
|
|
3
|
+
import type { Fragment } from "effect/sql/Statement";
|
|
4
|
+
export declare const inValues: (column: string | Fragment, values: readonly unknown[]) => Effect.Effect<import("effect/sql/Statement").Statement<import("effect/sql/SqlConnection").Row>, never, SqlClient.SqlClient>;
|
|
5
|
+
//# sourceMappingURL=inValues.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inValues.d.ts","sourceRoot":"","sources":["../src/inValues.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD,eAAO,MAAM,QAAQ,wLAMnB,CAAC"}
|
package/dist/inValues.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { SqlClient } from "effect/sql";
|
|
3
|
+
export const inValues = Effect.fnUntraced(function* (column, values) {
|
|
4
|
+
const sql = yield* SqlClient.SqlClient;
|
|
5
|
+
if (values.length === 0) {
|
|
6
|
+
return sql `1 = 0`;
|
|
7
|
+
}
|
|
8
|
+
return sql `${typeof column === "string" ? sql(column) : column} in ${sql.in(values)}`;
|
|
9
|
+
});
|
|
10
|
+
//# sourceMappingURL=inValues.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inValues.js","sourceRoot":"","sources":["../src/inValues.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAGvC,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,MAAyB,EAAE,MAA0B;IACzG,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC;IACvC,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,GAAG,CAAA,OAAO,CAAC;IACnB,CAAC;IACD,OAAO,GAAG,CAAA,GAAG,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,OAAO,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC;AACvF,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
export declare function int4Array<TField extends Schema.Top & {
|
|
3
|
+
readonly Encoded: readonly number[];
|
|
4
|
+
}>(field: TField): Schema.compose<TField, Schema.decodeTo<Schema.$Array<Schema.Int>, Schema.Unknown, never, never>>;
|
|
5
|
+
//# sourceMappingURL=int4Array.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"int4Array.d.ts","sourceRoot":"","sources":["../src/int4Array.ts"],"names":[],"mappings":"AACA,OAAO,EAAU,MAAM,EAA8B,MAAM,QAAQ,CAAC;AAIpE,wBAAgB,SAAS,CAAC,MAAM,SAAS,MAAM,CAAC,GAAG,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,EAAE,KAAK,EAAE,MAAM,oGAQ3G"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import * as PgTypes from "@effect/sql-pg/PgTypes";
|
|
2
|
+
import { Result, Schema, SchemaGetter, SchemaParser } from "effect";
|
|
3
|
+
const Integers = Schema.Array(Schema.Int);
|
|
4
|
+
export function int4Array(field) {
|
|
5
|
+
return Schema.Unknown.pipe(Schema.decodeTo(Integers, {
|
|
6
|
+
decode: SchemaGetter.transformEffect((value) => SchemaParser.decodeUnknownEffect(Integers)(PgTypes.isParameter(value) ? value.value : value)),
|
|
7
|
+
encode: SchemaGetter.transform((values) => Result.getOrThrow(PgTypes.array(values, PgTypes.OID.int4))),
|
|
8
|
+
}), Schema.decodeTo(field));
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=int4Array.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"int4Array.js","sourceRoot":"","sources":["../src/int4Array.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEpE,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AAE1C,MAAM,UAAU,SAAS,CAAsE,KAAa;IAC3G,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,CACzB,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE;QACzB,MAAM,EAAE,YAAY,CAAC,eAAe,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,YAAY,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAC7I,MAAM,EAAE,YAAY,CAAC,SAAS,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;KACtG,CAAC,EACF,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CACtB,CAAC;AACH,CAAC"}
|
package/dist/int8.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"int8.d.ts","sourceRoot":"","sources":["../src/int8.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAA8B,MAAM,QAAQ,CAAC;AAE5D,wBAAgB,IAAI,CAAC,MAAM,SAAS,MAAM,CAAC,GAAG,GAAG;IAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EAAE,KAAK,EAAE,MAAM,wFAU3F"}
|
package/dist/int8.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import * as PgTypes from "@effect/sql-pg/PgTypes";
|
|
2
|
+
import { Schema, SchemaGetter, SchemaParser } from "effect";
|
|
3
|
+
export function int8(field) {
|
|
4
|
+
return Schema.Unknown.pipe(Schema.decodeTo(Schema.BigInt, {
|
|
5
|
+
decode: SchemaGetter.transformEffect((value) => SchemaParser.decodeUnknownEffect(Schema.BigInt)(PgTypes.isParameter(value) ? value.value : value)),
|
|
6
|
+
encode: SchemaGetter.transform(PgTypes.int8),
|
|
7
|
+
}), Schema.decodeTo(field));
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=int8.js.map
|
package/dist/int8.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"int8.js","sourceRoot":"","sources":["../src/int8.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAE5D,MAAM,UAAU,IAAI,CAA2D,KAAa;IAC3F,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,CACzB,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE;QAC9B,MAAM,EAAE,YAAY,CAAC,eAAe,CAAC,CAAC,KAAK,EAAE,EAAE,CAC9C,YAAY,CAAC,mBAAmB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CACjG;QACD,MAAM,EAAE,YAAY,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC;KAC5C,CAAC,EACF,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CACtB,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MigrationStartedAt.d.ts","sourceRoot":"","sources":["../../src/internal/MigrationStartedAt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEjC,eAAO,MAAM,kBAAkB,uCAE7B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MigrationStartedAt.js","sourceRoot":"","sources":["../../src/internal/MigrationStartedAt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEjC,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAO,CAAC,SAAS,CAAqB,4CAA4C,EAAE;IACrH,YAAY,EAAE,GAAG,EAAE,CAAC,SAAS;CAC7B,CAAC,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { SqlClient } from "effect/sql";
|
|
3
|
+
export declare const migrationLock: (table: string, timeout: string) => Effect.Effect<void, import("effect/sql/SqlError").SqlError, import("effect/Scope").Scope | SqlClient.SqlClient>;
|
|
4
|
+
//# sourceMappingURL=migrationLock.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrationLock.d.ts","sourceRoot":"","sources":["../../src/internal/migrationLock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAGvC,eAAO,MAAM,aAAa,qJAexB,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
import { SqlClient } from "effect/sql";
|
|
3
|
+
export const migrationLock = Effect.fnUntraced(function* (table, timeout) {
|
|
4
|
+
const sql = yield* SqlClient.SqlClient;
|
|
5
|
+
const connection = yield* sql.reserve;
|
|
6
|
+
function execute(statement) {
|
|
7
|
+
const [text, parameters] = statement.compile();
|
|
8
|
+
return connection.execute(text, parameters, undefined);
|
|
9
|
+
}
|
|
10
|
+
const [previous] = yield* execute(sql `select current_setting('lock_timeout') as timeout`);
|
|
11
|
+
yield* Effect.acquireRelease(execute(sql `select set_config('lock_timeout', ${timeout}, false)`), () => execute(sql `select set_config('lock_timeout', ${previous?.timeout}, false)`).pipe(Effect.orDie));
|
|
12
|
+
const key = `effect_sql_migrations:${table}`;
|
|
13
|
+
// Register cleanup before waiting so interruption also releases a lock granted just before cancellation.
|
|
14
|
+
yield* Effect.addFinalizer(() => execute(sql `select pg_advisory_unlock(hashtextextended(${key}, 0))`).pipe(Effect.orDie));
|
|
15
|
+
yield* execute(sql `select pg_advisory_lock(hashtextextended(${key}, 0))`);
|
|
16
|
+
});
|
|
17
|
+
//# sourceMappingURL=migrationLock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrationLock.js","sourceRoot":"","sources":["../../src/internal/migrationLock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAGvC,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,KAAa,EAAE,OAAe;IACvF,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC;IACvC,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC;IACtC,SAAS,OAAO,CAAC,SAAuD;QACvE,MAAM,CAAC,IAAI,EAAE,UAAU,CAAC,GAAG,SAAS,CAAC,OAAO,EAAE,CAAC;QAC/C,OAAO,UAAU,CAAC,OAAO,CAAC,IAAI,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;IACxD,CAAC;IACD,MAAM,CAAC,QAAQ,CAAC,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,CAAA,mDAAmD,CAAC,CAAC;IAC1F,KAAK,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,GAAG,CAAA,qCAAqC,OAAO,UAAU,CAAC,EAAE,GAAG,EAAE,CACrG,OAAO,CAAC,GAAG,CAAA,qCAAqC,QAAQ,EAAE,OAAO,UAAU,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAC/F,CAAC;IACF,MAAM,GAAG,GAAG,yBAAyB,KAAK,EAAE,CAAC;IAC7C,yGAAyG;IACzG,KAAK,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAA,8CAA8C,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IAC1H,KAAK,CAAC,CAAC,OAAO,CAAC,GAAG,CAAA,4CAA4C,GAAG,OAAO,CAAC,CAAC;AAC3E,CAAC,CAAC,CAAC"}
|
package/dist/listenForChanges.js
CHANGED
|
@@ -67,11 +67,11 @@ export const listenForChanges = Effect.fn("PostgresChanges.listenForChanges")(fu
|
|
|
67
67
|
return;
|
|
68
68
|
}
|
|
69
69
|
if (lostAt !== undefined) {
|
|
70
|
-
yield* Effect.logDebug("PostgreSQL change listener reconnect failed; retrying"
|
|
70
|
+
yield* Effect.logDebug("PostgreSQL change listener reconnect failed; retrying");
|
|
71
71
|
return;
|
|
72
72
|
}
|
|
73
73
|
lostAt = yield* Clock.currentTimeMillis;
|
|
74
|
-
yield* Effect.logWarning("PostgreSQL change listener lost; resynchronizing live streams"
|
|
74
|
+
yield* Effect.logWarning("PostgreSQL change listener lost; resynchronizing live streams");
|
|
75
75
|
}).pipe(Effect.andThen(Effect.sleep(options.retryDelay ?? "1 second")))))).pipe(Effect.forkScoped);
|
|
76
76
|
yield* Deferred.await(ready);
|
|
77
77
|
});
|