@voltro/database 0.4.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +68 -0
- package/dist/{fileBased-CRXgPJkv.js → fileBased-Cc-F1oFv.js} +143 -143
- package/dist/index.d.ts +192 -14
- package/dist/index.js +695 -533
- package/dist/sql.d.ts +18 -0
- package/dist/sql.js +240 -231
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1124,6 +1124,18 @@ export declare interface ColumnDefinition<TsType, Type extends ColumnType = Colu
|
|
|
1124
1124
|
readonly __tsType?: TsType;
|
|
1125
1125
|
}
|
|
1126
1126
|
|
|
1127
|
+
/**
|
|
1128
|
+
* The per-column mapping, over a column DEFINITION — what `table.fields` holds.
|
|
1129
|
+
*
|
|
1130
|
+
* Not the builder `text()` returns: its `definition` is private, so a builder
|
|
1131
|
+
* cannot be read from outside. Worth stating because the natural call is
|
|
1132
|
+
* `columnSchema(timestamp())`, and an earlier version accepted it structurally,
|
|
1133
|
+
* found no `type`, and fell through to `Schema.Unknown` — which passes every
|
|
1134
|
+
* value through unchanged, so the mapping looked right in any test that only
|
|
1135
|
+
* checked a round trip. Take definitions from `table.fields`.
|
|
1136
|
+
*/
|
|
1137
|
+
export declare const columnSchema: <TsType, C extends ColumnType>(def: ColumnDefinition<TsType, C>) => Schema.Schema.Any;
|
|
1138
|
+
|
|
1127
1139
|
export declare type ColumnType = 'id' | 'text' | 'integer' | 'real' | 'decimal' | 'bigint' | 'boolean' | 'timestamp' | 'date' | 'json' | 'bytes' | 'reference' | 'vector' | 'enum' | 'array' | 'interval' | 'raw';
|
|
1128
1140
|
|
|
1129
1141
|
/**
|
|
@@ -1136,9 +1148,13 @@ export declare type ColumnType = 'id' | 'text' | 'integer' | 'real' | 'decimal'
|
|
|
1136
1148
|
* - A relation name in the eager tree isn't registered (corner case
|
|
1137
1149
|
* the walker would surface with a clearer error — better to fall
|
|
1138
1150
|
* back to it).
|
|
1139
|
-
* -
|
|
1140
|
-
*
|
|
1141
|
-
*
|
|
1151
|
+
* - An `inferForeignKey` on the eager tree is ambiguous, so there is no
|
|
1152
|
+
* single key to correlate on (the walker raises the explanatory error).
|
|
1153
|
+
*
|
|
1154
|
+
* There is no feature the compiler declines on purpose. `junction: [...]`
|
|
1155
|
+
* projection and the `one`-cardinality check — the two that used to be served
|
|
1156
|
+
* by the walker for correctness reasons — are compiled per dialect and covered
|
|
1157
|
+
* by walker-vs-JSON parity tests against every live engine.
|
|
1142
1158
|
*
|
|
1143
1159
|
* Callers (the dialect data stores) treat `null` as "fall back to the
|
|
1144
1160
|
* walker". This keeps the optimization opt-out-by-failure rather than
|
|
@@ -1251,7 +1267,15 @@ export declare type DatabaseHandle<Tables extends Record<string, Table<string, R
|
|
|
1251
1267
|
export declare const databaseHandle: <Tables extends Record<string, Table<string, Record<string, ColumnDefinition<unknown>>, boolean, string>>>(tables: Tables) => DatabaseHandle<Tables>;
|
|
1252
1268
|
|
|
1253
1269
|
export declare interface DataStore {
|
|
1254
|
-
/**
|
|
1270
|
+
/**
|
|
1271
|
+
* Execute a typed query descriptor and return matching rows.
|
|
1272
|
+
*
|
|
1273
|
+
* Deliberately NOT generic: `DataStore` is the driver SPI, implemented by
|
|
1274
|
+
* every dialect store and its transactional / namespace views. Threading a
|
|
1275
|
+
* phantom row type through all of them would be pure churn — they genuinely
|
|
1276
|
+
* do return untyped rows off the wire. The row type is re-applied one layer
|
|
1277
|
+
* up, on `FluentStore.query`, which is what handlers actually call.
|
|
1278
|
+
*/
|
|
1255
1279
|
query(descriptor: QueryDescriptor): Promise<ReadonlyArray<Row>>;
|
|
1256
1280
|
/** Insert a row. Emits an insert ChangeEvent (deferred if inside `transactional()`). */
|
|
1257
1281
|
insert(table: string, row: Row): Promise<Row>;
|
|
@@ -1748,6 +1772,22 @@ export declare const drainIdentifierWarnings: () => ReadonlyArray<string>;
|
|
|
1748
1772
|
*/
|
|
1749
1773
|
export declare const dropped: () => ColumnBuilder<unknown, "text">;
|
|
1750
1774
|
|
|
1775
|
+
/**
|
|
1776
|
+
* Raised when a relation declared `one` matches MORE than one row for the same
|
|
1777
|
+
* parent key — the mis-declaration that silently hands the app an arbitrary
|
|
1778
|
+
* child instead of the parent it asked for.
|
|
1779
|
+
*
|
|
1780
|
+
* It is its own class because BOTH eager paths raise it: the walker while
|
|
1781
|
+
* bucketing, the single-query JSON path while decoding an over-fetched array.
|
|
1782
|
+
* The stores' JSON-agg attempt falls back to the walker on any error; this one
|
|
1783
|
+
* must NOT trigger that fallback — the walker would re-run the same query and
|
|
1784
|
+
* raise the same message, costing a round trip to reach the identical outcome.
|
|
1785
|
+
*/
|
|
1786
|
+
export declare class EagerCardinalityError extends Error {
|
|
1787
|
+
readonly _tag = "EagerCardinalityError";
|
|
1788
|
+
constructor(message: string);
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1751
1791
|
/**
|
|
1752
1792
|
* Compiled JSON-aggregation plan: a `Statement.Statement` ready to
|
|
1753
1793
|
* execute via `SqlClient.SqlClient`, plus a `decode()` that restores
|
|
@@ -1794,6 +1834,28 @@ export declare interface EagerLoadSpec {
|
|
|
1794
1834
|
/** Per-parent offset. Only meaningful with `limit`. */
|
|
1795
1835
|
readonly offset?: number;
|
|
1796
1836
|
readonly with?: WithSpec;
|
|
1837
|
+
/**
|
|
1838
|
+
* Project the JUNCTION row's own columns into a `manyToMany` eager result.
|
|
1839
|
+
*
|
|
1840
|
+
* `true` takes every junction column; an array takes only those named. The
|
|
1841
|
+
* values land under `_junction` on each target row:
|
|
1842
|
+
*
|
|
1843
|
+
* projects.with({ teams: { junction: ['role', 'addedAt'] } })
|
|
1844
|
+
* // → team._junction.addedAt
|
|
1845
|
+
*
|
|
1846
|
+
* Nested rather than merged onto the target row, deliberately: a junction and
|
|
1847
|
+
* its target routinely share column names (`createdAt` is the obvious one),
|
|
1848
|
+
* and merging would silently overwrite real target data with membership data.
|
|
1849
|
+
* A name collision that corrupts a row is worse than one extra level.
|
|
1850
|
+
*
|
|
1851
|
+
* Why this exists: junction tables carrying meaningful columns — a role, a
|
|
1852
|
+
* joined-at stamp, a permission tier — are the rule rather than the
|
|
1853
|
+
* exception, and without this an eager load could FILTER on them
|
|
1854
|
+
* (`onJunction`) but never return them, so any relation with real membership
|
|
1855
|
+
* data had to stay a hand-written join. The junction rows were already being
|
|
1856
|
+
* fetched in full to resolve the target ids; this stops throwing them away.
|
|
1857
|
+
*/
|
|
1858
|
+
readonly junction?: true | ReadonlyArray<string>;
|
|
1797
1859
|
/**
|
|
1798
1860
|
* Filter a `manyToMany` eager branch on the THROUGH-table's columns.
|
|
1799
1861
|
* The predicate is evaluated against the junction row, not the target
|
|
@@ -1818,14 +1880,6 @@ export declare interface EagerLoadSpec {
|
|
|
1818
1880
|
*/
|
|
1819
1881
|
export declare type EagerLookup = (descriptor: QueryDescriptor) => Promise<ReadonlyArray<Row_2>>;
|
|
1820
1882
|
|
|
1821
|
-
/**
|
|
1822
|
-
* The compiler walks `descriptor.eager` ONCE to build this tree —
|
|
1823
|
-
* each node has the relation's resolved kind, the resolved FK column
|
|
1824
|
-
* names (auto-inferred when not explicit), the target table's column
|
|
1825
|
-
* shape, and the resolved nested children. Dialects render this
|
|
1826
|
-
* structure into their JSON-agg dialect; the decoder walks it to
|
|
1827
|
-
* restore types.
|
|
1828
|
-
*/
|
|
1829
1883
|
export declare interface EagerRelationNode {
|
|
1830
1884
|
readonly relationName: string;
|
|
1831
1885
|
readonly relation: Relation;
|
|
@@ -1839,6 +1893,8 @@ export declare interface EagerRelationNode {
|
|
|
1839
1893
|
readonly fkSide: 'source' | 'target';
|
|
1840
1894
|
readonly branch: EagerLoadSpec;
|
|
1841
1895
|
readonly children: ReadonlyArray<EagerRelationNode>;
|
|
1896
|
+
/** Set only on `manyToMany` branches that asked for junction columns. */
|
|
1897
|
+
readonly junction: JunctionProjection | undefined;
|
|
1842
1898
|
}
|
|
1843
1899
|
|
|
1844
1900
|
export declare interface EagerRootPlan {
|
|
@@ -2489,6 +2545,30 @@ export declare interface JsonIndexPath {
|
|
|
2489
2545
|
readonly numeric: boolean;
|
|
2490
2546
|
}
|
|
2491
2547
|
|
|
2548
|
+
/**
|
|
2549
|
+
* The compiler walks `descriptor.eager` ONCE to build this tree —
|
|
2550
|
+
* each node has the relation's resolved kind, the resolved FK column
|
|
2551
|
+
* names (auto-inferred when not explicit), the target table's column
|
|
2552
|
+
* shape, and the resolved nested children. Dialects render this
|
|
2553
|
+
* structure into their JSON-agg dialect; the decoder walks it to
|
|
2554
|
+
* restore types.
|
|
2555
|
+
*/
|
|
2556
|
+
/**
|
|
2557
|
+
* A resolved `junction: [...]` / `junction: true` projection on a `manyToMany`
|
|
2558
|
+
* branch. `requested` is what the caller asked for IN ORDER; `present` is the
|
|
2559
|
+
* subset the through table actually has — the only columns SQL can select. The
|
|
2560
|
+
* decoder re-materialises the difference as `undefined`, matching the walker,
|
|
2561
|
+
* which reads a missing key off the junction row and gets `undefined` rather
|
|
2562
|
+
* than raising.
|
|
2563
|
+
*/
|
|
2564
|
+
export declare interface JunctionProjection {
|
|
2565
|
+
readonly through: TableLike;
|
|
2566
|
+
readonly requested: ReadonlyArray<string>;
|
|
2567
|
+
readonly present: ReadonlyArray<string>;
|
|
2568
|
+
/** Present columns whose schema type needs ISO-string → Date restoration. */
|
|
2569
|
+
readonly dateColumns: ReadonlyArray<string>;
|
|
2570
|
+
}
|
|
2571
|
+
|
|
2492
2572
|
export declare const lag: (column: string, offset?: number, alias?: string) => WindowBuilder;
|
|
2493
2573
|
|
|
2494
2574
|
export declare const lead: (column: string, offset?: number, alias?: string) => WindowBuilder;
|
|
@@ -2801,6 +2881,19 @@ export declare const one: (target: TableRef, options?: {
|
|
|
2801
2881
|
readonly sourceKey?: string;
|
|
2802
2882
|
}) => OneRelation;
|
|
2803
2883
|
|
|
2884
|
+
/**
|
|
2885
|
+
* The single source of the `one`-cardinality message. Both eager paths call it
|
|
2886
|
+
* so a user comparing them can never be shown two different explanations of the
|
|
2887
|
+
* same mistake.
|
|
2888
|
+
*/
|
|
2889
|
+
export declare const oneCardinalityMessage: (args: {
|
|
2890
|
+
readonly relationName: string;
|
|
2891
|
+
readonly sourceTableName: string;
|
|
2892
|
+
readonly targetTableName: string;
|
|
2893
|
+
readonly foreignKey: string;
|
|
2894
|
+
readonly sourceKey: string;
|
|
2895
|
+
}) => string;
|
|
2896
|
+
|
|
2804
2897
|
/** 1:1 — the source table OR the target carries the FK. */
|
|
2805
2898
|
export declare interface OneRelation {
|
|
2806
2899
|
readonly kind: 'one';
|
|
@@ -2829,6 +2922,29 @@ export declare interface OrPredicate {
|
|
|
2829
2922
|
readonly or: ReadonlyArray<Predicate>;
|
|
2830
2923
|
}
|
|
2831
2924
|
|
|
2925
|
+
/**
|
|
2926
|
+
* Keyset pagination on ANY orderable column — the general form of
|
|
2927
|
+
* `paginateById`.
|
|
2928
|
+
*
|
|
2929
|
+
* `paginateById` hardcodes `id`, which is correct for a stable typeid/uuidv7
|
|
2930
|
+
* key and useless for the case real feeds actually need: "the next page by
|
|
2931
|
+
* `createdAt`". Apps hit that immediately and fell back to hand-rolled
|
|
2932
|
+
* `limit + 1` / slice / `hasMore` triples — often on a timestamp cursor the
|
|
2933
|
+
* helper could not express at all.
|
|
2934
|
+
*
|
|
2935
|
+
* The column MUST be unique, or monotonic enough that ties do not straddle a
|
|
2936
|
+
* page boundary; otherwise a keyset cursor can skip or repeat rows at the seam.
|
|
2937
|
+
* For a non-unique column (a timestamp with collisions) pass a composite order
|
|
2938
|
+
* on the descriptor first — `.orderBy('createdAt').orderBy('id')` — and
|
|
2939
|
+
* paginate by the tie-breaker.
|
|
2940
|
+
*
|
|
2941
|
+
* `direction` controls both the comparison and the ORDER BY, so a `desc` feed
|
|
2942
|
+
* ("newest first") pages with `<` instead of `>`. Getting that pairing wrong is
|
|
2943
|
+
* the classic keyset bug: an ascending comparison under a descending sort
|
|
2944
|
+
* silently returns the same page forever.
|
|
2945
|
+
*/
|
|
2946
|
+
export declare const paginateBy: (descriptor: QueryDescriptor, column: string, cursor: string | number | Date | undefined, limit: number, direction?: "asc" | "desc") => QueryDescriptor;
|
|
2947
|
+
|
|
2832
2948
|
/**
|
|
2833
2949
|
* Cursor pagination by primary key — `WHERE id > cursor ORDER BY id ASC LIMIT n`.
|
|
2834
2950
|
*
|
|
@@ -2968,7 +3084,8 @@ export declare interface Query<RowOf, IxNames extends string = string,
|
|
|
2968
3084
|
* `'alias.column'` → value type. Unjoined queries default to `{}`.
|
|
2969
3085
|
*/
|
|
2970
3086
|
Joins extends JoinsMap = {}> {
|
|
2971
|
-
|
|
3087
|
+
/** Carries `RowOf` through to `store.query()` — see `QueryDescriptor<R>`. */
|
|
3088
|
+
readonly descriptor: QueryDescriptor<RowOf>;
|
|
2972
3089
|
/** Refine the predicate. Multiple `.where(...)` calls combine with AND. */
|
|
2973
3090
|
where: (predicate: Predicate) => Query<RowOf, IxNames, Joins>;
|
|
2974
3091
|
orderBy: <K extends keyof RowOf & string>(column: K, direction?: 'asc' | 'desc') => Query<RowOf, IxNames, Joins>;
|
|
@@ -3338,9 +3455,30 @@ Joins extends JoinsMap = {}> {
|
|
|
3338
3455
|
}, IxNames, Joins>;
|
|
3339
3456
|
}
|
|
3340
3457
|
|
|
3341
|
-
|
|
3458
|
+
/**
|
|
3459
|
+
* The runtime-facing materialized query — a plain bag of strings the store and
|
|
3460
|
+
* the matcher consume.
|
|
3461
|
+
*
|
|
3462
|
+
* `R` is a PHANTOM row type. It exists so the row shape the typed builder
|
|
3463
|
+
* already knows survives the trip through `.descriptor` into `store.query()`,
|
|
3464
|
+
* which is where it used to be thrown away: `store.query(database.notes...)`
|
|
3465
|
+
* returned `Row` (`Record<string, unknown>`), so every field read needed a
|
|
3466
|
+
* hand-written cast. One downstream app wrote 2,032 of them. The type was
|
|
3467
|
+
* always available — it was just dropped at this boundary.
|
|
3468
|
+
*
|
|
3469
|
+
* It is an OPTIONAL field, never set at runtime. That matters for two reasons:
|
|
3470
|
+
* a plain object literal still satisfies `QueryDescriptor` (dispatcher, matcher
|
|
3471
|
+
* and store code build and forward these structurally), and `R` stays
|
|
3472
|
+
* covariant, so a `QueryDescriptor<Note>` is still assignable to the bare
|
|
3473
|
+
* `QueryDescriptor` that generic infrastructure passes around. Defaulting to
|
|
3474
|
+
* `Row` keeps every existing annotation meaning exactly what it meant before.
|
|
3475
|
+
*/
|
|
3476
|
+
export declare interface QueryDescriptor<R = Row> {
|
|
3342
3477
|
readonly table: string;
|
|
3343
3478
|
readonly predicate: Predicate | undefined;
|
|
3479
|
+
/** Phantom — carries the row type through to `store.query()`. Never set at
|
|
3480
|
+
* runtime; erased entirely by the compiler. */
|
|
3481
|
+
readonly __row?: R;
|
|
3344
3482
|
/**
|
|
3345
3483
|
* Opt OUT of the runtime's automatic `deletedAt IS NULL` scope on
|
|
3346
3484
|
* `softDelete()` tables. Set by `.withDeleted()`. The query builder is
|
|
@@ -3987,6 +4125,19 @@ declare type Row_3 = Readonly<Record<string, unknown>>;
|
|
|
3987
4125
|
|
|
3988
4126
|
export declare const rowNumber: (alias?: string) => WindowBuilder;
|
|
3989
4127
|
|
|
4128
|
+
/**
|
|
4129
|
+
* Build a row schema for `table`.
|
|
4130
|
+
*
|
|
4131
|
+
* `omit` drops columns from the OUTPUT — the way to keep an `.encrypted()`
|
|
4132
|
+
* token or an internal bookkeeping column off the wire. It is a convenience,
|
|
4133
|
+
* not a security boundary: a column omitted here is simply absent from this
|
|
4134
|
+
* schema, and a handler that returns it under a different schema still sends
|
|
4135
|
+
* it.
|
|
4136
|
+
*/
|
|
4137
|
+
export declare const rowSchema: <T extends TableLike>(table: T, options?: {
|
|
4138
|
+
readonly omit?: ReadonlyArray<string>;
|
|
4139
|
+
}) => Schema.Schema.Any;
|
|
4140
|
+
|
|
3990
4141
|
/**
|
|
3991
4142
|
* Lower a tenant id to the safe identifier fragment used inside a
|
|
3992
4143
|
* namespace name. ONLY `[a-z0-9_]` survive; every other character —
|
|
@@ -4884,6 +5035,33 @@ export declare const validateColumnName: (tableName: string, columnName: string)
|
|
|
4884
5035
|
*/
|
|
4885
5036
|
export declare const validateIndexName: (tableName: string, indexName: string, isAuto: boolean, fields: ReadonlyArray<string>) => void;
|
|
4886
5037
|
|
|
5038
|
+
/**
|
|
5039
|
+
* Validate every registered relation's key options against the real tables.
|
|
5040
|
+
*
|
|
5041
|
+
* Deliberately a BOOT SWEEP rather than a check inside `registerRelations`:
|
|
5042
|
+
* a relation's target is a thunk precisely so circular declarations can be
|
|
5043
|
+
* written (`one(() => teams, ...)`), and resolving it at registration time
|
|
5044
|
+
* would force the declaration order the DSL exists to avoid. By boot every
|
|
5045
|
+
* table and every spec is loaded, so the thunks are safe to call.
|
|
5046
|
+
*
|
|
5047
|
+
* Throws on the first bad relation, naming the option that would have been
|
|
5048
|
+
* right — see `validateRelationConfig` for the bug that motivated it. A source
|
|
5049
|
+
* table that is not in the table registry is SKIPPED rather than reported:
|
|
5050
|
+
* that is a different failure with its own clearer message at query time, and
|
|
5051
|
+
* turning it into a relations error here would misdirect.
|
|
5052
|
+
*/
|
|
5053
|
+
export declare const validateRegisteredRelations: () => void;
|
|
5054
|
+
|
|
5055
|
+
/**
|
|
5056
|
+
* Reject a relation whose explicit key options cannot possibly resolve.
|
|
5057
|
+
* Pure: takes the already-resolved tables, so it is safe to call from a boot
|
|
5058
|
+
* sweep AND from the join compiler.
|
|
5059
|
+
*
|
|
5060
|
+
* Throws with the corrected option named whenever the column exists on the
|
|
5061
|
+
* OTHER side — that mix-up is the whole reason this function exists.
|
|
5062
|
+
*/
|
|
5063
|
+
export declare const validateRelationConfig: (source: TableLike, relationName: string, relation: Relation) => void;
|
|
5064
|
+
|
|
4887
5065
|
/**
|
|
4888
5066
|
* Validate a table name supplied to `table('<name>', ...)`. Throws on
|
|
4889
5067
|
* empty / invalid characters / > 63 bytes. Records a WARN above 50.
|