@korajs/core 1.0.0-beta.12 → 1.0.0-beta.13
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-Cv5HfCQP.d.ts → build-scope-map-CIFa58oA.d.ts} +1 -1
- package/dist/{build-scope-map-CItP1gsz.d.cts → build-scope-map-ClIq_doj.d.cts} +1 -1
- package/dist/{chunk-5IICSH6H.js → chunk-QUD3DMSD.js} +668 -24
- package/dist/chunk-QUD3DMSD.js.map +1 -0
- package/dist/events-BSU-0z5O.d.cts +1737 -0
- package/dist/events-BSU-0z5O.d.ts +1737 -0
- package/dist/index.cjs +2526 -204
- 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 +2012 -337
- 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-ByOZIQU-.d.ts +622 -0
- package/dist/sql-gen-DdOkMojr.d.cts +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
|
@@ -1,1010 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Base field builder implementing the builder pattern for schema field definitions.
|
|
3
|
-
* Each builder is immutable — modifier methods return new builder instances.
|
|
4
|
-
*
|
|
5
|
-
* Type parameters track field metadata at the type level for inference:
|
|
6
|
-
* - Kind: the field kind ('string', 'number', etc.)
|
|
7
|
-
* - Req: whether the field is required (true = required on insert)
|
|
8
|
-
* - Auto: whether the field is auto-populated (true = excluded from insert input)
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* ```typescript
|
|
12
|
-
* t.string() // required string field
|
|
13
|
-
* t.string().optional() // optional string field
|
|
14
|
-
* t.string().default('hello') // string with default value
|
|
15
|
-
* t.timestamp().auto() // auto-populated timestamp
|
|
16
|
-
* ```
|
|
17
|
-
*/
|
|
18
|
-
declare class FieldBuilder<Kind extends FieldKind = FieldKind, Req extends boolean = true, Auto extends boolean = false> {
|
|
19
|
-
protected readonly _kind: Kind;
|
|
20
|
-
protected readonly _required: boolean;
|
|
21
|
-
protected readonly _defaultValue: unknown;
|
|
22
|
-
protected readonly _auto: boolean;
|
|
23
|
-
protected readonly _mergeStrategy: FieldMergeStrategy | null;
|
|
24
|
-
constructor(kind: Kind, required?: Req, defaultValue?: unknown, auto?: Auto, mergeStrategy?: FieldMergeStrategy | null);
|
|
25
|
-
/** Mark this field as optional (not required on insert) */
|
|
26
|
-
optional(): FieldBuilder<Kind, false, Auto>;
|
|
27
|
-
/** Set a default value for this field. Implicitly makes the field optional. */
|
|
28
|
-
default(value: unknown): FieldBuilder<Kind, false, Auto>;
|
|
29
|
-
/** Mark this field as auto-populated (e.g., createdAt timestamps). Developers cannot set auto fields. */
|
|
30
|
-
auto(): FieldBuilder<Kind, false, true>;
|
|
31
|
-
/**
|
|
32
|
-
* Declare a merge strategy for this field.
|
|
33
|
-
* Controls how concurrent modifications are resolved during sync.
|
|
34
|
-
*
|
|
35
|
-
* @param strategy - The merge strategy to use:
|
|
36
|
-
* - `'lww'`: Last-write-wins (default for scalar fields)
|
|
37
|
-
* - `'counter'`: Sum of deltas from base (for numbers)
|
|
38
|
-
* - `'max'`: Keep the maximum value (for numbers/timestamps)
|
|
39
|
-
* - `'min'`: Keep the minimum value (for numbers/timestamps)
|
|
40
|
-
* - `'union'`: Set-union merge (default for arrays)
|
|
41
|
-
* - `'append-only'`: Concatenate additions (for arrays)
|
|
42
|
-
* - `'server-authoritative'`: Always prefer the remote/server value
|
|
43
|
-
*/
|
|
44
|
-
merge(strategy: FieldMergeStrategy): FieldBuilder<Kind, Req, Auto>;
|
|
45
|
-
/** @internal Build the final FieldDescriptor. Used by defineSchema(). */
|
|
46
|
-
_build(): FieldDescriptor;
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Field builder for enum fields with constrained string values.
|
|
50
|
-
* Preserves the literal enum tuple type for inference.
|
|
51
|
-
*/
|
|
52
|
-
declare class EnumFieldBuilder<Values extends readonly string[] = readonly string[], Req extends boolean = true, Auto extends boolean = false> extends FieldBuilder<'enum', Req, Auto> {
|
|
53
|
-
private readonly _enumValues;
|
|
54
|
-
private readonly _transitions;
|
|
55
|
-
constructor(values: Values, required?: Req, defaultValue?: unknown, auto?: Auto, mergeStrategy?: FieldMergeStrategy | null, transitions?: TransitionMap | null);
|
|
56
|
-
optional(): EnumFieldBuilder<Values, false, Auto>;
|
|
57
|
-
default(value: Values[number]): EnumFieldBuilder<Values, false, Auto>;
|
|
58
|
-
auto(): EnumFieldBuilder<Values, false, true>;
|
|
59
|
-
merge(strategy: FieldMergeStrategy): EnumFieldBuilder<Values, Req, Auto>;
|
|
60
|
-
/**
|
|
61
|
-
* Declare allowed state transitions for this enum field.
|
|
62
|
-
* Enables state machine validation during mutations and merges.
|
|
63
|
-
*
|
|
64
|
-
* @param map - Map of state to allowed next states
|
|
65
|
-
*
|
|
66
|
-
* @example
|
|
67
|
-
* ```typescript
|
|
68
|
-
* t.enum(['draft', 'pending', 'confirmed', 'cancelled']).transitions({
|
|
69
|
-
* draft: ['pending', 'cancelled'],
|
|
70
|
-
* pending: ['confirmed', 'cancelled'],
|
|
71
|
-
* confirmed: [],
|
|
72
|
-
* cancelled: [],
|
|
73
|
-
* })
|
|
74
|
-
* ```
|
|
75
|
-
*/
|
|
76
|
-
transitions(map: Partial<Record<Values[number], Values[number][]>>): EnumFieldBuilder<Values, Req, Auto>;
|
|
77
|
-
_build(): FieldDescriptor;
|
|
78
|
-
}
|
|
79
|
-
/**
|
|
80
|
-
* Field builder for array fields with a typed item kind.
|
|
81
|
-
* Preserves the item kind type parameter for inference.
|
|
82
|
-
*/
|
|
83
|
-
declare class ArrayFieldBuilder<ItemKind extends FieldKind = FieldKind, Req extends boolean = true, Auto extends boolean = false> extends FieldBuilder<'array', Req, Auto> {
|
|
84
|
-
private readonly _itemKind;
|
|
85
|
-
constructor(itemBuilder: FieldBuilder<ItemKind>, required?: Req, defaultValue?: unknown, auto?: Auto, mergeStrategy?: FieldMergeStrategy | null);
|
|
86
|
-
optional(): ArrayFieldBuilder<ItemKind, false, Auto>;
|
|
87
|
-
default(value: unknown[]): ArrayFieldBuilder<ItemKind, false, Auto>;
|
|
88
|
-
auto(): ArrayFieldBuilder<ItemKind, false, true>;
|
|
89
|
-
merge(strategy: FieldMergeStrategy): ArrayFieldBuilder<ItemKind, Req, Auto>;
|
|
90
|
-
_build(): FieldDescriptor;
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Field builder for structured object fields with a nested field schema.
|
|
94
|
-
*
|
|
95
|
-
* Each nested key merges by its own declared kind (scalars via LWW, nested
|
|
96
|
-
* arrays via add-wins, nested objects recursively), so two devices editing
|
|
97
|
-
* different keys of the same object offline both converge on reconnect instead
|
|
98
|
-
* of one clobbering the other.
|
|
99
|
-
*/
|
|
100
|
-
declare class ObjectFieldBuilder<Req extends boolean = true, Auto extends boolean = false> extends FieldBuilder<'object', Req, Auto> {
|
|
101
|
-
private readonly _fields;
|
|
102
|
-
constructor(fields: Record<string, FieldBuilder>, required?: Req, defaultValue?: unknown, auto?: Auto, mergeStrategy?: FieldMergeStrategy | null);
|
|
103
|
-
optional(): ObjectFieldBuilder<false, Auto>;
|
|
104
|
-
default(value: Record<string, unknown>): ObjectFieldBuilder<false, Auto>;
|
|
105
|
-
auto(): ObjectFieldBuilder<false, true>;
|
|
106
|
-
merge(strategy: FieldMergeStrategy): ObjectFieldBuilder<Req, Auto>;
|
|
107
|
-
_build(): FieldDescriptor;
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Field builder for dynamic-key JSON values. Carries a compile-time shape `T`
|
|
111
|
-
* for inference while merging structurally as a convergent CRDT: a plain-object
|
|
112
|
-
* value recurses as a map, an array merges add-wins, any other value is a scalar
|
|
113
|
-
* leaf under last-write-wins.
|
|
114
|
-
*/
|
|
115
|
-
declare class JsonFieldBuilder<T = unknown, Req extends boolean = true, Auto extends boolean = false> extends FieldBuilder<'json', Req, Auto> {
|
|
116
|
-
optional(): JsonFieldBuilder<T, false, Auto>;
|
|
117
|
-
default(value: T): JsonFieldBuilder<T, false, Auto>;
|
|
118
|
-
auto(): JsonFieldBuilder<T, false, true>;
|
|
119
|
-
merge(strategy: FieldMergeStrategy): JsonFieldBuilder<T, Req, Auto>;
|
|
120
|
-
_build(): FieldDescriptor;
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Field builder for secret fields (passwords, tokens, API keys).
|
|
124
|
-
*
|
|
125
|
-
* A secret field's value is never exposed in merge traces, DevTools, or logs
|
|
126
|
-
* (it is redacted at the point traces are built). Its at-rest protection is
|
|
127
|
-
* chosen with `.hashed()` (one-way, for passwords) or `.encrypted()` (reversible,
|
|
128
|
-
* for tokens); the default is `encrypted`.
|
|
129
|
-
*/
|
|
130
|
-
declare class SecretFieldBuilder<Req extends boolean = true, Auto extends boolean = false> extends FieldBuilder<'secret', Req, Auto> {
|
|
131
|
-
private readonly _secretMode;
|
|
132
|
-
constructor(secretMode?: SecretMode, required?: Req, defaultValue?: unknown, auto?: Auto, mergeStrategy?: FieldMergeStrategy | null);
|
|
133
|
-
/** Store this secret as a one-way salted hash (passwords: verify, never read back). */
|
|
134
|
-
hashed(): SecretFieldBuilder<Req, Auto>;
|
|
135
|
-
/** Store this secret as reversible ciphertext (tokens/keys: decrypt to use). */
|
|
136
|
-
encrypted(): SecretFieldBuilder<Req, Auto>;
|
|
137
|
-
optional(): SecretFieldBuilder<false, Auto>;
|
|
138
|
-
auto(): SecretFieldBuilder<false, true>;
|
|
139
|
-
merge(strategy: FieldMergeStrategy): SecretFieldBuilder<Req, Auto>;
|
|
140
|
-
_build(): FieldDescriptor;
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* Type builder namespace. The developer's primary interface for defining field types.
|
|
144
|
-
*
|
|
145
|
-
* @example
|
|
146
|
-
* ```typescript
|
|
147
|
-
* import { t } from '@korajs/core'
|
|
148
|
-
*
|
|
149
|
-
* const fields = {
|
|
150
|
-
* title: t.string(),
|
|
151
|
-
* count: t.number(),
|
|
152
|
-
* active: t.boolean().default(true),
|
|
153
|
-
* notes: t.richtext(),
|
|
154
|
-
* tags: t.array(t.string()).default([]),
|
|
155
|
-
* priority: t.enum(['low', 'medium', 'high']).default('medium'),
|
|
156
|
-
* settings: t.object({ theme: t.string(), fontSize: t.number() }),
|
|
157
|
-
* metadata: t.json<{ source: string }>(),
|
|
158
|
-
* createdAt: t.timestamp().auto(),
|
|
159
|
-
* }
|
|
160
|
-
* ```
|
|
161
|
-
*/
|
|
162
|
-
declare const t: {
|
|
163
|
-
string(): FieldBuilder<"string", true, false>;
|
|
164
|
-
number(): FieldBuilder<"number", true, false>;
|
|
165
|
-
boolean(): FieldBuilder<"boolean", true, false>;
|
|
166
|
-
timestamp(): FieldBuilder<"timestamp", true, false>;
|
|
167
|
-
richtext(): FieldBuilder<"richtext", true, false>;
|
|
168
|
-
enum<const V extends readonly string[]>(values: V): EnumFieldBuilder<V, true, false>;
|
|
169
|
-
array<K extends FieldKind>(itemBuilder: FieldBuilder<K>): ArrayFieldBuilder<K, true, false>;
|
|
170
|
-
object(fields: Record<string, FieldBuilder>): ObjectFieldBuilder<true, false>;
|
|
171
|
-
json<T = unknown>(): JsonFieldBuilder<T, true, false>;
|
|
172
|
-
blob(): FieldBuilder<"blob", true, false>;
|
|
173
|
-
secret(): SecretFieldBuilder<true, false>;
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* A single migration step describing a schema change.
|
|
178
|
-
*/
|
|
179
|
-
type MigrationStep = {
|
|
180
|
-
type: 'addField';
|
|
181
|
-
collection: string;
|
|
182
|
-
field: string;
|
|
183
|
-
descriptor: FieldDescriptor;
|
|
184
|
-
} | {
|
|
185
|
-
type: 'removeField';
|
|
186
|
-
collection: string;
|
|
187
|
-
field: string;
|
|
188
|
-
descriptor?: FieldDescriptor;
|
|
189
|
-
} | {
|
|
190
|
-
type: 'renameField';
|
|
191
|
-
collection: string;
|
|
192
|
-
from: string;
|
|
193
|
-
to: string;
|
|
194
|
-
} | {
|
|
195
|
-
type: 'addIndex';
|
|
196
|
-
collection: string;
|
|
197
|
-
field: string;
|
|
198
|
-
} | {
|
|
199
|
-
type: 'removeIndex';
|
|
200
|
-
collection: string;
|
|
201
|
-
field: string;
|
|
202
|
-
} | {
|
|
203
|
-
type: 'backfill';
|
|
204
|
-
collection: string;
|
|
205
|
-
transform: (record: Record<string, unknown>) => Record<string, unknown>;
|
|
206
|
-
/** Reverse transform for rollback. If not provided, backfill is not safely reversible. */
|
|
207
|
-
reverseTransform?: (record: Record<string, unknown>) => Record<string, unknown>;
|
|
208
|
-
};
|
|
209
|
-
/**
|
|
210
|
-
* A completed migration definition containing ordered steps and optional rollback steps.
|
|
211
|
-
*/
|
|
212
|
-
interface MigrationDefinition {
|
|
213
|
-
readonly steps: readonly MigrationStep[];
|
|
214
|
-
/** Explicitly defined rollback steps. If undefined, inverse steps are auto-generated. */
|
|
215
|
-
readonly rollbackSteps: readonly MigrationStep[] | undefined;
|
|
216
|
-
/** Whether this migration can be safely rolled back. */
|
|
217
|
-
readonly safelyReversible: boolean;
|
|
218
|
-
}
|
|
219
|
-
/**
|
|
220
|
-
* Builder for defining rollback steps explicitly.
|
|
221
|
-
* Used with the `.down()` method on MigrationBuilder.
|
|
222
|
-
*
|
|
223
|
-
* @example
|
|
224
|
-
* ```typescript
|
|
225
|
-
* migrate()
|
|
226
|
-
* .addField('todos', 'priority', t.enum(['low', 'medium', 'high']).default('medium'))
|
|
227
|
-
* .addIndex('todos', 'priority')
|
|
228
|
-
* .down((rollback) => {
|
|
229
|
-
* rollback
|
|
230
|
-
* .removeIndex('todos', 'priority')
|
|
231
|
-
* .removeField('todos', 'priority')
|
|
232
|
-
* })
|
|
233
|
-
* ```
|
|
234
|
-
*/
|
|
235
|
-
declare class RollbackBuilder {
|
|
236
|
-
private _steps;
|
|
237
|
-
/**
|
|
238
|
-
* Add a field during rollback (to reverse a removeField).
|
|
239
|
-
*/
|
|
240
|
-
addField(collection: string, field: string, builder: FieldBuilder): RollbackBuilder;
|
|
241
|
-
/**
|
|
242
|
-
* Remove a field during rollback (to reverse an addField).
|
|
243
|
-
*/
|
|
244
|
-
removeField(collection: string, field: string): RollbackBuilder;
|
|
245
|
-
/**
|
|
246
|
-
* Rename a field during rollback (to reverse a renameField).
|
|
247
|
-
*/
|
|
248
|
-
renameField(collection: string, from: string, to: string): RollbackBuilder;
|
|
249
|
-
/**
|
|
250
|
-
* Add an index during rollback (to reverse a removeIndex).
|
|
251
|
-
*/
|
|
252
|
-
addIndex(collection: string, field: string): RollbackBuilder;
|
|
253
|
-
/**
|
|
254
|
-
* Remove an index during rollback (to reverse an addIndex).
|
|
255
|
-
*/
|
|
256
|
-
removeIndex(collection: string, field: string): RollbackBuilder;
|
|
257
|
-
/**
|
|
258
|
-
* Run a backfill during rollback (to reverse a previous backfill).
|
|
259
|
-
*/
|
|
260
|
-
backfill(collection: string, transform: (record: Record<string, unknown>) => Record<string, unknown>): RollbackBuilder;
|
|
261
|
-
/**
|
|
262
|
-
* Get the accumulated rollback steps.
|
|
263
|
-
*/
|
|
264
|
-
_getSteps(): readonly MigrationStep[];
|
|
265
|
-
}
|
|
266
|
-
/**
|
|
267
|
-
* Fluent builder for defining schema migrations.
|
|
268
|
-
*
|
|
269
|
-
* Each method returns a new builder instance (immutable).
|
|
270
|
-
*
|
|
271
|
-
* @example
|
|
272
|
-
* ```typescript
|
|
273
|
-
* migrate()
|
|
274
|
-
* .addField('products', 'taxInclusive', t.boolean().default(false))
|
|
275
|
-
* .renameField('products', 'cost', 'costPrice')
|
|
276
|
-
* .backfill('products', (record) => ({
|
|
277
|
-
* taxInclusive: record.taxRate > 0,
|
|
278
|
-
* }))
|
|
279
|
-
* ```
|
|
280
|
-
*/
|
|
281
|
-
declare class MigrationBuilder implements MigrationDefinition {
|
|
282
|
-
readonly steps: readonly MigrationStep[];
|
|
283
|
-
readonly rollbackSteps: readonly MigrationStep[] | undefined;
|
|
284
|
-
readonly safelyReversible: boolean;
|
|
285
|
-
constructor(steps?: readonly MigrationStep[], rollbackSteps?: readonly MigrationStep[], safelyReversible?: boolean);
|
|
286
|
-
/**
|
|
287
|
-
* Add a new field to a collection.
|
|
288
|
-
* The field builder provides the type and default value.
|
|
289
|
-
*/
|
|
290
|
-
addField(collection: string, field: string, builder: FieldBuilder): MigrationBuilder;
|
|
291
|
-
/**
|
|
292
|
-
* Remove a field from a collection.
|
|
293
|
-
* Optionally accepts a field builder to preserve the descriptor for rollback.
|
|
294
|
-
* Without the descriptor, rollback of this step requires a custom `.down()`.
|
|
295
|
-
*
|
|
296
|
-
* @param collection - The collection name
|
|
297
|
-
* @param field - The field name to remove
|
|
298
|
-
* @param builder - Optional field builder preserving the type info for rollback
|
|
299
|
-
*/
|
|
300
|
-
removeField(collection: string, field: string, builder?: FieldBuilder): MigrationBuilder;
|
|
301
|
-
/**
|
|
302
|
-
* Rename a field in a collection.
|
|
303
|
-
* Implemented as ALTER TABLE RENAME COLUMN (SQLite 3.25+).
|
|
304
|
-
*/
|
|
305
|
-
renameField(collection: string, from: string, to: string): MigrationBuilder;
|
|
306
|
-
/**
|
|
307
|
-
* Add an index on a field.
|
|
308
|
-
*/
|
|
309
|
-
addIndex(collection: string, field: string): MigrationBuilder;
|
|
310
|
-
/**
|
|
311
|
-
* Remove an index on a field.
|
|
312
|
-
*/
|
|
313
|
-
removeIndex(collection: string, field: string): MigrationBuilder;
|
|
314
|
-
/**
|
|
315
|
-
* Backfill records in a collection using a transform function.
|
|
316
|
-
* The transform receives each record and returns the fields to update.
|
|
317
|
-
* Runs after structural changes (addField, renameField, etc.).
|
|
318
|
-
*
|
|
319
|
-
* @param collection - The collection name
|
|
320
|
-
* @param transform - Forward transform function
|
|
321
|
-
* @param reverseTransform - Optional reverse transform for rollback support
|
|
322
|
-
*/
|
|
323
|
-
backfill(collection: string, transform: (record: Record<string, unknown>) => Record<string, unknown>, reverseTransform?: (record: Record<string, unknown>) => Record<string, unknown>): MigrationBuilder;
|
|
324
|
-
/**
|
|
325
|
-
* Define explicit rollback steps for this migration.
|
|
326
|
-
* If not called, inverse steps are auto-generated from forward steps.
|
|
327
|
-
*
|
|
328
|
-
* @param fn - Function that receives a RollbackBuilder to define rollback steps
|
|
329
|
-
* @returns A new MigrationBuilder with the rollback steps attached
|
|
330
|
-
*
|
|
331
|
-
* @example
|
|
332
|
-
* ```typescript
|
|
333
|
-
* migrate()
|
|
334
|
-
* .addField('todos', 'priority', t.enum(['low', 'medium', 'high']).default('medium'))
|
|
335
|
-
* .addIndex('todos', 'priority')
|
|
336
|
-
* .down((rollback) => {
|
|
337
|
-
* rollback
|
|
338
|
-
* .removeIndex('todos', 'priority')
|
|
339
|
-
* .removeField('todos', 'priority')
|
|
340
|
-
* })
|
|
341
|
-
* ```
|
|
342
|
-
*/
|
|
343
|
-
down(fn: (rollback: RollbackBuilder) => void): MigrationBuilder;
|
|
344
|
-
/**
|
|
345
|
-
* Determine if a migration is safely reversible based on its steps.
|
|
346
|
-
* A migration is not safely reversible if:
|
|
347
|
-
* - It has a backfill step without a reverseTransform and no explicit rollback
|
|
348
|
-
* - It has a removeField without a stored descriptor and no explicit rollback
|
|
349
|
-
*/
|
|
350
|
-
private _computeSafelyReversible;
|
|
351
|
-
}
|
|
352
|
-
/**
|
|
353
|
-
* Start building a migration.
|
|
354
|
-
*
|
|
355
|
-
* @returns A new MigrationBuilder
|
|
356
|
-
*
|
|
357
|
-
* @example
|
|
358
|
-
* ```typescript
|
|
359
|
-
* import { migrate, t } from '@korajs/core'
|
|
360
|
-
*
|
|
361
|
-
* const migration = migrate()
|
|
362
|
-
* .addField('products', 'taxInclusive', t.boolean().default(false))
|
|
363
|
-
* .renameField('products', 'cost', 'costPrice')
|
|
364
|
-
* ```
|
|
365
|
-
*/
|
|
366
|
-
declare function migrate(): MigrationBuilder;
|
|
367
|
-
|
|
368
|
-
/**
|
|
369
|
-
* Hybrid Logical Clock timestamp.
|
|
370
|
-
* Provides a total order that respects causality without requiring synchronized clocks.
|
|
371
|
-
*/
|
|
372
|
-
interface HLCTimestamp {
|
|
373
|
-
/** Physical wall-clock time in milliseconds */
|
|
374
|
-
wallTime: number;
|
|
375
|
-
/** Logical counter. Increments when wallTime hasn't changed since last event. */
|
|
376
|
-
logical: number;
|
|
377
|
-
/** Node ID for tie-breaking. Ensures total order even with identical wall+logical. */
|
|
378
|
-
nodeId: string;
|
|
379
|
-
}
|
|
380
|
-
/** The three mutation types an operation can represent */
|
|
381
|
-
type OperationType = 'insert' | 'update' | 'delete';
|
|
382
|
-
/** Supported atomic operation types for intent-preserving field updates */
|
|
383
|
-
type AtomicOpType = 'increment' | 'max' | 'min' | 'append' | 'remove';
|
|
384
|
-
/**
|
|
385
|
-
* Atomic operation stored in an Operation's atomicOps field.
|
|
386
|
-
* This is the serializable form that persists in the operation log.
|
|
387
|
-
*/
|
|
388
|
-
interface AtomicOp {
|
|
389
|
-
readonly type: AtomicOpType;
|
|
390
|
-
readonly value: unknown;
|
|
391
|
-
}
|
|
392
|
-
/**
|
|
393
|
-
* The atomic unit of the entire system. Every mutation produces an Operation.
|
|
394
|
-
* Operations are IMMUTABLE and CONTENT-ADDRESSED.
|
|
395
|
-
*/
|
|
396
|
-
interface Operation {
|
|
397
|
-
/** SHA-256 hash of (type + collection + recordId + data + timestamp + nodeId). Content-addressed. */
|
|
398
|
-
id: string;
|
|
399
|
-
/** UUID v7 of the originating device. Time-sortable. */
|
|
400
|
-
nodeId: string;
|
|
401
|
-
/** What happened */
|
|
402
|
-
type: OperationType;
|
|
403
|
-
/** Which collection (from schema) */
|
|
404
|
-
collection: string;
|
|
405
|
-
/** ID of the affected record. UUID v7 for inserts, existing ID for update/delete. */
|
|
406
|
-
recordId: string;
|
|
407
|
-
/** Field values. null for delete. For updates, contains ONLY changed fields. */
|
|
408
|
-
data: Record<string, unknown> | null;
|
|
409
|
-
/** For updates: previous values of changed fields (enables 3-way merge). null for insert/delete. */
|
|
410
|
-
previousData: Record<string, unknown> | null;
|
|
411
|
-
/** Hybrid Logical Clock timestamp. Used for causal ordering. */
|
|
412
|
-
timestamp: HLCTimestamp;
|
|
413
|
-
/** Monotonically increasing per node. Used in version vectors. */
|
|
414
|
-
sequenceNumber: number;
|
|
415
|
-
/** Operation IDs this operation causally depends on (direct parents in the DAG). */
|
|
416
|
-
causalDeps: string[];
|
|
417
|
-
/** Schema version at time of creation. Used for migration transforms. */
|
|
418
|
-
schemaVersion: number;
|
|
419
|
-
/** Atomic operation intents for fields in data (e.g., increment, max). Present only when atomic ops were used. */
|
|
420
|
-
atomicOps?: Record<string, AtomicOp>;
|
|
421
|
-
/** Groups this operation with others in an atomic transaction. Not part of the content hash. */
|
|
422
|
-
transactionId?: string;
|
|
423
|
-
/** Human-readable name for the mutation group (e.g., 'complete-sale'). For DevTools display. */
|
|
424
|
-
mutationName?: string;
|
|
425
|
-
}
|
|
426
|
-
/**
|
|
427
|
-
* Input for creating an operation (before id and timestamp are assigned).
|
|
428
|
-
*/
|
|
429
|
-
interface OperationInput {
|
|
430
|
-
nodeId: string;
|
|
431
|
-
type: OperationType;
|
|
432
|
-
collection: string;
|
|
433
|
-
recordId: string;
|
|
434
|
-
data: Record<string, unknown> | null;
|
|
435
|
-
previousData: Record<string, unknown> | null;
|
|
436
|
-
sequenceNumber: number;
|
|
437
|
-
causalDeps: string[];
|
|
438
|
-
schemaVersion: number;
|
|
439
|
-
/** Atomic operation intents for fields in data (e.g., increment, max). */
|
|
440
|
-
atomicOps?: Record<string, AtomicOp>;
|
|
441
|
-
/** Groups this operation with others in an atomic transaction. Not part of the content hash. */
|
|
442
|
-
transactionId?: string;
|
|
443
|
-
/** Human-readable name for the mutation group (e.g., 'complete-sale'). For DevTools display. */
|
|
444
|
-
mutationName?: string;
|
|
445
|
-
}
|
|
446
|
-
/** Version vector: maps nodeId to the max sequence number seen from that node */
|
|
447
|
-
type VersionVector = Map<string, number>;
|
|
448
|
-
/** Field kinds supported by the schema system */
|
|
449
|
-
type FieldKind = 'string' | 'number' | 'boolean' | 'timestamp' | 'richtext' | 'enum' | 'array' | 'object' | 'json' | 'blob' | 'secret';
|
|
450
|
-
/**
|
|
451
|
-
* How a `secret` field's value is protected at rest.
|
|
452
|
-
* - `hashed`: one-way (passwords). Stored as a salted hash; never read back.
|
|
453
|
-
* - `encrypted`: reversible (tokens, keys). Stored as ciphertext; decrypted to use.
|
|
454
|
-
*/
|
|
455
|
-
type SecretMode = 'hashed' | 'encrypted';
|
|
456
|
-
/**
|
|
457
|
-
* A content-addressed reference to binary data stored out of band.
|
|
458
|
-
*
|
|
459
|
-
* A `blob` field never carries the bytes in the operation log; it carries this
|
|
460
|
-
* small reference. The bytes live in a content-addressed blob store, keyed by
|
|
461
|
-
* `hash`, so identical content is stored once and never re-synced to a peer that
|
|
462
|
-
* already has that hash.
|
|
463
|
-
*/
|
|
464
|
-
interface BlobRef {
|
|
465
|
-
/** Hex-encoded SHA-256 hash of the bytes. The content address. */
|
|
466
|
-
hash: string;
|
|
467
|
-
/** Size of the bytes in bytes. */
|
|
468
|
-
size: number;
|
|
469
|
-
/** Optional MIME type (for example "image/png"). */
|
|
470
|
-
mimeType?: string;
|
|
471
|
-
/** Optional original filename. */
|
|
472
|
-
filename?: string;
|
|
473
|
-
/**
|
|
474
|
-
* Hex-encoded SHA-256 hash of this blob's manifest — the small chunk index a
|
|
475
|
-
* peer fetches first to learn how to pull the bytes out of band. Present when
|
|
476
|
-
* the blob was stored for transfer; absent for a bare reference created without
|
|
477
|
-
* chunking. Because it is a content address like `hash`, the manifest is fetched
|
|
478
|
-
* and integrity-verified over the same channel as the chunks.
|
|
479
|
-
*/
|
|
480
|
-
manifestHash?: string;
|
|
481
|
-
}
|
|
482
|
-
/**
|
|
483
|
-
* Comprehensive sync diagnostics snapshot.
|
|
484
|
-
* Provides connection, latency, throughput, queue, and error metrics
|
|
485
|
-
* for monitoring and DevTools integration.
|
|
486
|
-
*/
|
|
487
|
-
interface SyncDiagnosticsSnapshot {
|
|
488
|
-
/** Current developer-facing sync status */
|
|
489
|
-
status: 'connected' | 'reconnecting' | 'syncing' | 'synced' | 'offline' | 'error' | 'schema-mismatch' | 'clock-error' | 'auth-required';
|
|
490
|
-
/** Timestamp when the current connection was established, or null if disconnected */
|
|
491
|
-
connectedAt: number | null;
|
|
492
|
-
/** Timestamp when the last disconnection occurred, or null if never disconnected */
|
|
493
|
-
disconnectedAt: number | null;
|
|
494
|
-
/** Number of reconnection attempts since last successful connection */
|
|
495
|
-
reconnectAttempts: number;
|
|
496
|
-
/** Current round-trip time in milliseconds */
|
|
497
|
-
rttMs: number;
|
|
498
|
-
/** Median (50th percentile) RTT over the sliding window */
|
|
499
|
-
rttP50Ms: number;
|
|
500
|
-
/** 95th percentile RTT over the sliding window */
|
|
501
|
-
rttP95Ms: number;
|
|
502
|
-
/** 99th percentile RTT over the sliding window */
|
|
503
|
-
rttP99Ms: number;
|
|
504
|
-
/** Total operations sent during this session */
|
|
505
|
-
operationsSent: number;
|
|
506
|
-
/** Total operations received during this session */
|
|
507
|
-
operationsReceived: number;
|
|
508
|
-
/** Total bytes sent during this session */
|
|
509
|
-
bytesSent: number;
|
|
510
|
-
/** Total bytes received during this session */
|
|
511
|
-
bytesReceived: number;
|
|
512
|
-
/** Number of operations waiting to be sent */
|
|
513
|
-
pendingOperations: number;
|
|
514
|
-
/** Estimated bytes in the outbound queue */
|
|
515
|
-
outboundQueueSize: number;
|
|
516
|
-
/** Timestamp of the last successful sync, or null if never synced */
|
|
517
|
-
lastSyncedAt: number | null;
|
|
518
|
-
/** Duration of the last complete sync cycle in ms, or null if no cycle completed */
|
|
519
|
-
syncDuration: number | null;
|
|
520
|
-
/** Whether the initial sync (full delta exchange) has completed */
|
|
521
|
-
initialSyncComplete: boolean;
|
|
522
|
-
/** Progress of the initial sync as a 0-1 ratio */
|
|
523
|
-
initialSyncProgress: number;
|
|
524
|
-
/** Description of the last error, or null if no error occurred */
|
|
525
|
-
lastError: string | null;
|
|
526
|
-
/** Total number of errors during this session */
|
|
527
|
-
errorCount: number;
|
|
528
|
-
/** Assessed connection quality level */
|
|
529
|
-
quality: 'excellent' | 'good' | 'fair' | 'poor' | 'offline';
|
|
530
|
-
/** Estimated effective bandwidth in bytes per second, or null if not yet measured */
|
|
531
|
-
effectiveBandwidth: number | null;
|
|
532
|
-
}
|
|
533
|
-
/** Built-in field merge strategies that can be declared in the schema. */
|
|
534
|
-
type FieldMergeStrategy = 'lww' | 'counter' | 'max' | 'min' | 'union' | 'append-only' | 'server-authoritative';
|
|
535
|
-
/** Map of state to allowed next states for state machine constraints */
|
|
536
|
-
type TransitionMap = Record<string, string[]>;
|
|
537
|
-
/**
|
|
538
|
-
* A state machine constraint extracted from the schema.
|
|
539
|
-
* Used by merge and validation to enforce valid state transitions.
|
|
540
|
-
*/
|
|
541
|
-
interface StateMachineConstraint {
|
|
542
|
-
/** The enum field this constraint controls */
|
|
543
|
-
field: string;
|
|
544
|
-
/** The collection this constraint applies to */
|
|
545
|
-
collection: string;
|
|
546
|
-
/** Map of state to allowed next states */
|
|
547
|
-
transitions: TransitionMap;
|
|
548
|
-
}
|
|
549
|
-
/**
|
|
550
|
-
* Result of validating a state transition.
|
|
551
|
-
*/
|
|
552
|
-
interface TransitionValidationResult {
|
|
553
|
-
/** Whether the transition is allowed */
|
|
554
|
-
valid: boolean;
|
|
555
|
-
/** The source state */
|
|
556
|
-
from: string;
|
|
557
|
-
/** The target state */
|
|
558
|
-
to: string;
|
|
559
|
-
/** The field being transitioned */
|
|
560
|
-
field: string;
|
|
561
|
-
/** The collection containing the field */
|
|
562
|
-
collection: string;
|
|
563
|
-
/** All allowed target states from the source state */
|
|
564
|
-
allowedTargets: string[];
|
|
565
|
-
}
|
|
566
|
-
/**
|
|
567
|
-
* Descriptor produced by the type builder (t.string(), t.number(), etc.).
|
|
568
|
-
* Represents a fully configured field definition.
|
|
569
|
-
*/
|
|
570
|
-
interface FieldDescriptor {
|
|
571
|
-
kind: FieldKind;
|
|
572
|
-
required: boolean;
|
|
573
|
-
defaultValue: unknown;
|
|
574
|
-
auto: boolean;
|
|
575
|
-
enumValues: readonly string[] | null;
|
|
576
|
-
itemKind: FieldKind | null;
|
|
577
|
-
/** Declared merge strategy. Defaults to kind-appropriate strategy when undefined. */
|
|
578
|
-
mergeStrategy: FieldMergeStrategy | null;
|
|
579
|
-
/** State machine transition map for enum fields. Null if no transitions declared. */
|
|
580
|
-
transitions: TransitionMap | null;
|
|
581
|
-
/**
|
|
582
|
-
* Nested field descriptors for `object` fields (each key merges by its own
|
|
583
|
-
* kind). Null for `json` fields (dynamic keys, resolved structurally) and for
|
|
584
|
-
* all non-structured kinds.
|
|
585
|
-
*/
|
|
586
|
-
nestedFields?: Record<string, FieldDescriptor> | null;
|
|
587
|
-
/**
|
|
588
|
-
* At-rest protection mode for `secret` fields (`hashed` or `encrypted`).
|
|
589
|
-
* Null/absent for every other kind.
|
|
590
|
-
*/
|
|
591
|
-
secretMode?: SecretMode | null;
|
|
592
|
-
}
|
|
593
|
-
/**
|
|
594
|
-
* Defines a state machine on an enum field, constraining valid state transitions.
|
|
595
|
-
*
|
|
596
|
-
* When a state machine is defined on a collection, mutations and merges
|
|
597
|
-
* enforce that the controlled field only moves along declared transitions.
|
|
598
|
-
*
|
|
599
|
-
* @example
|
|
600
|
-
* ```typescript
|
|
601
|
-
* stateMachine: {
|
|
602
|
-
* field: 'status',
|
|
603
|
-
* transitions: {
|
|
604
|
-
* draft: ['pending', 'cancelled'],
|
|
605
|
-
* pending: ['confirmed', 'cancelled'],
|
|
606
|
-
* confirmed: ['shipped'],
|
|
607
|
-
* shipped: ['delivered'],
|
|
608
|
-
* delivered: [],
|
|
609
|
-
* cancelled: [],
|
|
610
|
-
* },
|
|
611
|
-
* onInvalidTransition: 'reject',
|
|
612
|
-
* }
|
|
613
|
-
* ```
|
|
614
|
-
*/
|
|
615
|
-
interface StateMachineDefinition {
|
|
616
|
-
/** The enum field this state machine controls */
|
|
617
|
-
field: string;
|
|
618
|
-
/** Map of state to allowed next states */
|
|
619
|
-
transitions: Record<string, string[]>;
|
|
620
|
-
/** What to do when an invalid transition is attempted */
|
|
621
|
-
onInvalidTransition: 'reject' | 'last-valid-state';
|
|
622
|
-
}
|
|
623
|
-
/**
|
|
624
|
-
* Definition of a collection within the schema.
|
|
625
|
-
*/
|
|
626
|
-
interface CollectionDefinition {
|
|
627
|
-
fields: Record<string, FieldDescriptor>;
|
|
628
|
-
indexes: string[];
|
|
629
|
-
constraints: Constraint[];
|
|
630
|
-
resolvers: Record<string, CustomResolver>;
|
|
631
|
-
/** Scope fields for sync filtering. Only records matching the client's scope values are synced. */
|
|
632
|
-
scope: string[];
|
|
633
|
-
/** Optional state machine constraining transitions on an enum field */
|
|
634
|
-
stateMachine?: StateMachineDefinition;
|
|
635
|
-
}
|
|
636
|
-
/** Custom resolver function for tier 3 merge resolution */
|
|
637
|
-
type CustomResolver = (local: unknown, remote: unknown, base: unknown) => unknown;
|
|
638
|
-
/**
|
|
639
|
-
* Constraint for tier 2 conflict resolution.
|
|
640
|
-
*/
|
|
641
|
-
interface Constraint {
|
|
642
|
-
type: 'unique' | 'capacity' | 'referential';
|
|
643
|
-
fields: string[];
|
|
644
|
-
where?: Record<string, unknown>;
|
|
645
|
-
onConflict: 'first-write-wins' | 'last-write-wins' | 'priority-field' | 'server-decides' | 'custom';
|
|
646
|
-
priorityField?: string;
|
|
647
|
-
resolve?: (local: unknown, remote: unknown, base: unknown) => unknown;
|
|
648
|
-
}
|
|
649
|
-
/** Relation type between collections */
|
|
650
|
-
type RelationType = 'one-to-one' | 'one-to-many' | 'many-to-one' | 'many-to-many';
|
|
651
|
-
/** On-delete behavior for relations */
|
|
652
|
-
type OnDeleteAction = 'cascade' | 'set-null' | 'restrict' | 'no-action';
|
|
653
|
-
/**
|
|
654
|
-
* Definition of a relation between two collections.
|
|
655
|
-
*/
|
|
656
|
-
interface RelationDefinition {
|
|
657
|
-
from: string;
|
|
658
|
-
to: string;
|
|
659
|
-
type: RelationType;
|
|
660
|
-
field: string;
|
|
661
|
-
onDelete: OnDeleteAction;
|
|
662
|
-
}
|
|
663
|
-
/**
|
|
664
|
-
* Declarative sync filter for a collection.
|
|
665
|
-
*
|
|
666
|
-
* `where` maps record field names to scope value keys. Use `true` to bind a field
|
|
667
|
-
* to a scope value with the same name (e.g. `{ userId: true }`).
|
|
668
|
-
*/
|
|
669
|
-
interface SyncRuleDefinition {
|
|
670
|
-
where: Record<string, string>;
|
|
671
|
-
}
|
|
672
|
-
/**
|
|
673
|
-
* The complete schema definition produced by defineSchema().
|
|
674
|
-
*/
|
|
675
|
-
interface SchemaDefinition {
|
|
676
|
-
version: number;
|
|
677
|
-
collections: Record<string, CollectionDefinition>;
|
|
678
|
-
relations: Record<string, RelationDefinition>;
|
|
679
|
-
/** Schema migrations keyed by target version. */
|
|
680
|
-
migrations: Record<number, MigrationDefinition>;
|
|
681
|
-
/**
|
|
682
|
-
* Declarative partial-sync rules per collection.
|
|
683
|
-
* When present, only listed collections (plus legacy `scope` collections) sync.
|
|
684
|
-
*/
|
|
685
|
-
sync?: Record<string, SyncRuleDefinition>;
|
|
686
|
-
}
|
|
687
|
-
/**
|
|
688
|
-
* Merge strategies available for auto-merge and constraints.
|
|
689
|
-
*/
|
|
690
|
-
declare const MERGE_STRATEGIES: readonly ["auto-merge", "lww", "first-write-wins", "server-decides", "custom"];
|
|
691
|
-
type MergeStrategy = (typeof MERGE_STRATEGIES)[number];
|
|
692
|
-
/**
|
|
693
|
-
* Connection quality levels for adaptive sync.
|
|
694
|
-
*/
|
|
695
|
-
declare const CONNECTION_QUALITIES: readonly ["excellent", "good", "fair", "poor", "offline"];
|
|
696
|
-
type ConnectionQuality = (typeof CONNECTION_QUALITIES)[number];
|
|
697
|
-
/**
|
|
698
|
-
* Injectable time source for deterministic testing of clocks.
|
|
699
|
-
*/
|
|
700
|
-
interface TimeSource {
|
|
701
|
-
now(): number;
|
|
702
|
-
}
|
|
703
|
-
/**
|
|
704
|
-
* Injectable random source for deterministic testing of UUID generation.
|
|
705
|
-
*/
|
|
706
|
-
interface RandomSource {
|
|
707
|
-
getRandomValues<T extends ArrayBufferView>(array: T): T;
|
|
708
|
-
}
|
|
709
|
-
/**
|
|
710
|
-
* Configuration for an offline-safe sequence.
|
|
711
|
-
*
|
|
712
|
-
* Sequences produce monotonically increasing, collision-free identifiers
|
|
713
|
-
* that work across offline devices. Each device maintains its own counter
|
|
714
|
-
* scoped by (name, scope, nodeId).
|
|
715
|
-
*
|
|
716
|
-
* Format tokens available:
|
|
717
|
-
* - `{date}` → YYYYMMDD
|
|
718
|
-
* - `{node4}` → first 4 chars of nodeId
|
|
719
|
-
* - `{node8}` → first 8 chars of nodeId
|
|
720
|
-
* - `{seq}` → zero-padded counter (4 digits)
|
|
721
|
-
* - `{seq:N}` → zero-padded counter with N digits
|
|
722
|
-
*
|
|
723
|
-
* @example
|
|
724
|
-
* ```typescript
|
|
725
|
-
* await app.sequences.next('receipt', {
|
|
726
|
-
* scope: storeId,
|
|
727
|
-
* format: 'S-{date}-{node4}-{seq}',
|
|
728
|
-
* })
|
|
729
|
-
* // → "S-20260508-a1b2-0042"
|
|
730
|
-
* ```
|
|
731
|
-
*/
|
|
732
|
-
interface SequenceConfig {
|
|
733
|
-
/** Namespace within the sequence (e.g., a storeId or orgId). Defaults to ''. */
|
|
734
|
-
scope?: string;
|
|
735
|
-
/** Format template. Defaults to `{name}-{seq:4}`. */
|
|
736
|
-
format?: string;
|
|
737
|
-
/** Starting counter value. Defaults to 1. */
|
|
738
|
-
startAt?: number;
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
interface SyncApplyFailureEvent {
|
|
742
|
-
operationId: string;
|
|
743
|
-
collection: string;
|
|
744
|
-
recordId: string;
|
|
745
|
-
code: string;
|
|
746
|
-
message: string;
|
|
747
|
-
retriable: boolean;
|
|
748
|
-
firstSeenAt: number;
|
|
749
|
-
retryCount: number;
|
|
750
|
-
}
|
|
751
|
-
/**
|
|
752
|
-
* Trace of a merge decision. Records all inputs and outputs for debugging and DevTools.
|
|
753
|
-
*/
|
|
754
|
-
interface MergeTrace {
|
|
755
|
-
operationA: Operation;
|
|
756
|
-
operationB: Operation;
|
|
757
|
-
field: string;
|
|
758
|
-
strategy: string;
|
|
759
|
-
inputA: unknown;
|
|
760
|
-
inputB: unknown;
|
|
761
|
-
base: unknown | null;
|
|
762
|
-
output: unknown;
|
|
763
|
-
tier: 1 | 2 | 3;
|
|
764
|
-
constraintViolated: string | null;
|
|
765
|
-
duration: number;
|
|
766
|
-
}
|
|
767
|
-
/**
|
|
768
|
-
* All events emitted by the Kora framework.
|
|
769
|
-
* These are consumed by DevTools and can be observed by the developer.
|
|
770
|
-
*/
|
|
771
|
-
type KoraEvent = {
|
|
772
|
-
type: 'operation:created';
|
|
773
|
-
operation: Operation;
|
|
774
|
-
} | {
|
|
775
|
-
type: 'operation:applied';
|
|
776
|
-
operation: Operation;
|
|
777
|
-
duration: number;
|
|
778
|
-
} | {
|
|
779
|
-
type: 'merge:started';
|
|
780
|
-
operationA: Operation;
|
|
781
|
-
operationB: Operation;
|
|
782
|
-
} | {
|
|
783
|
-
type: 'merge:completed';
|
|
784
|
-
trace: MergeTrace;
|
|
785
|
-
} | {
|
|
786
|
-
type: 'merge:conflict';
|
|
787
|
-
trace: MergeTrace;
|
|
788
|
-
} | {
|
|
789
|
-
type: 'constraint:violated';
|
|
790
|
-
constraint: string;
|
|
791
|
-
trace: MergeTrace;
|
|
792
|
-
} | {
|
|
793
|
-
type: 'sync:connected';
|
|
794
|
-
nodeId: string;
|
|
795
|
-
} | {
|
|
796
|
-
type: 'sync:disconnected';
|
|
797
|
-
reason: string;
|
|
798
|
-
} | {
|
|
799
|
-
type: 'sync:schema-mismatch';
|
|
800
|
-
clientSchemaVersion: number;
|
|
801
|
-
serverSchemaVersion: number;
|
|
802
|
-
supportedMin: number;
|
|
803
|
-
supportedMax: number;
|
|
804
|
-
reason: string;
|
|
805
|
-
} | {
|
|
806
|
-
type: 'sync:auth-failed';
|
|
807
|
-
reason: string;
|
|
808
|
-
} | {
|
|
809
|
-
type: 'sync:suspended';
|
|
810
|
-
reason: 'auth-loading' | 'auth-required' | 'auth-rejected' | 'device-revoked' | string;
|
|
811
|
-
} | {
|
|
812
|
-
type: 'sync:apply-blocked';
|
|
813
|
-
failure: SyncApplyFailureEvent;
|
|
814
|
-
} | {
|
|
815
|
-
type: 'sync:apply-retrying';
|
|
816
|
-
failure: SyncApplyFailureEvent;
|
|
817
|
-
} | {
|
|
818
|
-
type: 'sync:apply-recovered';
|
|
819
|
-
failure: SyncApplyFailureEvent;
|
|
820
|
-
} | {
|
|
821
|
-
type: 'sync:apply-abandoned';
|
|
822
|
-
failure: SyncApplyFailureEvent;
|
|
823
|
-
} | {
|
|
824
|
-
type: 'sync:clock-skew';
|
|
825
|
-
/** serverTime - localTime in ms. Negative = this device's clock is fast. */
|
|
826
|
-
skewMs: number;
|
|
827
|
-
severity: 'info' | 'slow-warning' | 'fast-blocked';
|
|
828
|
-
source: 'handshake' | 'server-reject';
|
|
829
|
-
} | {
|
|
830
|
-
type: 'sync:clock-rebase';
|
|
831
|
-
/** Number of unsynced operations that were re-stamped. */
|
|
832
|
-
rebasedCount: number;
|
|
833
|
-
/** How far ahead of server time the most future queued operation was, in ms. */
|
|
834
|
-
maxSkewMs: number;
|
|
835
|
-
} | {
|
|
836
|
-
type: 'sync:sent';
|
|
837
|
-
operations: Operation[];
|
|
838
|
-
batchSize: number;
|
|
839
|
-
} | {
|
|
840
|
-
type: 'sync:received';
|
|
841
|
-
operations: Operation[];
|
|
842
|
-
batchSize: number;
|
|
843
|
-
/** Server-ingest outcomes. Omitted for ordinary client-side receive events. */
|
|
844
|
-
uniqueOperations?: number;
|
|
845
|
-
duplicateOperations?: number;
|
|
846
|
-
rejectedOperations?: number;
|
|
847
|
-
} | {
|
|
848
|
-
type: 'sync:acknowledged';
|
|
849
|
-
sequenceNumber: number;
|
|
850
|
-
} | {
|
|
851
|
-
type: 'sync:scope-retracted';
|
|
852
|
-
collection: string;
|
|
853
|
-
recordId: string;
|
|
854
|
-
quarantinedOperationIds: string[];
|
|
855
|
-
} | {
|
|
856
|
-
type: 'sync:apply-failed';
|
|
857
|
-
operationId: string;
|
|
858
|
-
collection: string;
|
|
859
|
-
recordId: string;
|
|
860
|
-
code: string;
|
|
861
|
-
message: string;
|
|
862
|
-
retriable: boolean;
|
|
863
|
-
} | {
|
|
864
|
-
/**
|
|
865
|
-
* The server rejected one of THIS client's outbound operations before it
|
|
866
|
-
* became authoritative. The op has been diverted out of the pending sync
|
|
867
|
-
* queue into the durable rejected store (kept, not retried); the app can
|
|
868
|
-
* surface the reason and decide whether to roll back the optimistic local
|
|
869
|
-
* write or let the user edit and resubmit.
|
|
870
|
-
*/
|
|
871
|
-
type: 'sync:operation-rejected';
|
|
872
|
-
operationId: string;
|
|
873
|
-
collection: string;
|
|
874
|
-
recordId: string;
|
|
875
|
-
code: string;
|
|
876
|
-
message: string;
|
|
877
|
-
retriable: boolean;
|
|
878
|
-
} | {
|
|
879
|
-
type: 'sync:delivery-gap';
|
|
880
|
-
expectedBase: number;
|
|
881
|
-
receivedBase: number;
|
|
882
|
-
currentWatermark: number;
|
|
883
|
-
messageId: string;
|
|
884
|
-
repeatCount: number;
|
|
885
|
-
} | {
|
|
886
|
-
type: 'sync:delivery-stalled';
|
|
887
|
-
sessionId: string;
|
|
888
|
-
watermark: number;
|
|
889
|
-
outstandingMaxDeliverySequence: number;
|
|
890
|
-
repeatCount: number;
|
|
891
|
-
reason: 'unacknowledged-delivery';
|
|
892
|
-
} | {
|
|
893
|
-
type: 'query:subscribed';
|
|
894
|
-
queryId: string;
|
|
895
|
-
collection: string;
|
|
896
|
-
} | {
|
|
897
|
-
type: 'query:invalidated';
|
|
898
|
-
queryId: string;
|
|
899
|
-
trigger: Operation;
|
|
900
|
-
} | {
|
|
901
|
-
type: 'query:executed';
|
|
902
|
-
queryId: string;
|
|
903
|
-
duration: number;
|
|
904
|
-
resultCount: number;
|
|
905
|
-
} | {
|
|
906
|
-
type: 'connection:quality';
|
|
907
|
-
quality: ConnectionQuality;
|
|
908
|
-
} | {
|
|
909
|
-
type: 'sync:diagnostics';
|
|
910
|
-
diagnostics: SyncDiagnosticsSnapshot;
|
|
911
|
-
} | {
|
|
912
|
-
type: 'sync:bandwidth';
|
|
913
|
-
bytesPerSecond: number;
|
|
914
|
-
direction: 'in' | 'out';
|
|
915
|
-
} | {
|
|
916
|
-
type: 'sync:initial-sync-progress';
|
|
917
|
-
progress: number;
|
|
918
|
-
totalBatches: number;
|
|
919
|
-
receivedBatches: number;
|
|
920
|
-
} | {
|
|
921
|
-
type: 'awareness:updated';
|
|
922
|
-
states: Map<number, unknown>;
|
|
923
|
-
} | {
|
|
924
|
-
type: 'state-machine:transition';
|
|
925
|
-
collection: string;
|
|
926
|
-
recordId: string;
|
|
927
|
-
from: string;
|
|
928
|
-
to: string;
|
|
929
|
-
valid: boolean;
|
|
930
|
-
} | {
|
|
931
|
-
type: 'state-machine:rejected';
|
|
932
|
-
collection: string;
|
|
933
|
-
recordId: string;
|
|
934
|
-
from: string;
|
|
935
|
-
to: string;
|
|
936
|
-
allowed: string[];
|
|
937
|
-
} | {
|
|
938
|
-
type: 'store:persistence-error';
|
|
939
|
-
dbName: string;
|
|
940
|
-
message: string;
|
|
941
|
-
code: string;
|
|
942
|
-
} | {
|
|
943
|
-
type: 'store:quota-exceeded';
|
|
944
|
-
dbName: string;
|
|
945
|
-
message: string;
|
|
946
|
-
} | {
|
|
947
|
-
/**
|
|
948
|
-
* OPFS persistence was unavailable, so the store fell back to a
|
|
949
|
-
* NON-PERSISTENT in-memory database. Anything written this session is lost
|
|
950
|
-
* on reload. This is emitted instead of failing silently so the condition
|
|
951
|
-
* is observable rather than a data-loss trap.
|
|
952
|
-
*/
|
|
953
|
-
type: 'store:opfs-unavailable';
|
|
954
|
-
dbName: string;
|
|
955
|
-
/**
|
|
956
|
-
* Why OPFS could not be used. `lock-conflict` means another runtime on this
|
|
957
|
-
* origin already holds the OPFS pool for this database; `timeout` means the
|
|
958
|
-
* VFS install did not complete in time (common in headless CI); `unsupported`
|
|
959
|
-
* means the runtime has no usable OPFS.
|
|
960
|
-
*/
|
|
961
|
-
reason: 'lock-conflict' | 'timeout' | 'unsupported';
|
|
962
|
-
message: string;
|
|
963
|
-
} | {
|
|
964
|
-
/**
|
|
965
|
-
* A preferred storage backend could not provide durable storage, so Kora
|
|
966
|
-
* promoted the app to another durable backend before user code observed the
|
|
967
|
-
* store. This is informational: data still survives reloads.
|
|
968
|
-
*/
|
|
969
|
-
type: 'store:storage-fallback';
|
|
970
|
-
dbName: string;
|
|
971
|
-
from: 'opfs' | 'sqlite-wasm';
|
|
972
|
-
to: 'indexeddb';
|
|
973
|
-
reason: 'lock-conflict' | 'timeout' | 'unsupported';
|
|
974
|
-
message: string;
|
|
975
|
-
} | {
|
|
976
|
-
/**
|
|
977
|
-
* Another runtime on this origin was already using this database name, so
|
|
978
|
-
* this runtime attached to it as a follower and now SHARES that one
|
|
979
|
-
* database. That is intended for multiple tabs of the SAME app; it is a bug
|
|
980
|
-
* if these are logically separate apps, which should each use a distinct
|
|
981
|
-
* store name (`store: { name: '...' }`) to stay isolated.
|
|
982
|
-
*/
|
|
983
|
-
type: 'store:db-name-collision';
|
|
984
|
-
dbName: string;
|
|
985
|
-
message: string;
|
|
986
|
-
} | {
|
|
987
|
-
type: 'replay:completed';
|
|
988
|
-
targetOperationId: string;
|
|
989
|
-
operationsApplied: number;
|
|
990
|
-
duration: number;
|
|
991
|
-
};
|
|
992
|
-
/** Extract the event type string union from KoraEvent */
|
|
993
|
-
type KoraEventType = KoraEvent['type'];
|
|
994
|
-
/** Extract a specific event by its type */
|
|
995
|
-
type KoraEventByType<T extends KoraEventType> = Extract<KoraEvent, {
|
|
996
|
-
type: T;
|
|
997
|
-
}>;
|
|
998
|
-
/** Listener function for a specific event type */
|
|
999
|
-
type KoraEventListener<T extends KoraEventType> = (event: KoraEventByType<T>) => void;
|
|
1000
|
-
/**
|
|
1001
|
-
* Event emitter interface for the Kora framework.
|
|
1002
|
-
* All packages that emit events must implement this interface.
|
|
1003
|
-
*/
|
|
1004
|
-
interface KoraEventEmitter {
|
|
1005
|
-
on<T extends KoraEventType>(type: T, listener: KoraEventListener<T>): () => void;
|
|
1006
|
-
off<T extends KoraEventType>(type: T, listener: KoraEventListener<T>): void;
|
|
1007
|
-
emit<T extends KoraEventType>(event: KoraEventByType<T>): void;
|
|
1008
|
-
}
|
|
1009
|
-
|
|
1010
|
-
export { type AtomicOpType as A, type BlobRef as B, type CollectionDefinition as C, type SecretMode as D, EnumFieldBuilder as E, FieldBuilder as F, type SequenceConfig as G, type HLCTimestamp as H, type StateMachineDefinition as I, JsonFieldBuilder as J, type KoraEvent as K, type SyncDiagnosticsSnapshot as L, type MigrationDefinition as M, type SyncRuleDefinition as N, type OperationType as O, type TimeSource as P, migrate as Q, type RandomSource as R, type SchemaDefinition as S, type TransitionMap as T, t as U, type VersionVector as V, type AtomicOp as a, type CustomResolver as b, type RelationDefinition as c, ArrayFieldBuilder as d, type FieldKind as e, type Operation as f, type MigrationStep as g, type StateMachineConstraint as h, type TransitionValidationResult as i, CONNECTION_QUALITIES as j, type ConnectionQuality as k, type Constraint as l, type FieldDescriptor as m, type FieldMergeStrategy as n, type KoraEventByType as o, type KoraEventEmitter as p, type KoraEventListener as q, type KoraEventType as r, MERGE_STRATEGIES as s, type MergeStrategy as t, type MergeTrace as u, MigrationBuilder as v, type OnDeleteAction as w, type OperationInput as x, type RelationType as y, RollbackBuilder as z };
|