@shivaedev/sql 0.37.0 → 0.39.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 +24 -0
- package/README.md +117 -2
- package/dist/Model.d.ts +9 -0
- package/dist/Model.d.ts.map +1 -0
- package/dist/Model.js +6 -0
- package/dist/Model.js.map +1 -0
- package/dist/VersionedRepositoryShape.d.ts +6 -0
- package/dist/VersionedRepositoryShape.d.ts.map +1 -0
- package/dist/VersionedRepositoryShape.js +2 -0
- package/dist/VersionedRepositoryShape.js.map +1 -0
- package/dist/WorkerRelease.d.ts +1 -8
- package/dist/WorkerRelease.d.ts.map +1 -1
- package/dist/WorkerRelease.js +4 -29
- package/dist/WorkerRelease.js.map +1 -1
- package/dist/adapters/groupColumns.d.ts +14 -0
- package/dist/adapters/groupColumns.d.ts.map +1 -0
- package/dist/adapters/groupColumns.js +54 -0
- package/dist/adapters/groupColumns.js.map +1 -0
- package/dist/adapters/storageSchema.d.ts +4 -0
- package/dist/adapters/storageSchema.d.ts.map +1 -0
- package/dist/adapters/storageSchema.js +7 -0
- package/dist/adapters/storageSchema.js.map +1 -0
- package/dist/backfillPostgres.d.ts +18 -0
- package/dist/backfillPostgres.d.ts.map +1 -0
- package/dist/backfillPostgres.js +35 -0
- package/dist/backfillPostgres.js.map +1 -0
- package/dist/batchWrites.d.ts.map +1 -1
- package/dist/batchWrites.js +6 -3
- package/dist/batchWrites.js.map +1 -1
- package/dist/compositeRecordKey.d.ts +8 -0
- package/dist/compositeRecordKey.d.ts.map +1 -0
- package/dist/compositeRecordKey.js +15 -0
- package/dist/compositeRecordKey.js.map +1 -0
- package/dist/compositeRepository.d.ts.map +1 -1
- package/dist/compositeRepository.js +4 -1
- package/dist/compositeRepository.js.map +1 -1
- package/dist/conditionalUpsert.d.ts.map +1 -1
- package/dist/conditionalUpsert.js +5 -2
- package/dist/conditionalUpsert.js.map +1 -1
- package/dist/conflictWrites.d.ts.map +1 -1
- package/dist/conflictWrites.js +6 -3
- package/dist/conflictWrites.js.map +1 -1
- package/dist/coordinateWorkerRelease.js +1 -1
- package/dist/coordinateWorkerRelease.js.map +1 -1
- package/dist/initializeWorkerRelease.d.ts +7 -0
- package/dist/initializeWorkerRelease.d.ts.map +1 -0
- package/dist/initializeWorkerRelease.js +7 -0
- package/dist/initializeWorkerRelease.js.map +1 -0
- package/dist/insertFromSelect.d.ts.map +1 -1
- package/dist/insertFromSelect.js +4 -1
- package/dist/insertFromSelect.js.map +1 -1
- package/dist/latestPerGroup.d.ts.map +1 -1
- package/dist/latestPerGroup.js +7 -3
- package/dist/latestPerGroup.js.map +1 -1
- package/dist/makeOrderedChildren.d.ts +26 -0
- package/dist/makeOrderedChildren.d.ts.map +1 -0
- package/dist/makeOrderedChildren.js +38 -0
- package/dist/makeOrderedChildren.js.map +1 -0
- package/dist/makeWorkerRelease.d.ts +8 -0
- package/dist/makeWorkerRelease.d.ts.map +1 -0
- package/dist/makeWorkerRelease.js +12 -0
- package/dist/makeWorkerRelease.js.map +1 -0
- package/dist/orderedChildStatements.d.ts +30 -0
- package/dist/orderedChildStatements.d.ts.map +1 -0
- package/dist/orderedChildStatements.js +61 -0
- package/dist/orderedChildStatements.js.map +1 -0
- package/dist/registerWorkerRelease.d.ts +4 -0
- package/dist/registerWorkerRelease.d.ts.map +1 -0
- package/dist/registerWorkerRelease.js +14 -0
- package/dist/registerWorkerRelease.js.map +1 -0
- package/dist/repository.d.ts +9 -8
- package/dist/repository.d.ts.map +1 -1
- package/dist/repository.js.map +1 -1
- package/dist/repositoryQueries.d.ts +3 -2
- package/dist/repositoryQueries.d.ts.map +1 -1
- package/dist/repositoryQueries.js +9 -4
- package/dist/repositoryQueries.js.map +1 -1
- package/dist/repositoryUpdate.d.ts +3 -3
- package/dist/repositoryUpdate.d.ts.map +1 -1
- package/dist/repositoryWrites.d.ts.map +1 -1
- package/dist/repositoryWrites.js +21 -6
- package/dist/repositoryWrites.js.map +1 -1
- package/dist/setWorkerReleaseActive.d.ts +4 -0
- package/dist/setWorkerReleaseActive.d.ts.map +1 -0
- package/dist/setWorkerReleaseActive.js +13 -0
- package/dist/setWorkerReleaseActive.js.map +1 -0
- package/dist/sqlNameTransforms.d.ts +5 -0
- package/dist/sqlNameTransforms.d.ts.map +1 -0
- package/dist/sqlNameTransforms.js +8 -0
- package/dist/sqlNameTransforms.js.map +1 -0
- package/dist/upsertResult.d.ts +2 -1
- package/dist/upsertResult.d.ts.map +1 -1
- package/dist/upsertResult.js +3 -2
- package/dist/upsertResult.js.map +1 -1
- package/dist/versionedRepository.d.ts +15 -6
- package/dist/versionedRepository.d.ts.map +1 -1
- package/dist/versionedRepository.js +8 -5
- package/dist/versionedRepository.js.map +1 -1
- package/dist/versionedStatements.d.ts +1 -1
- package/dist/versionedStatements.d.ts.map +1 -1
- package/dist/versionedStatements.js +10 -4
- package/dist/versionedStatements.js.map +1 -1
- package/package.json +15 -15
- package/src/Model.ts +7 -0
- package/src/VersionedRepositoryShape.ts +11 -0
- package/src/WorkerRelease.ts +4 -33
- package/src/adapters/groupColumns.ts +63 -0
- package/src/adapters/storageSchema.ts +10 -0
- package/src/backfillPostgres.ts +47 -0
- package/src/batchWrites.ts +6 -3
- package/src/compositeRecordKey.ts +26 -0
- package/src/compositeRepository.ts +4 -1
- package/src/conditionalUpsert.ts +5 -2
- package/src/conflictWrites.ts +6 -3
- package/src/coordinateWorkerRelease.ts +1 -1
- package/src/initializeWorkerRelease.ts +7 -0
- package/src/insertFromSelect.ts +4 -1
- package/src/latestPerGroup.ts +7 -3
- package/src/makeOrderedChildren.ts +78 -0
- package/src/makeWorkerRelease.ts +12 -0
- package/src/orderedChildStatements.ts +76 -0
- package/src/registerWorkerRelease.ts +15 -0
- package/src/repository.ts +14 -13
- package/src/repositoryQueries.ts +18 -5
- package/src/repositoryUpdate.ts +3 -3
- package/src/repositoryWrites.ts +21 -6
- package/src/setWorkerReleaseActive.ts +17 -0
- package/src/sqlNameTransforms.ts +9 -0
- package/src/upsertResult.ts +4 -2
- package/src/versionedRepository.ts +31 -11
- package/src/versionedStatements.ts +14 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.39.0 - 2026-11-04
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Repositories expose `findOne` with model-derived filters, ordering and projections, returning `Option` for a matching decoded row.
|
|
8
|
+
- `compositeRecordKey` encodes decoded model key fields into the record key published by repository writes, including `NumberFromString` and bigint keys.
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- Require Effect 4.0.7 and its matching companion packages. A PostgreSQL connection the server terminates now fails with the server's error, such as `terminating connection due to administrator command`, instead of a generic `Connection closed`, following `@effect/sql-pg` 4.0.7.
|
|
13
|
+
|
|
14
|
+
## 0.38.0 - 2026-11-04
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- Models can declare struct-valued groups stored as prefixed flat columns and select or replace a group through repositories.
|
|
19
|
+
- Ordered child helpers append, move and reorder under a PostgreSQL parent lock without incrementing sibling versions.
|
|
20
|
+
- Versioned repository updates can choose columns while retaining optimistic concurrency checks.
|
|
21
|
+
- `backfillPostgres` processes bounded batches in separate commit-bound transactions, saving application-owned progress with each batch so interrupted runs can resume outside the numbered migration ledger.
|
|
22
|
+
|
|
23
|
+
### Breaking
|
|
24
|
+
|
|
25
|
+
- Import `makeWorkerRelease` from `@shivaedev/sql/makeWorkerRelease.ts` instead of `@shivaedev/sql/WorkerRelease.ts`. `WorkerRelease` stays in its existing module; worker registration and release coordination behave the same.
|
|
26
|
+
|
|
3
27
|
## 0.37.0 - 2026-11-04
|
|
4
28
|
|
|
5
29
|
### Breaking
|
package/README.md
CHANGED
|
@@ -89,6 +89,24 @@ const database = PgClient.layer(
|
|
|
89
89
|
|
|
90
90
|
To opt out for an existing database with quoted camelCase columns or models with snake_case fields, use `PgClient.layer(clientOptions(options, { snakeCase: false }))`. Existing native clients are unchanged until configured with this helper. Native `transformQueryNames` and `transformResultNames` supplied in `options` take precedence. For `appHost` from `@shivaedev/server/appHost.ts` or `appDatabase` from `@shivaedev/server/appDatabase.ts`, use `clientOptions` on their `config` object; the host forwards those native transforms to its client.
|
|
91
91
|
|
|
92
|
+
### Grouped values in flat columns
|
|
93
|
+
|
|
94
|
+
Use `Model.Group` from `@shivaedev/sql/Model.ts` inside a native Effect model when several columns form one value:
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
import { Schema } from "effect";
|
|
98
|
+
import { Model as NativeModel } from "effect/schema";
|
|
99
|
+
import { Model } from "@shivaedev/sql/Model.ts";
|
|
100
|
+
|
|
101
|
+
class Food extends NativeModel.Class<Food>("Food")({
|
|
102
|
+
id: NativeModel.Field({ select: Schema.Number, update: Schema.Number }),
|
|
103
|
+
name: Schema.String,
|
|
104
|
+
energy: Model.Group({ low: Schema.NumberFromString, expected: Schema.NumberFromString, high: Schema.NumberFromString }, { prefix: "energy_" }),
|
|
105
|
+
}) {}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Repository inputs and returned values contain `energy: { low, expected, high }`. Storage uses `energy_low`, `energy_expected` and `energy_high`; the prefix is concatenated with each member name. Member codecs still encode and decode each value. The application creates those columns. `findById` and `findMany`, including `select: ["energy"]`, return the struct. A chosen-column update with `columns: ["energy"]` replaces the group together and preserves other columns. Use scalar model fields for filters, ordering and conflict targets; a group declares a stored value, not a relation or a nested query language.
|
|
109
|
+
|
|
92
110
|
### 1. Once per model: fields and writable variants
|
|
93
111
|
|
|
94
112
|
```ts
|
|
@@ -210,7 +228,7 @@ yield* lines.updateVoid({ id: 1, note: null }, { columns: ["note"] });
|
|
|
210
228
|
yield* lines.update({ id: 2, name: "Travel" }, { columns: ["name"], onMissing: "fail" });
|
|
211
229
|
```
|
|
212
230
|
|
|
213
|
-
Each call executes one `UPDATE … RETURNING` without a preliminary read, encodes only the selected fields and every primary-key component through the model's update schemas, and publishes its table, record and old/new bucket keys after commit. Other columns retain their stored values, including omitted update timestamps. The column list is nonempty and contains writable fields other than the primary key; invalid selections fail with `SchemaError`. Composite-key inputs include every key component. `updateVoid` discards the returned row; `update` returns the decoded model and keeps the same missing-row behavior as a complete update.
|
|
231
|
+
Each call executes one `UPDATE … RETURNING` without a preliminary read, encodes only the selected fields and every primary-key component through the model's update schemas, and publishes its table, record and old/new bucket keys after commit. Other columns retain their stored values, including omitted update timestamps. The column list is nonempty and contains writable fields other than the primary key; invalid selections fail with `SchemaError`. Composite-key inputs include every key component. `updateVoid` discards the returned row; `update` returns the decoded model and keeps the same missing-row behavior as a complete update. Versioned repositories also accept column selection when the input includes the expected version.
|
|
214
232
|
|
|
215
233
|
A selected row can be passed unchanged to `update` or `updateVoid`, including a model with native `Model.DateTimeUpdateFromDate`. Repository update inputs accept its ordinary read-back date-time value without requiring `Model.Override`; a supplied value is preserved.
|
|
216
234
|
|
|
@@ -229,6 +247,18 @@ const listInvoiceLines = (name: string | undefined) =>
|
|
|
229
247
|
});
|
|
230
248
|
```
|
|
231
249
|
|
|
250
|
+
For a filtered single-row read, use `findOne`:
|
|
251
|
+
|
|
252
|
+
```ts
|
|
253
|
+
const found = yield* lines.findOne({
|
|
254
|
+
where: { amount: 52, note: null },
|
|
255
|
+
select: ["name", "amount"],
|
|
256
|
+
orderBy: { field: "name", direction: "asc" },
|
|
257
|
+
});
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
It returns `Option.some` of the selected decoded fields, or `Option.none` when no row matches. It accepts the same filters, projections and ordering as `findMany`, with a fixed SQL limit of one. Supply ordering when several rows can match and the choice matters. SQL and schema failures remain in the error channel.
|
|
261
|
+
|
|
232
262
|
Filter entries are joined with `AND`. A field value uses equality; a null filter matches SQL `NULL`, and an omitted or `undefined` value leaves the field unconstrained. Filters use decoded field values, so `amount` takes a number. Selected results are decoded records with the selected fields' types; this function returns `Array<{ readonly name: string; readonly amount: number }>`.
|
|
233
263
|
|
|
234
264
|
Use `{ in: values }` to match any value in a list, alongside equality entries:
|
|
@@ -426,7 +456,19 @@ const ingest = Effect.gen(function* () {
|
|
|
426
456
|
});
|
|
427
457
|
```
|
|
428
458
|
|
|
429
|
-
The application creates `readings` with `PRIMARY KEY (source, sequence)`; `sequence` and `amount` are text columns for these codecs. Composite keys contain at least two model fields. `findById` and `delete` take an object containing every key field and encode each through its field schema. `update` and `updateVoid` use the full key from the model's update input and leave the key columns unchanged. Missing key fields fail with `SchemaError` before SQL runs. A scalar `idColumn` keeps native CRUD signatures. Writes announce a composite row as the table name, a colon and a JSON array of the returned key values in `idColumn` order, such as `readings:["meter","1"]`; a PostgreSQL `bigint` key value appears as its decimal string.
|
|
459
|
+
The application creates `readings` with `PRIMARY KEY (source, sequence)`; `sequence` and `amount` are text columns for these codecs. Composite keys contain at least two model fields. `findById` and `delete` take an object containing every key field and encode each through its field schema. `update` and `updateVoid` use the full key from the model's update input and leave the key columns unchanged. Missing key fields fail with `SchemaError` before SQL runs. A scalar `idColumn` keeps native CRUD signatures. Writes announce a composite row as the table name, a colon and a JSON array of the returned key values in `idColumn` order, such as `readings:["meter","1"]`; a PostgreSQL `bigint` key value appears as its decimal string. Use `compositeRecordKey` to derive a matching subscription key from a decoded row:
|
|
460
|
+
|
|
461
|
+
```ts
|
|
462
|
+
import { compositeRecordKey } from "@shivaedev/sql/compositeRecordKey.ts";
|
|
463
|
+
|
|
464
|
+
const key = yield* compositeRecordKey(
|
|
465
|
+
Reading,
|
|
466
|
+
{ tableName: "readings", idColumn: ["source", "sequence"] },
|
|
467
|
+
{ source: "meter", sequence: 1 },
|
|
468
|
+
);
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
Pass `[key]` to native Reactivity subscriptions or `invalidateOnCommit`. The helper encodes only the named key fields through the model's select schemas, in `idColumn` order, so decoded `NumberFromString` fields and bigint ids match the writer's stored key values. Codec encoding services remain in the Effect environment, and encoding failures are typed `SchemaError`. It also unwraps native PostgreSQL parameters such as `int8`. The low-level `compositeId` from `@shivaedev/changes/keys.ts` continues to accept stored values in that same column order.
|
|
430
472
|
|
|
431
473
|
`conflictColumns` is a nonempty tuple of inserted model fields backed by a database unique constraint or primary key. It can differ from the primary key, including a unique external key beside a generated ID. Declaring it adds these methods:
|
|
432
474
|
|
|
@@ -620,6 +662,40 @@ A stale version or missing row fails with `OptimisticConcurrencyError` in the er
|
|
|
620
662
|
|
|
621
663
|
These writes use SQL `RETURNING`. Repositories without `versionColumn` retain native CRUD signatures and behavior.
|
|
622
664
|
|
|
665
|
+
A partial versioned update includes the primary key, expected version and chosen values:
|
|
666
|
+
|
|
667
|
+
```ts
|
|
668
|
+
yield* documents.update({ ...key, lockVersion: 0, name: "Published" }, { columns: ["name"] });
|
|
669
|
+
yield* documents.updateVoid({ ...key, lockVersion: 1, name: "Final" }, { columns: ["name"] });
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
Only the chosen columns are assigned, and the version increments once. Other values need not be read or sent back. The version is required and cannot appear in `columns`. Stale partial writes fail with the same `OptimisticConcurrencyError` and leave the row unchanged.
|
|
673
|
+
|
|
674
|
+
### Ordered children under a parent
|
|
675
|
+
|
|
676
|
+
`makeOrderedChildren` from `@shivaedev/sql/makeOrderedChildren.ts` uses a PostgreSQL parent-row lock to serialize `append`, `move` and `reorder` for that parent:
|
|
677
|
+
|
|
678
|
+
```ts
|
|
679
|
+
const orderedDays = yield* makeOrderedChildren(Day, {
|
|
680
|
+
tableName: "days",
|
|
681
|
+
idColumn: "id",
|
|
682
|
+
parentColumn: "planId",
|
|
683
|
+
parentTable: "plans",
|
|
684
|
+
parentIdColumn: "id",
|
|
685
|
+
positionColumn: "position",
|
|
686
|
+
versionColumn: "version",
|
|
687
|
+
spanPrefix: "Day",
|
|
688
|
+
});
|
|
689
|
+
|
|
690
|
+
yield* orderedDays.append(planId, { id: dayId, name: "Training" });
|
|
691
|
+
yield* orderedDays.move(planId, dayId, 0);
|
|
692
|
+
yield* orderedDays.reorder(planId, [thirdDayId, dayId, secondDayId]);
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
The native model declares the parent field and numeric position in its insert variant, and keeps its ID in select and update. When configured, the version field follows the versioned repository convention above. `append` supplies the parent and next position, starting at zero for an empty parent, and initializes the configured version to zero. `move` takes a zero-based destination and shifts siblings. `reorder` takes exactly the IDs currently under that parent, once each. These two operations assign positions in one statement without touching content or versions, so an existing versioned edit can still save. They fail with `ChildOrderError` from `@shivaedev/sql/orderedChildStatements.ts` for `membership` or `position`; a missing parent fails with `NoSuchElementError`.
|
|
696
|
+
|
|
697
|
+
The lock is held until the transaction commits, including when called inside `transact`. A concurrent reorder of that parent waits. Use these helpers for ordering writes under the same parent. The application owns the parent foreign key and DDL; if positions are unique within a parent, declare `UNIQUE (plan_id, position) DEFERRABLE INITIALLY DEFERRED` so positions can swap within the transaction.
|
|
698
|
+
|
|
623
699
|
### Transactions and invalidation
|
|
624
700
|
|
|
625
701
|
```ts
|
|
@@ -724,6 +800,43 @@ const serializeInvoiceJob = (input: typeof InvoiceLine.update.Type) =>
|
|
|
724
800
|
|
|
725
801
|
`advisoryXactLock(key: bigint | string)` waits for PostgreSQL's `pg_advisory_xact_lock` on the current client's transaction. A bigint is passed as a signed 64-bit PostgreSQL key without converting it to a JavaScript number. A string uses PostgreSQL's `hashtextextended(key, 0)` as its bigint key; use the same string in cooperating callers. Hash collisions share a lock. Different keys proceed independently. The lock lasts until commit or rollback, including rollback when the holding fiber is interrupted. Outside a transaction, the function fails with typed `AdvisoryLockOutsideTransaction` from the same module. SQL failures remain typed `SqlError` and follow the surrounding `transact` mapper.
|
|
726
802
|
|
|
803
|
+
### Bounded, resumable PostgreSQL backfills
|
|
804
|
+
|
|
805
|
+
Use `backfillPostgres` from `@shivaedev/sql/backfillPostgres.ts` for data work that needs separate commits. Run it after schema migrations, outside any transaction. The application owns its checkpoint table, the ordered selection and the row transformation. All runners of the same backfill must use the same advisory lock key.
|
|
806
|
+
|
|
807
|
+
```ts
|
|
808
|
+
import { Effect } from "effect";
|
|
809
|
+
import { SqlClient } from "effect/sql";
|
|
810
|
+
import { backfillPostgres } from "@shivaedev/sql/backfillPostgres.ts";
|
|
811
|
+
import { invalidateOnCommit } from "@shivaedev/sql/transact.ts";
|
|
812
|
+
|
|
813
|
+
const capitalizeNames = Effect.gen(function* () {
|
|
814
|
+
const sql = yield* SqlClient.SqlClient;
|
|
815
|
+
return yield* backfillPostgres({
|
|
816
|
+
batchSize: 100,
|
|
817
|
+
lock: "items:capitalize_names",
|
|
818
|
+
loadProgress: Effect.map(
|
|
819
|
+
sql<{ readonly id: number }>`select last_id as id from backfill_progress where name = 'capitalize_names'`,
|
|
820
|
+
([row]) => row?.id ?? 0,
|
|
821
|
+
),
|
|
822
|
+
select: (after, limit) => sql<{ readonly id: number }>`select id from items where id > ${after} order by id limit ${limit} for update`,
|
|
823
|
+
process: (row) =>
|
|
824
|
+
Effect.gen(function* () {
|
|
825
|
+
yield* sql`update items set name = upper(name) where id = ${row.id}`;
|
|
826
|
+
yield* invalidateOnCommit([`item:${row.id}`]);
|
|
827
|
+
}),
|
|
828
|
+
saveProgress: (last) => sql`insert into backfill_progress (name, last_id) values ('capitalize_names', ${last.id})
|
|
829
|
+
on conflict (name) do update set last_id = excluded.last_id`,
|
|
830
|
+
});
|
|
831
|
+
});
|
|
832
|
+
```
|
|
833
|
+
|
|
834
|
+
Create `items` and `backfill_progress` in numbered migrations; the example's checkpoint table has a text primary key `name` and an integer `last_id`. Each batch takes a transaction-scoped advisory lock before `loadProgress`, passes the checkpoint and `batchSize` to `select`, processes rows sequentially and saves progress from the last selected row in that same `transact`. It processes at most `batchSize` rows even if selection returns more; use SQL `LIMIT` to bound reads and locks too. An empty selection finishes the invocation and its result counts rows processed during that invocation, including rows whose transformation made no change. Progress must advance over every selected row, including unchanged rows.
|
|
835
|
+
|
|
836
|
+
Use repository writes or record raw writes with `invalidateOnCommit`; each committed batch announces its changes through the existing SQL channel. A failed or interrupted batch rolls back its rows, checkpoint and recorded changes while earlier commits remain. Call the helper again to reload the stored checkpoint and resume. A completed rerun processes zero rows. Callback failures and native SQL errors stay typed; invalid batch sizes fail with `SchemaError`, and an enclosing transaction fails with `BackfillInsideTransaction` before processing. `batchSize` must be a positive safe integer.
|
|
837
|
+
|
|
838
|
+
The helper adds no ledger entries and does not change `migratePostgres`'s single transaction for pending numbered migrations. Keep checkpoint writes on the provided client and inside `saveProgress`. Choose a stable, uniquely ordered cursor that cannot skip remaining work. The application decides how concurrent inserts or edits relate to that cursor and owns any retry policy. Effects outside PostgreSQL cannot be rolled back with a batch.
|
|
839
|
+
|
|
727
840
|
### PostgreSQL migrations: one discovered folder
|
|
728
841
|
|
|
729
842
|
Application migrations live in `src/server/migrations/`. Name each file with a four-digit zero-padded sequential number, an underscore and a snake_case name, such as `0007_add_note_tags.ts`. Export the migration's SQL Effect as the default export:
|
|
@@ -803,6 +916,7 @@ Import the module that defines the name; there is no root entry.
|
|
|
803
916
|
- `@shivaedev/sql/keysetPage.ts`: `keysetPage(options)`, `KeysetOrder`, `KeysetPageQuery`, `KeysetPage` — Native seek and order fragments with a required unique tie-breaker, validated page size and one-row lookahead; predicates, result schemas and cursor formats stay with the caller.
|
|
804
917
|
- `@shivaedev/sql/int8.ts`: `int8(field)` — PostgreSQL bigint storage codec using the supplied field schema.
|
|
805
918
|
- `@shivaedev/sql/int4Array.ts`: `int4Array(field)` — PostgreSQL integer-array storage codec, including typed empty arrays.
|
|
919
|
+
- `@shivaedev/sql/compositeRecordKey.ts`: `compositeRecordKey(model, { tableName, idColumn }, value)` — Encodes decoded composite key fields into the published record key.
|
|
806
920
|
- `@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`.
|
|
807
921
|
- `@shivaedev/sql/repository.ts`: `OptimisticConcurrencyError` — Typed stale or missing-row failure for versioned writes, with `tableName`, `id`, `expectedVersion`, `reason: "missing" | "stale"`.
|
|
808
922
|
- `@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.
|
|
@@ -815,6 +929,7 @@ Import the module that defines the name; there is no root entry.
|
|
|
815
929
|
- `@shivaedev/sql/migrationConvention.ts`: `MIGRATIONS_DIRECTORY`, `MIGRATION_FILE_PATTERN` — Application folder and four-digit sequential snake_case TypeScript filename pattern.
|
|
816
930
|
- `@shivaedev/sql/migrationsFromDirectory.ts`: `migrationsFromDirectory(root)` — Discover migrations under the application root with native `FileSystem` and `Path`.
|
|
817
931
|
- `@shivaedev/sql/migrations.ts`: `migratePostgres(options)` — Native loader, required `lockTimeout`, optional `components`, `release` and ledger `table`, default `effect_sql_migrations`.
|
|
932
|
+
- `@shivaedev/sql/backfillPostgres.ts`: `backfillPostgres({ batchSize, lock, loadProgress, select, process, saveProgress })` — Bounded transactions with application-owned checkpoints; returns the rows processed in this invocation. `BackfillInsideTransaction` rejects an enclosing transaction.
|
|
818
933
|
- `@shivaedev/sql/assertPostgresMigrations.ts`: `assertPostgresMigrations({ loader, table?, components? })` — Read-only readiness check of required migration IDs and names in the native ledger and registered schema components.
|
|
819
934
|
- `@shivaedev/sql/pendingPostgresMigrations.ts`: `pendingPostgresMigrations({ loader, table?, components? })` — Pending ledger migrations as ID and name, and pending schema components as name and detail.
|
|
820
935
|
- `@shivaedev/sql/postgresRelease.ts`: `postgresRelease(release, table?)`, `readPostgresRelease(table?)` — Release marker schema component and its reader.
|
package/dist/Model.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
declare function Group<TFields extends Schema.Struct.Fields>(fields: TFields, options: {
|
|
3
|
+
readonly prefix: string;
|
|
4
|
+
}): Schema.Struct<TFields>;
|
|
5
|
+
export declare const Model: {
|
|
6
|
+
Group: typeof Group;
|
|
7
|
+
};
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=Model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Model.d.ts","sourceRoot":"","sources":["../src/Model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,iBAAS,KAAK,CAAC,OAAO,SAAS,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,0BAEzG;AAED,eAAO,MAAM,KAAK;;CAAY,CAAC"}
|
package/dist/Model.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Model.js","sourceRoot":"","sources":["../src/Model.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,SAAS,KAAK,CAAuC,MAAe,EAAE,OAAoC;IACzG,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,EAAE,4BAA4B,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;AACzF,CAAC;AAED,MAAM,CAAC,MAAM,KAAK,GAAG,EAAE,KAAK,EAAE,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Model } from "effect/schema";
|
|
2
|
+
import type { PrimaryKey, Repository } from "#compositeRepository.ts";
|
|
3
|
+
import type { Queries } from "#repositoryQueries.ts";
|
|
4
|
+
import type { VersionColumn, VersionedWrites } from "#versionedRepository.ts";
|
|
5
|
+
export type VersionedRepositoryShape<TModel extends Model.Any, TId extends PrimaryKey<TModel>, TVersion extends VersionColumn<TModel, TId>> = Omit<Repository<TModel, TId>, keyof VersionedWrites<TModel, TId, TVersion>> & VersionedWrites<TModel, TId, TVersion> & Queries<TModel>;
|
|
6
|
+
//# sourceMappingURL=VersionedRepositoryShape.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VersionedRepositoryShape.d.ts","sourceRoot":"","sources":["../src/VersionedRepositoryShape.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAE9E,MAAM,MAAM,wBAAwB,CAAC,MAAM,SAAS,KAAK,CAAC,GAAG,EAAE,GAAG,SAAS,UAAU,CAAC,MAAM,CAAC,EAAE,QAAQ,SAAS,aAAa,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,IAAI,CACjJ,UAAU,CAAC,MAAM,EAAE,GAAG,CAAC,EACvB,MAAM,eAAe,CAAC,MAAM,EAAE,GAAG,EAAE,QAAQ,CAAC,CAC5C,GACA,eAAe,CAAC,MAAM,EAAE,GAAG,EAAE,QAAQ,CAAC,GACtC,OAAO,CAAC,MAAM,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VersionedRepositoryShape.js","sourceRoot":"","sources":["../src/VersionedRepositoryShape.ts"],"names":[],"mappings":""}
|
package/dist/WorkerRelease.d.ts
CHANGED
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
import { Effect } from "effect";
|
|
2
|
-
export declare const makeWorkerRelease: () => Effect.Effect<{
|
|
3
|
-
service: {
|
|
4
|
-
register: <E>(control: (active: boolean) => Effect.Effect<void, E>) => Effect.Effect<void, E, import("effect/Scope").Scope>;
|
|
5
|
-
};
|
|
6
|
-
setActive: (next: boolean) => Effect.Effect<void, unknown, never>;
|
|
7
|
-
}, never, never>;
|
|
8
1
|
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>;
|
|
2
|
+
readonly register: <E>(control: (active: boolean) => import("effect/Effect").Effect<void, E>) => import("effect/Effect").Effect<void, E, import("effect/Scope").Scope>;
|
|
10
3
|
}>> & {
|
|
11
4
|
readonly layer: import("effect/Layer").Layer<"@shivaedev/sql/WorkerRelease", never, never>;
|
|
12
5
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkerRelease.d.ts","sourceRoot":"","sources":["../src/WorkerRelease.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"WorkerRelease.d.ts","sourceRoot":"","sources":["../src/WorkerRelease.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,aAAa;;;;CAKxB,CAAC"}
|
package/dist/WorkerRelease.js
CHANGED
|
@@ -1,36 +1,11 @@
|
|
|
1
|
-
import { Effect, Semaphore } from "effect";
|
|
2
1
|
import { defineService } from "@shivaedev/service/defineService.ts";
|
|
3
2
|
import { genericMethod } from "@shivaedev/service/genericMethod.ts";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const controls = new Set();
|
|
7
|
-
let active = false;
|
|
8
|
-
const locked = mutex.withPermits(1);
|
|
9
|
-
function register(control) {
|
|
10
|
-
const attach = Effect.gen(function* () {
|
|
11
|
-
controls.add(control);
|
|
12
|
-
yield* control(active);
|
|
13
|
-
});
|
|
14
|
-
const detach = Effect.sync(() => {
|
|
15
|
-
controls.delete(control);
|
|
16
|
-
});
|
|
17
|
-
return Effect.acquireRelease(locked(attach), () => locked(detach));
|
|
18
|
-
}
|
|
19
|
-
return {
|
|
20
|
-
service: { register },
|
|
21
|
-
setActive: (next) => locked(Effect.gen(function* () {
|
|
22
|
-
if (next === active) {
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
active = next;
|
|
26
|
-
yield* Effect.forEach(controls, (control) => control(active), { concurrency: "unbounded", discard: true });
|
|
27
|
-
})).pipe(Effect.uninterruptible),
|
|
28
|
-
};
|
|
29
|
-
});
|
|
3
|
+
import { initializeWorkerRelease } from "./initializeWorkerRelease.js";
|
|
4
|
+
import { registerWorkerRelease } from "./registerWorkerRelease.js";
|
|
30
5
|
export const WorkerRelease = defineService({
|
|
31
6
|
id: "@shivaedev/sql/WorkerRelease",
|
|
32
|
-
initialize:
|
|
33
|
-
methods: (
|
|
7
|
+
initialize: initializeWorkerRelease,
|
|
8
|
+
methods: (state) => ({ register: genericMethod(registerWorkerRelease(state)) }),
|
|
34
9
|
requires: [],
|
|
35
10
|
});
|
|
36
11
|
//# sourceMappingURL=WorkerRelease.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"WorkerRelease.js","sourceRoot":"","sources":["../src/WorkerRelease.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"WorkerRelease.js","sourceRoot":"","sources":["../src/WorkerRelease.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AACpE,OAAO,EAAE,uBAAuB,EAAE,MAAM,8BAA8B,CAAC;AACvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAEnE,MAAM,CAAC,MAAM,aAAa,GAAG,aAAa,CAAC;IAC1C,EAAE,EAAE,8BAA8B;IAClC,UAAU,EAAE,uBAAuB;IACnC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,aAAa,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;IAC/E,QAAQ,EAAE,EAAE;CACZ,CAAC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Schema } from "effect";
|
|
2
|
+
import type { SqlNameTransforms } from "#clientOptions.ts";
|
|
3
|
+
interface Column {
|
|
4
|
+
readonly column: string;
|
|
5
|
+
readonly key: string;
|
|
6
|
+
readonly member: PropertyKey | undefined;
|
|
7
|
+
}
|
|
8
|
+
export declare function groupColumns(fields: Schema.Struct.Fields, keys?: readonly string[]): Column[];
|
|
9
|
+
export declare function rowMapping(fields: Schema.Struct.Fields, names?: SqlNameTransforms): {
|
|
10
|
+
expand: (row: Readonly<Record<string, unknown>>) => Record<string, unknown>;
|
|
11
|
+
flatten: (row: unknown) => Record<string, unknown>;
|
|
12
|
+
};
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=groupColumns.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"groupColumns.d.ts","sourceRoot":"","sources":["../../src/adapters/groupColumns.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE3D,UAAU,MAAM;IACf,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,SAAS,CAAC;CACzC;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,GAAE,SAAS,MAAM,EAAwB,GAAG,MAAM,EAAE,CAQlH;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,iBAAiB;IAahF,MAAM,QAAQ,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAW/C,OAAO,QAAQ,OAAO;EAkBvB"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Predicate, SchemaAST } from "effect";
|
|
2
|
+
export function groupColumns(fields, keys = Object.keys(fields)) {
|
|
3
|
+
return keys.flatMap((key) => {
|
|
4
|
+
const ast = fields[key]?.ast;
|
|
5
|
+
const prefix = ast?.annotations?.["@shivaedev/sql/groupPrefix"];
|
|
6
|
+
return typeof prefix === "string" && ast !== undefined && SchemaAST.isObjects(ast)
|
|
7
|
+
? ast.propertySignatures.map((field) => ({ column: `${prefix}${String(field.name)}`, key, member: field.name }))
|
|
8
|
+
: [{ column: key, key, member: undefined }];
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
export function rowMapping(fields, names) {
|
|
12
|
+
const columns = groupColumns(fields);
|
|
13
|
+
function returnedName(column) {
|
|
14
|
+
return names?.transformResultNames?.(names.transformQueryNames?.(column) ?? column) ?? column;
|
|
15
|
+
}
|
|
16
|
+
const seen = new Set();
|
|
17
|
+
for (const { column } of columns) {
|
|
18
|
+
if (seen.has(column)) {
|
|
19
|
+
throw new Error(`Duplicate grouped column: ${column}`);
|
|
20
|
+
}
|
|
21
|
+
seen.add(column);
|
|
22
|
+
}
|
|
23
|
+
return {
|
|
24
|
+
expand: (row) => {
|
|
25
|
+
const expanded = { ...row };
|
|
26
|
+
for (const { key, member, column } of columns) {
|
|
27
|
+
if (member !== undefined && Object.hasOwn(row, returnedName(column))) {
|
|
28
|
+
const held = expanded[key];
|
|
29
|
+
expanded[key] = { ...(typeof held === "object" && held !== null ? held : {}), [member]: row[returnedName(column)] };
|
|
30
|
+
delete expanded[returnedName(column)];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return expanded;
|
|
34
|
+
},
|
|
35
|
+
flatten: (row) => {
|
|
36
|
+
if (!Predicate.isObject(row)) {
|
|
37
|
+
return {};
|
|
38
|
+
}
|
|
39
|
+
const flat = { ...row };
|
|
40
|
+
for (const { key, member, column } of columns) {
|
|
41
|
+
if (member === undefined) {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
delete flat[key];
|
|
45
|
+
const value = row[key];
|
|
46
|
+
if (Predicate.isObject(value) && Object.hasOwn(value, member)) {
|
|
47
|
+
flat[column] = Reflect.get(value, member);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return flat;
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=groupColumns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"groupColumns.js","sourceRoot":"","sources":["../../src/adapters/groupColumns.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAC;AAS9C,MAAM,UAAU,YAAY,CAAC,MAA4B,EAAE,IAAI,GAAsB,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;IACvG,OAAO,IAAI,CAAC,OAAO,CAAS,CAAC,GAAG,EAAE,EAAE;QACnC,MAAM,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC;QAC7B,MAAM,MAAM,GAAG,GAAG,EAAE,WAAW,EAAE,CAAC,4BAA4B,CAAC,CAAC;QAChE,OAAO,OAAO,MAAM,KAAK,QAAQ,IAAI,GAAG,KAAK,SAAS,IAAI,SAAS,CAAC,SAAS,CAAC,GAAG,CAAC;YACjF,CAAC,CAAC,GAAG,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;YAChH,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,MAA4B,EAAE,KAAyB;IACjF,MAAM,OAAO,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACrC,SAAS,YAAY,CAAC,MAAc;QACnC,OAAO,KAAK,EAAE,oBAAoB,EAAE,CAAC,KAAK,CAAC,mBAAmB,EAAE,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,IAAI,MAAM,CAAC;IAC/F,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAC/B,KAAK,MAAM,EAAE,MAAM,EAAE,IAAI,OAAO,EAAE,CAAC;QAClC,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,6BAA6B,MAAM,EAAE,CAAC,CAAC;QACxD,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAClB,CAAC;IACD,OAAO;QACN,MAAM,EAAE,CAAC,GAAsC,EAAE,EAAE;YAClD,MAAM,QAAQ,GAA4B,EAAE,GAAG,GAAG,EAAE,CAAC;YACrD,KAAK,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,OAAO,EAAE,CAAC;gBAC/C,IAAI,MAAM,KAAK,SAAS,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,YAAY,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;oBACtE,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC;oBAC3B,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;oBACpH,OAAO,QAAQ,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC;gBACvC,CAAC;YACF,CAAC;YACD,OAAO,QAAQ,CAAC;QACjB,CAAC;QACD,OAAO,EAAE,CAAC,GAAY,EAAE,EAAE;YACzB,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC9B,OAAO,EAAE,CAAC;YACX,CAAC;YACD,MAAM,IAAI,GAA4B,EAAE,GAAG,GAAG,EAAE,CAAC;YACjD,KAAK,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,OAAO,EAAE,CAAC;gBAC/C,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;oBAC1B,SAAS;gBACV,CAAC;gBACD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;gBACjB,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC;gBACvB,IAAI,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC;oBAC/D,IAAI,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;gBAC3C,CAAC;YACF,CAAC;YACD,OAAO,IAAI,CAAC;QACb,CAAC;KACD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Schema } from "effect";
|
|
2
|
+
import type { SqlNameTransforms } from "#clientOptions.ts";
|
|
3
|
+
export declare function storageSchema<TSchema extends Schema.Constraint>(schema: TSchema, fields: Schema.Struct.Fields, names?: SqlNameTransforms): Schema.decodeTo<TSchema, Schema.$Record<Schema.String, Schema.Unknown>, never, never>;
|
|
4
|
+
//# sourceMappingURL=storageSchema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storageSchema.d.ts","sourceRoot":"","sources":["../../src/adapters/storageSchema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAgB,MAAM,QAAQ,CAAC;AAE9C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE3D,wBAAgB,aAAa,CAAC,OAAO,SAAS,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,iBAAiB,yFAKxI"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Schema, SchemaGetter } from "effect";
|
|
2
|
+
import { rowMapping } from "#adapters/groupColumns.ts";
|
|
3
|
+
export function storageSchema(schema, fields, names) {
|
|
4
|
+
const mapping = rowMapping(fields, names);
|
|
5
|
+
return Schema.Record(Schema.String, Schema.Unknown).pipe(Schema.decodeTo(schema, { decode: SchemaGetter.transform(mapping.expand), encode: SchemaGetter.transform(mapping.flatten) }));
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=storageSchema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storageSchema.js","sourceRoot":"","sources":["../../src/adapters/storageSchema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAGvD,MAAM,UAAU,aAAa,CAAoC,MAAe,EAAE,MAA4B,EAAE,KAAyB;IACxI,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC1C,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CACvD,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,YAAY,CAAC,SAAS,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,YAAY,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,CAC5H,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect";
|
|
2
|
+
import { SqlClient } from "effect/sql";
|
|
3
|
+
declare const BackfillInsideTransaction_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 & {
|
|
4
|
+
readonly _tag: "BackfillInsideTransaction";
|
|
5
|
+
} & Readonly<A>;
|
|
6
|
+
export declare class BackfillInsideTransaction extends BackfillInsideTransaction_base {
|
|
7
|
+
}
|
|
8
|
+
export interface BackfillOptions<TRow extends object, TProgress, E, R> {
|
|
9
|
+
readonly batchSize: number;
|
|
10
|
+
readonly loadProgress: Effect.Effect<TProgress, E, R>;
|
|
11
|
+
readonly lock: bigint | string;
|
|
12
|
+
readonly process: (row: TRow) => Effect.Effect<unknown, E, R>;
|
|
13
|
+
readonly saveProgress: (last: TRow, previous: TProgress) => Effect.Effect<unknown, E, R>;
|
|
14
|
+
readonly select: (progress: TProgress, limit: number) => Effect.Effect<readonly TRow[], E, R>;
|
|
15
|
+
}
|
|
16
|
+
export declare const backfillPostgres: <TRow extends object, TProgress, E, R>(options: BackfillOptions<TRow, TProgress, E, R>) => Effect.Effect<number, BackfillInsideTransaction | Schema.SchemaError | import("effect/sql/SqlError").SqlError | Exclude<E, import("effect/sql/SqlError").SqlError>, R | import("effect/reactivity/Reactivity").Reactivity | SqlClient.SqlClient>;
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=backfillPostgres.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backfillPostgres.d.ts","sourceRoot":"","sources":["../src/backfillPostgres.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,MAAM,EAAU,MAAM,EAAE,MAAM,QAAQ,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;;;;AAIvC,qBAAa,yBAA0B,SAAQ,8BAA6C;CAAG;AAE/F,MAAM,WAAW,eAAe,CAAC,IAAI,SAAS,MAAM,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC;IACpE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACtD,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,QAAQ,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IAC9D,QAAQ,CAAC,YAAY,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;IACzF,QAAQ,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,SAAS,IAAI,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;CAC9F;AAID,eAAO,MAAM,gBAAgB,GAAgD,IAAI,SAAS,MAAM,EAAE,SAAS,EAAE,CAAC,EAAE,CAAC,sSA4B/G,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Data, Effect, Option, Schema } from "effect";
|
|
2
|
+
import { SqlClient } from "effect/sql";
|
|
3
|
+
import { advisoryXactLock } from "#advisoryXactLock.ts";
|
|
4
|
+
import { transact } from "#transact.ts";
|
|
5
|
+
export class BackfillInsideTransaction extends Data.TaggedError("BackfillInsideTransaction") {
|
|
6
|
+
}
|
|
7
|
+
const BatchSize = Schema.Number.check(Schema.isInt(), Schema.isGreaterThan(0), Schema.isLessThanOrEqualTo(Number.MAX_SAFE_INTEGER));
|
|
8
|
+
export const backfillPostgres = Effect.fn("Sql.backfillPostgres")(function* (options) {
|
|
9
|
+
const sql = yield* SqlClient.SqlClient;
|
|
10
|
+
if (Option.isSome(yield* Effect.serviceOption(sql.transactionService))) {
|
|
11
|
+
return yield* new BackfillInsideTransaction();
|
|
12
|
+
}
|
|
13
|
+
const batchSize = yield* Schema.decodeUnknownEffect(BatchSize)(options.batchSize);
|
|
14
|
+
let processed = 0;
|
|
15
|
+
while (true) {
|
|
16
|
+
const count = yield* Effect.gen(function* () {
|
|
17
|
+
yield* advisoryXactLock(options.lock).pipe(Effect.catchTag("AdvisoryLockOutsideTransaction", Effect.die));
|
|
18
|
+
const progress = yield* options.loadProgress;
|
|
19
|
+
const rows = (yield* options.select(progress, batchSize)).slice(0, batchSize);
|
|
20
|
+
for (const row of rows) {
|
|
21
|
+
yield* options.process(row);
|
|
22
|
+
}
|
|
23
|
+
const last = rows.at(-1);
|
|
24
|
+
if (last !== undefined) {
|
|
25
|
+
yield* options.saveProgress(last, progress);
|
|
26
|
+
}
|
|
27
|
+
return rows.length;
|
|
28
|
+
}).pipe(transact({ onSqlError: (error) => error }));
|
|
29
|
+
processed += count;
|
|
30
|
+
if (count === 0) {
|
|
31
|
+
return processed;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
//# sourceMappingURL=backfillPostgres.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backfillPostgres.js","sourceRoot":"","sources":["../src/backfillPostgres.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExC,MAAM,OAAO,yBAA0B,SAAQ,IAAI,CAAC,WAAW,CAAC,2BAA2B,CAAC;CAAG;AAW/F,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,mBAAmB,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC,CAAC;AAEpI,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC,EAAE,CAAC,sBAAsB,CAAC,CAAC,QAAQ,CAAC,EAC1E,OAA+C;IAE/C,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC;IACvC,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,EAAE,CAAC;QACxE,OAAO,KAAK,CAAC,CAAC,IAAI,yBAAyB,EAAE,CAAC;IAC/C,CAAC;IACD,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,mBAAmB,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAClF,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,OAAO,IAAI,EAAE,CAAC;QACb,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YACxC,KAAK,CAAC,CAAC,gBAAgB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,gCAAgC,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAC1G,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC;YAC7C,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;YAC9E,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;gBACxB,KAAK,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YAC7B,CAAC;YACD,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;YACzB,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACxB,KAAK,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YAC7C,CAAC;YACD,OAAO,IAAI,CAAC,MAAM,CAAC;QACpB,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,UAAU,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QACpD,SAAS,IAAI,KAAK,CAAC;QACnB,IAAI,KAAK,KAAK,CAAC,EAAE,CAAC;YACjB,OAAO,SAAS,CAAC;QAClB,CAAC;IACF,CAAC;AACF,CAAC,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"batchWrites.d.ts","sourceRoot":"","sources":["../src/batchWrites.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"batchWrites.d.ts","sourceRoot":"","sources":["../src/batchWrites.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAI/D,MAAM,MAAM,UAAU,CAAC,MAAM,SAAS,KAAK,CAAC,GAAG,EAAE,SAAS,GAAG,KAAK,IAAI,CACrE,MAAM,EAAE,SAAS,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,EAAE,KACvC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,kBAAkB,CAAC,GAAG,MAAM,CAAC,kBAAkB,CAAC,GAAG,SAAS,CAAC,CAAC;AAEnJ,MAAM,WAAW,WAAW,CAAC,MAAM,SAAS,KAAK,CAAC,GAAG;IACpD,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;CACxC;AAED,eAAO,MAAM,cAAc,GAAgC,SAAS;wBAEpC,MAAM;yBAAuB,MAAM;sBAAoB,iBAAiB;uCAEvF,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAAC,WAAW,EAAE,SAAS,CAAC,uEAWvG,SAAS,OAAO,EAAE,kGA2CjC,CAAC"}
|
package/dist/batchWrites.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { Effect, Schema } from "effect";
|
|
2
2
|
import { SqlClient } from "effect/sql";
|
|
3
|
+
import { storageSchema } from "#adapters/storageSchema.ts";
|
|
4
|
+
import { sqlNameTransforms } from "#sqlNameTransforms.ts";
|
|
3
5
|
import { makeUpsertDecoder, upsertReturning } from "#upsertResult.ts";
|
|
4
6
|
export const makeInsertMany = Effect.fnUntraced(function* (model, options, operation, prepare, upsert = false) {
|
|
5
7
|
const sql = yield* SqlClient.SqlClient;
|
|
8
|
+
const nameTransforms = yield* sqlNameTransforms;
|
|
6
9
|
const prepareRow = prepare ?? (() => Effect.succeed(sql ``));
|
|
7
|
-
const encode = Schema.encodeEffect(model.insert);
|
|
10
|
+
const encode = Schema.encodeEffect(storageSchema(model.insert, model.fields, nameTransforms));
|
|
8
11
|
const record = Schema.decodeUnknownEffect(Schema.Record(Schema.String, Schema.Unknown));
|
|
9
|
-
const decode = Schema.decodeUnknownEffect(Schema.Array(model));
|
|
10
|
-
const decodeUpsert = makeUpsertDecoder(model);
|
|
12
|
+
const decode = Schema.decodeUnknownEffect(Schema.Array(storageSchema(model, model.fields, nameTransforms)));
|
|
13
|
+
const decodeUpsert = makeUpsertDecoder(model, nameTransforms);
|
|
11
14
|
const returning = options.changes.returning(upsert ? yield* upsertReturning() : sql `*`);
|
|
12
15
|
return (inputs) => Effect.gen(function* () {
|
|
13
16
|
if (inputs.length === 0) {
|
package/dist/batchWrites.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"batchWrites.js","sourceRoot":"","sources":["../src/batchWrites.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAExC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"batchWrites.js","sourceRoot":"","sources":["../src/batchWrites.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAExC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAGvC,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAE3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAUtE,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,EACxD,KAAgB,EAChB,OAAyG,EACzG,SAAiB,EACjB,OAAuH,EACvH,MAAM,GAAG,KAAK;IAEd,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC;IACvC,MAAM,cAAc,GAAG,KAAK,CAAC,CAAC,iBAAiB,CAAC;IAChD,MAAM,UAAU,GAAG,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAA,EAAE,CAAC,CAAC,CAAC;IAC5D,MAAM,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC,aAAa,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC;IAC9F,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,MAAM,CAAC,mBAAmB,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC;IAC5G,MAAM,YAAY,GAAG,iBAAiB,CAAC,KAAK,EAAE,cAAc,CAAC,CAAC;IAC9D,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC,GAAG,CAAA,GAAG,CAAC,CAAC;IACxF,OAAO,CAAC,MAA0B,EAAE,EAAE,CACrC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QACnB,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,EAAE,CAAC;QACX,CAAC;QACD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;QAC7F,MAAM,OAAO,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACxD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACxD,CAAC;QACD,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,GAAG,CAAA,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC;QACpE,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACnF,MAAM,OAAO,GAAc,EAAE,CAAC;QAC9B,MAAM,WAAW,GAAwC,EAAE,CAAC;QAC5D,IAAI,KAAK,GAAe,EAAE,CAAC;QAC3B,IAAI,UAAU,GAAG,CAAC,CAAC;QACnB,IAAI,MAAM,GAAa,GAAG,CAAA,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YACjC,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,GAAG,CAAA,eAAe,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,KAAK,YAAY,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,MAAM,cAAc,SAAS,EAAE,CAAC;YACxI,WAAW,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC;YAC9B,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC7F,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACnB,CAAC;YACD,KAAK,GAAG,EAAE,CAAC;YACX,UAAU,GAAG,CAAC,CAAC;QAChB,CAAC,CAAC,CAAC;QACH,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACxB,MAAM,KAAK,GAAG,GAAG,CAAA,IAAI,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAA,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,CAAA,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC;YAC5H,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;YACvC,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;YACxC,MAAM,UAAU,GAAG,GAAG,CAAA,GAAG,QAAQ,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;YACxD,6EAA6E;YAC7E,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,UAAU,GAAG,IAAI,GAAG,UAAU,GAAG,MAAM,CAAC,EAAE,CAAC;gBACjG,KAAK,CAAC,CAAC,KAAK,CAAC;YACd,CAAC;YACD,MAAM,GAAG,QAAQ,CAAC;YAClB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAClB,UAAU,IAAI,IAAI,CAAC;QACpB,CAAC;QACD,KAAK,CAAC,CAAC,KAAK,CAAC;QACb,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,IAAI,SAAS,EAAE,CAAC,CAAC,CAAC;AACxF,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect";
|
|
2
|
+
import type { Model } from "effect/schema";
|
|
3
|
+
import type { CompositeKey } from "#compositeRepository.ts";
|
|
4
|
+
export declare function compositeRecordKey<TModel extends Model.Any, const TColumns extends CompositeKey<TModel>>(model: TModel, options: {
|
|
5
|
+
readonly tableName: string;
|
|
6
|
+
readonly idColumn: TColumns;
|
|
7
|
+
}, value: Pick<Schema.Struct.Type<TModel["fields"]>, TColumns[number]>): Effect.Effect<string, Schema.SchemaError, TModel["fields"][TColumns[number]]["EncodingServices"]>;
|
|
8
|
+
//# sourceMappingURL=compositeRecordKey.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compositeRecordKey.d.ts","sourceRoot":"","sources":["../src/compositeRecordKey.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAE3C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAE5D,wBAAgB,kBAAkB,CAAC,MAAM,SAAS,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,QAAQ,SAAS,YAAY,CAAC,MAAM,CAAC,EACvG,KAAK,EAAE,MAAM,EACb,OAAO,EAAE;IAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;CAAE,EACpE,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,GACjE,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as PgTypes from "@effect/sql-pg/PgTypes";
|
|
2
|
+
import { Effect, Schema } from "effect";
|
|
3
|
+
import { compositeId, recordKey } from "@shivaedev/changes/keys.ts";
|
|
4
|
+
export function compositeRecordKey(model, options, value) {
|
|
5
|
+
return Effect.fn("Sql.compositeRecordKey")(function* () {
|
|
6
|
+
const fields = Object.fromEntries(Object.entries(model.fields).filter(([column]) => options.idColumn.includes(column)));
|
|
7
|
+
const encoded = yield* Schema.encodeUnknownEffect(Schema.Struct(fields))(value);
|
|
8
|
+
const values = options.idColumn.map((column) => {
|
|
9
|
+
const stored = encoded[column];
|
|
10
|
+
return PgTypes.isParameter(stored) ? stored.value : stored;
|
|
11
|
+
});
|
|
12
|
+
return recordKey(options.tableName, compositeId(values));
|
|
13
|
+
})();
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=compositeRecordKey.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compositeRecordKey.js","sourceRoot":"","sources":["../src/compositeRecordKey.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAExC,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAQpE,MAAM,UAAU,kBAAkB,CACjC,KAAgB,EAChB,OAA6E,EAC7E,KAAwC;IAExC,OAAO,MAAM,CAAC,EAAE,CAAC,wBAAwB,CAAC,CAAC,QAAQ,CAAC;QACnD,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACxH,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,mBAAmB,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QAChF,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;YAC9C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;YAC/B,OAAO,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;QAC5D,CAAC,CAAC,CAAC;QACH,OAAO,SAAS,CAAC,OAAO,CAAC,SAAS,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;IAC1D,CAAC,CAAC,EAAE,CAAC;AACN,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compositeRepository.d.ts","sourceRoot":"","sources":["../src/compositeRepository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAa,MAAM,YAAY,CAAC;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"compositeRepository.d.ts","sourceRoot":"","sources":["../src/compositeRepository.ts"],"names":[],"mappings":"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"}
|