@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,418 @@
|
|
|
1
|
+
import * as Option from "effect/Option";
|
|
2
|
+
import * as Schema from "effect/Schema";
|
|
3
|
+
declare const DerivationError_base: Schema.Class<DerivationError, Schema.TaggedStruct<"DerivationError", {
|
|
4
|
+
readonly message: Schema.String;
|
|
5
|
+
}>, import("effect/Cause").YieldableError>;
|
|
6
|
+
/**
|
|
7
|
+
* A construction-time derivation failure: a table or a tree could not be built
|
|
8
|
+
* from its declarations (no builder registered for a column kind, a
|
|
9
|
+
* `references` annotation with no resolver, numeric bounds on a column the
|
|
10
|
+
* table has no column for, an unresolvable FK target, a tree level whose
|
|
11
|
+
* parent is missing or forms a cycle).
|
|
12
|
+
*
|
|
13
|
+
* **Thrown, never failed.** Derivation runs at module load, before an Effect
|
|
14
|
+
* runtime exists, so the only way to stop a misdeclared table or tree from
|
|
15
|
+
* reaching production is to make its `import` fail. It is a tagged error all
|
|
16
|
+
* the same, so a consumer that does catch one (a registry loader, a codegen
|
|
17
|
+
* script) can match it structurally.
|
|
18
|
+
*/
|
|
19
|
+
declare class DerivationError extends DerivationError_base {}
|
|
20
|
+
/**
|
|
21
|
+
* How a level stores its columns. `"plain"` (the default): one version table
|
|
22
|
+
* carrying every level column. `"fact-scoped"`: an `(id, version)` spine plus
|
|
23
|
+
* one fact table per level column, where a fact's absence at a version is its
|
|
24
|
+
* `NULL` (website's 6NF `manifestation_version` pilot).
|
|
25
|
+
*/
|
|
26
|
+
type LevelKind = "plain" | "fact-scoped";
|
|
27
|
+
/**
|
|
28
|
+
* One level's declaration, generic in the names its `parent` may take and in
|
|
29
|
+
* the leaf payload. Optional members are `?: T | undefined` so a caller can
|
|
30
|
+
* forward its own optional.
|
|
31
|
+
*/
|
|
32
|
+
interface LevelSpec<Parent extends string = string, Leaf = unknown> {
|
|
33
|
+
/** The parent level. Absent: a child of the root (a branch root). */
|
|
34
|
+
readonly parent?: Parent | undefined;
|
|
35
|
+
/** How the level stores its columns; `"plain"` when absent. */
|
|
36
|
+
readonly kind?: LevelKind | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Level columns: `dbColumn`-annotated fields on this level's version table,
|
|
39
|
+
* contributed to every descendant's view. Every field must carry a
|
|
40
|
+
* `dbColumn` annotation.
|
|
41
|
+
*/
|
|
42
|
+
readonly columns?: Schema.Struct.Fields | undefined;
|
|
43
|
+
/** The leaf payload. Present: a concrete leaf; absent: a marker level. */
|
|
44
|
+
readonly leaf?: Leaf | undefined;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The constraint a tree declaration `S` is checked against: every entry is a
|
|
48
|
+
* {@link LevelSpec} whose `parent` names **another** level of `S`, plus the
|
|
49
|
+
* consumer's extension record `Ext` (pulpit's `owned` / `pageable` flags, say),
|
|
50
|
+
* whose names the library never learns. A parent naming no level, or the level
|
|
51
|
+
* itself, is a type error at the declaration.
|
|
52
|
+
*/
|
|
53
|
+
type CheckedTreeSpec<S, Leaf, Ext> = { readonly [K in keyof S]: LevelSpec<Exclude<keyof S & string, K>, Leaf> & Ext; };
|
|
54
|
+
/** Every level name a tree declaration `S` declares (a `Tree` reads its own). */
|
|
55
|
+
type LevelName<S> = keyof S & string;
|
|
56
|
+
/** The names of the leaf levels of declaration `S`: the discriminator values. */
|
|
57
|
+
type LeafName<S> = { readonly [K in keyof S]: S[K] extends {
|
|
58
|
+
readonly leaf?: infer L;
|
|
59
|
+
} ? [Exclude<L, undefined>] extends [never] ? never : K : never; }[keyof S] & string;
|
|
60
|
+
/** The names of the marker (non-leaf) levels of declaration `S`. */
|
|
61
|
+
type MarkerName<S> = Exclude<LevelName<S>, LeafName<S>>;
|
|
62
|
+
/** Everything derived about one level of a tree. */
|
|
63
|
+
interface TreeLevel<S> {
|
|
64
|
+
/** The level's name (a leaf's discriminator value). */
|
|
65
|
+
readonly name: LevelName<S>;
|
|
66
|
+
/** The declaration entry, extension flags included. */
|
|
67
|
+
readonly entry: S[LevelName<S>];
|
|
68
|
+
/** The parent level; `Option.none()` for a branch root. */
|
|
69
|
+
readonly parent: Option.Option<LevelName<S>>;
|
|
70
|
+
/** The level kind, `"plain"` when the entry names none. */
|
|
71
|
+
readonly kind: LevelKind;
|
|
72
|
+
/** Whether the level is a concrete leaf. */
|
|
73
|
+
readonly isLeaf: boolean;
|
|
74
|
+
/** Every ancestor, root-most first, the level itself excluded. */
|
|
75
|
+
readonly ancestors: ReadonlyArray<LevelName<S>>;
|
|
76
|
+
/** The direct children, in declaration order. */
|
|
77
|
+
readonly children: ReadonlyArray<LevelName<S>>;
|
|
78
|
+
/** Every descendant, in declaration order, the level itself excluded. */
|
|
79
|
+
readonly descendants: ReadonlyArray<LevelName<S>>;
|
|
80
|
+
/**
|
|
81
|
+
* The leaves of the level's subtree in declaration order, the level itself
|
|
82
|
+
* included when it is a leaf: the value set of its discriminator CHECK.
|
|
83
|
+
*/
|
|
84
|
+
readonly subtreeLeaves: ReadonlyArray<LeafName<S>>;
|
|
85
|
+
/** The branch root the level belongs to (itself for a branch root). */
|
|
86
|
+
readonly branch: LevelName<S>;
|
|
87
|
+
/** The level columns' db-column metadata, keyed by field, in order. */
|
|
88
|
+
readonly levelColumns: Readonly<Record<string, DbColumn>>;
|
|
89
|
+
/** A leaf's own domain columns, keyed by field, in order; empty for a marker. */
|
|
90
|
+
readonly leafColumns: Readonly<Record<string, DbColumn>>;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* The type-level column metadata of one level: its level columns' and its
|
|
94
|
+
* leaf's `DbColumn` maps, recovered from the declaration's phantoms. What a
|
|
95
|
+
* table derivation reads to type the tables it builds.
|
|
96
|
+
*/
|
|
97
|
+
interface LevelColumnsMeta {
|
|
98
|
+
readonly level: object;
|
|
99
|
+
readonly leaf: object;
|
|
100
|
+
}
|
|
101
|
+
/** The default column metadata of a declaration `S`: untyped maps per level. */
|
|
102
|
+
type AnyTreeColumnsMeta<S> = { readonly [K in keyof S]: LevelColumnsMeta; };
|
|
103
|
+
declare const TreeColumnsTypeId: unique symbol;
|
|
104
|
+
/**
|
|
105
|
+
* A checked tree: the declaration plus every level's derived facts. `M` is a
|
|
106
|
+
* type-only phantom (never present at runtime) carrying each level's typed
|
|
107
|
+
* column metadata, so the table derivation types every table it builds.
|
|
108
|
+
*/
|
|
109
|
+
interface Tree<S, M = AnyTreeColumnsMeta<S>> {
|
|
110
|
+
/** Type-only: each level's typed column metadata. */
|
|
111
|
+
readonly [TreeColumnsTypeId]?: M;
|
|
112
|
+
/** The declaration exactly as written, for type-level consumers. */
|
|
113
|
+
readonly spec: S;
|
|
114
|
+
/** Every level name, in declaration order. */
|
|
115
|
+
readonly names: ReadonlyArray<LevelName<S>>;
|
|
116
|
+
/** Every level, in declaration order. */
|
|
117
|
+
readonly levels: ReadonlyArray<TreeLevel<S>>;
|
|
118
|
+
/** The leaf names, in declaration order. */
|
|
119
|
+
readonly leaves: ReadonlyArray<LeafName<S>>;
|
|
120
|
+
/** The branch roots (children of the root), in declaration order. */
|
|
121
|
+
readonly branchRoots: ReadonlyArray<LevelName<S>>;
|
|
122
|
+
/** Look a level up by name. */
|
|
123
|
+
readonly level: (name: LevelName<S>) => TreeLevel<S>;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* A referential action for a derived foreign-key column — the Drizzle
|
|
127
|
+
* `onDelete` / `onUpdate` vocabulary, restated as plain data so the annotation
|
|
128
|
+
* layer stays Drizzle-free.
|
|
129
|
+
*/
|
|
130
|
+
type ReferentialAction = "cascade" | "restrict" | "no action" | "set null" | "set default";
|
|
131
|
+
/**
|
|
132
|
+
* Reference / foreign-key metadata a column annotation may carry: the target
|
|
133
|
+
* relation + column the derived column foreign-keys to, plus its referential
|
|
134
|
+
* actions. Expressed as **names** (not Drizzle column thunks) so it survives in
|
|
135
|
+
* the Drizzle-free annotation layer; the derivation resolves the names to real
|
|
136
|
+
* columns via its own `resolveReference` hook (see the {@link ./derive.ts}
|
|
137
|
+
* entry).
|
|
138
|
+
*
|
|
139
|
+
* This is the metadata that lets an FK-backed attribute be derived rather than
|
|
140
|
+
* hand-authored as a table-only `references()` column and re-stated in the
|
|
141
|
+
* consuming application's operation value map.
|
|
142
|
+
*/
|
|
143
|
+
interface DbColumnReference {
|
|
144
|
+
/** The referenced relation (table) name. */
|
|
145
|
+
readonly table: string;
|
|
146
|
+
/** The referenced column name. */
|
|
147
|
+
readonly column: string;
|
|
148
|
+
/** Referential action on delete of the referenced row. */
|
|
149
|
+
readonly onDelete?: ReferentialAction | undefined;
|
|
150
|
+
/** Referential action on update of the referenced row. */
|
|
151
|
+
readonly onUpdate?: ReferentialAction | undefined;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Column metadata a model attribute declares about the column it backs. Generic
|
|
155
|
+
* over the column-kind union `Kind` (open `string` by default) — a consumer pins
|
|
156
|
+
* it to its own kinds via {@link dbColumnFor}.
|
|
157
|
+
*/
|
|
158
|
+
interface DbColumn<Kind extends string = string> {
|
|
159
|
+
/** The snake_case column name (e.g. `published_at`). */
|
|
160
|
+
readonly column: string;
|
|
161
|
+
/** The column kind (selects the Drizzle builder in the derivation). */
|
|
162
|
+
readonly pg: Kind;
|
|
163
|
+
/** Whether the column is `NOT NULL`. */
|
|
164
|
+
readonly notNull: boolean;
|
|
165
|
+
/** Optional SQL `DEFAULT`. */
|
|
166
|
+
readonly default?: boolean | number | string | undefined;
|
|
167
|
+
/** Optional foreign-key metadata (see {@link DbColumnReference}). */
|
|
168
|
+
readonly references?: DbColumnReference | undefined;
|
|
169
|
+
/**
|
|
170
|
+
* Optional explicit name for the CHECK constraint the column's numeric
|
|
171
|
+
* bounds derive — a migration-compat escape hatch for keeping a historical
|
|
172
|
+
* constraint name where the canonical `${table}_${column}_${suffix}` would
|
|
173
|
+
* rename it. Omit for new columns.
|
|
174
|
+
*/
|
|
175
|
+
readonly checkName?: string | undefined;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* The annotation key under which {@link dbColumn} stores its metadata.
|
|
179
|
+
* {@link dbColumn} writes it; {@link readDbColumn} and {@link fieldColumns}
|
|
180
|
+
* read it. Namespaced to the package so a consumer's own annotations never
|
|
181
|
+
* collide.
|
|
182
|
+
*/
|
|
183
|
+
declare const DB_COLUMN_KEY = "effect-drizzle-ext/dbColumn";
|
|
184
|
+
declare const DbColumnTypeId: unique symbol;
|
|
185
|
+
/**
|
|
186
|
+
* A schema carrying db-column metadata `M` in a type-level phantom (the
|
|
187
|
+
* annotation value itself is erased from the type, so the phantom is what keeps
|
|
188
|
+
* the derived column map precise). The phantom property is **required for type
|
|
189
|
+
* matching** — a non-annotated schema must yield `never`, not `unknown`, so the
|
|
190
|
+
* column derivation can tell columns from plain / computed fields — but it is
|
|
191
|
+
* never present at runtime (the runtime reads the real annotation instead).
|
|
192
|
+
*/
|
|
193
|
+
type Annotated<S extends Schema.Top, M extends DbColumn> = S & {
|
|
194
|
+
readonly [DbColumnTypeId]: M;
|
|
195
|
+
};
|
|
196
|
+
/** Recovers the db-column metadata phantom from a schema, or `never`. */
|
|
197
|
+
type DbColumnOf<S> = S extends {
|
|
198
|
+
readonly [DbColumnTypeId]: infer M extends DbColumn;
|
|
199
|
+
} ? M : never;
|
|
200
|
+
/**
|
|
201
|
+
* Annotate a base schema with the database column it backs. Attaches a genuine
|
|
202
|
+
* Effect annotation (the runtime reads it via {@link readDbColumn}) and carries
|
|
203
|
+
* the metadata literally in the type (so the column derivation stays precisely
|
|
204
|
+
* typed). Wrap the result in a model helper as usual — the helper's projection
|
|
205
|
+
* carries the annotation through to the table factory.
|
|
206
|
+
*
|
|
207
|
+
* Accepts any `pg` string; pin the kind union with {@link dbColumnFor} to catch
|
|
208
|
+
* unknown-kind typos at the annotation site.
|
|
209
|
+
*/
|
|
210
|
+
declare const dbColumn: <S extends Schema.Top, const M extends DbColumn>(schema: S, meta: M) => Annotated<S, M>;
|
|
211
|
+
/**
|
|
212
|
+
* A {@link dbColumn} constrained to a specific column-kind union `Kind`. A
|
|
213
|
+
* consumer pins its own kinds once — `const dbColumn = dbColumnFor<PgKind>()` —
|
|
214
|
+
* so an annotation naming a kind the derivation can't build is a type error at
|
|
215
|
+
* the annotation site rather than a runtime surprise.
|
|
216
|
+
*/
|
|
217
|
+
declare const dbColumnFor: <Kind extends string>() => <S extends Schema.Top, const M extends DbColumn<Kind>>(schema: S, meta: M) => Annotated<S, M>;
|
|
218
|
+
/**
|
|
219
|
+
* Read the db-column annotation off a schema at runtime. `Option.none()` when
|
|
220
|
+
* the schema declares none.
|
|
221
|
+
*
|
|
222
|
+
* Reads the schema's **own** annotations only: `Schema.resolveAnnotations` on a
|
|
223
|
+
* `Schema.optional(...)` / `Schema.NullOr(...)` wrapper does not see the inner
|
|
224
|
+
* schema's annotation. Use {@link fieldColumns} for a struct's fields, which
|
|
225
|
+
* peels those wrappers structurally.
|
|
226
|
+
*/
|
|
227
|
+
declare const readDbColumn: (schema: Schema.Constraint) => Option.Option<DbColumn>;
|
|
228
|
+
/**
|
|
229
|
+
* The base table metadata {@link dbTable} attaches: the SQL leaf name the
|
|
230
|
+
* annotated target backs (a versioned consumer's version table is
|
|
231
|
+
* `${name}_version`).
|
|
232
|
+
*
|
|
233
|
+
* `TableMeta` is the floor, not the whole vocabulary: {@link dbTable} accepts
|
|
234
|
+
* any `TableMeta`, so a **flat** consumer — one whose tables anchor at no
|
|
235
|
+
* hierarchy at all — annotates with `{ name }` and nothing else, instead of
|
|
236
|
+
* re-implementing `dbTable` locally just to drop a field it has no use for.
|
|
237
|
+
* A level-dispatching consumer pins {@link DbTable} via {@link dbTableFor}.
|
|
238
|
+
*/
|
|
239
|
+
interface TableMeta {
|
|
240
|
+
/** The snake_case leaf table name (e.g. `podcast_series`). */
|
|
241
|
+
readonly name: string;
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Table metadata that additionally names the hierarchy level the leaf anchors
|
|
245
|
+
* at (selecting the parent table pair in a level-dispatching consumer). Generic
|
|
246
|
+
* over the level union `Level` (open `string` by default) — a consumer pins it
|
|
247
|
+
* to its own levels via {@link dbTableFor}, so a leaf naming an unknown level is
|
|
248
|
+
* a type error at the annotation site.
|
|
249
|
+
*/
|
|
250
|
+
interface DbTable<Level extends string = string> extends TableMeta {
|
|
251
|
+
/** The hierarchy level the leaf anchors at (selects the parent table pair). */
|
|
252
|
+
readonly level: Level;
|
|
253
|
+
}
|
|
254
|
+
declare const DbTableTypeId: unique symbol;
|
|
255
|
+
/**
|
|
256
|
+
* A target object carrying db-table metadata `M` in a type-level phantom — the
|
|
257
|
+
* table-level counterpart of {@link Annotated}. The phantom keeps a consumer's
|
|
258
|
+
* level dispatch precise (a `"work"`-anchored leaf cannot be passed to a
|
|
259
|
+
* `"manifestation"` table factory); the runtime reads the real attached
|
|
260
|
+
* metadata via {@link readDbTable}.
|
|
261
|
+
*/
|
|
262
|
+
type TableAnnotated<T extends object, M extends TableMeta> = T & {
|
|
263
|
+
readonly [DbTableTypeId]: M;
|
|
264
|
+
};
|
|
265
|
+
/** Recovers the db-table metadata phantom from a target, or `never`. */
|
|
266
|
+
type DbTableOf<T> = T extends {
|
|
267
|
+
readonly [DbTableTypeId]: infer M extends TableMeta;
|
|
268
|
+
} ? M : never;
|
|
269
|
+
/**
|
|
270
|
+
* Annotate any object with the database table it backs. Attaches the metadata
|
|
271
|
+
* as a non-enumerable symbol property (see {@link DB_TABLE_KEY} for why not
|
|
272
|
+
* `annotate`) and carries it literally in the type, so a level-bound table
|
|
273
|
+
* factory can constrain its input to leaves anchored at its level. Returns the
|
|
274
|
+
* **same** object.
|
|
275
|
+
*
|
|
276
|
+
* Accepts any {@link TableMeta}; pin a level union with {@link dbTableFor}.
|
|
277
|
+
*/
|
|
278
|
+
declare const dbTable: <T extends object, const M extends TableMeta>(target: T, meta: M) => TableAnnotated<T, M>;
|
|
279
|
+
/**
|
|
280
|
+
* A {@link dbTable} constrained to a specific level union `Level`, requiring the
|
|
281
|
+
* full {@link DbTable} metadata. A consumer pins its own levels once — `const
|
|
282
|
+
* dbTable = dbTableFor<LeafLevel>()` — so an annotation naming a level the
|
|
283
|
+
* derivation has no parent binding for is a type error at the annotation site
|
|
284
|
+
* rather than a runtime surprise.
|
|
285
|
+
*/
|
|
286
|
+
declare const dbTableFor: <Level extends string>() => <T extends object, const M extends DbTable<Level>>(target: T, meta: M) => TableAnnotated<T, M>;
|
|
287
|
+
/**
|
|
288
|
+
* Read the db-table metadata off a target at runtime. `Option.none()` when the
|
|
289
|
+
* target carries none.
|
|
290
|
+
*
|
|
291
|
+
* Typed to the {@link TableMeta} floor: a consumer that annotated with a
|
|
292
|
+
* {@link DbTable} recovers its `level` through the {@link DbTableOf} phantom,
|
|
293
|
+
* which is where the level is statically known.
|
|
294
|
+
*/
|
|
295
|
+
declare const readDbTable: <T extends object>(target: T) => Option.Option<TableMeta>;
|
|
296
|
+
/**
|
|
297
|
+
* Numeric bounds recovered from a schema's checks — the JSON-Schema bound
|
|
298
|
+
* vocabulary. The source of a derived SQL `CHECK` constraint: one declaration
|
|
299
|
+
* (the model's `Schema.isGreaterThanOrEqualTo(0)` / `isBetween` check) drives
|
|
300
|
+
* wire validation **and** the column's database constraint.
|
|
301
|
+
*/
|
|
302
|
+
interface NumericBounds {
|
|
303
|
+
readonly minimum?: number | undefined;
|
|
304
|
+
readonly maximum?: number | undefined;
|
|
305
|
+
readonly exclusiveMinimum?: number | undefined;
|
|
306
|
+
readonly exclusiveMaximum?: number | undefined;
|
|
307
|
+
}
|
|
308
|
+
/** The bounds of one column-backed attribute, paired with its column metadata. */
|
|
309
|
+
interface ColumnBounds extends NumericBounds {
|
|
310
|
+
readonly column: DbColumn;
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Recover the {@link NumericBounds} a single attribute schema declares, or
|
|
314
|
+
* `Option.none()` when it declares none. Reads the schema's own checks plus the
|
|
315
|
+
* checks of each union member — the `Schema.NullOr(Schema.Finite.check(...))`
|
|
316
|
+
* shape a nullable numeric attribute takes. Deliberately shallow: bounds
|
|
317
|
+
* buried deeper than one union level are a modelling smell, not a supported
|
|
318
|
+
* shape.
|
|
319
|
+
*
|
|
320
|
+
* Exported because the `./jsonapi` entry's resource-level bounds reader is the
|
|
321
|
+
* same walk over a different field source.
|
|
322
|
+
*/
|
|
323
|
+
declare const numericBounds: (schema: Schema.Constraint) => Option.Option<NumericBounds>;
|
|
324
|
+
/**
|
|
325
|
+
* Peels the `Schema.optional(...)` / `Schema.optionalKey(...)` /
|
|
326
|
+
* `Schema.NullOr(...)` / `Schema.UndefinedOr(...)` wrappers a model attribute
|
|
327
|
+
* applies, to reach a {@link dbColumn}-annotated base schema's phantom, or
|
|
328
|
+
* `never`. A required attribute carries the annotation on its base schema
|
|
329
|
+
* directly (no peel); an optional / nullable projection wraps it.
|
|
330
|
+
*
|
|
331
|
+
* The wrapper set mirrors what {@link fieldColumns} peels at runtime — note
|
|
332
|
+
* that `Schema.optional(S)` *is* `optionalKey(UndefinedOr(S))`, so the two pairs
|
|
333
|
+
* are one chain, not four independent cases.
|
|
334
|
+
*/
|
|
335
|
+
type UnwrapColumn<S> = [DbColumnOf<S>] extends [never] ? [S] extends [Schema.optional<infer Inner>] ? UnwrapColumn<Inner> : [S] extends [Schema.optionalKey<infer Inner>] ? UnwrapColumn<Inner> : [S] extends [Schema.NullOr<infer Inner>] ? UnwrapColumn<Inner> : [S] extends [Schema.UndefinedOr<infer Inner>] ? UnwrapColumn<Inner> : never : DbColumnOf<S>;
|
|
336
|
+
/**
|
|
337
|
+
* The db-column metadata map a `Schema.Struct`'s fields project to, keyed by the
|
|
338
|
+
* fields that declare a {@link dbColumn} (others drop out of the map entirely).
|
|
339
|
+
* The type-level counterpart of {@link fieldColumns}.
|
|
340
|
+
*/
|
|
341
|
+
type FieldColumnsMetaOf<F extends Schema.Struct.Fields> = { readonly [K in keyof F as [UnwrapColumn<F[K]>] extends [never] ? never : K]: UnwrapColumn<F[K]>; };
|
|
342
|
+
/**
|
|
343
|
+
* Project a `Schema.Struct`'s fields to the per-field {@link DbColumn} metadata
|
|
344
|
+
* the derivation consumes, dropping fields that declare no column. Peels the
|
|
345
|
+
* `optional` / `NullOr` wrappers a field may apply. The type-level counterpart
|
|
346
|
+
* is {@link FieldColumnsMetaOf}.
|
|
347
|
+
*/
|
|
348
|
+
declare const fieldColumns: (fields: Schema.Struct.Fields) => Record<string, DbColumn>;
|
|
349
|
+
/**
|
|
350
|
+
* Project a `Schema.Struct`'s per-field numeric bounds into plain data a
|
|
351
|
+
* `CHECK`-constraint derivation consumes: for each {@link dbColumn}-annotated
|
|
352
|
+
* field whose schema carries numeric-bound checks, the bounds paired with the
|
|
353
|
+
* column metadata. Fields without a column, or without bounds, drop. The
|
|
354
|
+
* Drizzle-emitting counterpart is the `./derive` entry's
|
|
355
|
+
* `deriveFieldCheckConstraints`.
|
|
356
|
+
*/
|
|
357
|
+
declare const fieldColumnBounds: (fields: Schema.Struct.Fields) => Record<string, ColumnBounds>;
|
|
358
|
+
/** The fields an entry's member `K` (`columns` or `leaf`) declares; `{}` when absent. */
|
|
359
|
+
type EntryFields<E, K extends "columns" | "leaf"> = E extends { readonly [P in K]?: infer F; } ? Exclude<F, undefined> extends Schema.Struct.Fields ? Exclude<F, undefined> : Record<never, never> : Record<never, never>;
|
|
360
|
+
/**
|
|
361
|
+
* The typed column metadata of a {@link defineTree} declaration `S`: per level,
|
|
362
|
+
* the `DbColumn` maps its level columns and its leaf fields carry. The phantom
|
|
363
|
+
* a `Tree` built by `defineTree` holds for the table derivation.
|
|
364
|
+
*/
|
|
365
|
+
type FieldsTreeMeta<S> = { readonly [K in keyof S]: {
|
|
366
|
+
readonly level: FieldColumnsMetaOf<EntryFields<S[K], "columns">>;
|
|
367
|
+
readonly leaf: FieldColumnsMetaOf<EntryFields<S[K], "leaf">>;
|
|
368
|
+
}; };
|
|
369
|
+
/**
|
|
370
|
+
* A {@link defineTree} whose every entry also carries the consumer's
|
|
371
|
+
* extension record `Ext` (pulpit's `owned` / `pageable` flags, say); the
|
|
372
|
+
* library reads none of its members and hands each entry back on
|
|
373
|
+
* `tree.level(name).entry`. Pin it once — `const defineTree =
|
|
374
|
+
* defineTreeFor<RegistryFlags>()` — so an entry missing a flag is a type
|
|
375
|
+
* error. Throws {@link DerivationError} at module load as `defineTree` does.
|
|
376
|
+
*/
|
|
377
|
+
declare const defineTreeFor: <Ext extends object>() => <const S extends CheckedTreeSpec<S, Schema.Struct.Fields, Ext>>(spec: S) => Tree<S, FieldsTreeMeta<S>>;
|
|
378
|
+
/**
|
|
379
|
+
* Declare an ontology's level tree once, Drizzle-free, so a models package can
|
|
380
|
+
* state it and the database layer derive every table, constraint and view from
|
|
381
|
+
* it. Each entry is a level: a **marker** (no `leaf`) or a **leaf**, whose
|
|
382
|
+
* `leaf` is the struct fields of its domain columns (`dbColumn`-annotated) and
|
|
383
|
+
* whose name is its discriminator value. An entry names its `parent` (absent:
|
|
384
|
+
* a branch root under the tree's root), and may carry level `columns`
|
|
385
|
+
* contributed to every descendant's view and a level `kind`.
|
|
386
|
+
*
|
|
387
|
+
* Returns the checked {@link Tree}: per level, its ancestors, children,
|
|
388
|
+
* descendants, subtree leaves (the discriminator CHECK value set) and branch,
|
|
389
|
+
* all in declaration order. A parent naming no level of the tree, or the level
|
|
390
|
+
* itself, is a type error. Throws {@link DerivationError} at module load for
|
|
391
|
+
* every structural fault the types can't catch: a missing parent or a cycle, a
|
|
392
|
+
* leaf with children, a marker with no leaf below it, an unannotated level
|
|
393
|
+
* column, a reserved column name (`id`, `version`, `action`, `type`), or two
|
|
394
|
+
* columns on one root-to-leaf path sharing a field key or a column name.
|
|
395
|
+
*
|
|
396
|
+
* @example
|
|
397
|
+
* ```ts
|
|
398
|
+
* import { dbColumn, defineTree } from "@thomasfosterau/effect-drizzle-ext";
|
|
399
|
+
* import * as Schema from "effect/Schema";
|
|
400
|
+
*
|
|
401
|
+
* const tree = defineTree({
|
|
402
|
+
* work: {
|
|
403
|
+
* columns: { title: dbColumn(Schema.String, { column: "title", pg: "text", notNull: true }) },
|
|
404
|
+
* },
|
|
405
|
+
* book: {
|
|
406
|
+
* parent: "work",
|
|
407
|
+
* leaf: { pages: dbColumn(Schema.Finite, { column: "pages", pg: "integer", notNull: false }) },
|
|
408
|
+
* },
|
|
409
|
+
* film: { parent: "work", leaf: {} },
|
|
410
|
+
* });
|
|
411
|
+
*
|
|
412
|
+
* console.log(tree.level("work").subtreeLeaves); // ["book", "film"]
|
|
413
|
+
* console.log(tree.level("book").ancestors); // ["work"]
|
|
414
|
+
* ```
|
|
415
|
+
*/
|
|
416
|
+
declare const defineTree: <const S extends CheckedTreeSpec<S, Schema.Struct.Fields, Record<never, never>>>(spec: S) => Tree<S, FieldsTreeMeta<S>>;
|
|
417
|
+
export { LevelColumnsMeta as A, fieldColumns as C, AnyTreeColumnsMeta as D, readDbTable as E, Tree as F, TreeLevel as I, DerivationError as L, LevelName as M, LevelSpec as N, CheckedTreeSpec as O, MarkerName as P, fieldColumnBounds as S, readDbColumn as T, dbColumnFor as _, DbColumnOf as a, defineTree as b, DbTableOf as c, NumericBounds as d, ReferentialAction as f, dbColumn as g, UnwrapColumn as h, DbColumn as i, LevelKind as j, LeafName as k, FieldColumnsMetaOf as l, TableMeta as m, ColumnBounds as n, DbColumnReference as o, TableAnnotated as p, DB_COLUMN_KEY as r, DbTable as s, Annotated as t, FieldsTreeMeta as u, dbTable as v, numericBounds as w, defineTreeFor as x, dbTableFor as y };
|
|
418
|
+
//# sourceMappingURL=index-CFhgcF4N.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-CFhgcF4N.d.ts","names":[],"sources":["../src/derivation-error.ts","../src/tree.ts","../src/index.ts"],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { A as LevelColumnsMeta, C as fieldColumns, D as AnyTreeColumnsMeta, E as readDbTable, F as Tree, I as TreeLevel, L as DerivationError, M as LevelName, N as LevelSpec, O as CheckedTreeSpec, P as MarkerName, S as fieldColumnBounds, T as readDbColumn, _ as dbColumnFor, a as DbColumnOf, b as defineTree, c as DbTableOf, d as NumericBounds, f as ReferentialAction, g as dbColumn, h as UnwrapColumn, i as DbColumn, j as LevelKind, k as LeafName, l as FieldColumnsMetaOf, m as TableMeta, n as ColumnBounds, o as DbColumnReference, p as TableAnnotated, r as DB_COLUMN_KEY, s as DbTable, t as Annotated, u as FieldsTreeMeta, v as dbTable, w as numericBounds, x as defineTreeFor, y as dbTableFor } from "./index-CFhgcF4N.js";
|
|
2
|
+
export { Annotated, type AnyTreeColumnsMeta, type CheckedTreeSpec, ColumnBounds, DB_COLUMN_KEY, DbColumn, DbColumnOf, DbColumnReference, DbTable, DbTableOf, DerivationError, FieldColumnsMetaOf, FieldsTreeMeta, type LeafName, type LevelColumnsMeta, type LevelKind, type LevelName, type LevelSpec, type MarkerName, NumericBounds, ReferentialAction, TableAnnotated, TableMeta, type Tree, type TreeLevel, UnwrapColumn, dbColumn, dbColumnFor, dbTable, dbTableFor, defineTree, defineTreeFor, fieldColumnBounds, fieldColumns, numericBounds, readDbColumn, readDbTable };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { t as DB_COLUMN_KEY, h as DerivationError, n as dbColumn, r as dbColumnFor, i as dbTable, a as dbTableFor, o as defineTree, s as defineTreeFor, c as fieldColumnBounds, l as fieldColumns, u as numericBounds, d as readDbColumn, f as readDbTable } from "./src-CBeuhy5b.js";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { ColumnBuilders, DeriveOptions, DerivedColumns } from "../derive.js";
|
|
2
|
+
import { AnyResource, ResourceColumnsMetaOf } from "./index.js";
|
|
3
|
+
import { ExtraConfigColumn, PgTableExtraConfigValue } from "drizzle-orm/pg-core";
|
|
4
|
+
/**
|
|
5
|
+
* The Drizzle column builders a `Resource.make` definition's annotated
|
|
6
|
+
* attributes produce against registry `B`, keyed and typed per attribute.
|
|
7
|
+
*/
|
|
8
|
+
export type DerivedResourceColumns<R extends AnyResource, B extends ColumnBuilders> = DerivedColumns<ResourceColumnsMetaOf<R>, B>;
|
|
9
|
+
/**
|
|
10
|
+
* Build a `deriveResourceColumns` bound to a specific builder registry. The
|
|
11
|
+
* returned function interprets a `Resource.make` definition's per-attribute
|
|
12
|
+
* `dbColumn` annotations into the Drizzle column builders a table factory's
|
|
13
|
+
* `columns` expects.
|
|
14
|
+
*/
|
|
15
|
+
export declare const makeDeriveResourceColumns: <B extends ColumnBuilders>(builders: B, options?: DeriveOptions) => (<R extends AnyResource>(resource: R) => DerivedResourceColumns<R, B>);
|
|
16
|
+
/**
|
|
17
|
+
* Derive the SQL `CHECK` constraints a resource definition's numeric-bound
|
|
18
|
+
* checks imply, named `${tableName}_${column}_${suffix}` unless the annotation
|
|
19
|
+
* pins a `checkName`, and guarded with `col IS NULL OR …` for a nullable
|
|
20
|
+
* column. Returns a callback shaped for a table factory's `extraConstraints`
|
|
21
|
+
* seam; a bounded attribute whose column is absent from the table throws
|
|
22
|
+
* {@link DerivationError}.
|
|
23
|
+
*
|
|
24
|
+
* The projection is deferred to the returned callback, so this composes inside
|
|
25
|
+
* a `pgTable(...)` call whose columns are still being assembled.
|
|
26
|
+
*/
|
|
27
|
+
export declare const deriveCheckConstraints: (resource: AnyResource, tableName: string) => (table: Record<string, ExtraConfigColumn>) => PgTableExtraConfigValue[];
|
|
28
|
+
//# sourceMappingURL=derive.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derive.d.ts","names":[],"sources":["../../src/jsonapi/derive.ts"],"mappings":";;;;;;;YAqCY,uBACV,UAAU,aACV,UAAU,kBACR,eAAe,sBAAsB,IAAI;;;;;;;qBAQhC,4BAA6B,UAAU,gBAAc,UACtD,GAAC,UACF,oBACN,UAAU,aAAa,UAAU,MAAM,uBAAuB,GAAG;;;;;;;;;;;;qBAmBzD,yBAAsB,UACtB,aAAW,uBAAmB,OACjC,eAAe,uBAAqB"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { deriveCheckConstraintsFrom, makeDeriveColumns } from "../derive.js";
|
|
2
|
+
import { resourceColumnBounds, resourceColumns } from "./index.js";
|
|
3
|
+
// src/jsonapi/derive.ts
|
|
4
|
+
/**
|
|
5
|
+
* Build a `deriveResourceColumns` bound to a specific builder registry. The
|
|
6
|
+
* returned function interprets a `Resource.make` definition's per-attribute
|
|
7
|
+
* `dbColumn` annotations into the Drizzle column builders a table factory's
|
|
8
|
+
* `columns` expects.
|
|
9
|
+
*/
|
|
10
|
+
const makeDeriveResourceColumns = (builders, options = {}) => {
|
|
11
|
+
const derive = makeDeriveColumns(builders, options);
|
|
12
|
+
return (resource) => derive.fromMeta(resourceColumns(resource));
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* Derive the SQL `CHECK` constraints a resource definition's numeric-bound
|
|
16
|
+
* checks imply, named `${tableName}_${column}_${suffix}` unless the annotation
|
|
17
|
+
* pins a `checkName`, and guarded with `col IS NULL OR …` for a nullable
|
|
18
|
+
* column. Returns a callback shaped for a table factory's `extraConstraints`
|
|
19
|
+
* seam; a bounded attribute whose column is absent from the table throws
|
|
20
|
+
* {@link DerivationError}.
|
|
21
|
+
*
|
|
22
|
+
* The projection is deferred to the returned callback, so this composes inside
|
|
23
|
+
* a `pgTable(...)` call whose columns are still being assembled.
|
|
24
|
+
*/
|
|
25
|
+
const deriveCheckConstraints = (resource, tableName) => (table) => deriveCheckConstraintsFrom(resourceColumnBounds(resource), tableName)(table);
|
|
26
|
+
export { deriveCheckConstraints, makeDeriveResourceColumns };
|
|
27
|
+
|
|
28
|
+
//# sourceMappingURL=derive.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"derive.js","names":[],"sources":["../../src/jsonapi/derive.ts"],"mappings":";;;;;;;;;AAgDA,MAAa,6BACX,UACA,UAAyB,CAAC,MACiD;CAC3E,MAAM,SAAS,kBAAkB,UAAU,OAAO;CAClD,QAA+B,aAG7B,OAAO,SAAS,gBAAgB,QAAQ,CAAC;AAC7C;;;;;;;;;;;;AAaA,MAAa,0BACV,UAAuB,eACvB,UACC,2BAA2B,qBAAqB,QAAQ,GAAG,SAAS,CAAC,CAAC,KAAK"}
|