@korajs/core 1.0.0-beta.12 → 1.0.0-beta.14
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/README.md +21 -13
- package/dist/bindings/index.d.cts +26 -6
- package/dist/bindings/index.d.ts +26 -6
- package/dist/{build-scope-map-CItP1gsz.d.cts → build-scope-map-C9zBTJ1R.d.cts} +1 -1
- package/dist/{build-scope-map-Cv5HfCQP.d.ts → build-scope-map-CZaMyZLV.d.ts} +1 -1
- package/dist/{chunk-5IICSH6H.js → chunk-QUD3DMSD.js} +668 -24
- package/dist/chunk-QUD3DMSD.js.map +1 -0
- package/dist/events-Da29Dt_3.d.cts +1737 -0
- package/dist/events-Da29Dt_3.d.ts +1737 -0
- package/dist/index.cjs +2548 -207
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +810 -220
- package/dist/index.d.ts +810 -220
- package/dist/index.js +2028 -334
- package/dist/index.js.map +1 -1
- package/dist/internal.cjs +857 -85
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.d.cts +251 -21
- package/dist/internal.d.ts +251 -21
- package/dist/internal.js +447 -1
- package/dist/internal.js.map +1 -1
- package/dist/sql-gen-BOeuiyqu.d.cts +622 -0
- package/dist/sql-gen-CXTA6w-w.d.ts +622 -0
- package/package.json +2 -1
- package/dist/chunk-5IICSH6H.js.map +0 -1
- package/dist/events-D7JCtQSD.d.cts +0 -1010
- package/dist/events-D7JCtQSD.d.ts +0 -1010
- package/dist/operation-BX4ok7U4.d.ts +0 -164
- package/dist/operation-BXvJ6RiA.d.cts +0 -164
package/dist/internal.d.cts
CHANGED
|
@@ -1,23 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export {
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Compute the content-addressed ID for an operation using SHA-256.
|
|
6
|
-
* The same operation content always produces the same hash, ensuring deduplication.
|
|
7
|
-
*
|
|
8
|
-
* @param input - The operation input (without id/timestamp, which are assigned separately)
|
|
9
|
-
* @param timestamp - The HLC timestamp serialized as a string
|
|
10
|
-
* @returns A hex-encoded SHA-256 hash
|
|
11
|
-
*/
|
|
12
|
-
declare function computeOperationId(input: OperationInput, timestamp: string): Promise<string>;
|
|
13
|
-
/**
|
|
14
|
-
* Deterministic JSON serialization with sorted keys.
|
|
15
|
-
* Ensures identical objects always produce identical strings regardless of property insertion order.
|
|
16
|
-
*
|
|
17
|
-
* @param obj - The value to serialize
|
|
18
|
-
* @returns A deterministic JSON string
|
|
19
|
-
*/
|
|
20
|
-
declare function canonicalize(obj: unknown): string;
|
|
1
|
+
import { S as Stamp, k as FoldFieldKind } from './sql-gen-BOeuiyqu.cjs';
|
|
2
|
+
export { B as SCHEMA_CEILING_DIRECTIVE, G as canonicalize, I as collectionIndexName, u as computeOperationId, J as enumCheckConstraint, L as legacyCollectionIndexName, N as sqlDefaultLiteral, P as sqlStringLiteral, Q as validateOperationParams } from './sql-gen-BOeuiyqu.cjs';
|
|
3
|
+
import { X as KoraEventEmitter, Z as KoraEventType, Y as KoraEventListener, W as KoraEventByType, a as Operation, H as HLCTimestamp, b as CustomResolver, C as CollectionDefinition } from './events-Da29Dt_3.cjs';
|
|
21
4
|
|
|
22
5
|
/**
|
|
23
6
|
* Concrete implementation of KoraEventEmitter.
|
|
@@ -56,4 +39,251 @@ declare class SimpleEventEmitter implements KoraEventEmitter {
|
|
|
56
39
|
*/
|
|
57
40
|
declare function topologicalSort(operations: Operation[]): Operation[];
|
|
58
41
|
|
|
59
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Value-domain rules live in validation only (RT-101).
|
|
44
|
+
*
|
|
45
|
+
* Kora's generated DDL used to restate two schema rules as storage constraints: an enum
|
|
46
|
+
* field's `CHECK (col IN (...))` and a required field's `NOT NULL`. The value domain
|
|
47
|
+
* already enforces both at write time on every replica, and storage constraints cannot
|
|
48
|
+
* evolve with the schema (SQLite cannot alter a column constraint): an enum value added
|
|
49
|
+
* by a schema upgrade was valid everywhere but refused by every existing table. The DDL
|
|
50
|
+
* no longer emits them, and existing tables are relaxed once, by these helpers.
|
|
51
|
+
*
|
|
52
|
+
* Kora's own bookkeeping columns (`id` and every `_`-prefixed column) keep their
|
|
53
|
+
* constraints: they are written by Kora only and never evolve with the app schema.
|
|
54
|
+
*/
|
|
55
|
+
/** A row of `PRAGMA table_info(<table>)`. */
|
|
56
|
+
interface SqliteColumnInfo {
|
|
57
|
+
cid: number;
|
|
58
|
+
name: string;
|
|
59
|
+
type: string;
|
|
60
|
+
notnull: number;
|
|
61
|
+
dflt_value: string | null;
|
|
62
|
+
pk: number;
|
|
63
|
+
}
|
|
64
|
+
/** A row of `PRAGMA foreign_key_list(<table>)`. */
|
|
65
|
+
interface SqliteForeignKeyInfo {
|
|
66
|
+
id: number;
|
|
67
|
+
seq: number;
|
|
68
|
+
table: string;
|
|
69
|
+
from: string;
|
|
70
|
+
to: string | null;
|
|
71
|
+
on_update: string;
|
|
72
|
+
on_delete: string;
|
|
73
|
+
match?: string;
|
|
74
|
+
}
|
|
75
|
+
/** What a SQLite table looks like, read from its catalog. */
|
|
76
|
+
interface SqliteTableCatalog {
|
|
77
|
+
/** Table name */
|
|
78
|
+
table: string;
|
|
79
|
+
/** The table's `CREATE TABLE` statement (`sqlite_master.sql`) */
|
|
80
|
+
sql: string;
|
|
81
|
+
columns: readonly SqliteColumnInfo[];
|
|
82
|
+
foreignKeys: readonly SqliteForeignKeyInfo[];
|
|
83
|
+
/** Column lists of the table's `UNIQUE` constraints (`index_list` origin `u`) */
|
|
84
|
+
uniqueConstraints: readonly (readonly string[])[];
|
|
85
|
+
/** `CREATE INDEX` / `CREATE TRIGGER` statements on the table (`sqlite_master.sql`) */
|
|
86
|
+
dependents: readonly string[];
|
|
87
|
+
}
|
|
88
|
+
/** Columns Kora writes itself; their constraints are kept. */
|
|
89
|
+
declare function isKoraInternalColumn(name: string): boolean;
|
|
90
|
+
/** One `CHECK` constraint of a SQLite `CREATE TABLE` statement. */
|
|
91
|
+
interface SqliteCheckConstraint {
|
|
92
|
+
/** The constraint name (`CONSTRAINT <name> CHECK ...`), unquoted, or null */
|
|
93
|
+
name: string | null;
|
|
94
|
+
/** The checked expression, verbatim (the text between the parentheses) */
|
|
95
|
+
expression: string;
|
|
96
|
+
/** The column whose definition carries the check (a column constraint), or null */
|
|
97
|
+
column: string | null;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Every `CHECK` constraint of a SQLite `CREATE TABLE` statement, column and table
|
|
101
|
+
* constraints alike, with the expression verbatim (RT-111). The catalog pragmas do not
|
|
102
|
+
* report checks, so a table rebuild must carry them over from the statement itself.
|
|
103
|
+
*
|
|
104
|
+
* @param createTableSql - The table's `CREATE TABLE` statement (`sqlite_master.sql`)
|
|
105
|
+
* @returns The checks in statement order
|
|
106
|
+
*/
|
|
107
|
+
declare function parseSqliteCheckConstraints(createTableSql: string): SqliteCheckConstraint[];
|
|
108
|
+
/**
|
|
109
|
+
* Whether a check is one of Kora's own value-domain checks (beta.12 and earlier DDL):
|
|
110
|
+
* an enum check (see {@link parseEnumCheckDefinition}) on one column that is not Kora's
|
|
111
|
+
* own and, when `enumColumns` is given, is an enum field of the schema. A check of any
|
|
112
|
+
* other shape, or on another column, was added by hand and is kept (RT-111).
|
|
113
|
+
*
|
|
114
|
+
* @param check - A check of the table
|
|
115
|
+
* @param enumColumns - The table's enum fields (undefined: any enum-shaped check counts)
|
|
116
|
+
*/
|
|
117
|
+
declare function isKoraSqliteEnumCheck(check: SqliteCheckConstraint, enumColumns?: readonly string[]): boolean;
|
|
118
|
+
/**
|
|
119
|
+
* Whether a table still carries a value-domain constraint of Kora's: an enum `CHECK` on
|
|
120
|
+
* an enum field (see {@link isKoraSqliteEnumCheck}), or `NOT NULL` on a column that is not
|
|
121
|
+
* Kora's own. Checks added by hand never make a table need relaxation.
|
|
122
|
+
*
|
|
123
|
+
* @param catalog - The table as read from SQLite's catalog
|
|
124
|
+
* @param enumColumns - The table's enum fields (undefined: any enum-shaped check counts)
|
|
125
|
+
*/
|
|
126
|
+
declare function sqliteTableNeedsRelaxation(catalog: SqliteTableCatalog, enumColumns?: readonly string[]): boolean;
|
|
127
|
+
/**
|
|
128
|
+
* Whether a check names any of `columns` (its own column, or an identifier in its
|
|
129
|
+
* expression). A rebuild that drops a column cannot keep a check that reads it.
|
|
130
|
+
*
|
|
131
|
+
* @param check - A check of the table
|
|
132
|
+
* @param columns - Column names
|
|
133
|
+
*/
|
|
134
|
+
declare function sqliteCheckReferencesColumn(check: SqliteCheckConstraint, columns: ReadonlySet<string>): boolean;
|
|
135
|
+
/**
|
|
136
|
+
* The table constraint that re-creates a kept check in a rebuilt table: a column check
|
|
137
|
+
* becomes a table check (SQLite evaluates both the same way, per row).
|
|
138
|
+
*
|
|
139
|
+
* @param check - The check to keep
|
|
140
|
+
*/
|
|
141
|
+
declare function sqliteCheckConstraintDefinition(check: SqliteCheckConstraint): string;
|
|
142
|
+
/**
|
|
143
|
+
* Statements that rebuild a SQLite table without value-domain constraints (SQLite's
|
|
144
|
+
* documented "make other kinds of table schema changes" procedure), keeping every row,
|
|
145
|
+
* column (types and defaults), primary key, foreign key, `UNIQUE` constraint, index and
|
|
146
|
+
* trigger, and every `CHECK` that is not one of Kora's enum checks (RT-111). Run them in
|
|
147
|
+
* ONE transaction, with foreign key enforcement off when the database enforces foreign
|
|
148
|
+
* keys (the drop of a referenced table would otherwise fail or fire its actions). Returns
|
|
149
|
+
* an empty list when the table needs nothing, so running it again is a no-op
|
|
150
|
+
* (idempotent); a rebuild interrupted mid-way rolls back with its transaction and runs
|
|
151
|
+
* again at the next open (resumable).
|
|
152
|
+
*
|
|
153
|
+
* @param catalog - The table as read from SQLite's catalog
|
|
154
|
+
* @param enumColumns - The table's enum fields, which decide which enum-shaped checks are
|
|
155
|
+
* Kora's (undefined: any enum-shaped check on a non-internal column)
|
|
156
|
+
* @returns The rebuild statements, or `[]`
|
|
157
|
+
*/
|
|
158
|
+
declare function sqliteConstraintRelaxationStatements(catalog: SqliteTableCatalog, enumColumns?: readonly string[]): string[];
|
|
159
|
+
/** A `CHECK` that restricts one column to a list of string literals (an enum check). */
|
|
160
|
+
interface EnumCheckShape {
|
|
161
|
+
/** The column, unquoted */
|
|
162
|
+
column: string;
|
|
163
|
+
/** The allowed values, unquoted */
|
|
164
|
+
values: string[];
|
|
165
|
+
/** The check also allows NULL (`... OR col IS NULL`) */
|
|
166
|
+
allowsNull: boolean;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Parse a `CHECK` constraint definition as an enum check, structurally: one column
|
|
170
|
+
* compared to string literals, in any form Postgres normalizes `col IN (...)` to
|
|
171
|
+
* (`col = ANY (ARRAY[...])`, the single-value `col = 'x'`, casts such as
|
|
172
|
+
* `(col)::text = ANY ((ARRAY['x'::character varying])::text[])`), a disjunction of
|
|
173
|
+
* equalities (`col = 'a' OR col = 'b'`), the SQLite source form `col IN ('a', 'b')`, an
|
|
174
|
+
* optional `OR col IS NULL`, and a trailing `NOT VALID`. Anything else (another operator,
|
|
175
|
+
* a function call, two columns, `AND`, `NOT`) is not an enum check and returns null.
|
|
176
|
+
*
|
|
177
|
+
* @param definition - The constraint definition (`pg_get_constraintdef`, or SQLite DDL)
|
|
178
|
+
* @returns The column and values, or null when the check has another shape
|
|
179
|
+
*/
|
|
180
|
+
declare function parseEnumCheckDefinition(definition: string): EnumCheckShape | null;
|
|
181
|
+
/**
|
|
182
|
+
* Whether a Postgres `CHECK` constraint definition (`pg_get_constraintdef`) has the shape
|
|
183
|
+
* of a Kora enum check (see {@link parseEnumCheckDefinition}). Other checks (added by hand)
|
|
184
|
+
* are left alone.
|
|
185
|
+
*
|
|
186
|
+
* @param definition - The constraint definition text
|
|
187
|
+
*/
|
|
188
|
+
declare function isPostgresEnumCheckDefinition(definition: string): boolean;
|
|
189
|
+
/** Runs a read-only SQL statement and returns its rows (an adapter's or a transaction's query). */
|
|
190
|
+
type SqliteQueryFn = (sql: string) => Promise<Array<Record<string, unknown>>>;
|
|
191
|
+
/**
|
|
192
|
+
* Read a SQLite table's catalog through `query`, or null when the table does not exist.
|
|
193
|
+
*
|
|
194
|
+
* @param query - Runs a SELECT/PRAGMA and returns rows
|
|
195
|
+
* @param table - The table name
|
|
196
|
+
*/
|
|
197
|
+
declare function readSqliteTableCatalog(query: SqliteQueryFn, table: string): Promise<SqliteTableCatalog | null>;
|
|
198
|
+
/**
|
|
199
|
+
* Every statement that relaxes the given SQLite tables (one rebuild per table that still
|
|
200
|
+
* carries a value-domain constraint; tables that do not exist or need nothing are
|
|
201
|
+
* skipped). Run them in one transaction; see {@link sqliteConstraintRelaxationStatements}.
|
|
202
|
+
*
|
|
203
|
+
* @param query - Runs a SELECT/PRAGMA and returns rows (inside the caller's transaction)
|
|
204
|
+
* @param tables - The collection tables to check
|
|
205
|
+
* @param enumColumnsByTable - Each table's enum fields (only enum checks on them are
|
|
206
|
+
* Kora's, RT-111); a table missing here treats any enum-shaped check as Kora's
|
|
207
|
+
* @returns The statements, or `[]` when every table is already relaxed (idempotent)
|
|
208
|
+
*/
|
|
209
|
+
declare function planSqliteConstraintRelaxation(query: SqliteQueryFn, tables: readonly string[], enumColumnsByTable?: Readonly<Record<string, readonly string[]>>): Promise<string[]>;
|
|
210
|
+
/**
|
|
211
|
+
* The `ALTER TABLE` statements that relax Postgres collection tables: drop every Kora enum
|
|
212
|
+
* `CHECK` (one column compared to a literal list, see {@link isPostgresEnumCheckDefinition})
|
|
213
|
+
* and every `NOT NULL` on a schema field. Kora's own columns, multi-column checks and
|
|
214
|
+
* checks of any other shape (added by hand) are kept. Reads the catalog of the current
|
|
215
|
+
* schema through `query`, so run it and the statements in one transaction.
|
|
216
|
+
*
|
|
217
|
+
* A check is matched structurally against the schema (RT-108): it is parsed as an enum
|
|
218
|
+
* check ({@link parseEnumCheckDefinition}, every form Postgres normalizes `IN (...)` to,
|
|
219
|
+
* including the single-value `col = 'x'`) and dropped when its one column is an enum field
|
|
220
|
+
* of the table (`enumFieldsByTable`; without it, any schema field), or when it restricts
|
|
221
|
+
* any other non-internal column to two or more literals (the multi-value shape every
|
|
222
|
+
* beta.12 enum check had).
|
|
223
|
+
*
|
|
224
|
+
* @param query - Runs a SELECT and returns rows
|
|
225
|
+
* @param fieldsByTable - Collection table name to its schema field names
|
|
226
|
+
* @param enumFieldsByTable - Collection table name to its enum field names (before and
|
|
227
|
+
* after the change); when a table is missing, every schema field counts as one
|
|
228
|
+
* @returns The statements, or `[]` when nothing is left to relax (idempotent)
|
|
229
|
+
*/
|
|
230
|
+
declare function planPostgresConstraintRelaxation(query: SqliteQueryFn, fieldsByTable: Readonly<Record<string, readonly string[]>>, enumFieldsByTable?: Readonly<Record<string, readonly string[]>>): Promise<string[]>;
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Total order on stamps: authority class (only `merge('server-authoritative')`
|
|
234
|
+
* fields carry one), then serialized HLC (which sorts exactly like
|
|
235
|
+
* `HybridLogicalClock.compare`), then operation id.
|
|
236
|
+
*
|
|
237
|
+
* @returns Negative if a < b, positive if a > b, 0 if equal
|
|
238
|
+
*/
|
|
239
|
+
declare function compareStamps(a: Stamp, b: Stamp): number;
|
|
240
|
+
/**
|
|
241
|
+
* Build a stamp from an HLC timestamp and an operation id. Throws
|
|
242
|
+
* `InvalidTimestampError` for a timestamp that cannot be serialized (for example
|
|
243
|
+
* a null wallTime left by a beta.12 backup restore): such an op must be repaired
|
|
244
|
+
* by the log-integrity scan (W8) before it is folded, never folded with a guess.
|
|
245
|
+
*/
|
|
246
|
+
declare function stampOf(timestamp: HLCTimestamp, opId: string): Stamp;
|
|
247
|
+
|
|
248
|
+
/** How the fold merges one field, derived from the schema. */
|
|
249
|
+
interface FieldPlan {
|
|
250
|
+
kind: FoldFieldKind;
|
|
251
|
+
/** For 'set': removals are ignored (`merge('append-only')`). */
|
|
252
|
+
appendOnly: boolean;
|
|
253
|
+
/** For 'res': the custom resolver. */
|
|
254
|
+
resolver?: CustomResolver;
|
|
255
|
+
/** Name of the strategy for traces. */
|
|
256
|
+
strategy: string;
|
|
257
|
+
/** Tier for traces: 3 for custom resolvers, 1 otherwise. */
|
|
258
|
+
tier: 1 | 3;
|
|
259
|
+
/** Secret field: values are redacted from traces. */
|
|
260
|
+
secret: boolean;
|
|
261
|
+
/** `merge('server-authoritative')`: writes of authoritative nodes win (class 1 stamps). */
|
|
262
|
+
authoritative?: boolean;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Decide how a field is folded. Precedence matches the pairwise engine it
|
|
266
|
+
* replaces: a custom resolver (tier 3), then a schema-declared merge strategy,
|
|
267
|
+
* then the field kind's default.
|
|
268
|
+
*
|
|
269
|
+
* | Schema | Fold kind |
|
|
270
|
+
* |---|---|
|
|
271
|
+
* | `resolve: { field }` | 'res' (resolver over the write log) |
|
|
272
|
+
* | `merge('counter')` | 'ctr' (base + deltas) |
|
|
273
|
+
* | `merge('max')` / `merge('min')` | 'max' / 'min' |
|
|
274
|
+
* | array, `merge('append-only')` | 'set' with removals ignored |
|
|
275
|
+
* | array (default or `merge('union')`) | 'set' (LWW element set) |
|
|
276
|
+
* | object / json | 'map' (per-top-level-key LWW) |
|
|
277
|
+
* | richtext | 'rt' (Yjs updates + string reset register) |
|
|
278
|
+
* | `merge('server-authoritative')` | 'reg' with authority-classed stamps |
|
|
279
|
+
* | everything else, `merge('lww')` | 'reg' |
|
|
280
|
+
*
|
|
281
|
+
* A field that is not in the schema (an older schema version's field, or an
|
|
282
|
+
* unknown collection) folds as a last-write-wins register.
|
|
283
|
+
*
|
|
284
|
+
* @param collection - The collection definition, if the schema has it
|
|
285
|
+
* @param field - The field name
|
|
286
|
+
*/
|
|
287
|
+
declare function planField(collection: CollectionDefinition | undefined, field: string): FieldPlan;
|
|
288
|
+
|
|
289
|
+
export { type EnumCheckShape, type FieldPlan, SimpleEventEmitter, type SqliteCheckConstraint, type SqliteColumnInfo, type SqliteForeignKeyInfo, type SqliteQueryFn, type SqliteTableCatalog, compareStamps, isKoraInternalColumn, isKoraSqliteEnumCheck, isPostgresEnumCheckDefinition, parseEnumCheckDefinition, parseSqliteCheckConstraints, planField, planPostgresConstraintRelaxation, planSqliteConstraintRelaxation, readSqliteTableCatalog, sqliteCheckConstraintDefinition, sqliteCheckReferencesColumn, sqliteConstraintRelaxationStatements, sqliteTableNeedsRelaxation, stampOf, topologicalSort };
|
package/dist/internal.d.ts
CHANGED
|
@@ -1,23 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export {
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Compute the content-addressed ID for an operation using SHA-256.
|
|
6
|
-
* The same operation content always produces the same hash, ensuring deduplication.
|
|
7
|
-
*
|
|
8
|
-
* @param input - The operation input (without id/timestamp, which are assigned separately)
|
|
9
|
-
* @param timestamp - The HLC timestamp serialized as a string
|
|
10
|
-
* @returns A hex-encoded SHA-256 hash
|
|
11
|
-
*/
|
|
12
|
-
declare function computeOperationId(input: OperationInput, timestamp: string): Promise<string>;
|
|
13
|
-
/**
|
|
14
|
-
* Deterministic JSON serialization with sorted keys.
|
|
15
|
-
* Ensures identical objects always produce identical strings regardless of property insertion order.
|
|
16
|
-
*
|
|
17
|
-
* @param obj - The value to serialize
|
|
18
|
-
* @returns A deterministic JSON string
|
|
19
|
-
*/
|
|
20
|
-
declare function canonicalize(obj: unknown): string;
|
|
1
|
+
import { S as Stamp, k as FoldFieldKind } from './sql-gen-CXTA6w-w.js';
|
|
2
|
+
export { B as SCHEMA_CEILING_DIRECTIVE, G as canonicalize, I as collectionIndexName, u as computeOperationId, J as enumCheckConstraint, L as legacyCollectionIndexName, N as sqlDefaultLiteral, P as sqlStringLiteral, Q as validateOperationParams } from './sql-gen-CXTA6w-w.js';
|
|
3
|
+
import { X as KoraEventEmitter, Z as KoraEventType, Y as KoraEventListener, W as KoraEventByType, a as Operation, H as HLCTimestamp, b as CustomResolver, C as CollectionDefinition } from './events-Da29Dt_3.js';
|
|
21
4
|
|
|
22
5
|
/**
|
|
23
6
|
* Concrete implementation of KoraEventEmitter.
|
|
@@ -56,4 +39,251 @@ declare class SimpleEventEmitter implements KoraEventEmitter {
|
|
|
56
39
|
*/
|
|
57
40
|
declare function topologicalSort(operations: Operation[]): Operation[];
|
|
58
41
|
|
|
59
|
-
|
|
42
|
+
/**
|
|
43
|
+
* Value-domain rules live in validation only (RT-101).
|
|
44
|
+
*
|
|
45
|
+
* Kora's generated DDL used to restate two schema rules as storage constraints: an enum
|
|
46
|
+
* field's `CHECK (col IN (...))` and a required field's `NOT NULL`. The value domain
|
|
47
|
+
* already enforces both at write time on every replica, and storage constraints cannot
|
|
48
|
+
* evolve with the schema (SQLite cannot alter a column constraint): an enum value added
|
|
49
|
+
* by a schema upgrade was valid everywhere but refused by every existing table. The DDL
|
|
50
|
+
* no longer emits them, and existing tables are relaxed once, by these helpers.
|
|
51
|
+
*
|
|
52
|
+
* Kora's own bookkeeping columns (`id` and every `_`-prefixed column) keep their
|
|
53
|
+
* constraints: they are written by Kora only and never evolve with the app schema.
|
|
54
|
+
*/
|
|
55
|
+
/** A row of `PRAGMA table_info(<table>)`. */
|
|
56
|
+
interface SqliteColumnInfo {
|
|
57
|
+
cid: number;
|
|
58
|
+
name: string;
|
|
59
|
+
type: string;
|
|
60
|
+
notnull: number;
|
|
61
|
+
dflt_value: string | null;
|
|
62
|
+
pk: number;
|
|
63
|
+
}
|
|
64
|
+
/** A row of `PRAGMA foreign_key_list(<table>)`. */
|
|
65
|
+
interface SqliteForeignKeyInfo {
|
|
66
|
+
id: number;
|
|
67
|
+
seq: number;
|
|
68
|
+
table: string;
|
|
69
|
+
from: string;
|
|
70
|
+
to: string | null;
|
|
71
|
+
on_update: string;
|
|
72
|
+
on_delete: string;
|
|
73
|
+
match?: string;
|
|
74
|
+
}
|
|
75
|
+
/** What a SQLite table looks like, read from its catalog. */
|
|
76
|
+
interface SqliteTableCatalog {
|
|
77
|
+
/** Table name */
|
|
78
|
+
table: string;
|
|
79
|
+
/** The table's `CREATE TABLE` statement (`sqlite_master.sql`) */
|
|
80
|
+
sql: string;
|
|
81
|
+
columns: readonly SqliteColumnInfo[];
|
|
82
|
+
foreignKeys: readonly SqliteForeignKeyInfo[];
|
|
83
|
+
/** Column lists of the table's `UNIQUE` constraints (`index_list` origin `u`) */
|
|
84
|
+
uniqueConstraints: readonly (readonly string[])[];
|
|
85
|
+
/** `CREATE INDEX` / `CREATE TRIGGER` statements on the table (`sqlite_master.sql`) */
|
|
86
|
+
dependents: readonly string[];
|
|
87
|
+
}
|
|
88
|
+
/** Columns Kora writes itself; their constraints are kept. */
|
|
89
|
+
declare function isKoraInternalColumn(name: string): boolean;
|
|
90
|
+
/** One `CHECK` constraint of a SQLite `CREATE TABLE` statement. */
|
|
91
|
+
interface SqliteCheckConstraint {
|
|
92
|
+
/** The constraint name (`CONSTRAINT <name> CHECK ...`), unquoted, or null */
|
|
93
|
+
name: string | null;
|
|
94
|
+
/** The checked expression, verbatim (the text between the parentheses) */
|
|
95
|
+
expression: string;
|
|
96
|
+
/** The column whose definition carries the check (a column constraint), or null */
|
|
97
|
+
column: string | null;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Every `CHECK` constraint of a SQLite `CREATE TABLE` statement, column and table
|
|
101
|
+
* constraints alike, with the expression verbatim (RT-111). The catalog pragmas do not
|
|
102
|
+
* report checks, so a table rebuild must carry them over from the statement itself.
|
|
103
|
+
*
|
|
104
|
+
* @param createTableSql - The table's `CREATE TABLE` statement (`sqlite_master.sql`)
|
|
105
|
+
* @returns The checks in statement order
|
|
106
|
+
*/
|
|
107
|
+
declare function parseSqliteCheckConstraints(createTableSql: string): SqliteCheckConstraint[];
|
|
108
|
+
/**
|
|
109
|
+
* Whether a check is one of Kora's own value-domain checks (beta.12 and earlier DDL):
|
|
110
|
+
* an enum check (see {@link parseEnumCheckDefinition}) on one column that is not Kora's
|
|
111
|
+
* own and, when `enumColumns` is given, is an enum field of the schema. A check of any
|
|
112
|
+
* other shape, or on another column, was added by hand and is kept (RT-111).
|
|
113
|
+
*
|
|
114
|
+
* @param check - A check of the table
|
|
115
|
+
* @param enumColumns - The table's enum fields (undefined: any enum-shaped check counts)
|
|
116
|
+
*/
|
|
117
|
+
declare function isKoraSqliteEnumCheck(check: SqliteCheckConstraint, enumColumns?: readonly string[]): boolean;
|
|
118
|
+
/**
|
|
119
|
+
* Whether a table still carries a value-domain constraint of Kora's: an enum `CHECK` on
|
|
120
|
+
* an enum field (see {@link isKoraSqliteEnumCheck}), or `NOT NULL` on a column that is not
|
|
121
|
+
* Kora's own. Checks added by hand never make a table need relaxation.
|
|
122
|
+
*
|
|
123
|
+
* @param catalog - The table as read from SQLite's catalog
|
|
124
|
+
* @param enumColumns - The table's enum fields (undefined: any enum-shaped check counts)
|
|
125
|
+
*/
|
|
126
|
+
declare function sqliteTableNeedsRelaxation(catalog: SqliteTableCatalog, enumColumns?: readonly string[]): boolean;
|
|
127
|
+
/**
|
|
128
|
+
* Whether a check names any of `columns` (its own column, or an identifier in its
|
|
129
|
+
* expression). A rebuild that drops a column cannot keep a check that reads it.
|
|
130
|
+
*
|
|
131
|
+
* @param check - A check of the table
|
|
132
|
+
* @param columns - Column names
|
|
133
|
+
*/
|
|
134
|
+
declare function sqliteCheckReferencesColumn(check: SqliteCheckConstraint, columns: ReadonlySet<string>): boolean;
|
|
135
|
+
/**
|
|
136
|
+
* The table constraint that re-creates a kept check in a rebuilt table: a column check
|
|
137
|
+
* becomes a table check (SQLite evaluates both the same way, per row).
|
|
138
|
+
*
|
|
139
|
+
* @param check - The check to keep
|
|
140
|
+
*/
|
|
141
|
+
declare function sqliteCheckConstraintDefinition(check: SqliteCheckConstraint): string;
|
|
142
|
+
/**
|
|
143
|
+
* Statements that rebuild a SQLite table without value-domain constraints (SQLite's
|
|
144
|
+
* documented "make other kinds of table schema changes" procedure), keeping every row,
|
|
145
|
+
* column (types and defaults), primary key, foreign key, `UNIQUE` constraint, index and
|
|
146
|
+
* trigger, and every `CHECK` that is not one of Kora's enum checks (RT-111). Run them in
|
|
147
|
+
* ONE transaction, with foreign key enforcement off when the database enforces foreign
|
|
148
|
+
* keys (the drop of a referenced table would otherwise fail or fire its actions). Returns
|
|
149
|
+
* an empty list when the table needs nothing, so running it again is a no-op
|
|
150
|
+
* (idempotent); a rebuild interrupted mid-way rolls back with its transaction and runs
|
|
151
|
+
* again at the next open (resumable).
|
|
152
|
+
*
|
|
153
|
+
* @param catalog - The table as read from SQLite's catalog
|
|
154
|
+
* @param enumColumns - The table's enum fields, which decide which enum-shaped checks are
|
|
155
|
+
* Kora's (undefined: any enum-shaped check on a non-internal column)
|
|
156
|
+
* @returns The rebuild statements, or `[]`
|
|
157
|
+
*/
|
|
158
|
+
declare function sqliteConstraintRelaxationStatements(catalog: SqliteTableCatalog, enumColumns?: readonly string[]): string[];
|
|
159
|
+
/** A `CHECK` that restricts one column to a list of string literals (an enum check). */
|
|
160
|
+
interface EnumCheckShape {
|
|
161
|
+
/** The column, unquoted */
|
|
162
|
+
column: string;
|
|
163
|
+
/** The allowed values, unquoted */
|
|
164
|
+
values: string[];
|
|
165
|
+
/** The check also allows NULL (`... OR col IS NULL`) */
|
|
166
|
+
allowsNull: boolean;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Parse a `CHECK` constraint definition as an enum check, structurally: one column
|
|
170
|
+
* compared to string literals, in any form Postgres normalizes `col IN (...)` to
|
|
171
|
+
* (`col = ANY (ARRAY[...])`, the single-value `col = 'x'`, casts such as
|
|
172
|
+
* `(col)::text = ANY ((ARRAY['x'::character varying])::text[])`), a disjunction of
|
|
173
|
+
* equalities (`col = 'a' OR col = 'b'`), the SQLite source form `col IN ('a', 'b')`, an
|
|
174
|
+
* optional `OR col IS NULL`, and a trailing `NOT VALID`. Anything else (another operator,
|
|
175
|
+
* a function call, two columns, `AND`, `NOT`) is not an enum check and returns null.
|
|
176
|
+
*
|
|
177
|
+
* @param definition - The constraint definition (`pg_get_constraintdef`, or SQLite DDL)
|
|
178
|
+
* @returns The column and values, or null when the check has another shape
|
|
179
|
+
*/
|
|
180
|
+
declare function parseEnumCheckDefinition(definition: string): EnumCheckShape | null;
|
|
181
|
+
/**
|
|
182
|
+
* Whether a Postgres `CHECK` constraint definition (`pg_get_constraintdef`) has the shape
|
|
183
|
+
* of a Kora enum check (see {@link parseEnumCheckDefinition}). Other checks (added by hand)
|
|
184
|
+
* are left alone.
|
|
185
|
+
*
|
|
186
|
+
* @param definition - The constraint definition text
|
|
187
|
+
*/
|
|
188
|
+
declare function isPostgresEnumCheckDefinition(definition: string): boolean;
|
|
189
|
+
/** Runs a read-only SQL statement and returns its rows (an adapter's or a transaction's query). */
|
|
190
|
+
type SqliteQueryFn = (sql: string) => Promise<Array<Record<string, unknown>>>;
|
|
191
|
+
/**
|
|
192
|
+
* Read a SQLite table's catalog through `query`, or null when the table does not exist.
|
|
193
|
+
*
|
|
194
|
+
* @param query - Runs a SELECT/PRAGMA and returns rows
|
|
195
|
+
* @param table - The table name
|
|
196
|
+
*/
|
|
197
|
+
declare function readSqliteTableCatalog(query: SqliteQueryFn, table: string): Promise<SqliteTableCatalog | null>;
|
|
198
|
+
/**
|
|
199
|
+
* Every statement that relaxes the given SQLite tables (one rebuild per table that still
|
|
200
|
+
* carries a value-domain constraint; tables that do not exist or need nothing are
|
|
201
|
+
* skipped). Run them in one transaction; see {@link sqliteConstraintRelaxationStatements}.
|
|
202
|
+
*
|
|
203
|
+
* @param query - Runs a SELECT/PRAGMA and returns rows (inside the caller's transaction)
|
|
204
|
+
* @param tables - The collection tables to check
|
|
205
|
+
* @param enumColumnsByTable - Each table's enum fields (only enum checks on them are
|
|
206
|
+
* Kora's, RT-111); a table missing here treats any enum-shaped check as Kora's
|
|
207
|
+
* @returns The statements, or `[]` when every table is already relaxed (idempotent)
|
|
208
|
+
*/
|
|
209
|
+
declare function planSqliteConstraintRelaxation(query: SqliteQueryFn, tables: readonly string[], enumColumnsByTable?: Readonly<Record<string, readonly string[]>>): Promise<string[]>;
|
|
210
|
+
/**
|
|
211
|
+
* The `ALTER TABLE` statements that relax Postgres collection tables: drop every Kora enum
|
|
212
|
+
* `CHECK` (one column compared to a literal list, see {@link isPostgresEnumCheckDefinition})
|
|
213
|
+
* and every `NOT NULL` on a schema field. Kora's own columns, multi-column checks and
|
|
214
|
+
* checks of any other shape (added by hand) are kept. Reads the catalog of the current
|
|
215
|
+
* schema through `query`, so run it and the statements in one transaction.
|
|
216
|
+
*
|
|
217
|
+
* A check is matched structurally against the schema (RT-108): it is parsed as an enum
|
|
218
|
+
* check ({@link parseEnumCheckDefinition}, every form Postgres normalizes `IN (...)` to,
|
|
219
|
+
* including the single-value `col = 'x'`) and dropped when its one column is an enum field
|
|
220
|
+
* of the table (`enumFieldsByTable`; without it, any schema field), or when it restricts
|
|
221
|
+
* any other non-internal column to two or more literals (the multi-value shape every
|
|
222
|
+
* beta.12 enum check had).
|
|
223
|
+
*
|
|
224
|
+
* @param query - Runs a SELECT and returns rows
|
|
225
|
+
* @param fieldsByTable - Collection table name to its schema field names
|
|
226
|
+
* @param enumFieldsByTable - Collection table name to its enum field names (before and
|
|
227
|
+
* after the change); when a table is missing, every schema field counts as one
|
|
228
|
+
* @returns The statements, or `[]` when nothing is left to relax (idempotent)
|
|
229
|
+
*/
|
|
230
|
+
declare function planPostgresConstraintRelaxation(query: SqliteQueryFn, fieldsByTable: Readonly<Record<string, readonly string[]>>, enumFieldsByTable?: Readonly<Record<string, readonly string[]>>): Promise<string[]>;
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Total order on stamps: authority class (only `merge('server-authoritative')`
|
|
234
|
+
* fields carry one), then serialized HLC (which sorts exactly like
|
|
235
|
+
* `HybridLogicalClock.compare`), then operation id.
|
|
236
|
+
*
|
|
237
|
+
* @returns Negative if a < b, positive if a > b, 0 if equal
|
|
238
|
+
*/
|
|
239
|
+
declare function compareStamps(a: Stamp, b: Stamp): number;
|
|
240
|
+
/**
|
|
241
|
+
* Build a stamp from an HLC timestamp and an operation id. Throws
|
|
242
|
+
* `InvalidTimestampError` for a timestamp that cannot be serialized (for example
|
|
243
|
+
* a null wallTime left by a beta.12 backup restore): such an op must be repaired
|
|
244
|
+
* by the log-integrity scan (W8) before it is folded, never folded with a guess.
|
|
245
|
+
*/
|
|
246
|
+
declare function stampOf(timestamp: HLCTimestamp, opId: string): Stamp;
|
|
247
|
+
|
|
248
|
+
/** How the fold merges one field, derived from the schema. */
|
|
249
|
+
interface FieldPlan {
|
|
250
|
+
kind: FoldFieldKind;
|
|
251
|
+
/** For 'set': removals are ignored (`merge('append-only')`). */
|
|
252
|
+
appendOnly: boolean;
|
|
253
|
+
/** For 'res': the custom resolver. */
|
|
254
|
+
resolver?: CustomResolver;
|
|
255
|
+
/** Name of the strategy for traces. */
|
|
256
|
+
strategy: string;
|
|
257
|
+
/** Tier for traces: 3 for custom resolvers, 1 otherwise. */
|
|
258
|
+
tier: 1 | 3;
|
|
259
|
+
/** Secret field: values are redacted from traces. */
|
|
260
|
+
secret: boolean;
|
|
261
|
+
/** `merge('server-authoritative')`: writes of authoritative nodes win (class 1 stamps). */
|
|
262
|
+
authoritative?: boolean;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Decide how a field is folded. Precedence matches the pairwise engine it
|
|
266
|
+
* replaces: a custom resolver (tier 3), then a schema-declared merge strategy,
|
|
267
|
+
* then the field kind's default.
|
|
268
|
+
*
|
|
269
|
+
* | Schema | Fold kind |
|
|
270
|
+
* |---|---|
|
|
271
|
+
* | `resolve: { field }` | 'res' (resolver over the write log) |
|
|
272
|
+
* | `merge('counter')` | 'ctr' (base + deltas) |
|
|
273
|
+
* | `merge('max')` / `merge('min')` | 'max' / 'min' |
|
|
274
|
+
* | array, `merge('append-only')` | 'set' with removals ignored |
|
|
275
|
+
* | array (default or `merge('union')`) | 'set' (LWW element set) |
|
|
276
|
+
* | object / json | 'map' (per-top-level-key LWW) |
|
|
277
|
+
* | richtext | 'rt' (Yjs updates + string reset register) |
|
|
278
|
+
* | `merge('server-authoritative')` | 'reg' with authority-classed stamps |
|
|
279
|
+
* | everything else, `merge('lww')` | 'reg' |
|
|
280
|
+
*
|
|
281
|
+
* A field that is not in the schema (an older schema version's field, or an
|
|
282
|
+
* unknown collection) folds as a last-write-wins register.
|
|
283
|
+
*
|
|
284
|
+
* @param collection - The collection definition, if the schema has it
|
|
285
|
+
* @param field - The field name
|
|
286
|
+
*/
|
|
287
|
+
declare function planField(collection: CollectionDefinition | undefined, field: string): FieldPlan;
|
|
288
|
+
|
|
289
|
+
export { type EnumCheckShape, type FieldPlan, SimpleEventEmitter, type SqliteCheckConstraint, type SqliteColumnInfo, type SqliteForeignKeyInfo, type SqliteQueryFn, type SqliteTableCatalog, compareStamps, isKoraInternalColumn, isKoraSqliteEnumCheck, isPostgresEnumCheckDefinition, parseEnumCheckDefinition, parseSqliteCheckConstraints, planField, planPostgresConstraintRelaxation, planSqliteConstraintRelaxation, readSqliteTableCatalog, sqliteCheckConstraintDefinition, sqliteCheckReferencesColumn, sqliteConstraintRelaxationStatements, sqliteTableNeedsRelaxation, stampOf, topologicalSort };
|