@kairos-es/read-postgres 0.0.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/LICENSE +28 -0
- package/README.md +238 -0
- package/dist/cjs/config.js +86 -0
- package/dist/cjs/config.js.map +1 -0
- package/dist/cjs/ensureCheckpointSchema.js +58 -0
- package/dist/cjs/ensureCheckpointSchema.js.map +1 -0
- package/dist/cjs/index.js +20 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/internal/ddl.js +172 -0
- package/dist/cjs/internal/ddl.js.map +1 -0
- package/dist/cjs/internal/statements.js +83 -0
- package/dist/cjs/internal/statements.js.map +1 -0
- package/dist/cjs/projectionStore.js +154 -0
- package/dist/cjs/projectionStore.js.map +1 -0
- package/dist/dts/config.d.ts +65 -0
- package/dist/dts/config.d.ts.map +1 -0
- package/dist/dts/ensureCheckpointSchema.d.ts +39 -0
- package/dist/dts/ensureCheckpointSchema.d.ts.map +1 -0
- package/dist/dts/index.d.ts +90 -0
- package/dist/dts/index.d.ts.map +1 -0
- package/dist/dts/internal/ddl.d.ts +146 -0
- package/dist/dts/internal/ddl.d.ts.map +1 -0
- package/dist/dts/internal/statements.d.ts +100 -0
- package/dist/dts/internal/statements.d.ts.map +1 -0
- package/dist/dts/projectionStore.d.ts +30 -0
- package/dist/dts/projectionStore.d.ts.map +1 -0
- package/dist/esm/config.js +79 -0
- package/dist/esm/config.js.map +1 -0
- package/dist/esm/ensureCheckpointSchema.js +50 -0
- package/dist/esm/ensureCheckpointSchema.js.map +1 -0
- package/dist/esm/index.js +3 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/internal/ddl.js +161 -0
- package/dist/esm/internal/ddl.js.map +1 -0
- package/dist/esm/internal/statements.js +74 -0
- package/dist/esm/internal/statements.js.map +1 -0
- package/dist/esm/package.json +4 -0
- package/dist/esm/projectionStore.js +147 -0
- package/dist/esm/projectionStore.js.map +1 -0
- package/package.json +33 -0
- package/src/config.ts +83 -0
- package/src/ensureCheckpointSchema.ts +51 -0
- package/src/index.ts +89 -0
- package/src/internal/ddl.ts +246 -0
- package/src/internal/statements.ts +144 -0
- package/src/projectionStore.ts +300 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Neverbland
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# @kairos-es/read-postgres
|
|
2
|
+
|
|
3
|
+
The Postgres `ProjectionStore` for kairos-es: a durable checkpoint table plus the
|
|
4
|
+
guarded compare-and-set that commits a read model's view writes and its checkpoint
|
|
5
|
+
advance in one transaction, and an idempotent `ensureCheckpointSchema` for the
|
|
6
|
+
consumer's own migrator (ADR-0007).
|
|
7
|
+
|
|
8
|
+
Peers `@effect/sql` and needs only the generic `SqlClient` — no `@effect/sql-pg`
|
|
9
|
+
and no reserved connection, because the event store owns the LISTEN.
|
|
10
|
+
|
|
11
|
+
A store is defined entirely by its `{ schema, tablePrefix }` pair — the same pair
|
|
12
|
+
the event-store backend is configured with — so the checkpoint table cohabits per
|
|
13
|
+
prefix and [ADR-0002](../../docs/adr)'s "each prefix is a complete, isolated
|
|
14
|
+
store" keeps holding across the read side.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
`@kairos-es/core`, `@kairos-es/read`, `effect` and `@effect/sql` are peer
|
|
19
|
+
dependencies (so the app owns the single shared Effect + `SqlClient` instance,
|
|
20
|
+
per ADR-0003):
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
pnpm add @kairos-es/read-postgres@alpha @kairos-es/read@alpha @kairos-es/codec@alpha \
|
|
24
|
+
@kairos-es/core@alpha @effect/sql @effect/platform @effect/experimental effect
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Three of those are on the line because something ELSE on it requires them, not
|
|
28
|
+
because this package names them. `@kairos-es/codec` is a peer of
|
|
29
|
+
`@kairos-es/read`, which imports from it; `@effect/platform` and
|
|
30
|
+
`@effect/experimental` are peers of `@effect/sql`. Neither requirer marks any of
|
|
31
|
+
them optional — an optional peer is one the REQUIRER lists in its own
|
|
32
|
+
`peerDependenciesMeta`, and neither declares that field at all — so all three
|
|
33
|
+
are required. This package re-declares none of them, so the unmet-peer warning
|
|
34
|
+
you get without them names the requirer rather than anything here.
|
|
35
|
+
|
|
36
|
+
A `SqlClient` **implementation** is deliberately not peered: this package needs
|
|
37
|
+
only the generic `SqlClient` tag, so the driver (`@effect/sql-pg`, or whatever
|
|
38
|
+
already provides that tag in your app) stays your choice.
|
|
39
|
+
|
|
40
|
+
## Schema: `ensureCheckpointSchema` as a migration
|
|
41
|
+
|
|
42
|
+
The package ships **no migrator**. `ensureCheckpointSchema(config)` is a single
|
|
43
|
+
idempotent `Effect<void, SqlError, SqlClient>` that creates (or refreshes) the one
|
|
44
|
+
object it owns: the checkpoint table. Every statement is `CREATE … IF NOT EXISTS`,
|
|
45
|
+
so re-running it is a no-op. Migration **orchestration** is yours; the package
|
|
46
|
+
only provides the idempotent **payload**.
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import { Migrator } from '@effect/sql'
|
|
50
|
+
import { ensureSchema } from '@kairos-es/store-postgres'
|
|
51
|
+
import { ensureCheckpointSchema } from '@kairos-es/read-postgres'
|
|
52
|
+
|
|
53
|
+
const config = { schema: 'public', tablePrefix: 'dcb_events' }
|
|
54
|
+
|
|
55
|
+
const migrations = Migrator.fromRecord({
|
|
56
|
+
'0001_kairos_store_dcb_events': ensureSchema(config),
|
|
57
|
+
'0002_kairos_checkpoints': ensureCheckpointSchema(config),
|
|
58
|
+
})
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Because it requires the **generic** `SqlClient` tag, it slots into any migrator or
|
|
62
|
+
boot sequence that already provides one. The table it creates:
|
|
63
|
+
|
|
64
|
+
```sql
|
|
65
|
+
CREATE TABLE IF NOT EXISTS <schema>.<prefix>_checkpoints (
|
|
66
|
+
"projection" text NOT NULL,
|
|
67
|
+
"partition" text NOT NULL,
|
|
68
|
+
"last_position" bigint NOT NULL,
|
|
69
|
+
PRIMARY KEY ("projection", "partition")
|
|
70
|
+
);
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The primary key is load-bearing twice over: it makes a checkpoint read an index
|
|
74
|
+
lookup, and it is the arbiter index the first commit's `ON CONFLICT` names.
|
|
75
|
+
|
|
76
|
+
## Wiring a read model
|
|
77
|
+
|
|
78
|
+
`makeSqlProjectionStore(config?)` is an `Effect`, not a `Layer` — the view store
|
|
79
|
+
is selected **per materialisation**, so it is a value handed to a read model rather
|
|
80
|
+
than a context tag one store could claim process-wide. Per materialisation and not
|
|
81
|
+
merely per read model: one read model may be materialised several times at once, a
|
|
82
|
+
durable store from here beside an in-memory one, which is a shape a process-wide
|
|
83
|
+
tag could not express at all. It closes over the `SqlClient` it acquires, so the
|
|
84
|
+
returned store's `commit` adds no `SqlClient` to the read model's own requirements.
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import {
|
|
88
|
+
checkpointKey,
|
|
89
|
+
DurableEventLog,
|
|
90
|
+
forKey,
|
|
91
|
+
ProjectionId,
|
|
92
|
+
projectionLayer,
|
|
93
|
+
} from '@kairos-es/read'
|
|
94
|
+
import { makeSqlProjectionStore } from '@kairos-es/read-postgres'
|
|
95
|
+
import { Effect, Layer } from 'effect'
|
|
96
|
+
|
|
97
|
+
const config = { schema: 'public', tablePrefix: 'dcb_events' }
|
|
98
|
+
|
|
99
|
+
const key = checkpointKey(ProjectionId.make('course-subscriptions'))
|
|
100
|
+
|
|
101
|
+
const courseView = Layer.unwrapEffect(
|
|
102
|
+
Effect.gen(function* () {
|
|
103
|
+
const store = yield* makeSqlProjectionStore(config)
|
|
104
|
+
return projectionLayer({
|
|
105
|
+
// The store is multi-key — one `SqlClient` serves every read model — so it
|
|
106
|
+
// is bound to this read model's checkpoint once, here. From then on `commit`
|
|
107
|
+
// takes no key at all.
|
|
108
|
+
store: forKey(store, key),
|
|
109
|
+
slices,
|
|
110
|
+
apply,
|
|
111
|
+
})
|
|
112
|
+
}),
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
// What the LOG is worth is declared ONCE, beside the layer that chose it — not on
|
|
116
|
+
// each read model, because it is a fact about the log and N read models over one
|
|
117
|
+
// log could otherwise hold N copies that disagree. `DcbEventStore` exposes no
|
|
118
|
+
// durability, so the application is the only honest source, and this is R2's other
|
|
119
|
+
// input: a durable view store (which this package's is) needs a durable log.
|
|
120
|
+
const main = Layer.provide(
|
|
121
|
+
Layer.mergeAll(courseView, studentView),
|
|
122
|
+
Layer.mergeAll(eventStoreLayer, DurableEventLog, SerializerDefault),
|
|
123
|
+
)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### A worked SQL `apply`
|
|
127
|
+
|
|
128
|
+
`slices` and `apply` above are the consumer's. The one the library cannot write
|
|
129
|
+
for you is `apply`, so there is a worked one — and it lives with the read model it
|
|
130
|
+
materialises rather than here, because that is where a consumer reads worked code:
|
|
131
|
+
[`examples/course-subscriptions/src/courseRosterSql.ts`](../../examples/course-subscriptions/src/courseRosterSql.ts)
|
|
132
|
+
materialises that example's course roster into two real tables — its own
|
|
133
|
+
idempotent DDL, its identifier quoting, a typed dispatch onto the domain's event
|
|
134
|
+
defs, and ADR-0007's apply-throughput escape hatch in its hybrid shape — one fold
|
|
135
|
+
over the batch, so the capacity row's whole batch coalesces into one upsert while
|
|
136
|
+
each seat is its own row and its own insert, then those writes issued in order
|
|
137
|
+
inside the one `commit`, sequentially because one transaction pins one connection
|
|
138
|
+
and parallel writes are not on offer there. It needs nothing from this package: a
|
|
139
|
+
read model owns its own view schema, and the only object this package owns is the
|
|
140
|
+
checkpoint table.
|
|
141
|
+
|
|
142
|
+
It imports the example's slices, `CheckpointKey` derivation and derived figures
|
|
143
|
+
**verbatim**, which is what makes it a second **materialisation of one read model**
|
|
144
|
+
rather than a second read model — the pattern `@kairos-es/read`'s `runProjections`
|
|
145
|
+
names and wires, holding one `slices` value and one `ProjectionId` across N
|
|
146
|
+
materialisations, each with its own store, `apply`, checkpoint and runner. The
|
|
147
|
+
example runs the durable materialisation beside an in-memory one under a single
|
|
148
|
+
such call in
|
|
149
|
+
[`src/courseRosterMaterialisations.ts`](../../examples/course-subscriptions/src/courseRosterMaterialisations.ts).
|
|
150
|
+
What lives in this package is the COMPARISON that needs a container:
|
|
151
|
+
[`test/courseRosterGraduation.test.ts`](./test/courseRosterGraduation.test.ts)
|
|
152
|
+
runs the same slice record into a `Ref` and into Postgres side by side over one
|
|
153
|
+
durable log and asserts the two composed queries, the two checkpoint keys and the
|
|
154
|
+
two rosters agree. The example's own base roster stays database-free, which is what
|
|
155
|
+
keeps it runnable with no container anywhere.
|
|
156
|
+
|
|
157
|
+
The one thing to copy carefully: **the store and your `apply` must share one
|
|
158
|
+
`SqlClient`**. See "The atomic bracket" below.
|
|
159
|
+
|
|
160
|
+
`EventLogDurability` is a **required** input of every projection layer, with no
|
|
161
|
+
default, and one declaration satisfies all of them. Declaring `EphemeralEventLog`
|
|
162
|
+
over this store is what R2 rejects at construction: the checkpoint table would
|
|
163
|
+
survive a restart the log did not, and the view would sit frozen and permanently
|
|
164
|
+
stale without ever erroring.
|
|
165
|
+
|
|
166
|
+
The store performs **no DDL**: `ensureCheckpointSchema` must have run first, just
|
|
167
|
+
as the event store's `layer` assumes `ensureSchema` has.
|
|
168
|
+
|
|
169
|
+
## The atomic bracket
|
|
170
|
+
|
|
171
|
+
`commit` is `sql.withTransaction(viewWrites *> guardedAdvance)`. Your own `sql`
|
|
172
|
+
statements join that transaction because `@effect/sql` carries the transaction's
|
|
173
|
+
connection in the Effect **context**, not in the client value; `withTransaction`
|
|
174
|
+
is savepoint-aware, so a projection already inside a transaction composes without
|
|
175
|
+
a second physical one. A write through an unrelated client (or to another
|
|
176
|
+
database) is **not** bracketed.
|
|
177
|
+
|
|
178
|
+
The guarded advance touches zero rows when the stored position no longer equals
|
|
179
|
+
`expected`, which fails `CheckpointSuperseded` — and that failure aborts the
|
|
180
|
+
transaction, rolling the view writes back with it. That abort is the exactly-once
|
|
181
|
+
mechanism, so the shared `@kairos-es/projection-store-contract-tests` suite proves
|
|
182
|
+
it against a known-broken store rather than assuming it.
|
|
183
|
+
|
|
184
|
+
The advance has **two** SQL shapes, and neither can serve both cases:
|
|
185
|
+
|
|
186
|
+
- from `ORIGIN`, an `INSERT … ON CONFLICT (projection, partition) DO UPDATE …
|
|
187
|
+
WHERE last_position = 0`, because a never-committed key has no row at all and a
|
|
188
|
+
plain `UPDATE` would report a lost guard forever;
|
|
189
|
+
- from a non-origin position, a plain `UPDATE … WHERE last_position = expected`,
|
|
190
|
+
because an upsert would **insert** an absent row — silently undoing a
|
|
191
|
+
`resetCheckpoint` and skipping the whole backlog it existed to replay.
|
|
192
|
+
|
|
193
|
+
Both `RETURNING` a column, so a lost guard is a **row count**, never a caught SQL
|
|
194
|
+
error — the same doctrine as the event store's zero-row append conflict.
|
|
195
|
+
|
|
196
|
+
## Infrastructure faults are channel values here
|
|
197
|
+
|
|
198
|
+
A `SqlError` surfaces as `ProjectionStoreError` on the **error channel**, the
|
|
199
|
+
opposite of the event store, where an infrastructure fault is a defect
|
|
200
|
+
(`Effect.die`, ADR-0002). The read side has a supervisor above it whose job is to
|
|
201
|
+
tell "retry the subscription with backoff" from "give up and report a stalled
|
|
202
|
+
projection", and a defect carries no such distinction.
|
|
203
|
+
|
|
204
|
+
## Positions are strictly-increasing, not gapless
|
|
205
|
+
|
|
206
|
+
`last_position` is a `bigint` because a `Position` is 64-bit and exceeds JS
|
|
207
|
+
`number` safety at 2^53; it is read back as a decimal string and reconstructed
|
|
208
|
+
with `BigInt(…)`, never coerced through `number`. The stored position is the last
|
|
209
|
+
processed event's position **as is** — never `expected + 1` — so a gap is normal
|
|
210
|
+
and arithmetic on a position would be wrong.
|
|
211
|
+
|
|
212
|
+
A key that has never been committed has **no row** and reads back as `ORIGIN`.
|
|
213
|
+
`resetCheckpoint` deletes the row rather than zeroing it, so "never committed" and
|
|
214
|
+
"reset" stay one state.
|
|
215
|
+
|
|
216
|
+
## Configuration
|
|
217
|
+
|
|
218
|
+
```ts
|
|
219
|
+
interface ReadPostgresConfig {
|
|
220
|
+
readonly schema?: string // default 'public'
|
|
221
|
+
readonly tablePrefix?: string // default 'dcb_events'
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Validated at construction, so a malformed config fails loudly: an empty or dotted
|
|
226
|
+
atom is rejected (a dot would re-partition the qualified name into the wrong
|
|
227
|
+
schema), as is any pair whose derived table name would exceed Postgres's 63-byte
|
|
228
|
+
identifier limit and be silently truncated — two prefixes sharing one checkpoint
|
|
229
|
+
table is the failure that guard prevents.
|
|
230
|
+
|
|
231
|
+
The config is deliberately **duplicated** from `@kairos-es/store-postgres` rather
|
|
232
|
+
than imported: peering an event-store backend would couple the read side to one
|
|
233
|
+
specific log implementation, and a durable view must be free to sit over any store
|
|
234
|
+
satisfying `DcbEventStore`.
|
|
235
|
+
|
|
236
|
+
## Licence
|
|
237
|
+
|
|
238
|
+
BSD 3-Clause. See [`LICENSE`](./LICENSE). Copyright (c) 2026 Neverbland.
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.decodeReadPostgresConfig = exports.ReadPostgresConfig = void 0;
|
|
7
|
+
var _effect = require("effect");
|
|
8
|
+
var _ddl = require("./internal/ddl.js");
|
|
9
|
+
/**
|
|
10
|
+
* Public configuration for the Postgres `ProjectionStore`.
|
|
11
|
+
*
|
|
12
|
+
* A read-side store is defined entirely by its `{ schema, tablePrefix }` naming
|
|
13
|
+
* pair: the checkpoint table name is DERIVED from it (see `internal/ddl.ts`), so
|
|
14
|
+
* one config value is one completely isolated checkpoint namespace, cohabiting
|
|
15
|
+
* per prefix exactly as the event store's objects do (ADR-0002).
|
|
16
|
+
*
|
|
17
|
+
* It is a `Schema`, not a bare interface, so malformed input fails LOUDLY at
|
|
18
|
+
* construction (`makeSqlProjectionStore`/`ensureCheckpointSchema` decode through
|
|
19
|
+
* it) rather than late and obscurely on the first commit — the same
|
|
20
|
+
* fail-at-the-boundary discipline core applies to `Tag`/`ReadLimit`. It rejects an
|
|
21
|
+
* empty or dotted `schema`/`tablePrefix` atom (a dot would break the
|
|
22
|
+
* qualified-name dot-split) and — via `oversizedIdentifier` — any pair whose
|
|
23
|
+
* derived table name would exceed Postgres's 63-byte identifier limit and be
|
|
24
|
+
* silently truncated.
|
|
25
|
+
*
|
|
26
|
+
* **WHY this config is DUPLICATED rather than imported from
|
|
27
|
+
* `@kairos-es/store-postgres`.** Importing it would mean peering that package,
|
|
28
|
+
* and peering an event-store BACKEND from the read side is precisely what the
|
|
29
|
+
* per-materialisation view-store design exists to avoid: a durable view must be
|
|
30
|
+
* free to sit over any log satisfying `DcbEventStore` — the in-memory oracle,
|
|
31
|
+
* Postgres, a future backend — so this package depends on the PORT
|
|
32
|
+
* (`@kairos-es/read`) and on `@effect/sql`, never on a sibling backend. The
|
|
33
|
+
* duplication also keeps the two guards honestly independent: each package
|
|
34
|
+
* measures only the identifiers IT materialises, so neither inherits a byte budget
|
|
35
|
+
* from names it never creates.
|
|
36
|
+
*
|
|
37
|
+
* The shape is intentionally the event store's MINUS everything read-side wiring
|
|
38
|
+
* has no business choosing: no `lockTimeout` (the checkpoint's guarded
|
|
39
|
+
* compare-and-set takes only ordinary row locks, and a blocked writer is a
|
|
40
|
+
* competing runner whose loss is a `CheckpointSuperseded` the supervisor already
|
|
41
|
+
* handles), no `pollInterval`/`catchUpPageSize` (those belong to `subscribe`, on
|
|
42
|
+
* the log side). What remains is naming, and only naming.
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* A single Postgres identifier atom: non-empty and dot-free. Dots are barred
|
|
47
|
+
* because `schema`/`tablePrefix` are composed into a qualified name that is
|
|
48
|
+
* dot-split when quoted, so an embedded dot would silently re-partition the name
|
|
49
|
+
* into the wrong schema/object.
|
|
50
|
+
*/
|
|
51
|
+
const IdentifierAtom = /*#__PURE__*/_effect.Schema.String.pipe(/*#__PURE__*/_effect.Schema.nonEmptyString(), /*#__PURE__*/_effect.Schema.pattern(/^[^.]+$/, {
|
|
52
|
+
identifier: 'PostgresIdentifierAtom',
|
|
53
|
+
description: 'a non-empty identifier atom containing no "."'
|
|
54
|
+
}));
|
|
55
|
+
/**
|
|
56
|
+
* Configuration for a read-side checkpoint store.
|
|
57
|
+
*
|
|
58
|
+
* - `schema` — the Postgres schema that owns the checkpoint table (default
|
|
59
|
+
* `public`). `ensureCheckpointSchema` creates it defensively.
|
|
60
|
+
* - `tablePrefix` — the base name the checkpoint table derives from (default
|
|
61
|
+
* `dcb_events`, giving `dcb_events_checkpoints`). Pass the SAME pair the event
|
|
62
|
+
* store is configured with and the checkpoint table lands beside that store's
|
|
63
|
+
* objects, one isolated store per prefix.
|
|
64
|
+
*/
|
|
65
|
+
const ReadPostgresConfig = exports.ReadPostgresConfig = /*#__PURE__*/_effect.Schema.Struct({
|
|
66
|
+
// `exact: true` keeps the decoded type at `key?: T` (never `T | undefined`),
|
|
67
|
+
// matching the repo's `exactOptionalPropertyTypes` discipline — an absent key,
|
|
68
|
+
// not an explicit `undefined`.
|
|
69
|
+
schema: _effect.Schema.optionalWith(IdentifierAtom, {
|
|
70
|
+
exact: true
|
|
71
|
+
}),
|
|
72
|
+
tablePrefix: _effect.Schema.optionalWith(IdentifierAtom, {
|
|
73
|
+
exact: true
|
|
74
|
+
})
|
|
75
|
+
}).pipe(/*#__PURE__*/
|
|
76
|
+
// Cross-field guard: the derived table name must fit Postgres's 63-byte
|
|
77
|
+
// identifier limit. Returning the message string fails the decode with it;
|
|
78
|
+
// `undefined`/`true` passes.
|
|
79
|
+
_effect.Schema.filter(config => (0, _ddl.oversizedIdentifier)(config) ?? true));
|
|
80
|
+
/**
|
|
81
|
+
* Decode an unknown value into a validated `ReadPostgresConfig`, throwing a
|
|
82
|
+
* `ParseError` on any violation. Used by `makeSqlProjectionStore` and
|
|
83
|
+
* `ensureCheckpointSchema` so a bad config fails at construction time.
|
|
84
|
+
*/
|
|
85
|
+
const decodeReadPostgresConfig = exports.decodeReadPostgresConfig = /*#__PURE__*/_effect.Schema.decodeUnknownSync(ReadPostgresConfig);
|
|
86
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","names":["_effect","require","_ddl","IdentifierAtom","Schema","String","pipe","nonEmptyString","pattern","identifier","description","ReadPostgresConfig","exports","Struct","schema","optionalWith","exact","tablePrefix","filter","config","oversizedIdentifier","decodeReadPostgresConfig","decodeUnknownSync"],"sources":["../../src/config.ts"],"sourcesContent":[null],"mappings":";;;;;;AAmCA,IAAAA,OAAA,GAAAC,OAAA;AACA,IAAAC,IAAA,GAAAD,OAAA;AApCA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA;;;;;;AAMA,MAAME,cAAc,gBAAGC,cAAM,CAACC,MAAM,CAACC,IAAI,cACvCF,cAAM,CAACG,cAAc,EAAE,eACvBH,cAAM,CAACI,OAAO,CAAC,SAAS,EAAE;EACxBC,UAAU,EAAE,wBAAwB;EACpCC,WAAW,EAAE;CACd,CAAC,CACH;AAED;;;;;;;;;;AAUO,MAAMC,kBAAkB,GAAAC,OAAA,CAAAD,kBAAA,gBAAGP,cAAM,CAACS,MAAM,CAAC;EAC9C;EACA;EACA;EACAC,MAAM,EAAEV,cAAM,CAACW,YAAY,CAACZ,cAAc,EAAE;IAAEa,KAAK,EAAE;EAAI,CAAE,CAAC;EAC5DC,WAAW,EAAEb,cAAM,CAACW,YAAY,CAACZ,cAAc,EAAE;IAAEa,KAAK,EAAE;EAAI,CAAE;CACjE,CAAC,CAACV,IAAI;AACL;AACA;AACA;AACAF,cAAM,CAACc,MAAM,CAAEC,MAAM,IAAK,IAAAC,wBAAmB,EAACD,MAAM,CAAC,IAAI,IAAI,CAAC,CAC/D;AAGD;;;;;AAKO,MAAME,wBAAwB,GAAAT,OAAA,CAAAS,wBAAA,gBACnCjB,cAAM,CAACkB,iBAAiB,CAACX,kBAAkB,CAAC","ignoreList":[]}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.ensureCheckpointSchema = void 0;
|
|
7
|
+
var _sql = require("@effect/sql");
|
|
8
|
+
var _effect = require("effect");
|
|
9
|
+
var _config = require("./config.js");
|
|
10
|
+
var _ddl = require("./internal/ddl.js");
|
|
11
|
+
/**
|
|
12
|
+
* `ensureCheckpointSchema` — the idempotent checkpoint DDL, as a building block
|
|
13
|
+
* rather than a migrator (ADR-0007, mirroring the event store's `ensureSchema`).
|
|
14
|
+
*
|
|
15
|
+
* It is a single `Effect<void, SqlError, SqlClient>` that creates (or refreshes)
|
|
16
|
+
* the one object this package owns: the checkpoint table. It requires the GENERIC
|
|
17
|
+
* `SqlClient` tag — not a package-owned client tag — precisely so a consumer can
|
|
18
|
+
* drop it straight into THEIR OWN migrator, e.g.:
|
|
19
|
+
*
|
|
20
|
+
* ```ts
|
|
21
|
+
* Migrator.fromRecord({
|
|
22
|
+
* '0002_kairos_checkpoints': ensureCheckpointSchema(config),
|
|
23
|
+
* })
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* or simply run it once at boot, beside `ensureSchema` from the event-store
|
|
27
|
+
* backend. We ship no `FileSystem`/`Path`/`ChildProcess` dependency and no
|
|
28
|
+
* migrator: migration ORCHESTRATION belongs to the consumer, this package only
|
|
29
|
+
* provides the idempotent PAYLOAD. That is also why schema ownership never
|
|
30
|
+
* silently moves — a read model cannot conjure its own table at runtime.
|
|
31
|
+
*
|
|
32
|
+
* Idempotence: every statement is `CREATE … IF NOT EXISTS`, so re-running against
|
|
33
|
+
* an existing checkpoint table is a no-op. Names come pre-quoted from
|
|
34
|
+
* `internal/ddl.ts`; the DDL carries no bound values (pure schema).
|
|
35
|
+
*
|
|
36
|
+
* Unlike the runtime store, this path needs no `withoutTransforms()` guard: it
|
|
37
|
+
* reads no rows and passes no identifier through `sql(identifier)`, so a
|
|
38
|
+
* consumer's name transforms have nothing here to rewrite.
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Produce the checkpoint migration payload for `config`. Resolves the table name
|
|
43
|
+
* once and runs the ordered idempotent DDL against whatever `SqlClient` the
|
|
44
|
+
* caller's migrator (or boot code) provides.
|
|
45
|
+
*/
|
|
46
|
+
const ensureCheckpointSchema = config => _effect.Effect.gen(function* () {
|
|
47
|
+
const sql = yield* _sql.SqlClient.SqlClient;
|
|
48
|
+
// Decode through the config schema (the same guard `makeSqlProjectionStore`
|
|
49
|
+
// applies) so a malformed config fails at construction rather than
|
|
50
|
+
// mid-migration.
|
|
51
|
+
const names = (0, _ddl.resolveCheckpointNames)((0, _config.decodeReadPostgresConfig)(config));
|
|
52
|
+
// Sequentially, in dependency order (schema → table).
|
|
53
|
+
yield* _effect.Effect.all((0, _ddl.runCheckpointDdl)(sql, names), {
|
|
54
|
+
discard: true
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
exports.ensureCheckpointSchema = ensureCheckpointSchema;
|
|
58
|
+
//# sourceMappingURL=ensureCheckpointSchema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ensureCheckpointSchema.js","names":["_sql","require","_effect","_config","_ddl","ensureCheckpointSchema","config","Effect","gen","sql","SqlClient","names","resolveCheckpointNames","decodeReadPostgresConfig","all","runCheckpointDdl","discard","exports"],"sources":["../../src/ensureCheckpointSchema.ts"],"sourcesContent":[null],"mappings":";;;;;;AA6BA,IAAAA,IAAA,GAAAC,OAAA;AACA,IAAAC,OAAA,GAAAD,OAAA;AACA,IAAAE,OAAA,GAAAF,OAAA;AACA,IAAAG,IAAA,GAAAH,OAAA;AAhCA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA;;;;;AAKO,MAAMI,sBAAsB,GACjCC,MAA0B,IAE1BC,cAAM,CAACC,GAAG,CAAC,aAAS;EAClB,MAAMC,GAAG,GAAG,OAAOC,cAAS,CAACA,SAAS;EACtC;EACA;EACA;EACA,MAAMC,KAAK,GAAG,IAAAC,2BAAsB,EAAC,IAAAC,gCAAwB,EAACP,MAAM,CAAC,CAAC;EACtE;EACA,OAAOC,cAAM,CAACO,GAAG,CAAC,IAAAC,qBAAgB,EAACN,GAAG,EAAEE,KAAK,CAAC,EAAE;IAAEK,OAAO,EAAE;EAAI,CAAE,CAAC;AACpE,CAAC,CAAC;AAAAC,OAAA,CAAAZ,sBAAA,GAAAA,sBAAA","ignoreList":[]}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
Object.defineProperty(exports, "ensureCheckpointSchema", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function () {
|
|
9
|
+
return _ensureCheckpointSchema.ensureCheckpointSchema;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
Object.defineProperty(exports, "makeSqlProjectionStore", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
get: function () {
|
|
15
|
+
return _projectionStore.makeSqlProjectionStore;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
var _ensureCheckpointSchema = require("./ensureCheckpointSchema.js");
|
|
19
|
+
var _projectionStore = require("./projectionStore.js");
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["_ensureCheckpointSchema","require","_projectionStore"],"sources":["../../src/index.ts"],"sourcesContent":[null],"mappings":";;;;;;;;;;;;;;;;;AAuFA,IAAAA,uBAAA,GAAAC,OAAA;AACA,IAAAC,gBAAA,GAAAD,OAAA","ignoreList":[]}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.runCheckpointDdl = exports.resolveCheckpointNames = exports.quoteQualified = exports.oversizedIdentifier = exports.checkpointDdlStatements = exports.DEFAULT_TABLE_PREFIX = exports.DEFAULT_SCHEMA = exports.CHECKPOINT_COLUMNS = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* Default schema when the config omits one. Identical to the event store's
|
|
9
|
+
* default, on purpose: the common wiring is one `{ schema, tablePrefix }` pair
|
|
10
|
+
* used for both sides, so both defaults must agree or a caller omitting the
|
|
11
|
+
* config would silently split the log and its checkpoints across two schemas.
|
|
12
|
+
*/
|
|
13
|
+
const DEFAULT_SCHEMA = exports.DEFAULT_SCHEMA = 'public';
|
|
14
|
+
/** Default table prefix when the config omits one — again the event store's. */
|
|
15
|
+
const DEFAULT_TABLE_PREFIX = exports.DEFAULT_TABLE_PREFIX = 'dcb_events';
|
|
16
|
+
/** The suffix appended to the prefix to name the checkpoint table. */
|
|
17
|
+
const CHECKPOINT_SUFFIX = '_checkpoints';
|
|
18
|
+
/**
|
|
19
|
+
* Postgres truncates any identifier to 63 bytes (`NAMEDATALEN - 1`) with only a
|
|
20
|
+
* notice. Two configs whose derived checkpoint-table names share a 63-byte
|
|
21
|
+
* prefix would truncate onto ONE table, and `CREATE TABLE IF NOT EXISTS` would
|
|
22
|
+
* then see a name-match and SKIP creating the second: two supposedly isolated
|
|
23
|
+
* stores would silently share one checkpoint namespace, so a rebuild
|
|
24
|
+
* (`resetCheckpoint`) of one read model could delete the other's cursor. Loud
|
|
25
|
+
* rejection at the boundary beats that.
|
|
26
|
+
*/
|
|
27
|
+
const MAX_IDENTIFIER_BYTES = 63;
|
|
28
|
+
/** UTF-8 byte length (identifiers are bytes, not chars, to Postgres). */
|
|
29
|
+
const utf8ByteLength = value => new TextEncoder().encode(value).length;
|
|
30
|
+
/**
|
|
31
|
+
* The single-atom identifiers Postgres actually materialises for a config. A
|
|
32
|
+
* `schema.table` qualified name is TWO atoms to Postgres, so each is measured
|
|
33
|
+
* separately rather than the dotted whole.
|
|
34
|
+
*
|
|
35
|
+
* The primary key's implicit index (`<table>_pkey`) is deliberately not measured:
|
|
36
|
+
* Postgres derives and de-duplicates that name itself (appending a digit when it
|
|
37
|
+
* is taken), so it cannot collide silently the way an explicitly-named
|
|
38
|
+
* `CREATE INDEX IF NOT EXISTS` can.
|
|
39
|
+
*/
|
|
40
|
+
const derivedAtoms = config => {
|
|
41
|
+
const schema = config.schema ?? DEFAULT_SCHEMA;
|
|
42
|
+
const tablePrefix = config.tablePrefix ?? DEFAULT_TABLE_PREFIX;
|
|
43
|
+
return [['schema', schema], ['checkpoint table', `${tablePrefix}${CHECKPOINT_SUFFIX}`]];
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* The first derived identifier that exceeds 63 bytes, as a human-readable
|
|
47
|
+
* message, or `undefined` when every name fits. Shared by
|
|
48
|
+
* `resolveCheckpointNames` (which throws) and the `ReadPostgresConfig` schema
|
|
49
|
+
* filter (which fails validation), so the byte bound is enforced identically at
|
|
50
|
+
* both the name-derivation and the config-decode boundary.
|
|
51
|
+
*
|
|
52
|
+
* The bound is checked INDEPENDENTLY of the event store's: this package measures
|
|
53
|
+
* only the names IT materialises, so a prefix accepted there may be rejected here
|
|
54
|
+
* (and vice versa). Each package guards its own names, which is what lets the
|
|
55
|
+
* read side stay uncoupled from any particular event-store backend.
|
|
56
|
+
*/
|
|
57
|
+
const oversizedIdentifier = config => {
|
|
58
|
+
for (const [label, atom] of derivedAtoms(config)) {
|
|
59
|
+
const bytes = utf8ByteLength(atom);
|
|
60
|
+
if (bytes > MAX_IDENTIFIER_BYTES) {
|
|
61
|
+
return `the ${label} identifier "${atom}" is ${bytes} bytes, over Postgres's ${MAX_IDENTIFIER_BYTES}-byte limit; it would be silently truncated, letting two prefixes share one checkpoint table`;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return undefined;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Derive the checkpoint table name from `{ schema, tablePrefix }`. The table
|
|
68
|
+
* carries its schema explicitly so `quoteQualified` dot-splits it into the two
|
|
69
|
+
* atoms Postgres expects.
|
|
70
|
+
*
|
|
71
|
+
* Throws at construction if a derived identifier would be silently truncated (see
|
|
72
|
+
* `oversizedIdentifier`) — a loud failure at the single choke point beats an
|
|
73
|
+
* invisible cross-prefix checkpoint collision later.
|
|
74
|
+
*/
|
|
75
|
+
exports.oversizedIdentifier = oversizedIdentifier;
|
|
76
|
+
const resolveCheckpointNames = config => {
|
|
77
|
+
const problem = oversizedIdentifier(config);
|
|
78
|
+
if (problem !== undefined) {
|
|
79
|
+
throw new Error(`resolveCheckpointNames: ${problem}`);
|
|
80
|
+
}
|
|
81
|
+
const schema = config.schema ?? DEFAULT_SCHEMA;
|
|
82
|
+
const tablePrefix = config.tablePrefix ?? DEFAULT_TABLE_PREFIX;
|
|
83
|
+
return {
|
|
84
|
+
schema,
|
|
85
|
+
tablePrefix,
|
|
86
|
+
checkpointTable: `${schema}.${tablePrefix}${CHECKPOINT_SUFFIX}`
|
|
87
|
+
};
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Quote a possibly-qualified identifier for embedding as literal SQL text.
|
|
91
|
+
* Splits on `.` and double-quotes each atom, mirroring what `sql(identifier)`
|
|
92
|
+
* emits, so a pre-quoted name and an `sql(identifier)` reference resolve to
|
|
93
|
+
* exactly the same object. Any embedded `"` is doubled per SQL
|
|
94
|
+
* identifier-quoting rules. (The dot-split is why `ReadPostgresConfig` bars a dot
|
|
95
|
+
* inside either atom: an embedded dot would silently re-partition the name into
|
|
96
|
+
* the wrong schema.)
|
|
97
|
+
*/
|
|
98
|
+
exports.resolveCheckpointNames = resolveCheckpointNames;
|
|
99
|
+
const quoteQualified = name => name.split('.').map(atom => `"${atom.replace(/"/g, '""')}"`).join('.');
|
|
100
|
+
/**
|
|
101
|
+
* A quoted single-atom identifier (no dot-splitting) — used where a bare name is
|
|
102
|
+
* required, e.g. the schema in `CREATE SCHEMA` and the column names below.
|
|
103
|
+
*/
|
|
104
|
+
exports.quoteQualified = quoteQualified;
|
|
105
|
+
const quoteAtom = name => `"${name.replace(/"/g, '""')}"`;
|
|
106
|
+
/**
|
|
107
|
+
* The checkpoint table's columns, PRE-QUOTED, as one shared descriptor.
|
|
108
|
+
*
|
|
109
|
+
* Shared by the DDL here and by every runtime statement in `projectionStore.ts`,
|
|
110
|
+
* so a column rename cannot half-land: the table it creates and the statements
|
|
111
|
+
* that address it read the same constant. Pre-quoted because `partition` is
|
|
112
|
+
* Postgres's own partitioning vocabulary — it parses unquoted today, but quoting
|
|
113
|
+
* removes the question, and quoting all three keeps the emitted SQL uniform.
|
|
114
|
+
*
|
|
115
|
+
* These names are FIXED and owned by this package (never config-derived), which
|
|
116
|
+
* is why they are plain constants rather than something the caller can influence.
|
|
117
|
+
*/
|
|
118
|
+
const CHECKPOINT_COLUMNS = exports.CHECKPOINT_COLUMNS = {
|
|
119
|
+
/** The read model's `ProjectionId`. */
|
|
120
|
+
projection: /*#__PURE__*/quoteAtom('projection'),
|
|
121
|
+
/** The read model's `PartitionId` (`default` while sharding is cut). */
|
|
122
|
+
partition: /*#__PURE__*/quoteAtom('partition'),
|
|
123
|
+
/**
|
|
124
|
+
* The scalar checkpoint: `bigint`, because a `Position` is 64-bit and exceeds
|
|
125
|
+
* JS `number` safety at 2^53. It is read back as a decimal STRING and
|
|
126
|
+
* reconstructed with `BigInt(...)`, never coerced through `number`.
|
|
127
|
+
*/
|
|
128
|
+
lastPosition: /*#__PURE__*/quoteAtom('last_position')
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* The ordered, idempotent DDL that creates (or refreshes) the checkpoint table.
|
|
132
|
+
* Each entry is a `sql.unsafe` statement string; no VALUES are involved (pure
|
|
133
|
+
* DDL), and every identifier is pre-quoted to match the runtime's resolution.
|
|
134
|
+
*
|
|
135
|
+
* Notes on the shape, both load-bearing:
|
|
136
|
+
*
|
|
137
|
+
* - `PRIMARY KEY (projection, partition)` is what makes `readCheckpoint` an index
|
|
138
|
+
* lookup AND supplies the arbiter index the first-commit upsert's
|
|
139
|
+
* `ON CONFLICT (projection, partition)` names. Without it that upsert would not
|
|
140
|
+
* compile, so the key is not merely hygiene.
|
|
141
|
+
* - `last_position bigint NOT NULL` with NO row for a never-committed key: a key
|
|
142
|
+
* reads back as `ORIGIN` by ABSENCE, so "never committed" and "reset" are one
|
|
143
|
+
* state (`resetCheckpoint` deletes the row rather than zeroing it), exactly as
|
|
144
|
+
* the in-memory store's map deletes its entry.
|
|
145
|
+
* - No autovacuum storage parameters, unlike the event store's tables. Those are
|
|
146
|
+
* tuned for an append-heavy, effectively-immutable log; this table is a handful
|
|
147
|
+
* of hot-UPDATEd rows, where the stock thresholds already fire aggressively.
|
|
148
|
+
*/
|
|
149
|
+
const checkpointDdlStatements = names => {
|
|
150
|
+
const table = quoteQualified(names.checkpointTable);
|
|
151
|
+
const schema = quoteAtom(names.schema);
|
|
152
|
+
const columns = CHECKPOINT_COLUMNS;
|
|
153
|
+
return [
|
|
154
|
+
// The schema may already exist (e.g. `public`); create it defensively so a
|
|
155
|
+
// custom schema does not require a separate provisioning step.
|
|
156
|
+
`CREATE SCHEMA IF NOT EXISTS ${schema};`, `CREATE TABLE IF NOT EXISTS ${table} (
|
|
157
|
+
${columns.projection} text NOT NULL,
|
|
158
|
+
${columns.partition} text NOT NULL,
|
|
159
|
+
${columns.lastPosition} bigint NOT NULL,
|
|
160
|
+
PRIMARY KEY (${columns.projection}, ${columns.partition})
|
|
161
|
+
);`];
|
|
162
|
+
};
|
|
163
|
+
/**
|
|
164
|
+
* Run the ordered DDL against the generic `SqlClient`. Kept here (not in the
|
|
165
|
+
* public `ensureCheckpointSchema`) so the same builder feeds both the public
|
|
166
|
+
* migration payload and any internal test setup. Each statement is a separate
|
|
167
|
+
* `sql.unsafe` call so a driver that rejects multi-statement strings still works.
|
|
168
|
+
*/
|
|
169
|
+
exports.checkpointDdlStatements = checkpointDdlStatements;
|
|
170
|
+
const runCheckpointDdl = (sql, names) => checkpointDdlStatements(names).map(statement => sql.unsafe(statement));
|
|
171
|
+
exports.runCheckpointDdl = runCheckpointDdl;
|
|
172
|
+
//# sourceMappingURL=ddl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ddl.js","names":["DEFAULT_SCHEMA","exports","DEFAULT_TABLE_PREFIX","CHECKPOINT_SUFFIX","MAX_IDENTIFIER_BYTES","utf8ByteLength","value","TextEncoder","encode","length","derivedAtoms","config","schema","tablePrefix","oversizedIdentifier","label","atom","bytes","undefined","resolveCheckpointNames","problem","Error","checkpointTable","quoteQualified","name","split","map","replace","join","quoteAtom","CHECKPOINT_COLUMNS","projection","partition","lastPosition","checkpointDdlStatements","names","table","columns","runCheckpointDdl","sql","statement","unsafe"],"sources":["../../../src/internal/ddl.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
|