@thomasfosterau/effect-drizzle-ext 0.0.0 → 0.1.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 +21 -0
- package/README.md +508 -2
- package/dist/columns.d.ts +113 -0
- package/dist/columns.d.ts.map +1 -0
- package/dist/columns.js +146 -0
- package/dist/columns.js.map +1 -0
- package/dist/constraints.d.ts +117 -0
- package/dist/constraints.d.ts.map +1 -0
- package/dist/constraints.js +93 -0
- package/dist/constraints.js.map +1 -0
- package/dist/database.d.ts +189 -0
- package/dist/database.d.ts.map +1 -0
- package/dist/database.js +51 -0
- package/dist/database.js.map +1 -0
- package/dist/derive.d.ts +97 -0
- package/dist/derive.d.ts.map +1 -0
- package/dist/derive.js +142 -0
- package/dist/derive.js.map +1 -0
- package/dist/errors.d.ts +157 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +287 -0
- package/dist/errors.js.map +1 -0
- package/dist/index-CFhgcF4N.d.ts +418 -0
- package/dist/index-CFhgcF4N.d.ts.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/jsonapi/derive.d.ts +28 -0
- package/dist/jsonapi/derive.d.ts.map +1 -0
- package/dist/jsonapi/derive.js +28 -0
- package/dist/jsonapi/derive.js.map +1 -0
- package/dist/jsonapi/index.d.ts +189 -0
- package/dist/jsonapi/index.d.ts.map +1 -0
- package/dist/jsonapi/index.js +164 -0
- package/dist/jsonapi/index.js.map +1 -0
- package/dist/query.d.ts +111 -0
- package/dist/query.d.ts.map +1 -0
- package/dist/query.js +142 -0
- package/dist/query.js.map +1 -0
- package/dist/references.d.ts +38 -0
- package/dist/references.d.ts.map +1 -0
- package/dist/references.js +60 -0
- package/dist/references.js.map +1 -0
- package/dist/sort.d.ts +129 -0
- package/dist/sort.d.ts.map +1 -0
- package/dist/sort.js +225 -0
- package/dist/sort.js.map +1 -0
- package/dist/src-CBeuhy5b.js +478 -0
- package/dist/src-CBeuhy5b.js.map +1 -0
- package/dist/update.d.ts +61 -0
- package/dist/update.d.ts.map +1 -0
- package/dist/update.js +64 -0
- package/dist/update.js.map +1 -0
- package/dist/versioned-chain.d.ts +524 -0
- package/dist/versioned-chain.d.ts.map +1 -0
- package/dist/versioned-chain.js +298 -0
- package/dist/versioned-chain.js.map +1 -0
- package/package.json +102 -4
- package/src/columns.ts +186 -0
- package/src/constraints.ts +225 -0
- package/src/database.ts +273 -0
- package/src/derivation-error.ts +27 -0
- package/src/derive.ts +320 -0
- package/src/errors.ts +466 -0
- package/src/index.ts +653 -0
- package/src/jsonapi/derive.ts +74 -0
- package/src/jsonapi/index.ts +355 -0
- package/src/query.ts +183 -0
- package/src/references.ts +103 -0
- package/src/sort.ts +368 -0
- package/src/tree.ts +367 -0
- package/src/update.ts +88 -0
- package/src/versioned-chain.ts +1027 -0
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
import { F as Tree } from "./index-CFhgcF4N.js";
|
|
2
|
+
import { ColumnBuilders, DeriveOptions, DerivedColumns } from "./derive.js";
|
|
3
|
+
import { ColumnBuilderBase, GetColumnData, SQL } from "drizzle-orm";
|
|
4
|
+
import { AnyPgColumn, ExtraConfigColumn, PgBigInt64Builder, PgBuildColumns, PgSchema, PgTable, PgTableExtraConfigValue, PgTableWithColumns, PgTextBuilder, PgTimestampBuilder, PgUUIDBuilder, PgViewWithSelection, SetHasDefault, SetIsPrimaryKey, SetNotNull } from "drizzle-orm/pg-core";
|
|
5
|
+
/** A parent version table contributes its `(id, version)` key as the FK target. */
|
|
6
|
+
type ParentVersionTable = PgTable & {
|
|
7
|
+
id: AnyPgColumn;
|
|
8
|
+
version: AnyPgColumn;
|
|
9
|
+
};
|
|
10
|
+
/** The stable row's `id` + `type` columns a discriminator hook constrains. */
|
|
11
|
+
export interface DiscriminatorColumns {
|
|
12
|
+
id: AnyPgColumn;
|
|
13
|
+
type: AnyPgColumn;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Declaration accepted by a {@link makeSubtypeTables} builder.
|
|
17
|
+
*
|
|
18
|
+
* **The optional members below are NOT migrated to `Option`, and must not be.**
|
|
19
|
+
* Two reasons, either sufficient:
|
|
20
|
+
*
|
|
21
|
+
* 1. This is the config a hierarchy hands the Drizzle table builder, and it is
|
|
22
|
+
* handed on unchanged to a consuming application's database package — a
|
|
23
|
+
* signature change here is a signature change for every consumer.
|
|
24
|
+
* 2. Every one of them is threaded straight into a Drizzle builder call
|
|
25
|
+
* (`bigint().default(versionDefault)`, `discriminator`, …). Drizzle reads
|
|
26
|
+
* plain `undefined` as "not supplied"; an `Option` would be an object it
|
|
27
|
+
* would try to emit as a value, producing wrong DDL that no type error and
|
|
28
|
+
* no unit test would catch.
|
|
29
|
+
*
|
|
30
|
+
* The explicit `| undefined` on each is likewise deliberate: a configuring
|
|
31
|
+
* layer can thread its own optional overrides through without per-property
|
|
32
|
+
* conditional spreads (`exactOptionalPropertyTypes`).
|
|
33
|
+
*
|
|
34
|
+
* Constraint and column names are canonical (`${name}_version_pkey`,
|
|
35
|
+
* `${name}_version_${parentVersion}_fk`, an `id` column): no deployed schema
|
|
36
|
+
* needs the historical overrides (effect-drizzle-ext#112, decision 4).
|
|
37
|
+
*/
|
|
38
|
+
export interface SubtypeTablesConfig<TColumns extends Record<string, ColumnBuilderBase>, TDiscriminator> {
|
|
39
|
+
/** Leaf name; the version table is `${name}_version` (e.g. `video`). */
|
|
40
|
+
name: string;
|
|
41
|
+
/**
|
|
42
|
+
* The parent branch's stable `id` column the leaf's stable row references
|
|
43
|
+
* (cascade delete).
|
|
44
|
+
*/
|
|
45
|
+
parentId: AnyPgColumn;
|
|
46
|
+
/**
|
|
47
|
+
* The parent branch's version table; the leaf version's `(id, version)`
|
|
48
|
+
* foreign-keys to its `(id, version)`. Its name seeds the FK constraint name
|
|
49
|
+
* (`${name}_version_${parentVersionName}_fk`).
|
|
50
|
+
*/
|
|
51
|
+
parentVersion: ParentVersionTable;
|
|
52
|
+
/** The leaf's own domain columns (beyond the generated id/version). */
|
|
53
|
+
columns: TColumns;
|
|
54
|
+
/**
|
|
55
|
+
* Default for the `version` column — e.g. `0n` for branch-extending leaves
|
|
56
|
+
* whose first version row is written with an explicit `0`. Omitted leaves get
|
|
57
|
+
* a plain `NOT NULL` version.
|
|
58
|
+
*/
|
|
59
|
+
versionDefault?: bigint | undefined;
|
|
60
|
+
/**
|
|
61
|
+
* Extra version-table constraints/indexes appended after the generated primary
|
|
62
|
+
* key and parent-version foreign key. The callback receives the version
|
|
63
|
+
* table's columns (including any generated column declared in `columns`).
|
|
64
|
+
*/
|
|
65
|
+
extraConstraints?: ((table: Record<string, ExtraConfigColumn>) => PgTableExtraConfigValue[]) | undefined;
|
|
66
|
+
/**
|
|
67
|
+
* The discriminator declaration for this leaf's stable row. When present, the
|
|
68
|
+
* stable `type` column is made `NOT NULL` and the injected discriminator hook
|
|
69
|
+
* appends its constraints; when absent the stable row keeps a nullable `type`.
|
|
70
|
+
* Its shape is whatever the injected hook consumes.
|
|
71
|
+
*/
|
|
72
|
+
discriminator?: TDiscriminator | undefined;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* The stable identity row's generated column builders — `id` (the primary key
|
|
76
|
+
* that references the parent branch's stable `id`) and the denormalised leaf
|
|
77
|
+
* `type` discriminator.
|
|
78
|
+
*
|
|
79
|
+
* `type` is a plain {@link PgTextBuilder} rather than a union with its
|
|
80
|
+
* `.notNull()` form because the two reduce: `SetNotNull<T>` is `T & …`, so
|
|
81
|
+
* `SetNotNull<T> | T` collapses to `T` under union subtype reduction. This
|
|
82
|
+
* restates what the compiler already infers for the ternary in the body, it does
|
|
83
|
+
* not widen it — the `NOT NULL` a discriminator adds was never in the inferred
|
|
84
|
+
* type either.
|
|
85
|
+
*/
|
|
86
|
+
type SubtypeStableColumnBuilders = {
|
|
87
|
+
id: SetIsPrimaryKey<PgUUIDBuilder>;
|
|
88
|
+
type: PgTextBuilder<[string, ...string[]]>;
|
|
89
|
+
};
|
|
90
|
+
/**
|
|
91
|
+
* The version row's column builders: the generated `(id, version)` key columns
|
|
92
|
+
* plus the leaf's own domain columns. `version` is a plain
|
|
93
|
+
* `SetNotNull<PgBigInt64Builder>` for the same subtype-reduction reason `type`
|
|
94
|
+
* is above — `bigint().notNull().default(…)` is `HasDefault<SetNotNull<…>>`, a
|
|
95
|
+
* subtype of the no-default branch, so the union the ternary produces has
|
|
96
|
+
* already collapsed.
|
|
97
|
+
*
|
|
98
|
+
* This alias is referenced **both** by {@link SubtypeTables} and by the explicit
|
|
99
|
+
* type arguments at the `schema.table` call that builds the table, and it has to
|
|
100
|
+
* be: see the note on that call.
|
|
101
|
+
*/
|
|
102
|
+
type SubtypeVersionColumnBuilders<TColumns extends Record<string, ColumnBuilderBase>> = {
|
|
103
|
+
id: SetNotNull<PgUUIDBuilder>;
|
|
104
|
+
version: SetNotNull<PgBigInt64Builder>;
|
|
105
|
+
} & TColumns;
|
|
106
|
+
/**
|
|
107
|
+
* The `{ stable, version }` table pair a {@link makeSubtypeTables} builder
|
|
108
|
+
* returns.
|
|
109
|
+
*
|
|
110
|
+
* **Why this is written out rather than inferred.** Declaration emit cannot
|
|
111
|
+
* name the inferred form. `PgBuildColumns<TTableName, TColumnsMap>` is a
|
|
112
|
+
* homomorphic mapped type; when `TColumnsMap` is generic (it contains this
|
|
113
|
+
* builder's `TColumns`) the compiler cannot reduce it, and the declaration
|
|
114
|
+
* printer expands the mapped type's *body* while printing drizzle's own
|
|
115
|
+
* `TTableName` type parameter by name — a name that is not in scope in the
|
|
116
|
+
* emitted `.d.ts`. The result is an unbound type reference: `tsc` resolves it to
|
|
117
|
+
* the error type, `skipLibCheck: true` hides the `TS2304`, and every consumer
|
|
118
|
+
* reading these tables through the emitted declarations silently gets `any`
|
|
119
|
+
* where it should get the column's real type — a consuming application's
|
|
120
|
+
* database package degrading a table's `$inferSelect["id"]` from `string` to
|
|
121
|
+
* `any` with no type error anywhere in the chain (website #1579). This
|
|
122
|
+
* package's own `tests/dist-consumer` type-checks a consumer fixture against
|
|
123
|
+
* the emitted `dist/`, asserting the chain tables' row types precisely to
|
|
124
|
+
* catch this regression (an unbound name degrades them to `any`, and an
|
|
125
|
+
* `Equals<…>` assertion against `any` is false); it is the gate this module
|
|
126
|
+
* exists chiefly to satisfy.
|
|
127
|
+
*
|
|
128
|
+
* An explicit annotation is emitted verbatim as a type *reference*, so the
|
|
129
|
+
* printer never expands the mapped type and `TTableName` stays bound to
|
|
130
|
+
* drizzle's own declaration. Both `PgBuildColumns` instantiations below restate
|
|
131
|
+
* exactly the arguments the call sites already infer — the tables are still
|
|
132
|
+
* built by `schema.table(…)`, and this only names what it returns.
|
|
133
|
+
*
|
|
134
|
+
* Identical behaviour under TypeScript 6.0.3 and the 7.x tsgo build; this is a
|
|
135
|
+
* printer limitation in both, not a version-specific bug.
|
|
136
|
+
*/
|
|
137
|
+
export interface SubtypeTables<TColumns extends Record<string, ColumnBuilderBase>> {
|
|
138
|
+
stable: PgTableWithColumns<{
|
|
139
|
+
name: string;
|
|
140
|
+
schema: string;
|
|
141
|
+
columns: PgBuildColumns<string, SubtypeStableColumnBuilders>;
|
|
142
|
+
dialect: "pg";
|
|
143
|
+
isAlias: false;
|
|
144
|
+
}>;
|
|
145
|
+
version: PgTableWithColumns<{
|
|
146
|
+
name: `${string}_version`;
|
|
147
|
+
schema: string;
|
|
148
|
+
columns: PgBuildColumns<`${string}_version`, SubtypeVersionColumnBuilders<TColumns>>;
|
|
149
|
+
dialect: "pg";
|
|
150
|
+
isAlias: false;
|
|
151
|
+
}>;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Build a `defineSubtypeTables` bound to a discriminator hook. The hook turns a
|
|
155
|
+
* leaf's `discriminator` declaration into the stable-table constraints that
|
|
156
|
+
* enforce it (e.g. a `UNIQUE(id, type)` + composite `(id, type)` → parent FK +
|
|
157
|
+
* per-level CHECK trio); a hierarchy without a discriminator passes a hook that
|
|
158
|
+
* is simply never invoked (its leaves omit `discriminator`).
|
|
159
|
+
*
|
|
160
|
+
* The returned builder emits a regular subtype's `{ stable, version }` table
|
|
161
|
+
* pair with the composite-`(id, version)` FK chain to its parent branch.
|
|
162
|
+
*/
|
|
163
|
+
export declare const makeSubtypeTables: <TDiscriminator>(applyDiscriminator: (level: string, stable: DiscriminatorColumns, discriminator: TDiscriminator) => PgTableExtraConfigValue[]) => <TColumns extends Record<string, ColumnBuilderBase>>(schema: PgSchema, { name, parentId, parentVersion, columns, versionDefault, extraConstraints, discriminator }: SubtypeTablesConfig<TColumns, TDiscriminator>) => SubtypeTables<TColumns>;
|
|
164
|
+
/** A version table participates in the chain via its `(id, version)` key. */
|
|
165
|
+
interface VersionTable {
|
|
166
|
+
id: AnyPgColumn;
|
|
167
|
+
version: AnyPgColumn;
|
|
168
|
+
}
|
|
169
|
+
/** A stable identity table is joined on `id` alone. */
|
|
170
|
+
interface StableTable {
|
|
171
|
+
id: AnyPgColumn;
|
|
172
|
+
}
|
|
173
|
+
/** The explicit, projection-shaped column selection a leaf authors. */
|
|
174
|
+
type ViewColumns = Record<string, AnyPgColumn | SQL | SQL.Aliased>;
|
|
175
|
+
/** Resolve one selected field to its inferred (nullability-aware) row type. */
|
|
176
|
+
type ViewField<TField> = TField extends AnyPgColumn ? GetColumnData<TField> : TField extends SQL<infer U> ? U : TField extends SQL.Aliased<infer U> ? U : never;
|
|
177
|
+
/** Inferred `$inferSelect` row for an explicit {@link ViewColumns} map. */
|
|
178
|
+
type ViewRow<TColumns extends ViewColumns> = { [K in keyof TColumns]: ViewField<TColumns[K]>; };
|
|
179
|
+
/**
|
|
180
|
+
* The view type a {@link makeCurrentVersionView} builder returns: a real Drizzle
|
|
181
|
+
* view (usable in `from` / `join` and exposing the column proxies for
|
|
182
|
+
* `eq(view.col, …)`) with `$inferSelect` pinned to the explicit column map plus
|
|
183
|
+
* the injected always-merged columns `TMerge`.
|
|
184
|
+
*
|
|
185
|
+
* The selection is built directly from `columns` + `merge`, so this never
|
|
186
|
+
* depends on the in-body select's nullability/table-name generics — which
|
|
187
|
+
* collapse to `{ [x: string]: never }` because the join chain is assembled
|
|
188
|
+
* untyped.
|
|
189
|
+
*/
|
|
190
|
+
type CurrentVersionViewResult<TColumns extends ViewColumns, TMerge extends ViewColumns> = Omit<PgViewWithSelection<{
|
|
191
|
+
name: string;
|
|
192
|
+
schema: string;
|
|
193
|
+
existing: false;
|
|
194
|
+
selectedFields: TColumns & TMerge;
|
|
195
|
+
isAlias: false;
|
|
196
|
+
}>, "$inferSelect"> & {
|
|
197
|
+
$inferSelect: ViewRow<TColumns & TMerge>;
|
|
198
|
+
};
|
|
199
|
+
/**
|
|
200
|
+
* Declaration accepted by a {@link makeCurrentVersionView} builder.
|
|
201
|
+
*
|
|
202
|
+
* **The optional members below are NOT migrated to `Option`, and must not be** —
|
|
203
|
+
* for the same two reasons as {@link SubtypeTablesConfig}: the interface is
|
|
204
|
+
* handed on unchanged to a consuming application's database package, and each
|
|
205
|
+
* member is spread straight into the generated Drizzle join chain
|
|
206
|
+
* (`for (const table of through ?? [])`), where `undefined` means "no such
|
|
207
|
+
* join" and an `Option` would be a truthy object.
|
|
208
|
+
*/
|
|
209
|
+
export interface CurrentVersionViewConfig<TColumns extends ViewColumns> {
|
|
210
|
+
/** Leaf version table the view resolves to its latest row. */
|
|
211
|
+
leaf: VersionTable;
|
|
212
|
+
/**
|
|
213
|
+
* Intermediate version tables joined on the composite `(id, version)` key,
|
|
214
|
+
* ordered leaf → branch. The root version table is always appended after these
|
|
215
|
+
* by the builder. A **required fact table** (a 6NF satellite keyed
|
|
216
|
+
* `(id, version)` whose row must exist at every version) also joins here —
|
|
217
|
+
* the INNER JOIN doubles as its fail-closed witness.
|
|
218
|
+
*/
|
|
219
|
+
through?: VersionTable[] | undefined;
|
|
220
|
+
/**
|
|
221
|
+
* **Optional fact tables** LEFT JOINed at the current `(id, version)`: 6NF
|
|
222
|
+
* satellites whose row-absence at a version encodes NULL (the fact-scoped
|
|
223
|
+
* version storage decision). Selected columns sourced from these must be
|
|
224
|
+
* projected nullability-aware (an `sql<T | null>` aliased expression), since
|
|
225
|
+
* the LEFT JOIN widens a NOT NULL fact column to nullable in the row.
|
|
226
|
+
*/
|
|
227
|
+
optional?: VersionTable[] | undefined;
|
|
228
|
+
/** Stable identity tables joined on `id` (explicit `undefined` admitted, as above). */
|
|
229
|
+
on?: StableTable[] | undefined;
|
|
230
|
+
/** Explicit column selection, including `id`, `action`, and `timestamp`. */
|
|
231
|
+
columns: TColumns;
|
|
232
|
+
}
|
|
233
|
+
/** Injected dependencies for a {@link makeCurrentVersionView} builder. */
|
|
234
|
+
export interface CurrentVersionViewDeps<TRoot extends PgTable & VersionTable, TMerge extends ViewColumns> {
|
|
235
|
+
/**
|
|
236
|
+
* The root version table currency resolves from — a `MAX(version)` sub-select
|
|
237
|
+
* groups it by `id`, and the leaf / intermediate version tables INNER JOIN at
|
|
238
|
+
* that current `(id, version)`. Also joined for the current row's own columns
|
|
239
|
+
* (e.g. an `action` soft-delete gate and a `timestamp`).
|
|
240
|
+
*/
|
|
241
|
+
rootVersion: TRoot;
|
|
242
|
+
/** The always-joined identity row (joined on `id`). */
|
|
243
|
+
identity: StableTable;
|
|
244
|
+
/**
|
|
245
|
+
* Columns merged into every view's selection (sourced from `identity`), so no
|
|
246
|
+
* leaf needs to name them — e.g. a shared `slug`.
|
|
247
|
+
*
|
|
248
|
+
* Passing a **function** instead of a plain object additionally INNER JOINs a
|
|
249
|
+
* `version = 0` alias of `rootVersion` — the node's birth row — and hands it
|
|
250
|
+
* to the callback, so a hierarchy can merge in a true `createdAt` sourced
|
|
251
|
+
* from the first version's own timestamp, distinct from the current
|
|
252
|
+
* version's. The plain-object form skips that join entirely, leaving the
|
|
253
|
+
* emitted view definition byte-identical to before: this matters for a root
|
|
254
|
+
* version table whose current-version view is itself joined **as a view** by
|
|
255
|
+
* another hand-written view, where changing its definition would require
|
|
256
|
+
* drizzle-kit to DROP it — and drizzle-kit doesn't topologically order
|
|
257
|
+
* view-on-view DROPs, so it fails against the still-dependent view.
|
|
258
|
+
*
|
|
259
|
+
* That hazard is confined to a hierarchy's own hand-written views layered on
|
|
260
|
+
* top: the join chain this builder generates only ever reaches version and
|
|
261
|
+
* stable tables (`leaf`, `through`, `optional`, `on`, `identity`,
|
|
262
|
+
* `rootVersion`), never a {@link CurrentVersionViewResult} this module
|
|
263
|
+
* itself returns. No view built by `makeCurrentVersionView` may select from
|
|
264
|
+
* another view built by `makeCurrentVersionView` — that is a library-level
|
|
265
|
+
* rule, not just this comment's observation.
|
|
266
|
+
*/
|
|
267
|
+
merge: TMerge | ((versionZero: TRoot) => TMerge);
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Build a `currentVersionView` bound to a root version table + identity row.
|
|
271
|
+
* `schema.view(name).as(...)` with the uniform join chain generated from
|
|
272
|
+
* {@link CurrentVersionViewConfig}.
|
|
273
|
+
*
|
|
274
|
+
* The joins only widen the `FROM` clause; they never change the explicit
|
|
275
|
+
* `columns` selection, so the view's `$inferSelect` is derived directly from
|
|
276
|
+
* `columns` + the injected `merge` via {@link CurrentVersionViewResult}. The
|
|
277
|
+
* runtime query is assembled untyped (the chain alternates join shapes), which
|
|
278
|
+
* is why the return type is asserted rather than inferred: letting `.as()` infer
|
|
279
|
+
* the selection through `.from(current)` collapses every column to `never`.
|
|
280
|
+
*
|
|
281
|
+
* Currency is resolved from `rootVersion` (fail-closed): a `MAX(version)`
|
|
282
|
+
* sub-select drives the FROM, and the leaf / intermediate version tables INNER
|
|
283
|
+
* JOIN at that current `(id, version)`. One row per node (the sub-select groups
|
|
284
|
+
* by id) landing on unique `(id, version)` PKs, so no `DISTINCT ON` / `ORDER BY`
|
|
285
|
+
* is needed. A leaf missing at the current version drops out of the INNER JOIN —
|
|
286
|
+
* the view yields no row.
|
|
287
|
+
*/
|
|
288
|
+
export declare const makeCurrentVersionView: <TRoot extends PgTable & VersionTable, TMerge extends ViewColumns>({ rootVersion, identity, merge }: CurrentVersionViewDeps<TRoot, TMerge>) => <TColumns extends ViewColumns>(schema: PgSchema, name: string, { leaf, through, optional, on, columns }: CurrentVersionViewConfig<TColumns>) => CurrentVersionViewResult<TColumns, TMerge>;
|
|
289
|
+
/**
|
|
290
|
+
* The actions a version-chain row records by default: a chain appends a
|
|
291
|
+
* `(version, action, timestamp)` row per edit rather than rewriting in place,
|
|
292
|
+
* and a delete is a tombstone version.
|
|
293
|
+
*/
|
|
294
|
+
export type VersionAction = "create" | "update" | "delete";
|
|
295
|
+
/**
|
|
296
|
+
* One entry in a node's append-only version chain — what a version-history
|
|
297
|
+
* listing returns per row. `Action` is the chain's own action vocabulary
|
|
298
|
+
* (default {@link VersionAction}).
|
|
299
|
+
*/
|
|
300
|
+
export interface VersionChainEntry<Action extends string = VersionAction> {
|
|
301
|
+
/** The version number (0 is the create). */
|
|
302
|
+
readonly version: bigint;
|
|
303
|
+
/** The edit action this version recorded. */
|
|
304
|
+
readonly action: Action;
|
|
305
|
+
/** When the version was written. */
|
|
306
|
+
readonly timestamp: Date;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* One version's reconstructed state — what a browse / compare surface shows,
|
|
310
|
+
* and what a restore re-applies as a new version. Every leaf-chain domain
|
|
311
|
+
* column at `(id, version)`, keyed by column name (leaf-most level wins on a
|
|
312
|
+
* shared name), plus the root chain's `action` / `timestamp`. Generated
|
|
313
|
+
* columns (a search vector) are opaque and belong outside `columns`.
|
|
314
|
+
*
|
|
315
|
+
* `Extra` carries the branch-level facts a hierarchy adds to a snapshot (a
|
|
316
|
+
* content branch's visibility and draft flag, say); the library knows none of
|
|
317
|
+
* them.
|
|
318
|
+
*/
|
|
319
|
+
export type VersionSnapshot<Action extends string = VersionAction, Extra extends object = Record<never, never>> = VersionChainEntry<Action> & Extra & {
|
|
320
|
+
/** Every leaf-chain domain column at this version, keyed by column name. */
|
|
321
|
+
readonly columns: Readonly<Record<string, unknown>>;
|
|
322
|
+
};
|
|
323
|
+
/** The `createdAt` / `updatedAt` pair {@link versionTimestamps} returns. */
|
|
324
|
+
export interface VersionTimestamps {
|
|
325
|
+
/** The birth row's timestamp, or `updatedAt` when the view merges none in. */
|
|
326
|
+
readonly createdAt: Date;
|
|
327
|
+
/** The current version's `timestamp`. */
|
|
328
|
+
readonly updatedAt: Date;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* The `createdAt` / `updatedAt` pair of a current-version row: `updatedAt` is
|
|
332
|
+
* the current version's `timestamp`; `createdAt` is the birth row's own
|
|
333
|
+
* timestamp where the view merges one in (a function `merge` of
|
|
334
|
+
* {@link makeCurrentVersionView}), and falls back to `updatedAt` for a view
|
|
335
|
+
* that carries none (website #1223).
|
|
336
|
+
*/
|
|
337
|
+
export declare const versionTimestamps: (row: {
|
|
338
|
+
readonly timestamp: Date;
|
|
339
|
+
readonly createdAt?: Date | null | undefined;
|
|
340
|
+
}) => VersionTimestamps;
|
|
341
|
+
/** A builder map a table's `columns` takes. */
|
|
342
|
+
type ColumnMap = Record<string, ColumnBuilderBase>;
|
|
343
|
+
/** `T` as a {@link ColumnMap}, so a mapped column type satisfies the table constraint. */
|
|
344
|
+
type AsColumns<T> = T extends ColumnMap ? T : never;
|
|
345
|
+
/** The metadata map member `P` of level `K` in tree metadata `M`; `{}` when absent. */
|
|
346
|
+
type MetaOf<M, K, P extends "level" | "leaf"> = K extends keyof M ? M[K] extends { readonly [Q in P]: infer X; } ? X : Record<never, never> : Record<never, never>;
|
|
347
|
+
/** The hand-authored columns the hooks `H` give level `K`; `{}` when none. */
|
|
348
|
+
type HookColumnsOf<H, K> = K extends keyof H ? H[K] extends {
|
|
349
|
+
readonly columns?: infer C;
|
|
350
|
+
} ? Exclude<C, undefined> extends ColumnMap ? Exclude<C, undefined> : Record<never, never> : Record<never, never> : Record<never, never>;
|
|
351
|
+
/** Whether an entry declares the `"fact-scoped"` level kind. */
|
|
352
|
+
type IsFactScoped<E> = E extends {
|
|
353
|
+
readonly kind?: infer Kind;
|
|
354
|
+
} ? [Exclude<Kind, undefined>] extends ["fact-scoped"] ? true : false : false;
|
|
355
|
+
/** Level `K`'s own derived columns: its level columns, then its leaf's. */
|
|
356
|
+
type OwnColumns<M, K, B extends ColumnBuilders> = DerivedColumns<MetaOf<M, K, "level">, B> & DerivedColumns<MetaOf<M, K, "leaf">, B>;
|
|
357
|
+
/**
|
|
358
|
+
* The columns level `K`'s version table carries beyond `(id, version)`: its own
|
|
359
|
+
* derived columns (a fact-scoped level keeps them in fact tables instead), then
|
|
360
|
+
* the hand-authored hook columns.
|
|
361
|
+
*/
|
|
362
|
+
export type TreeVersionColumns<S, M, B extends ColumnBuilders, H, K extends keyof S> = AsColumns<(IsFactScoped<S[K]> extends true ? Record<never, never> : OwnColumns<M, K, B>) & HookColumnsOf<H, K>>;
|
|
363
|
+
/** A fact table's column builders: the `(id, version)` key plus one fact. */
|
|
364
|
+
type FactColumnBuilders<TColumns extends ColumnMap> = {
|
|
365
|
+
id: SetNotNull<PgUUIDBuilder>;
|
|
366
|
+
version: SetNotNull<PgBigInt64Builder>;
|
|
367
|
+
} & TColumns;
|
|
368
|
+
/**
|
|
369
|
+
* One fact table of a fact-scoped level: `(id, version)` keyed, carrying one
|
|
370
|
+
* column, foreign-keyed to the level's version spine. Written out for the
|
|
371
|
+
* declaration printer, as {@link SubtypeTables} is.
|
|
372
|
+
*/
|
|
373
|
+
export type FactTable<TColumns extends ColumnMap> = PgTableWithColumns<{
|
|
374
|
+
name: string;
|
|
375
|
+
schema: string;
|
|
376
|
+
columns: PgBuildColumns<string, FactColumnBuilders<TColumns>>;
|
|
377
|
+
dialect: "pg";
|
|
378
|
+
isAlias: false;
|
|
379
|
+
}>;
|
|
380
|
+
/** Every level's `{ stable, version }` pair, keyed by level name. */
|
|
381
|
+
export type TreeLevelTables<S, M, B extends ColumnBuilders, H> = { readonly [K in keyof S & string]: SubtypeTables<TreeVersionColumns<S, M, B, H, K>>; };
|
|
382
|
+
/** Every fact-scoped level's fact tables, keyed by level then field; `{}` for a plain level. */
|
|
383
|
+
export type TreeFactTables<S, M, B extends ColumnBuilders> = { readonly [K in keyof S & string]: IsFactScoped<S[K]> extends true ? { readonly [F in keyof OwnColumns<M, K, B>]: FactTable<AsColumns<Pick<OwnColumns<M, K, B>, F>>>; } : Record<never, never>; };
|
|
384
|
+
/** The root identity table's column builders: `id`, the leaf `type`, then the injected extras. */
|
|
385
|
+
type RootIdentityColumnBuilders<IE extends ColumnMap> = {
|
|
386
|
+
id: SetIsPrimaryKey<PgUUIDBuilder>;
|
|
387
|
+
type: SetNotNull<PgTextBuilder<[string, ...string[]]>>;
|
|
388
|
+
} & IE;
|
|
389
|
+
/**
|
|
390
|
+
* The root version table's column builders: the `(id, version)` key, `action`
|
|
391
|
+
* (the chain's only currency and tombstone state), `timestamp`, the actor,
|
|
392
|
+
* then the injected extras.
|
|
393
|
+
*/
|
|
394
|
+
type RootVersionColumnBuilders<VE extends ColumnMap> = {
|
|
395
|
+
id: SetNotNull<PgUUIDBuilder>;
|
|
396
|
+
version: SetNotNull<PgBigInt64Builder>;
|
|
397
|
+
action: SetNotNull<PgTextBuilder<[string, ...string[]]>>;
|
|
398
|
+
timestamp: SetHasDefault<SetNotNull<PgTimestampBuilder>>;
|
|
399
|
+
actor: PgTextBuilder<[string, ...string[]]>;
|
|
400
|
+
} & VE;
|
|
401
|
+
/** The root `{ identity, version }` pair. Written out for the declaration printer. */
|
|
402
|
+
export interface TreeRootTables<IE extends ColumnMap, VE extends ColumnMap> {
|
|
403
|
+
identity: PgTableWithColumns<{
|
|
404
|
+
name: string;
|
|
405
|
+
schema: string;
|
|
406
|
+
columns: PgBuildColumns<string, RootIdentityColumnBuilders<IE>>;
|
|
407
|
+
dialect: "pg";
|
|
408
|
+
isAlias: false;
|
|
409
|
+
}>;
|
|
410
|
+
version: PgTableWithColumns<{
|
|
411
|
+
name: string;
|
|
412
|
+
schema: string;
|
|
413
|
+
columns: PgBuildColumns<string, RootVersionColumnBuilders<VE>>;
|
|
414
|
+
dialect: "pg";
|
|
415
|
+
isAlias: false;
|
|
416
|
+
}>;
|
|
417
|
+
}
|
|
418
|
+
/** Everything {@link makeTreeTables} derives from a tree. */
|
|
419
|
+
export interface TreeTables<S, M, B extends ColumnBuilders, H, IE extends ColumnMap, VE extends ColumnMap> {
|
|
420
|
+
/** The root identity and version tables. */
|
|
421
|
+
readonly root: TreeRootTables<IE, VE>;
|
|
422
|
+
/** Every level's stable + version pair, keyed by level name. */
|
|
423
|
+
readonly levels: TreeLevelTables<S, M, B, H>;
|
|
424
|
+
/** Every fact-scoped level's fact tables, keyed by level then field. */
|
|
425
|
+
readonly facts: TreeFactTables<S, M, B>;
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Per-level hooks for what a model can't carry. Every member is threaded into
|
|
429
|
+
* a Drizzle builder call, so each is `?: T | undefined` (see
|
|
430
|
+
* {@link SubtypeTablesConfig}).
|
|
431
|
+
*/
|
|
432
|
+
export interface TreeLevelHooks {
|
|
433
|
+
/** Hand-authored version-table columns (an FK backing a relationship, a generated column). */
|
|
434
|
+
readonly columns?: ColumnMap | undefined;
|
|
435
|
+
/** Extra version-table constraints and indexes, after the generated key and FK. */
|
|
436
|
+
readonly extraConstraints?: ((table: Record<string, ExtraConfigColumn>) => PgTableExtraConfigValue[]) | undefined;
|
|
437
|
+
/**
|
|
438
|
+
* A refinable branch: the `(id, type)` arc of this level and of every level
|
|
439
|
+
* below it gets `ON UPDATE CASCADE`, so flipping the root's `type` from a
|
|
440
|
+
* bare value to a concrete leaf cascades down the existing chain (website's
|
|
441
|
+
* entity refinement).
|
|
442
|
+
*/
|
|
443
|
+
readonly refinable?: boolean | undefined;
|
|
444
|
+
}
|
|
445
|
+
/** The root tables' names and injected columns. */
|
|
446
|
+
export interface TreeRootConfig<IE extends ColumnMap, VE extends ColumnMap> {
|
|
447
|
+
/** The identity table's name; `node` when absent. */
|
|
448
|
+
readonly identity?: string | undefined;
|
|
449
|
+
/** The root version table's name; `${identity}_version` when absent. */
|
|
450
|
+
readonly version?: string | undefined;
|
|
451
|
+
/** Extra identity columns (website's `slug`). */
|
|
452
|
+
readonly identityColumns?: IE | undefined;
|
|
453
|
+
/** Extra root-version columns (pulpit's `proposed_by`). */
|
|
454
|
+
readonly versionColumns?: VE | undefined;
|
|
455
|
+
/** The actor column's name; `actor` when absent. */
|
|
456
|
+
readonly actorColumn?: string | undefined;
|
|
457
|
+
}
|
|
458
|
+
/** The configuration {@link makeTreeTables} takes. */
|
|
459
|
+
export interface TreeTablesConfig<B extends ColumnBuilders, H, IE extends ColumnMap, VE extends ColumnMap> {
|
|
460
|
+
/** The schema every table is created in. */
|
|
461
|
+
readonly schema: PgSchema;
|
|
462
|
+
/** The builder registry the `dbColumn` kinds resolve against. */
|
|
463
|
+
readonly builders: B;
|
|
464
|
+
/** Resolves a `references` annotation to its target column (see `./derive`). */
|
|
465
|
+
readonly resolveReference?: DeriveOptions["resolveReference"];
|
|
466
|
+
/** The root tables' names and injected columns. */
|
|
467
|
+
readonly root?: TreeRootConfig<IE, VE> | undefined;
|
|
468
|
+
/** Per-level hooks, keyed by level name. */
|
|
469
|
+
readonly levels?: H | undefined;
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Derive every table and constraint of a tree in one call: the root identity
|
|
473
|
+
* table (`id`, `type`, extras; `UNIQUE (id, type)`) and root version table
|
|
474
|
+
* (`(id, version)` key, `action` checked to `create` / `update` / `delete`,
|
|
475
|
+
* `timestamp`, the actor, extras); then per level a stable table (`id` →
|
|
476
|
+
* parent `id`, cascade delete; `type` NOT NULL with `UNIQUE (id, type)`,
|
|
477
|
+
* `(id, type)` → parent `(id, type)`, and a CHECK narrowing `type` to the
|
|
478
|
+
* level's subtree leaves) and a version table (`(id, version)` key and
|
|
479
|
+
* `(id, version)` → parent version FK) carrying the level's level columns,
|
|
480
|
+
* its leaf's columns (both derived through `builders`) and its hook columns.
|
|
481
|
+
* A fact-scoped level's version table is a spine, with one
|
|
482
|
+
* `${level}_${column}` fact table per own column, keyed and foreign-keyed on
|
|
483
|
+
* `(id, version)` (cascade delete).
|
|
484
|
+
*
|
|
485
|
+
* Constraint names are canonical (`${table}_pkey`, `${table}_${parent}_fk`,
|
|
486
|
+
* `${level}_id_type_unique` / `_fk` / `_type_valid`); no version table carries
|
|
487
|
+
* `action`. Column, constraint and CHECK value order follow declaration order,
|
|
488
|
+
* so the same tree always generates the same migration. Throws
|
|
489
|
+
* `DerivationError` at module load for a column kind `builders` lacks or an
|
|
490
|
+
* unresolvable reference.
|
|
491
|
+
*
|
|
492
|
+
* Every table is typed from the tree's column phantom. The one type-erasure
|
|
493
|
+
* seam is inside: the level and fact records are assembled from the runtime
|
|
494
|
+
* tree, then named by {@link TreeTables}.
|
|
495
|
+
*
|
|
496
|
+
* @example
|
|
497
|
+
* ```ts
|
|
498
|
+
* import { dbColumn, defineTree } from "@thomasfosterau/effect-drizzle-ext";
|
|
499
|
+
* import { makeTreeTables } from "@thomasfosterau/effect-drizzle-ext/versioned-chain";
|
|
500
|
+
* import { getTableConfig, integer, pgSchema, text } from "drizzle-orm/pg-core";
|
|
501
|
+
* import * as Schema from "effect/Schema";
|
|
502
|
+
*
|
|
503
|
+
* const tree = defineTree({
|
|
504
|
+
* work: {
|
|
505
|
+
* columns: { title: dbColumn(Schema.String, { column: "title", pg: "text", notNull: true }) },
|
|
506
|
+
* },
|
|
507
|
+
* book: {
|
|
508
|
+
* parent: "work",
|
|
509
|
+
* leaf: { pages: dbColumn(Schema.Finite, { column: "pages", pg: "integer", notNull: false }) },
|
|
510
|
+
* },
|
|
511
|
+
* });
|
|
512
|
+
*
|
|
513
|
+
* const tables = makeTreeTables(tree, {
|
|
514
|
+
* schema: pgSchema("catalogue"),
|
|
515
|
+
* builders: { text: (c: string) => text(c), integer: (c: string) => integer(c) },
|
|
516
|
+
* });
|
|
517
|
+
*
|
|
518
|
+
* console.log(getTableConfig(tables.levels.book.version).columns.map((c) => c.name));
|
|
519
|
+
* // ["id", "version", "pages"]
|
|
520
|
+
* ```
|
|
521
|
+
*/
|
|
522
|
+
export declare const makeTreeTables: <S, M, B extends ColumnBuilders, const H extends { readonly [K in keyof S]?: TreeLevelHooks; } = Record<never, never>, IE extends ColumnMap = Record<never, never>, VE extends ColumnMap = Record<never, never>>(tree: Tree<S, M>, config: TreeTablesConfig<B, H, IE, VE>) => TreeTables<S, M, B, H, IE, VE>;
|
|
523
|
+
export {};
|
|
524
|
+
//# sourceMappingURL=versioned-chain.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"versioned-chain.d.ts","names":[],"sources":["../src/versioned-chain.ts"],"mappings":"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"}
|