@voltro/database 0.27.0 → 0.29.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 +513 -0
- package/dist/{frameworkLiveTables-CS_Hyhgz.js → frameworkLiveTables-CMyumigs.js} +169 -125
- package/dist/index.d.ts +305 -0
- package/dist/index.js +634 -594
- package/dist/sql.d.ts +649 -0
- package/dist/sql.js +1 -1
- package/package.json +2 -2
package/dist/sql.d.ts
CHANGED
|
@@ -9,6 +9,49 @@ import { Statement } from '@effect/sql';
|
|
|
9
9
|
|
|
10
10
|
export declare const acquireMigrationLock: (sql: SqlClient.SqlClient) => Effect.Effect<void, SqlError_2>;
|
|
11
11
|
|
|
12
|
+
declare interface AggregateColumn<TResult = number | Date | null> {
|
|
13
|
+
readonly op: 'count' | 'sum' | 'avg' | 'min' | 'max' | 'count-distinct' | 'window-row-number' | 'window-rank' | 'window-dense-rank' | 'window-lag' | 'window-lead' | 'window-sum-over' | 'window-avg-over' | 'column';
|
|
14
|
+
readonly column?: string;
|
|
15
|
+
readonly alias: string;
|
|
16
|
+
readonly window?: WindowSpec;
|
|
17
|
+
/** Used by lag/lead — offset rows (default 1). */
|
|
18
|
+
readonly offset?: number;
|
|
19
|
+
/** Phantom — carries the projected/aggregated result type so
|
|
20
|
+
* `aggregate<Spec>()` can infer a precise result row. Never set at
|
|
21
|
+
* runtime; exists purely for `AggregateColumn<infer T>` inference. */
|
|
22
|
+
readonly _result?: TResult;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
declare interface AndPredicate {
|
|
26
|
+
readonly and: ReadonlyArray<Predicate>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Approximate-nearest-neighbour search clause carried on a
|
|
31
|
+
* {@link QueryDescriptor}. See {@link QueryDescriptor.annClause}.
|
|
32
|
+
*/
|
|
33
|
+
declare interface AnnClause {
|
|
34
|
+
/** Vector column to search. */
|
|
35
|
+
readonly column: string;
|
|
36
|
+
/** Literal query vector (array overload). Mutually exclusive with `queryText`. */
|
|
37
|
+
readonly queryVector?: ReadonlyArray<number>;
|
|
38
|
+
/** Raw query string (string overload) — embedded by the runtime before
|
|
39
|
+
* compile. Mutually exclusive with `queryVector`. */
|
|
40
|
+
readonly queryText?: string;
|
|
41
|
+
/** Distance metric — MUST match the index opclass to accelerate. Default 'cosine'. */
|
|
42
|
+
readonly distance: VectorDistance;
|
|
43
|
+
/** Per-query HNSW search effort (pgvector `hnsw.ef_search`). Set by `.efSearch(n)`. */
|
|
44
|
+
readonly ef?: number;
|
|
45
|
+
/**
|
|
46
|
+
* Hybrid-search fusion weight in `[0, 1]`. Set by `hybridSearch(...)`
|
|
47
|
+
* when both an ANN ranking and an FTS filter are present: `0` = pure
|
|
48
|
+
* FTS, `1` = pure vector, `0.5` = balanced. The runtime fuses the two
|
|
49
|
+
* rank signals (Reciprocal-Rank-Fusion) weighted by this. Absent for a
|
|
50
|
+
* plain `nearestNeighbours(...)` query.
|
|
51
|
+
*/
|
|
52
|
+
readonly alpha?: number;
|
|
53
|
+
}
|
|
54
|
+
|
|
12
55
|
declare type AnyMixin = MixinDefinition<Record<string, ColumnDefinition<unknown>>>;
|
|
13
56
|
|
|
14
57
|
declare type AnyTable = Table<string, Record<string, ColumnDefinition<unknown>>, boolean, string>;
|
|
@@ -399,6 +442,45 @@ declare interface ColumnDefinition<TsType, Type extends ColumnType = ColumnType,
|
|
|
399
442
|
* COSTS is per command — see `serverOnly()` below for the matrix.
|
|
400
443
|
*/
|
|
401
444
|
readonly serverOnly?: boolean;
|
|
445
|
+
/**
|
|
446
|
+
* Field-level read scopes, set by `.readableBy(...scopes)`. A NON-EMPTY list
|
|
447
|
+
* of scope strings; the column is stripped from wire OUTPUT for any subject
|
|
448
|
+
* that holds NONE of them, and present for a subject holding at least ONE.
|
|
449
|
+
* This is the graded middle of the exposure axis: `.serverOnly()` hides a
|
|
450
|
+
* column from EVERYONE on the wire, a plain column is visible to everyone,
|
|
451
|
+
* and `.readableBy('billing:read')` is visible only to callers scoped for it.
|
|
452
|
+
*
|
|
453
|
+
* The scope strings are the SAME vocabulary as `guards:` / `ctx.access` — they
|
|
454
|
+
* are checked against the subject's EFFECTIVE scope set (raw subject scopes ∪
|
|
455
|
+
* any rbac role-derived scopes), and the `admin:full` bypass sees every
|
|
456
|
+
* `.readableBy()` column, exactly as it satisfies every guard. Empty here is a
|
|
457
|
+
* declaration error (`.readableBy()` with no scope would mean "nobody" — use
|
|
458
|
+
* `.serverOnly()` for that), so the field is never present as `[]`.
|
|
459
|
+
*
|
|
460
|
+
* Orthogonal to `.encrypted()` (storage-at-rest) and to `.sensitive()` /
|
|
461
|
+
* `.safe()` (export-time masking): this is purely the WIRE-exposure axis.
|
|
462
|
+
*/
|
|
463
|
+
readonly readableBy?: ReadonlyArray<string>;
|
|
464
|
+
/**
|
|
465
|
+
* CRDT-managed marker, set by the `crdtText()` column constructor. When `true`
|
|
466
|
+
* the column holds an ENCODED CRDT state (opaque bytes — a Yjs update blob
|
|
467
|
+
* behind `@voltro/local-first`'s swappable backend) rather than a plain value.
|
|
468
|
+
*
|
|
469
|
+
* It changes NOTHING about the column's DDL or how the declarative differ
|
|
470
|
+
* treats it: the storage type is `bytes` (BYTEA / BLOB / LONGBLOB /
|
|
471
|
+
* VARBINARY), a normal storable type, and the planner compares it structurally
|
|
472
|
+
* like any other `bytes` column — introspection never reports this flag, and
|
|
473
|
+
* `sameColumnShape` never reads it, so a `crdtText()` column round-trips
|
|
474
|
+
* through a plan with zero churn (no phantom re-plan).
|
|
475
|
+
*
|
|
476
|
+
* What it DOES drive lives above the DDL, in the runtime write path: the
|
|
477
|
+
* MutationStore reads it (via the SchemaRegistry) to run the AUTHORITATIVE
|
|
478
|
+
* server-side merge on write — an incoming encoded update is folded into the
|
|
479
|
+
* stored state with `mergeCrdtStates` before the row is written, so two
|
|
480
|
+
* concurrent clients converge. Orthogonal to every other flag here; a CRDT
|
|
481
|
+
* column is just `bytes` with server-merge semantics.
|
|
482
|
+
*/
|
|
483
|
+
readonly crdtManaged?: boolean;
|
|
402
484
|
/**
|
|
403
485
|
* Optimistic-concurrency marker, set by `.version()`.
|
|
404
486
|
*
|
|
@@ -851,6 +933,75 @@ export declare interface DriftReport {
|
|
|
851
933
|
}>;
|
|
852
934
|
}
|
|
853
935
|
|
|
936
|
+
/**
|
|
937
|
+
* Per-branch refinement passed to `.with({ relation: { ... } })`.
|
|
938
|
+
*
|
|
939
|
+
* `true` is the zero-config shorthand — equivalent to `{}`. Otherwise
|
|
940
|
+
* each branch can carry its own `where` / `orderBy` / `limit` /
|
|
941
|
+
* `offset` / nested `with`, so `users.with({ posts: { limit: 10,
|
|
942
|
+
* orderBy: 'createdAt' } })` works without dropping into a sub-query
|
|
943
|
+
* builder.
|
|
944
|
+
*
|
|
945
|
+
* Untyped at runtime: the QueryDescriptor is the boundary between the
|
|
946
|
+
* typed builder API and the dialect-agnostic JoinCompiler. End-user
|
|
947
|
+
* types live one layer up in the typed `.with()` method on `Query`.
|
|
948
|
+
*/
|
|
949
|
+
declare interface EagerLoadSpec {
|
|
950
|
+
readonly where?: Predicate;
|
|
951
|
+
readonly orderBy?: ReadonlyArray<OrderClause>;
|
|
952
|
+
/**
|
|
953
|
+
* Per-parent cap on the relation. `{ limit: 10 }` returns up to 10
|
|
954
|
+
* rows FOR EACH parent — same semantics as Drizzle / Prisma /
|
|
955
|
+
* Hibernate. This matches what users actually mean when they write
|
|
956
|
+
* "the 10 latest posts" inside `users.with({ posts: { limit: 10 } })`.
|
|
957
|
+
*
|
|
958
|
+
* The SQL stores express this natively as a `LIMIT` clause in a
|
|
959
|
+
* correlated subquery (postgres / sqlite / mysql / mariadb) or
|
|
960
|
+
* via `TOP N` inside `FOR JSON PATH` (mssql) when the JSON-agg path
|
|
961
|
+
* is active. The walker fallback post-buckets the batch fetch and
|
|
962
|
+
* slices each bucket.
|
|
963
|
+
*/
|
|
964
|
+
readonly limit?: number;
|
|
965
|
+
/** Per-parent offset. Only meaningful with `limit`. */
|
|
966
|
+
readonly offset?: number;
|
|
967
|
+
readonly with?: WithSpec;
|
|
968
|
+
/**
|
|
969
|
+
* Project the JUNCTION row's own columns into a `manyToMany` eager result.
|
|
970
|
+
*
|
|
971
|
+
* `true` takes every junction column; an array takes only those named. The
|
|
972
|
+
* values land under `_junction` on each target row:
|
|
973
|
+
*
|
|
974
|
+
* projects.with({ teams: { junction: ['role', 'addedAt'] } })
|
|
975
|
+
* // → team._junction.addedAt
|
|
976
|
+
*
|
|
977
|
+
* Nested rather than merged onto the target row, deliberately: a junction and
|
|
978
|
+
* its target routinely share column names (`createdAt` is the obvious one),
|
|
979
|
+
* and merging would silently overwrite real target data with membership data.
|
|
980
|
+
* A name collision that corrupts a row is worse than one extra level.
|
|
981
|
+
*
|
|
982
|
+
* Why this exists: junction tables carrying meaningful columns — a role, a
|
|
983
|
+
* joined-at stamp, a permission tier — are the rule rather than the
|
|
984
|
+
* exception, and without this an eager load could FILTER on them
|
|
985
|
+
* (`onJunction`) but never return them, so any relation with real membership
|
|
986
|
+
* data had to stay a hand-written join. The junction rows were already being
|
|
987
|
+
* fetched in full to resolve the target ids; this stops throwing them away.
|
|
988
|
+
*/
|
|
989
|
+
readonly junction?: true | ReadonlyArray<string>;
|
|
990
|
+
/**
|
|
991
|
+
* Filter a `manyToMany` eager branch on the THROUGH-table's columns.
|
|
992
|
+
* The predicate is evaluated against the junction row, not the target
|
|
993
|
+
* row — e.g. for `users.with({ organizations: { onJunction: eq('role',
|
|
994
|
+
* 'admin') } })` it keeps only the memberships where `role = 'admin'`,
|
|
995
|
+
* so the loaded `organizations` are exactly the orgs the user is an
|
|
996
|
+
* admin of.
|
|
997
|
+
*
|
|
998
|
+
* Compiles into the correlated subquery's WHERE alongside the
|
|
999
|
+
* source-key correlation, qualified to the junction-table alias.
|
|
1000
|
+
* Ignored on `one` / `many` branches (no junction table exists).
|
|
1001
|
+
*/
|
|
1002
|
+
readonly onJunction?: Predicate;
|
|
1003
|
+
}
|
|
1004
|
+
|
|
854
1005
|
export declare const emitDropColumnDdl: (op: Extract<MigrationOperation, {
|
|
855
1006
|
kind: "drop-column";
|
|
856
1007
|
}>, q: typeof quote) => string;
|
|
@@ -936,6 +1087,18 @@ cdcChannel?: string) => string;
|
|
|
936
1087
|
|
|
937
1088
|
declare type EmptyMerge = unknown;
|
|
938
1089
|
|
|
1090
|
+
/**
|
|
1091
|
+
* `EXISTS (SELECT 1 FROM ... WHERE ...)` / `NOT EXISTS (...)`.
|
|
1092
|
+
* Mirrors `SubqueryInPredicate` but doesn't bind to a specific
|
|
1093
|
+
* outer column — the predicate is "the sub-query produces at least
|
|
1094
|
+
* one row" (or doesn't). Non-correlated in v1; the sub-query's
|
|
1095
|
+
* WHERE clause references only its own table.
|
|
1096
|
+
*/
|
|
1097
|
+
declare interface ExistsPredicate {
|
|
1098
|
+
readonly op: 'exists' | 'not-exists';
|
|
1099
|
+
readonly subquery: QueryDescriptor;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
939
1102
|
/**
|
|
940
1103
|
* Filename pattern the discovery walker matches: timestamp-prefixed
|
|
941
1104
|
* `.ts` files in any depth under the project's `migrations/` dir.
|
|
@@ -1215,6 +1378,8 @@ declare interface JsonIndexPath {
|
|
|
1215
1378
|
readonly numeric: boolean;
|
|
1216
1379
|
}
|
|
1217
1380
|
|
|
1381
|
+
declare type LeafOperator = 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'notIn' | 'contains' | 'startsWith' | 'arrayContains' | 'arrayOverlaps' | 'arrayHas' | 'isNull' | 'isNotNull' | 'spatial';
|
|
1382
|
+
|
|
1218
1383
|
export declare const mapPgType: (dataType: string, udtName?: string) => ColumnType;
|
|
1219
1384
|
|
|
1220
1385
|
declare type MergeMixinFields<Mixins extends ReadonlyArray<AnyMixin>> = Mixins extends readonly [] ? EmptyMerge : Mixins extends readonly [infer Head, ...infer Rest] ? (Head extends MixinDefinition<infer F> ? F : EmptyMerge) & (Rest extends ReadonlyArray<AnyMixin> ? MergeMixinFields<Rest> : EmptyMerge) : EmptyMerge;
|
|
@@ -1453,6 +1618,13 @@ declare interface NarrowedFromSpec {
|
|
|
1453
1618
|
readonly using?: string;
|
|
1454
1619
|
}
|
|
1455
1620
|
|
|
1621
|
+
/** Negation of an arbitrary sub-predicate → `NOT (...)`. Wraps any
|
|
1622
|
+
* leaf/and/or/subquery subtree (distinct from the per-column `neq` and
|
|
1623
|
+
* the narrow `notInSubquery`/`notExists`). */
|
|
1624
|
+
declare interface NotPredicate {
|
|
1625
|
+
readonly not: Predicate;
|
|
1626
|
+
}
|
|
1627
|
+
|
|
1456
1628
|
/**
|
|
1457
1629
|
* DDL fragment for a numeric primary key column. Each dialect has its
|
|
1458
1630
|
* own auto-increment idiom — there's no portable shorthand, so we
|
|
@@ -1509,6 +1681,15 @@ export declare type OperationClass =
|
|
|
1509
1681
|
*/
|
|
1510
1682
|
export declare type OperationKind = 'create-table' | 'drop-table' | 'add-column' | 'drop-column' | 'rename-column' | 'alter-column-nullability' | 'alter-column-type' | 'alter-column-default' | 'add-index' | 'drop-index' | 'add-unique' | 'drop-unique' | 'add-unique-composite' | 'drop-unique-composite' | 'add-foreign-key' | 'drop-foreign-key' | 'add-check' | 'drop-check';
|
|
1511
1683
|
|
|
1684
|
+
declare interface OrderClause {
|
|
1685
|
+
readonly column: string;
|
|
1686
|
+
readonly direction: 'asc' | 'desc';
|
|
1687
|
+
}
|
|
1688
|
+
|
|
1689
|
+
declare interface OrPredicate {
|
|
1690
|
+
readonly or: ReadonlyArray<Predicate>;
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1512
1693
|
export declare const parseEnumCheck: (clause: string) => {
|
|
1513
1694
|
readonly column: string;
|
|
1514
1695
|
readonly values: ReadonlyArray<string>;
|
|
@@ -1622,6 +1803,23 @@ declare type PluginRefOrphanPolicy =
|
|
|
1622
1803
|
* omitting the option, which is the same behaviour arrived at by accident. */
|
|
1623
1804
|
| 'keep';
|
|
1624
1805
|
|
|
1806
|
+
declare type Predicate = PredicateLeaf | AndPredicate | OrPredicate | NotPredicate | SubqueryInPredicate | ExistsPredicate;
|
|
1807
|
+
|
|
1808
|
+
declare interface PredicateLeaf {
|
|
1809
|
+
readonly column: string;
|
|
1810
|
+
readonly op: LeafOperator;
|
|
1811
|
+
readonly value?: unknown;
|
|
1812
|
+
/**
|
|
1813
|
+
* JSON-path segments INTO a `json()` column (object keys / array indices).
|
|
1814
|
+
* When set, the leaf filters on the extracted value, not the whole column —
|
|
1815
|
+
* built via {@link jsonField}. The sql compiler lowers it to the dialect's
|
|
1816
|
+
* json-extract expression; the reactive matcher treats it as non-indexable
|
|
1817
|
+
* (rides the unindexed bucket → re-checked on any change to the table, like
|
|
1818
|
+
* `contains`). Absent for a normal column leaf.
|
|
1819
|
+
*/
|
|
1820
|
+
readonly path?: ReadonlyArray<string | number>;
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1625
1823
|
/**
|
|
1626
1824
|
* Imperative `applyNamespacedSchema` against a fresh SqlClient — the
|
|
1627
1825
|
* entry point the CLI / runtime use to provision a tenant's namespace
|
|
@@ -1629,6 +1827,275 @@ declare type PluginRefOrphanPolicy =
|
|
|
1629
1827
|
*/
|
|
1630
1828
|
export declare const provisionTenantNamespace: (tables: ReadonlyArray<AnyTable>, namespace: string, sqlLayer: Layer.Layer<SqlClient.SqlClient, ConfigError.ConfigError | SqlError.SqlError, never>, dialect?: DialectId) => Promise<void>;
|
|
1631
1829
|
|
|
1830
|
+
/**
|
|
1831
|
+
* The runtime-facing materialized query — a plain bag of strings the store and
|
|
1832
|
+
* the matcher consume.
|
|
1833
|
+
*
|
|
1834
|
+
* `R` is a PHANTOM row type. It exists so the row shape the typed builder
|
|
1835
|
+
* already knows survives the trip through `.descriptor` into `store.query()`,
|
|
1836
|
+
* which is where it used to be thrown away: `store.query(database.notes...)`
|
|
1837
|
+
* returned `Row` (`Record<string, unknown>`), so every field read needed a
|
|
1838
|
+
* hand-written cast. One downstream app wrote 2,032 of them. The type was
|
|
1839
|
+
* always available — it was just dropped at this boundary.
|
|
1840
|
+
*
|
|
1841
|
+
* It is an OPTIONAL field, never set at runtime. That matters for two reasons:
|
|
1842
|
+
* a plain object literal still satisfies `QueryDescriptor` (dispatcher, matcher
|
|
1843
|
+
* and store code build and forward these structurally), and `R` stays
|
|
1844
|
+
* covariant, so a `QueryDescriptor<Note>` is still assignable to the bare
|
|
1845
|
+
* `QueryDescriptor` that generic infrastructure passes around. Defaulting to
|
|
1846
|
+
* `Row` keeps every existing annotation meaning exactly what it meant before.
|
|
1847
|
+
*/
|
|
1848
|
+
declare interface QueryDescriptor<R = Row> {
|
|
1849
|
+
readonly table: string;
|
|
1850
|
+
readonly predicate: Predicate | undefined;
|
|
1851
|
+
/** Phantom — carries the row type through to `store.query()`. Never set at
|
|
1852
|
+
* runtime; erased entirely by the compiler. */
|
|
1853
|
+
readonly __row?: R;
|
|
1854
|
+
/**
|
|
1855
|
+
* Opt OUT of the runtime's automatic `deletedAt IS NULL` scope on
|
|
1856
|
+
* `softDelete()` tables. Set by `.withDeleted()`. The query builder is
|
|
1857
|
+
* browser-safe and mixin-unaware, so this is just a flag the RUNTIME
|
|
1858
|
+
* (storeMiddleware read path + reactive-query finalize) honors.
|
|
1859
|
+
*/
|
|
1860
|
+
readonly includeDeleted?: boolean;
|
|
1861
|
+
/** Opt OUT of the `expires()` read filter — a deliberate read of expired
|
|
1862
|
+
* rows (an admin view, a grace-period check). Same shape and same posture as
|
|
1863
|
+
* `includeDeleted`. */
|
|
1864
|
+
readonly includeExpired?: boolean;
|
|
1865
|
+
/**
|
|
1866
|
+
* Opt OUT of the runtime's automatic tenant scope on `tenant()` tables.
|
|
1867
|
+
* Set by `.unscoped()`. Honored by the runtime, same as `includeDeleted`.
|
|
1868
|
+
*/
|
|
1869
|
+
readonly crossTenant?: boolean;
|
|
1870
|
+
readonly order: ReadonlyArray<OrderClause>;
|
|
1871
|
+
readonly take: number | undefined;
|
|
1872
|
+
readonly skip: number | undefined;
|
|
1873
|
+
readonly projection: ReadonlyArray<string> | undefined;
|
|
1874
|
+
/**
|
|
1875
|
+
* Aggregate projection. When set, `SELECT` emits the aggregates
|
|
1876
|
+
* instead of row columns; the result is always exactly ONE row
|
|
1877
|
+
* (per group when combined with `groupBy`, ONE row total when not).
|
|
1878
|
+
*
|
|
1879
|
+
* Cannot coexist with `projection` (the two are mutually exclusive
|
|
1880
|
+
* select clauses). The builder enforces this at chain time.
|
|
1881
|
+
*/
|
|
1882
|
+
readonly aggregations?: ReadonlyArray<AggregateColumn<unknown>>;
|
|
1883
|
+
/**
|
|
1884
|
+
* `GROUP BY` columns. When set, the query returns ONE row per
|
|
1885
|
+
* distinct combination of these columns + the aggregations.
|
|
1886
|
+
* Requires `aggregations` to be set; ungrouped aggregates always
|
|
1887
|
+
* produce exactly one row.
|
|
1888
|
+
*/
|
|
1889
|
+
readonly groupBy?: ReadonlyArray<string>;
|
|
1890
|
+
/**
|
|
1891
|
+
* `HAVING` clause — predicate evaluated AFTER GROUP BY. Distinct
|
|
1892
|
+
* from `predicate` (which becomes `WHERE` and runs BEFORE grouping).
|
|
1893
|
+
* Use HAVING to filter groups based on their aggregate values
|
|
1894
|
+
* (e.g. "users with more than 10 posts").
|
|
1895
|
+
*
|
|
1896
|
+
* Stored as a `Predicate` so the same predicate compiler that
|
|
1897
|
+
* handles WHERE handles HAVING — but the column names on the LHS
|
|
1898
|
+
* of HAVING refer to aggregate aliases, not raw rows.
|
|
1899
|
+
*/
|
|
1900
|
+
readonly having?: Predicate;
|
|
1901
|
+
/**
|
|
1902
|
+
* Full-text-search predicate (S7). Set by `.matching('indexName',
|
|
1903
|
+
* 'query')`. `.matching()` resolves the index back to its declared
|
|
1904
|
+
* columns + config + weights from the table registry at call time and
|
|
1905
|
+
* stores them here, so the SQL compiler can emit the right per-dialect
|
|
1906
|
+
* predicate (and an optional relevance-ranking expression) without
|
|
1907
|
+
* re-consulting the registry.
|
|
1908
|
+
*
|
|
1909
|
+
* Per-dialect emission:
|
|
1910
|
+
* - postgres → `<tsvCol> @@ plainto_tsquery('<cfg>', ?)`
|
|
1911
|
+
* - mysql / mariadb → `MATCH(col1, col2) AGAINST (? IN NATURAL LANGUAGE MODE)`
|
|
1912
|
+
* - sqlite → `<table>_<idx>_fts MATCH ?` against the FTS5 table
|
|
1913
|
+
* - mssql → ranked LOWER(...) LIKE fallback (no server FTS catalog)
|
|
1914
|
+
*
|
|
1915
|
+
* `rank` carries an optional relevance-ranking request. When set, the
|
|
1916
|
+
* compiler also surfaces a synthetic relevance column aliased `rank.alias`
|
|
1917
|
+
* and (when `rank.order` is set) orders by it DESC. The score expression
|
|
1918
|
+
* is per-dialect: postgres `ts_rank`, mysql/mariadb `MATCH ... AGAINST`
|
|
1919
|
+
* score, sqlite FTS5 `bm25`, mssql a LIKE-hit count.
|
|
1920
|
+
*/
|
|
1921
|
+
/**
|
|
1922
|
+
* Full-text indexes declared on the FROM table — captured by
|
|
1923
|
+
* `queryFor()` so `.matching()` can resolve an index's columns/config
|
|
1924
|
+
* without a global table-registry lookup. Pure builder metadata; the
|
|
1925
|
+
* SQL compiler never reads it (it reads the resolved `fullTextSearch`).
|
|
1926
|
+
*/
|
|
1927
|
+
readonly fullTextIndexes?: ReadonlyArray<{
|
|
1928
|
+
readonly name: string;
|
|
1929
|
+
readonly columns: ReadonlyArray<string>;
|
|
1930
|
+
readonly config?: string;
|
|
1931
|
+
readonly weights?: Readonly<Record<string, 'A' | 'B' | 'C' | 'D'>>;
|
|
1932
|
+
}>;
|
|
1933
|
+
/**
|
|
1934
|
+
* The SOURCE table object this query was built from (`queryFor(t)` / a
|
|
1935
|
+
* `databaseHandle` entry sets it). The eager-load resolver (`.with(...)`)
|
|
1936
|
+
* needs the source table's shape + relations; it used to re-resolve it
|
|
1937
|
+
* BY NAME via `requireTable(descriptor.table)`, which forces every queried
|
|
1938
|
+
* table to be pre-registered (`databaseHandle(...)` / discovery). A direct
|
|
1939
|
+
* query needs no such registration — it carries the object — so an eager
|
|
1940
|
+
* query that threw "table 'X' is not registered" for a table you can query
|
|
1941
|
+
* directly was an asymmetry. Carrying the object here (same precedent as
|
|
1942
|
+
* `fullTextIndexes`) lets the store use it directly, with `requireTable` as
|
|
1943
|
+
* the fallback for reconstructed descriptors (e.g. `paginateById`). NOT
|
|
1944
|
+
* serialized/fingerprinted — the reactive engine keys on table+predicate.
|
|
1945
|
+
*/
|
|
1946
|
+
readonly sourceTable?: TableLike;
|
|
1947
|
+
readonly fullTextSearch?: {
|
|
1948
|
+
readonly indexName: string;
|
|
1949
|
+
readonly query: string;
|
|
1950
|
+
/** Columns the index covers — resolved from the table registry. */
|
|
1951
|
+
readonly columns: ReadonlyArray<string>;
|
|
1952
|
+
/** Postgres tsvector config (default 'english'). */
|
|
1953
|
+
readonly config?: string;
|
|
1954
|
+
/** Per-column weight letters (postgres ts_rank). */
|
|
1955
|
+
readonly weights?: Readonly<Record<string, 'A' | 'B' | 'C' | 'D'>>;
|
|
1956
|
+
/** Relevance-ranking request — set by `.rankBy(alias?)`. */
|
|
1957
|
+
readonly rank?: {
|
|
1958
|
+
readonly alias: string;
|
|
1959
|
+
readonly order: boolean;
|
|
1960
|
+
};
|
|
1961
|
+
};
|
|
1962
|
+
/**
|
|
1963
|
+
* Common-Table-Expression definitions (Q4) — `WITH <name> AS (...)`.
|
|
1964
|
+
* Each entry binds a `name` to a sub-query descriptor; the outer
|
|
1965
|
+
* SELECT can reference the CTE by name in `from`, `inSubquery`,
|
|
1966
|
+
* `exists`, etc.
|
|
1967
|
+
*
|
|
1968
|
+
* `.withCte()` adds a non-recursive CTE. For tree-walks use
|
|
1969
|
+
* `.recursiveCte()`, which sets `recursive: true` and emits
|
|
1970
|
+
* `WITH RECURSIVE` (the inner descriptor self-references the CTE
|
|
1971
|
+
* name). Both are reactive — see `runtime/dependencyGraph.ts`.
|
|
1972
|
+
*/
|
|
1973
|
+
readonly ctes?: ReadonlyArray<{
|
|
1974
|
+
readonly name: string;
|
|
1975
|
+
readonly descriptor: QueryDescriptor;
|
|
1976
|
+
/**
|
|
1977
|
+
* Recursive CTE marker (Q4 v2). When true, the compiler emits
|
|
1978
|
+
* `WITH RECURSIVE name AS (...)`. The inner descriptor must be a
|
|
1979
|
+
* UNION (via the `union` / `unionAll` helpers) whose first arm is
|
|
1980
|
+
* the anchor and subsequent arms reference the CTE by name (the
|
|
1981
|
+
* recursion). Cycle-detection per-dialect: postgres uses
|
|
1982
|
+
* `UNION` semantics (implicit dedup); other dialects mirror.
|
|
1983
|
+
*/
|
|
1984
|
+
readonly recursive?: boolean;
|
|
1985
|
+
}>;
|
|
1986
|
+
/**
|
|
1987
|
+
* SELECT DISTINCT (Q7). When true, dedup rows by the full result
|
|
1988
|
+
* shape. Cross-dialect.
|
|
1989
|
+
*/
|
|
1990
|
+
readonly distinct?: boolean;
|
|
1991
|
+
/**
|
|
1992
|
+
* SELECT DISTINCT ON (cols) — postgres-only (Q7). Picks ONE row
|
|
1993
|
+
* per distinct combination of `distinctOn` columns; the row picked
|
|
1994
|
+
* is the FIRST per the descriptor's `order`. The compiler emits a
|
|
1995
|
+
* windowed-rownumber fallback on other dialects.
|
|
1996
|
+
*
|
|
1997
|
+
* Common use: "latest message per channel".
|
|
1998
|
+
*/
|
|
1999
|
+
readonly distinctOn?: ReadonlyArray<string>;
|
|
2000
|
+
/**
|
|
2001
|
+
* Alias for the FROM table — `FROM <table> AS <alias>` (Q8).
|
|
2002
|
+
* Needed for self-joins where the same table appears multiple
|
|
2003
|
+
* times with different aliases. When unset, the FROM clause is
|
|
2004
|
+
* the bare table name.
|
|
2005
|
+
*/
|
|
2006
|
+
readonly alias?: string;
|
|
2007
|
+
/**
|
|
2008
|
+
* Joined tables (Q8). Each entry adds an INNER JOIN or LEFT JOIN
|
|
2009
|
+
* with an explicit ON predicate. Self-joins re-use the same
|
|
2010
|
+
* source table with a different alias.
|
|
2011
|
+
*
|
|
2012
|
+
* `on` is a regular `Predicate` — the leaf columns may carry
|
|
2013
|
+
* `alias.column` style qualifiers since the compiler emits the
|
|
2014
|
+
* column string verbatim.
|
|
2015
|
+
*/
|
|
2016
|
+
readonly joins?: ReadonlyArray<{
|
|
2017
|
+
readonly kind: 'inner' | 'left';
|
|
2018
|
+
readonly table: string;
|
|
2019
|
+
readonly alias: string;
|
|
2020
|
+
readonly on: Predicate;
|
|
2021
|
+
}>;
|
|
2022
|
+
/**
|
|
2023
|
+
* Aliased projection for joined queries (Q8 v2). When set, the
|
|
2024
|
+
* SELECT emits `alias.col AS outputKey` for each entry, producing
|
|
2025
|
+
* a flat row with the user's chosen keys.
|
|
2026
|
+
*
|
|
2027
|
+
* Mutually exclusive with `projection` (regular select-cols).
|
|
2028
|
+
* Bypassed by `aggregations`.
|
|
2029
|
+
*/
|
|
2030
|
+
readonly joinedProjection?: ReadonlyArray<{
|
|
2031
|
+
readonly outputKey: string;
|
|
2032
|
+
readonly source: string;
|
|
2033
|
+
}>;
|
|
2034
|
+
/**
|
|
2035
|
+
* Set-operation composition (Q6). When set, the SELECT shape is
|
|
2036
|
+
* replaced by `(SELECT ...) <OP> (SELECT ...) ...`.
|
|
2037
|
+
*
|
|
2038
|
+
* - `'union'` → dedup-merge (`UNION`)
|
|
2039
|
+
* - `'union-all'` → no dedup (`UNION ALL`)
|
|
2040
|
+
* - `'intersect'` → rows in BOTH (`INTERSECT`)
|
|
2041
|
+
* - `'except'` → rows in LHS not in RHS (`EXCEPT`)
|
|
2042
|
+
*
|
|
2043
|
+
* All four are standard SQL + supported on every dialect we ship.
|
|
2044
|
+
*/
|
|
2045
|
+
readonly setOp?: {
|
|
2046
|
+
readonly kind: 'union' | 'union-all' | 'intersect' | 'except';
|
|
2047
|
+
readonly queries: ReadonlyArray<QueryDescriptor>;
|
|
2048
|
+
};
|
|
2049
|
+
/**
|
|
2050
|
+
* Optional index hint. Set by `.using('indexName')` on the query
|
|
2051
|
+
* builder. The framework stores it on the descriptor for two reasons:
|
|
2052
|
+
*
|
|
2053
|
+
* 1. Suppresses the dev-mode "non-indexed query" warning for this
|
|
2054
|
+
* descriptor (the user explicitly signalled they thought about
|
|
2055
|
+
* the access pattern).
|
|
2056
|
+
* 2. The matcher's tuple-routing path keys subscriptions on the
|
|
2057
|
+
* declared index's column set — see `IndexedMatcher` in
|
|
2058
|
+
* `@voltro/runtime/matcher.ts`. Subscriptions that declare a
|
|
2059
|
+
* covering index get O(1) candidate lookup per event instead
|
|
2060
|
+
* of column-union routing.
|
|
2061
|
+
*/
|
|
2062
|
+
readonly usingIndex?: string;
|
|
2063
|
+
/**
|
|
2064
|
+
* Eager-load tree declared via `.with({ ... })`. Consumed by the
|
|
2065
|
+
* JoinCompiler (`./joinCompiler.ts`) when present — the SQL stores
|
|
2066
|
+
* dispatch nested-JSON-aggregation queries; the in-memory store
|
|
2067
|
+
* walks it via post-hoc lookups.
|
|
2068
|
+
*/
|
|
2069
|
+
readonly eager?: WithSpec;
|
|
2070
|
+
/**
|
|
2071
|
+
* Approximate-nearest-neighbour clause (plan 01). Set by
|
|
2072
|
+
* `.nearestNeighbours(...)`. Drives a synthetic `distance` projection
|
|
2073
|
+
* + `ORDER BY <col> <op> <query>` in the SQL compiler:
|
|
2074
|
+
*
|
|
2075
|
+
* - postgres (pgvector): `<col> <=> $q` (cosine) / `<->` (l2) /
|
|
2076
|
+
* `<#>` (inner).
|
|
2077
|
+
* - mariadb / mysql: `VEC_DISTANCE_COSINE/EUCLIDEAN(<col>, $q)`.
|
|
2078
|
+
* - mssql / sqlite: no portable ANN operator → the compiler warns +
|
|
2079
|
+
* skips the distance ordering (sequential scan; rows still return).
|
|
2080
|
+
*
|
|
2081
|
+
* `queryVector` is the literal float array; `queryText` is the
|
|
2082
|
+
* raw string the runtime embeds to a vector *before* compiling
|
|
2083
|
+
* (only valid when the table carries `vectorEmbedding()`). Exactly
|
|
2084
|
+
* one of the two is set.
|
|
2085
|
+
*/
|
|
2086
|
+
readonly annClause?: AnnClause;
|
|
2087
|
+
/**
|
|
2088
|
+
* PostGIS spatial distance clause (`@voltro/plugin-postgis`). Set by the
|
|
2089
|
+
* `withDistance(...)` / `nearestBy(...)` transforms applied via `.use(...)`.
|
|
2090
|
+
* Postgres-only — the SQL compiler lowers it to a `ST_Distance(...)`
|
|
2091
|
+
* projected column and/or a `<->` KNN `ORDER BY`; on any non-postgres
|
|
2092
|
+
* dialect the compiler THROWS (mirrors the spatial predicate + column
|
|
2093
|
+
* guards). Never evaluated in-memory (no PostGIS in JS). Composes with
|
|
2094
|
+
* `.where(...)`, `.limit(k)`, spatial predicates, etc.
|
|
2095
|
+
*/
|
|
2096
|
+
readonly spatialClause?: SpatialClause;
|
|
2097
|
+
}
|
|
2098
|
+
|
|
1632
2099
|
declare const quote: (name: string) => string;
|
|
1633
2100
|
|
|
1634
2101
|
/**
|
|
@@ -1750,6 +2217,52 @@ export declare const rollingDeployUnsafeOps: (operations: ReadonlyArray<PlannedO
|
|
|
1750
2217
|
readonly remedy: string;
|
|
1751
2218
|
}>;
|
|
1752
2219
|
|
|
2220
|
+
declare type Row = Readonly<Record<string, unknown>>;
|
|
2221
|
+
|
|
2222
|
+
/** The context a rule predicate is evaluated with. */
|
|
2223
|
+
declare interface RuleContext {
|
|
2224
|
+
/** Transactional store — read other tables in the mutation's snapshot. */
|
|
2225
|
+
readonly store: RuleReadStore;
|
|
2226
|
+
/** The calling subject (tenant / user), for tenant-aware rules. */
|
|
2227
|
+
readonly subject: unknown;
|
|
2228
|
+
}
|
|
2229
|
+
|
|
2230
|
+
/**
|
|
2231
|
+
* What a rule predicate may return. `true` / `void` / `undefined` = the rule
|
|
2232
|
+
* HOLDS. `false` = violated with the rule's default detail. A
|
|
2233
|
+
* `RuleViolationDetail` object = violated, carrying per-row i18n params / a
|
|
2234
|
+
* field pointer for THIS specific row.
|
|
2235
|
+
*/
|
|
2236
|
+
declare type RuleOutcome = boolean | void | RuleViolationDetail;
|
|
2237
|
+
|
|
2238
|
+
/**
|
|
2239
|
+
* A rule predicate over the POST-WRITE row. Runs inside the mutation
|
|
2240
|
+
* transaction. Return `true`/nothing when the invariant holds; return `false`
|
|
2241
|
+
* or a `RuleViolationDetail` to signal a violation. May be async (cross-table
|
|
2242
|
+
* reads).
|
|
2243
|
+
*/
|
|
2244
|
+
declare type RulePredicate = (row: Record<string, unknown>, context: RuleContext) => RuleOutcome | Promise<RuleOutcome>;
|
|
2245
|
+
|
|
2246
|
+
/** Minimal READ surface (a subset of `DataStore`) handed to a rule predicate —
|
|
2247
|
+
* the SAME transactional store the mutation writes through, so cross-table
|
|
2248
|
+
* reads share its MVCC snapshot and cannot race the write the rule guards. */
|
|
2249
|
+
declare interface RuleReadStore {
|
|
2250
|
+
query(descriptor: QueryDescriptor): Promise<ReadonlyArray<Record<string, unknown>>>;
|
|
2251
|
+
}
|
|
2252
|
+
|
|
2253
|
+
/** Severity of a business rule. `error` rolls the mutation back; `warning`
|
|
2254
|
+
* logs + audits but lets the write commit (useful during data migrations). */
|
|
2255
|
+
declare type RuleSeverity = 'error' | 'warning';
|
|
2256
|
+
|
|
2257
|
+
declare interface RuleViolationDetail {
|
|
2258
|
+
/** i18n params for the violation message (per this row). */
|
|
2259
|
+
readonly params?: Record<string, unknown>;
|
|
2260
|
+
/** Field path the violation pinpoints, if any. */
|
|
2261
|
+
readonly field?: string;
|
|
2262
|
+
/** Human-readable message (fallback / dev aid). */
|
|
2263
|
+
readonly message?: string;
|
|
2264
|
+
}
|
|
2265
|
+
|
|
1753
2266
|
export declare const runFileBasedMigrations: (sql: SqlClient.SqlClient, ctx: RunFileBasedMigrationsCtx) => Effect.Effect<FileMigrationRunResult, unknown>;
|
|
1754
2267
|
|
|
1755
2268
|
export declare interface RunFileBasedMigrationsCtx {
|
|
@@ -1819,6 +2332,41 @@ export declare const shortFingerprint: (fp: string) => string;
|
|
|
1819
2332
|
*/
|
|
1820
2333
|
export declare const snapshotColumn: (name: string, def: ColumnDefinition<unknown>) => ColumnSnapshot;
|
|
1821
2334
|
|
|
2335
|
+
/**
|
|
2336
|
+
* PostGIS spatial-distance clause carried on a {@link QueryDescriptor}.
|
|
2337
|
+
* Drives a `ST_Distance` projected column (distance-as-a-value) and/or a
|
|
2338
|
+
* `<->` nearest-neighbour `ORDER BY` (KNN). Built by `@voltro/plugin-postgis`;
|
|
2339
|
+
* postgres-only (the compiler throws on other dialects).
|
|
2340
|
+
*/
|
|
2341
|
+
declare interface SpatialClause {
|
|
2342
|
+
/** The spatial column to measure from. */
|
|
2343
|
+
readonly column: string;
|
|
2344
|
+
/** The comparison geometry as WKT/EWKT (e.g. `SRID=4326;POINT(8 50)`). Bound as a parameter. */
|
|
2345
|
+
readonly geom: string;
|
|
2346
|
+
/**
|
|
2347
|
+
* Measure metric metres via a `::geography` cast on both operands
|
|
2348
|
+
* (great-circle distance). When false, distance is in the SRID's own
|
|
2349
|
+
* planar units (`::geometry`). Default true — the "distance in metres"
|
|
2350
|
+
* users expect for lat/lon data.
|
|
2351
|
+
*/
|
|
2352
|
+
readonly useGeography: boolean;
|
|
2353
|
+
/**
|
|
2354
|
+
* When set, surface the distance as a projected column aliased this name
|
|
2355
|
+
* (`ST_Distance(col, geom) AS "<projectAs>"`). The row type gains a
|
|
2356
|
+
* `number` field. Absent → no distance projection (KNN-order-only).
|
|
2357
|
+
*/
|
|
2358
|
+
readonly projectAs?: string;
|
|
2359
|
+
/**
|
|
2360
|
+
* When set, add a KNN `ORDER BY col <-> geom <dir>` clause (indexed
|
|
2361
|
+
* nearest-neighbour when a GiST index covers the column). Leads any
|
|
2362
|
+
* explicit `.orderBy(...)`. Absent → no spatial ordering. `<->` is always
|
|
2363
|
+
* a planar operator (postgres has no geography `<->`), so KNN ordering
|
|
2364
|
+
* ranks by planar distance even when `useGeography` is true for the
|
|
2365
|
+
* projected metric distance.
|
|
2366
|
+
*/
|
|
2367
|
+
readonly order?: 'asc' | 'desc';
|
|
2368
|
+
}
|
|
2369
|
+
|
|
1822
2370
|
/**
|
|
1823
2371
|
* The `sql` template tag — captures a tagged-template literal into a
|
|
1824
2372
|
* {@link RawSqlFragment} descriptor without binding it to any client.
|
|
@@ -1888,6 +2436,25 @@ export declare interface SquashResult {
|
|
|
1888
2436
|
readonly snapshotId: string;
|
|
1889
2437
|
}
|
|
1890
2438
|
|
|
2439
|
+
/**
|
|
2440
|
+
* `col IN (SELECT col FROM ...)` / `col NOT IN (SELECT ...)` — the
|
|
2441
|
+
* RHS is a sub-query descriptor. The sub-query MUST project a
|
|
2442
|
+
* single column (via `.select('col')`); the SQL compiler emits the
|
|
2443
|
+
* full inner SELECT inline, and the in-memory store evaluates the
|
|
2444
|
+
* sub-query first + then runs the IN check against the materialized
|
|
2445
|
+
* set.
|
|
2446
|
+
*
|
|
2447
|
+
* The sub-query is non-correlated in v1 — it can't reference outer
|
|
2448
|
+
* row columns. Correlated sub-queries (`WHERE EXISTS (... WHERE
|
|
2449
|
+
* inner.userId = outer.id)`) would require a table-alias mechanism;
|
|
2450
|
+
* tracked separately.
|
|
2451
|
+
*/
|
|
2452
|
+
declare interface SubqueryInPredicate {
|
|
2453
|
+
readonly column: string;
|
|
2454
|
+
readonly op: 'subquery-in' | 'subquery-not-in';
|
|
2455
|
+
readonly subquery: QueryDescriptor;
|
|
2456
|
+
}
|
|
2457
|
+
|
|
1891
2458
|
declare interface Table<Name extends string, Fields extends Record<string, ColumnDefinition<unknown>>, Reactive extends boolean = true, IxNames extends string = never> extends TableLike {
|
|
1892
2459
|
readonly tableName: Name;
|
|
1893
2460
|
readonly fields: Fields;
|
|
@@ -1911,6 +2478,12 @@ declare interface Table<Name extends string, Fields extends Record<string, Colum
|
|
|
1911
2478
|
readonly appliedUniques: ReadonlyArray<TableUnique>;
|
|
1912
2479
|
readonly appliedFullText: ReadonlyArray<TableFullTextIndex>;
|
|
1913
2480
|
readonly appliedChecks: ReadonlyArray<TableCheck>;
|
|
2481
|
+
/**
|
|
2482
|
+
* The cross-table business rules DECLARED on this table, set by
|
|
2483
|
+
* `.rule(name, predicate)`. Evaluated by the runtime inside the mutation
|
|
2484
|
+
* transaction (see `TableRule`); they emit no DDL.
|
|
2485
|
+
*/
|
|
2486
|
+
readonly appliedRules: ReadonlyArray<TableRule>;
|
|
1914
2487
|
/**
|
|
1915
2488
|
* The name this table used to have, set by `.renamedFrom('old_name')`.
|
|
1916
2489
|
* Named apart from the METHOD that sets it — one interface cannot carry both.
|
|
@@ -2172,6 +2745,38 @@ declare interface Table<Name extends string, Fields extends Record<string, Colum
|
|
|
2172
2745
|
* For a single-column check, `column.check(expr)` is the terser form.
|
|
2173
2746
|
*/
|
|
2174
2747
|
check: <const CkName extends string>(name: CkName, expr: string) => Table<Name, Fields, Reactive, IxNames>;
|
|
2748
|
+
/**
|
|
2749
|
+
* Declare a CROSS-TABLE business rule (S3). Unlike `.check(name, expr)` — a
|
|
2750
|
+
* single-row SQL `CHECK` the database enforces — a rule is a PREDICATE the
|
|
2751
|
+
* runtime evaluates INSIDE the mutation's transaction, after the write and
|
|
2752
|
+
* before commit. It can read OTHER tables in the same snapshot and rolls the
|
|
2753
|
+
* mutation back with a typed `BusinessRuleViolation` on failure.
|
|
2754
|
+
*
|
|
2755
|
+
* The predicate receives the POST-WRITE row and a `context` whose `store` is
|
|
2756
|
+
* the SAME transactional store the mutation wrote through. Return `true`
|
|
2757
|
+
* (or nothing) when the invariant holds; return `false` or a
|
|
2758
|
+
* `RuleViolationDetail` (i18n `params` / a `field` pointer) to signal a
|
|
2759
|
+
* violation.
|
|
2760
|
+
*
|
|
2761
|
+
* ```ts
|
|
2762
|
+
* table('invoices', { id: id(), total: integer() }).rule(
|
|
2763
|
+
* 'totalMatchesLineItems',
|
|
2764
|
+
* async (row, { store }) => {
|
|
2765
|
+
* const items = await store.query(
|
|
2766
|
+
* lineItems.where(eq('invoiceId', row.id)).descriptor,
|
|
2767
|
+
* )
|
|
2768
|
+
* const sum = items.reduce((a, l) => a + (l.amount as number), 0)
|
|
2769
|
+
* return sum === row.total || { params: { computed: sum, declared: row.total } }
|
|
2770
|
+
* },
|
|
2771
|
+
* )
|
|
2772
|
+
* ```
|
|
2773
|
+
*
|
|
2774
|
+
* `severity: 'warning'` logs + audits the violation but lets the write
|
|
2775
|
+
* commit (useful during data migration); the default `'error'` rolls back.
|
|
2776
|
+
*/
|
|
2777
|
+
rule: <const RuleName extends string>(name: RuleName, predicate: RulePredicate, options?: {
|
|
2778
|
+
readonly severity?: RuleSeverity;
|
|
2779
|
+
}) => Table<Name, Fields, Reactive, IxNames>;
|
|
2175
2780
|
/**
|
|
2176
2781
|
* Declare a full-text-search index (S7). One method, three back-
|
|
2177
2782
|
* ends:
|
|
@@ -2383,6 +2988,21 @@ declare interface TableLike {
|
|
|
2383
2988
|
readonly fields: Record<string, ColumnDefinition<unknown>>;
|
|
2384
2989
|
}
|
|
2385
2990
|
|
|
2991
|
+
/**
|
|
2992
|
+
* A named cross-table business rule, attached by `.rule(name, predicate)`.
|
|
2993
|
+
*
|
|
2994
|
+
* UNLIKE `.check(name, expr)` — a single-row SQL `CHECK` emitted as DDL and
|
|
2995
|
+
* enforced by the database — a rule is a PREDICATE the runtime evaluates INSIDE
|
|
2996
|
+
* the mutation's transaction, AFTER the write and BEFORE commit. It can read
|
|
2997
|
+
* OTHER tables in the same MVCC snapshot and rolls the whole mutation back
|
|
2998
|
+
* (typed `BusinessRuleViolation`) on violation. It emits no DDL.
|
|
2999
|
+
*/
|
|
3000
|
+
declare interface TableRule {
|
|
3001
|
+
readonly name: string;
|
|
3002
|
+
readonly predicate: RulePredicate;
|
|
3003
|
+
readonly severity: RuleSeverity;
|
|
3004
|
+
}
|
|
3005
|
+
|
|
2386
3006
|
export declare interface TableSnapshot {
|
|
2387
3007
|
readonly name: string;
|
|
2388
3008
|
readonly columns: ReadonlyArray<ColumnSnapshot>;
|
|
@@ -2477,6 +3097,32 @@ declare interface View<Name extends string, Fields extends Record<string, Column
|
|
|
2477
3097
|
*/
|
|
2478
3098
|
export declare const VOLTRO_MIGRATION_LOCK_KEY = 6322741009312437n;
|
|
2479
3099
|
|
|
3100
|
+
/**
|
|
3101
|
+
* One aggregate column in a `SELECT` projection — `COUNT(*)`,
|
|
3102
|
+
* `SUM(amount) AS total`, etc. Built via the `count() / sum() /
|
|
3103
|
+
* avg() / min() / max() / countDistinct()` helpers exported from
|
|
3104
|
+
* `@voltro/database`.
|
|
3105
|
+
*
|
|
3106
|
+
* `column` is undefined for `count()` (which compiles to `COUNT(*)`)
|
|
3107
|
+
* and required for every other op. `alias` is the column name on the
|
|
3108
|
+
* result row.
|
|
3109
|
+
*/
|
|
3110
|
+
/**
|
|
3111
|
+
* Window-function spec (Q3). When set on an AggregateColumn, the
|
|
3112
|
+
* compiler emits `<OP>(...) OVER (PARTITION BY ... ORDER BY ...)`
|
|
3113
|
+
* instead of a plain aggregate. The query is reactive coarsely — the
|
|
3114
|
+
* matcher can't bucket on a window-function result, so the engine
|
|
3115
|
+
* widens the dependency to the whole source table and re-queries on
|
|
3116
|
+
* any write to it (see `runtime/relevantFields.ts`).
|
|
3117
|
+
*/
|
|
3118
|
+
declare interface WindowSpec {
|
|
3119
|
+
readonly partitionBy?: ReadonlyArray<string>;
|
|
3120
|
+
readonly orderBy?: ReadonlyArray<{
|
|
3121
|
+
column: string;
|
|
3122
|
+
direction: 'asc' | 'desc';
|
|
3123
|
+
}>;
|
|
3124
|
+
}
|
|
3125
|
+
|
|
2480
3126
|
/**
|
|
2481
3127
|
* Convenience wrapper: acquire → run `work` → release. Releases even
|
|
2482
3128
|
* on failure via `Effect.ensuring`, but a process crash mid-work
|
|
@@ -2485,4 +3131,7 @@ export declare const VOLTRO_MIGRATION_LOCK_KEY = 6322741009312437n;
|
|
|
2485
3131
|
*/
|
|
2486
3132
|
export declare const withMigrationLock: <A, E, R = never>(sql: SqlClient.SqlClient, work: Effect.Effect<A, E, R>) => Effect.Effect<A, E | SqlError_2, R>;
|
|
2487
3133
|
|
|
3134
|
+
/** Tree of relations to eager-load. */
|
|
3135
|
+
declare type WithSpec = Readonly<Record<string, true | EagerLoadSpec>>;
|
|
3136
|
+
|
|
2488
3137
|
export { }
|