@korajs/core 0.3.2 → 0.5.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/dist/{chunk-KQYXWUFV.js → chunk-H4FXU5OP.js} +66 -20
- package/dist/chunk-H4FXU5OP.js.map +1 -0
- package/dist/events-BeIEDJBW.d.cts +887 -0
- package/dist/events-BeIEDJBW.d.ts +887 -0
- package/dist/index.cjs +1410 -72
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +564 -98
- package/dist/index.d.ts +564 -98
- package/dist/index.js +1287 -25
- package/dist/index.js.map +1 -1
- package/dist/internal.cjs +56 -17
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.d.cts +2 -2
- package/dist/internal.d.ts +2 -2
- package/dist/internal.js +1 -1
- package/package.json +2 -1
- package/dist/chunk-KQYXWUFV.js.map +0 -1
- package/dist/events-7Fhdjxd2.d.cts +0 -327
- package/dist/events-7Fhdjxd2.d.ts +0 -327
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { R as RandomSource,
|
|
2
|
-
export {
|
|
1
|
+
import { R as RandomSource, A as AtomicOpType, a as AtomicOp, F as FieldBuilder, C as CustomResolver, M as MigrationDefinition, S as SchemaDefinition, b as CollectionDefinition, c as RelationDefinition, O as OperationType, E as EnumFieldBuilder, d as ArrayFieldBuilder, e as FieldKind, f as Operation, V as VersionVector, g as MigrationStep, h as StateMachineConstraint, T as TransitionMap, i as TransitionValidationResult } from './events-BeIEDJBW.js';
|
|
2
|
+
export { j as CONNECTION_QUALITIES, k as ConnectionQuality, l as Constraint, m as FieldDescriptor, n as FieldMergeStrategy, H as HLCTimestamp, o as HybridLogicalClock, K as KoraEvent, p as KoraEventByType, q as KoraEventEmitter, r as KoraEventListener, s as KoraEventType, t as MERGE_STRATEGIES, u as MergeStrategy, v as MergeTrace, w as MigrationBuilder, x as OnDeleteAction, y as OperationInput, z as RelationType, B as RollbackBuilder, D as SequenceConfig, G as StateMachineDefinition, I as SyncDiagnosticsSnapshot, J as SyncRuleDefinition, L as TimeSource, N as createOperation, P as isValidOperation, Q as migrate, U as t, W as verifyOperationIntegrity } from './events-BeIEDJBW.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Base error class for all Kora errors.
|
|
@@ -9,6 +9,10 @@ declare class KoraError extends Error {
|
|
|
9
9
|
readonly code: string;
|
|
10
10
|
readonly context?: Record<string, unknown> | undefined;
|
|
11
11
|
constructor(message: string, code: string, context?: Record<string, unknown> | undefined);
|
|
12
|
+
/**
|
|
13
|
+
* Actionable hint for resolving this error (from registry or error context).
|
|
14
|
+
*/
|
|
15
|
+
get fix(): string | undefined;
|
|
12
16
|
}
|
|
13
17
|
/**
|
|
14
18
|
* Thrown when schema validation fails during defineSchema() or at app initialization.
|
|
@@ -65,6 +69,41 @@ declare class ClockDriftError extends KoraError {
|
|
|
65
69
|
constructor(currentHlcTime: number, physicalTime: number);
|
|
66
70
|
}
|
|
67
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Human-readable remediation hints keyed by {@link KoraError} `code`.
|
|
74
|
+
*/
|
|
75
|
+
declare const KORA_ERROR_FIX_SUGGESTIONS: Record<string, string>;
|
|
76
|
+
/**
|
|
77
|
+
* Returns a suggested fix for a Kora error code, if one is known.
|
|
78
|
+
*/
|
|
79
|
+
declare function getKoraErrorFix(code: string): string | undefined;
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Tracks the latest operation per collection and within an open transaction
|
|
83
|
+
* to populate {@link Operation.causalDeps} for local mutations.
|
|
84
|
+
*/
|
|
85
|
+
declare class CausalTracker {
|
|
86
|
+
private readonly lastOpIdByCollection;
|
|
87
|
+
private lastTransactionOpId;
|
|
88
|
+
/**
|
|
89
|
+
* Start a new transaction boundary. Clears in-transaction op ids.
|
|
90
|
+
*/
|
|
91
|
+
beginTransaction(): void;
|
|
92
|
+
/**
|
|
93
|
+
* Clear the transaction boundary without recording ops (after rollback).
|
|
94
|
+
*/
|
|
95
|
+
clearTransaction(): void;
|
|
96
|
+
/**
|
|
97
|
+
* Compute causal dependencies for the next operation in a collection.
|
|
98
|
+
* Uses direct parents only: collection head and the previous op in the open transaction.
|
|
99
|
+
*/
|
|
100
|
+
nextCausalDeps(collection: string, inTransaction: boolean): string[];
|
|
101
|
+
/**
|
|
102
|
+
* Record an operation after it has been created and assigned an id.
|
|
103
|
+
*/
|
|
104
|
+
afterOperation(collection: string, operationId: string, inTransaction: boolean): void;
|
|
105
|
+
}
|
|
106
|
+
|
|
68
107
|
/**
|
|
69
108
|
* Generates a UUID v7 per RFC 9562.
|
|
70
109
|
* UUID v7 encodes a Unix timestamp in milliseconds in the most significant 48 bits,
|
|
@@ -98,88 +137,149 @@ declare function extractTimestamp(uuid: string): number;
|
|
|
98
137
|
declare function isValidUUIDv7(uuid: string): boolean;
|
|
99
138
|
|
|
100
139
|
/**
|
|
101
|
-
*
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
*
|
|
107
|
-
|
|
140
|
+
* Outcome of applying a single operation to materialized storage (local or remote).
|
|
141
|
+
*/
|
|
142
|
+
declare const APPLY_RESULTS: readonly ["applied", "duplicate", "skipped", "rejected", "deferred"];
|
|
143
|
+
type ApplyResult = (typeof APPLY_RESULTS)[number];
|
|
144
|
+
/**
|
|
145
|
+
* Structured failure metadata when apply returns `skipped`, `rejected`, or `deferred`.
|
|
146
|
+
*/
|
|
147
|
+
interface ApplyFailureReason {
|
|
148
|
+
code: string;
|
|
149
|
+
message: string;
|
|
150
|
+
retriable: boolean;
|
|
151
|
+
}
|
|
152
|
+
/** Well-known apply failure codes for DevTools and `sync:apply-failed` events. */
|
|
153
|
+
declare const APPLY_FAILURE_CODES: {
|
|
154
|
+
readonly APPLY_FAILED: "APPLY_FAILED";
|
|
155
|
+
readonly APPLY_SKIPPED: "APPLY_SKIPPED";
|
|
156
|
+
readonly APPLY_REJECTED: "APPLY_REJECTED";
|
|
157
|
+
readonly APPLY_DEFERRED: "APPLY_DEFERRED";
|
|
158
|
+
readonly CLOCK_DRIFT: "CLOCK_DRIFT";
|
|
159
|
+
readonly REFERENTIAL_INTEGRITY: "REFERENTIAL_INTEGRITY";
|
|
160
|
+
readonly SCHEMA_MISMATCH: "SCHEMA_MISMATCH";
|
|
161
|
+
};
|
|
162
|
+
/**
|
|
163
|
+
* Returns true when the apply outcome should surface as a failure to the developer.
|
|
164
|
+
*/
|
|
165
|
+
declare function isApplyFailure(result: ApplyResult): boolean;
|
|
166
|
+
/**
|
|
167
|
+
* Default failure metadata when the store does not attach a specific reason.
|
|
168
|
+
*/
|
|
169
|
+
declare function defaultApplyFailureReason(result: Exclude<ApplyResult, 'applied' | 'duplicate'>, overrides?: Partial<ApplyFailureReason>): ApplyFailureReason;
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Atomic field operations for intent-preserving updates.
|
|
108
173
|
*
|
|
109
|
-
*
|
|
174
|
+
* Instead of read-modify-write patterns, developers express intent:
|
|
110
175
|
* ```typescript
|
|
111
|
-
*
|
|
112
|
-
* t.string().optional() // optional string field
|
|
113
|
-
* t.string().default('hello') // string with default value
|
|
114
|
-
* t.timestamp().auto() // auto-populated timestamp
|
|
176
|
+
* await app.stock.update(id, { quantity: op.increment(-5) })
|
|
115
177
|
* ```
|
|
178
|
+
*
|
|
179
|
+
* The operation intent is preserved in the Operation for merge:
|
|
180
|
+
* concurrent increments compose (sum of deltas) instead of LWW.
|
|
116
181
|
*/
|
|
117
|
-
|
|
118
|
-
protected readonly _kind: Kind;
|
|
119
|
-
protected readonly _required: boolean;
|
|
120
|
-
protected readonly _defaultValue: unknown;
|
|
121
|
-
protected readonly _auto: boolean;
|
|
122
|
-
constructor(kind: Kind, required?: Req, defaultValue?: unknown, auto?: Auto);
|
|
123
|
-
/** Mark this field as optional (not required on insert) */
|
|
124
|
-
optional(): FieldBuilder<Kind, false, Auto>;
|
|
125
|
-
/** Set a default value for this field. Implicitly makes the field optional. */
|
|
126
|
-
default(value: unknown): FieldBuilder<Kind, false, Auto>;
|
|
127
|
-
/** Mark this field as auto-populated (e.g., createdAt timestamps). Developers cannot set auto fields. */
|
|
128
|
-
auto(): FieldBuilder<Kind, false, true>;
|
|
129
|
-
/** @internal Build the final FieldDescriptor. Used by defineSchema(). */
|
|
130
|
-
_build(): FieldDescriptor;
|
|
131
|
-
}
|
|
182
|
+
|
|
132
183
|
/**
|
|
133
|
-
*
|
|
134
|
-
*
|
|
184
|
+
* Sentinel marker for detecting atomic op objects in update data.
|
|
185
|
+
* Uses Symbol.for() so the sentinel survives module boundary crossings.
|
|
135
186
|
*/
|
|
136
|
-
declare
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
optional(): EnumFieldBuilder<Values, false, Auto>;
|
|
140
|
-
default(value: Values[number]): EnumFieldBuilder<Values, false, Auto>;
|
|
141
|
-
auto(): EnumFieldBuilder<Values, false, true>;
|
|
142
|
-
_build(): FieldDescriptor;
|
|
143
|
-
}
|
|
187
|
+
declare const KORA_ATOMIC_OP: unique symbol;
|
|
188
|
+
/** String key used for the sentinel symbol — exported for internal use */
|
|
189
|
+
declare const KORA_ATOMIC_OP_KEY: typeof KORA_ATOMIC_OP;
|
|
144
190
|
/**
|
|
145
|
-
*
|
|
146
|
-
*
|
|
191
|
+
* Sentinel object returned by op.* helpers.
|
|
192
|
+
* Detected by Collection.update() and resolved to concrete values.
|
|
147
193
|
*/
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
default(value: unknown[]): ArrayFieldBuilder<ItemKind, false, Auto>;
|
|
153
|
-
auto(): ArrayFieldBuilder<ItemKind, false, true>;
|
|
154
|
-
_build(): FieldDescriptor;
|
|
194
|
+
interface AtomicOpSentinel {
|
|
195
|
+
readonly [KORA_ATOMIC_OP_KEY]: true;
|
|
196
|
+
readonly type: AtomicOpType;
|
|
197
|
+
readonly value: unknown;
|
|
155
198
|
}
|
|
156
199
|
/**
|
|
157
|
-
* Type
|
|
200
|
+
* Type guard: checks whether a value is an atomic op sentinel.
|
|
201
|
+
*
|
|
202
|
+
* @param value - Any value to check
|
|
203
|
+
* @returns true if the value is an AtomicOpSentinel
|
|
204
|
+
*/
|
|
205
|
+
declare function isAtomicOp(value: unknown): value is AtomicOpSentinel;
|
|
206
|
+
/**
|
|
207
|
+
* Resolve an atomic op sentinel against a current value to produce a concrete result.
|
|
208
|
+
*
|
|
209
|
+
* @param currentValue - The current value of the field in the database
|
|
210
|
+
* @param sentinel - The atomic op sentinel from the developer's update call
|
|
211
|
+
* @returns The resolved concrete value
|
|
212
|
+
*/
|
|
213
|
+
declare function resolveAtomicOp(currentValue: unknown, sentinel: AtomicOpSentinel): unknown;
|
|
214
|
+
/**
|
|
215
|
+
* Atomic operation helpers. Use these in collection.update() calls
|
|
216
|
+
* to express intent-preserving mutations.
|
|
158
217
|
*
|
|
159
218
|
* @example
|
|
160
219
|
* ```typescript
|
|
161
|
-
* import {
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
*
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
* }
|
|
220
|
+
* import { op } from 'korajs'
|
|
221
|
+
*
|
|
222
|
+
* // Increment a counter (works correctly with concurrent updates)
|
|
223
|
+
* await app.stock.update(id, { quantity: op.increment(-5) })
|
|
224
|
+
*
|
|
225
|
+
* // Keep the maximum value
|
|
226
|
+
* await app.scores.update(id, { highScore: op.max(newScore) })
|
|
227
|
+
*
|
|
228
|
+
* // Append to an array
|
|
229
|
+
* await app.todos.update(id, { tags: op.append('urgent') })
|
|
172
230
|
* ```
|
|
173
231
|
*/
|
|
174
|
-
declare const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
232
|
+
declare const op: {
|
|
233
|
+
/**
|
|
234
|
+
* Increment a number field by the given amount.
|
|
235
|
+
* Concurrent increments compose: the sum of all deltas is applied to the base.
|
|
236
|
+
*
|
|
237
|
+
* @param n - The amount to increment (use negative values to decrement)
|
|
238
|
+
*/
|
|
239
|
+
increment(n: number): AtomicOpSentinel;
|
|
240
|
+
/**
|
|
241
|
+
* Decrement a number field by the given amount.
|
|
242
|
+
* Syntactic sugar for `op.increment(-n)`.
|
|
243
|
+
*
|
|
244
|
+
* @param n - The amount to decrement
|
|
245
|
+
*/
|
|
246
|
+
decrement(n: number): AtomicOpSentinel;
|
|
247
|
+
/**
|
|
248
|
+
* Set the field to the maximum of the current value and the given value.
|
|
249
|
+
* Concurrent max operations take the maximum of all values.
|
|
250
|
+
*
|
|
251
|
+
* @param n - The value to compare against the current value
|
|
252
|
+
*/
|
|
253
|
+
max(n: number): AtomicOpSentinel;
|
|
254
|
+
/**
|
|
255
|
+
* Set the field to the minimum of the current value and the given value.
|
|
256
|
+
* Concurrent min operations take the minimum of all values.
|
|
257
|
+
*
|
|
258
|
+
* @param n - The value to compare against the current value
|
|
259
|
+
*/
|
|
260
|
+
min(n: number): AtomicOpSentinel;
|
|
261
|
+
/**
|
|
262
|
+
* Append an item to an array field.
|
|
263
|
+
* Concurrent appends include all appended items.
|
|
264
|
+
*
|
|
265
|
+
* @param item - The item to append to the array
|
|
266
|
+
*/
|
|
267
|
+
append(item: unknown): AtomicOpSentinel;
|
|
268
|
+
/**
|
|
269
|
+
* Remove an item from an array field (by value equality).
|
|
270
|
+
* Concurrent removes of the same item are idempotent.
|
|
271
|
+
*
|
|
272
|
+
* @param item - The item to remove from the array
|
|
273
|
+
*/
|
|
274
|
+
remove(item: unknown): AtomicOpSentinel;
|
|
182
275
|
};
|
|
276
|
+
/**
|
|
277
|
+
* Extract the serializable AtomicOp from a sentinel (strips the Symbol marker).
|
|
278
|
+
*
|
|
279
|
+
* @param sentinel - The atomic op sentinel
|
|
280
|
+
* @returns A plain object suitable for JSON serialization
|
|
281
|
+
*/
|
|
282
|
+
declare function toAtomicOp(sentinel: AtomicOpSentinel): AtomicOp;
|
|
183
283
|
|
|
184
284
|
/**
|
|
185
285
|
* Input shape for defineSchema() — what the developer writes.
|
|
@@ -188,12 +288,49 @@ interface SchemaInput {
|
|
|
188
288
|
version: number;
|
|
189
289
|
collections: Record<string, CollectionInput>;
|
|
190
290
|
relations?: Record<string, RelationInput>;
|
|
291
|
+
/** Schema migrations keyed by target version number. */
|
|
292
|
+
migrations?: Record<number, MigrationDefinition>;
|
|
293
|
+
/**
|
|
294
|
+
* Declarative partial-sync rules.
|
|
295
|
+
*
|
|
296
|
+
* @example
|
|
297
|
+
* ```typescript
|
|
298
|
+
* sync: {
|
|
299
|
+
* todos: { where: { userId: true, orgId: true } },
|
|
300
|
+
* projects: { where: { orgId: true } },
|
|
301
|
+
* }
|
|
302
|
+
* ```
|
|
303
|
+
*/
|
|
304
|
+
sync?: Record<string, SyncRuleInput>;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Developer input for a collection sync rule.
|
|
308
|
+
*/
|
|
309
|
+
interface SyncRuleInput {
|
|
310
|
+
/**
|
|
311
|
+
* Field filters bound to scope values.
|
|
312
|
+
* Use `true` to bind a field to a scope value with the same name.
|
|
313
|
+
* Use a string to bind to a different scope value key.
|
|
314
|
+
*/
|
|
315
|
+
where: Record<string, boolean | string>;
|
|
316
|
+
}
|
|
317
|
+
interface StateMachineInput {
|
|
318
|
+
/** The enum field this state machine controls */
|
|
319
|
+
field: string;
|
|
320
|
+
/** Map of state to allowed next states */
|
|
321
|
+
transitions: Record<string, string[]>;
|
|
322
|
+
/** What to do when an invalid transition is attempted */
|
|
323
|
+
onInvalidTransition: 'reject' | 'last-valid-state';
|
|
191
324
|
}
|
|
192
325
|
interface CollectionInput {
|
|
193
326
|
fields: Record<string, FieldBuilder<any, any, any>>;
|
|
194
327
|
indexes?: string[];
|
|
195
328
|
constraints?: ConstraintInput[];
|
|
196
329
|
resolve?: Record<string, CustomResolver>;
|
|
330
|
+
/** Scope fields for sync filtering. Only records matching the client's scope values are synced. */
|
|
331
|
+
scope?: string[];
|
|
332
|
+
/** State machine definition constraining transitions on an enum field */
|
|
333
|
+
stateMachine?: StateMachineInput;
|
|
197
334
|
}
|
|
198
335
|
interface ConstraintInput {
|
|
199
336
|
type: 'unique' | 'capacity' | 'referential';
|
|
@@ -292,15 +429,23 @@ interface FieldKindToType {
|
|
|
292
429
|
number: number;
|
|
293
430
|
boolean: boolean;
|
|
294
431
|
timestamp: number;
|
|
295
|
-
richtext:
|
|
432
|
+
richtext: string;
|
|
296
433
|
enum: string;
|
|
297
434
|
array: unknown[];
|
|
298
435
|
}
|
|
299
436
|
/**
|
|
300
|
-
* Infers the TypeScript type for a single
|
|
301
|
-
*
|
|
437
|
+
* Infers the TypeScript type for a single field descriptor or builder.
|
|
438
|
+
* Supports both FieldBuilder (class instances) and FieldDescriptor (compiled objects).
|
|
302
439
|
*/
|
|
303
|
-
type InferFieldType<F> = F extends EnumFieldBuilder<infer V,
|
|
440
|
+
type InferFieldType<F> = F extends EnumFieldBuilder<infer V, any, any> ? V[number] : F extends ArrayFieldBuilder<infer K, any, any> ? FieldKindToType[K][] : F extends FieldBuilder<infer K, any, any> ? K extends keyof FieldKindToType ? FieldKindToType[K] : unknown : F extends {
|
|
441
|
+
kind: 'enum';
|
|
442
|
+
enumValues: infer V;
|
|
443
|
+
} ? V extends readonly (infer S)[] ? S : string : F extends {
|
|
444
|
+
kind: 'array';
|
|
445
|
+
itemKind: infer K extends FieldKind;
|
|
446
|
+
} ? FieldKindToType[K][] : F extends {
|
|
447
|
+
kind: infer K extends FieldKind;
|
|
448
|
+
} ? FieldKindToType[K] : unknown;
|
|
304
449
|
/**
|
|
305
450
|
* Infers the full record type returned from queries.
|
|
306
451
|
* Includes `id`, `createdAt`, `updatedAt` metadata fields.
|
|
@@ -314,40 +459,22 @@ type InferRecord<Fields extends Record<string, FieldBuilder<any, any, any>>> = {
|
|
|
314
459
|
readonly [K in keyof Fields]: Fields[K] extends FieldBuilder<any, true, any> ? InferFieldType<Fields[K]> : InferFieldType<Fields[K]> | null;
|
|
315
460
|
};
|
|
316
461
|
/**
|
|
317
|
-
*
|
|
318
|
-
*/
|
|
319
|
-
type RequiredInsertKeys<Fields extends Record<string, FieldBuilder<any, any, any>>> = {
|
|
320
|
-
[K in keyof Fields]: Fields[K] extends FieldBuilder<any, any, true> ? never : Fields[K] extends FieldBuilder<any, true, false> ? K : never;
|
|
321
|
-
}[keyof Fields];
|
|
322
|
-
/**
|
|
323
|
-
* Helper: extract keys where the field is optional (not required) and not auto.
|
|
324
|
-
*/
|
|
325
|
-
type OptionalInsertKeys<Fields extends Record<string, FieldBuilder<any, any, any>>> = {
|
|
326
|
-
[K in keyof Fields]: Fields[K] extends FieldBuilder<any, any, true> ? never : Fields[K] extends FieldBuilder<any, true, false> ? never : K;
|
|
327
|
-
}[keyof Fields];
|
|
328
|
-
/**
|
|
329
|
-
* Infers the insert input type.
|
|
462
|
+
* Infers the insert input type using inline key remapping.
|
|
330
463
|
* - Required non-auto fields are required keys
|
|
331
464
|
* - Optional/defaulted non-auto fields are optional keys
|
|
332
465
|
* - Auto fields are excluded entirely
|
|
333
466
|
*/
|
|
334
467
|
type InferInsertInput<Fields extends Record<string, FieldBuilder<any, any, any>>> = {
|
|
335
|
-
[K in
|
|
468
|
+
[K in keyof Fields as Fields[K] extends FieldBuilder<any, any, true> ? never : Fields[K] extends FieldBuilder<any, true, false> ? K : never]: InferFieldType<Fields[K]>;
|
|
336
469
|
} & {
|
|
337
|
-
[K in
|
|
470
|
+
[K in keyof Fields as Fields[K] extends FieldBuilder<any, any, true> ? never : Fields[K] extends FieldBuilder<any, true, false> ? never : K]?: InferFieldType<Fields[K]>;
|
|
338
471
|
};
|
|
339
472
|
/**
|
|
340
|
-
*
|
|
341
|
-
*/
|
|
342
|
-
type NonAutoKeys<Fields extends Record<string, FieldBuilder<any, any, any>>> = {
|
|
343
|
-
[K in keyof Fields]: Fields[K] extends FieldBuilder<any, any, true> ? never : K;
|
|
344
|
-
}[keyof Fields];
|
|
345
|
-
/**
|
|
346
|
-
* Infers the update input type.
|
|
473
|
+
* Infers the update input type using inline key remapping.
|
|
347
474
|
* All non-auto fields are optional (partial update semantics).
|
|
348
475
|
*/
|
|
349
476
|
type InferUpdateInput<Fields extends Record<string, FieldBuilder<any, any, any>>> = {
|
|
350
|
-
[K in
|
|
477
|
+
[K in keyof Fields as Fields[K] extends FieldBuilder<any, any, true> ? never : K]?: InferFieldType<Fields[K]>;
|
|
351
478
|
};
|
|
352
479
|
|
|
353
480
|
/**
|
|
@@ -375,9 +502,10 @@ declare function dominates(a: VersionVector, b: VersionVector): boolean;
|
|
|
375
502
|
declare function vectorsEqual(a: VersionVector, b: VersionVector): boolean;
|
|
376
503
|
/**
|
|
377
504
|
* Operation log interface for computing deltas.
|
|
505
|
+
* getRange may be async (for DB-backed implementations) or sync (for in-memory).
|
|
378
506
|
*/
|
|
379
507
|
interface OperationLog {
|
|
380
|
-
getRange(nodeId: string, fromSeq: number, toSeq: number): Operation[]
|
|
508
|
+
getRange(nodeId: string, fromSeq: number, toSeq: number): Operation[] | Promise<Operation[]>;
|
|
381
509
|
}
|
|
382
510
|
/**
|
|
383
511
|
* Compute the operations that `local` has but `remote` does not.
|
|
@@ -388,7 +516,7 @@ interface OperationLog {
|
|
|
388
516
|
* @param operationLog - The operation log to fetch operations from
|
|
389
517
|
* @returns Operations sorted in causal order
|
|
390
518
|
*/
|
|
391
|
-
declare function computeDelta(localVector: VersionVector, remoteVector: VersionVector, operationLog: OperationLog): Operation[]
|
|
519
|
+
declare function computeDelta(localVector: VersionVector, remoteVector: VersionVector, operationLog: OperationLog): Promise<Operation[]>;
|
|
392
520
|
/**
|
|
393
521
|
* Serialize a version vector to a JSON-compatible string.
|
|
394
522
|
*/
|
|
@@ -398,4 +526,342 @@ declare function serializeVector(vector: VersionVector): string;
|
|
|
398
526
|
*/
|
|
399
527
|
declare function deserializeVector(s: string): VersionVector;
|
|
400
528
|
|
|
401
|
-
|
|
529
|
+
/**
|
|
530
|
+
* Returns true when the schema declares partial sync rules at the root level.
|
|
531
|
+
*/
|
|
532
|
+
declare function hasSchemaSyncRules(schema: SchemaDefinition): boolean;
|
|
533
|
+
/**
|
|
534
|
+
* Resolve field → scope-value-key bindings for a collection.
|
|
535
|
+
*
|
|
536
|
+
* Sync rules take precedence over legacy `collection.scope` arrays.
|
|
537
|
+
* Returns `null` when the collection has no sync filtering configured.
|
|
538
|
+
*/
|
|
539
|
+
declare function getCollectionScopeBindings(schema: SchemaDefinition, collectionName: string): Record<string, string> | null;
|
|
540
|
+
/**
|
|
541
|
+
* Returns whether a collection participates in sync filtering for this schema.
|
|
542
|
+
*/
|
|
543
|
+
declare function isCollectionSyncScoped(schema: SchemaDefinition, collectionName: string): boolean;
|
|
544
|
+
/**
|
|
545
|
+
* Collect unique scope value keys referenced by sync rules and legacy scope fields.
|
|
546
|
+
*/
|
|
547
|
+
declare function collectSchemaScopeValueKeys(schema: SchemaDefinition): string[];
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* Per-collection scope map: `{ collectionName: { field: value, ... } }`
|
|
551
|
+
*
|
|
552
|
+
* Used to filter which operations a client should receive during sync.
|
|
553
|
+
*/
|
|
554
|
+
type ScopeMap = Record<string, Record<string, unknown>>;
|
|
555
|
+
/**
|
|
556
|
+
* Build a per-collection scope map from the schema's scope declarations
|
|
557
|
+
* and the client's flat scope values.
|
|
558
|
+
*
|
|
559
|
+
* Supports both legacy `collection.scope` arrays and declarative `schema.sync`
|
|
560
|
+
* rules (`sync: { todos: { where: { userId: true } } }`).
|
|
561
|
+
*
|
|
562
|
+
* When `schema.sync` is present, only collections with sync rules or legacy
|
|
563
|
+
* scope fields are included in the result. Other collections are omitted so
|
|
564
|
+
* sync engines treat them as out of scope (partial sync).
|
|
565
|
+
*
|
|
566
|
+
* @param schema - The schema definition with scope declarations
|
|
567
|
+
* @param scopeValues - Flat key-value scope values from the client
|
|
568
|
+
* @returns A per-collection scope map
|
|
569
|
+
*
|
|
570
|
+
* @example
|
|
571
|
+
* ```typescript
|
|
572
|
+
* // Schema declares: sales.scope = ['orgId', 'storeId']
|
|
573
|
+
* // Client provides: { orgId: 'org-123', storeId: 'store-456' }
|
|
574
|
+
* // Result: { sales: { orgId: 'org-123', storeId: 'store-456' }, products: {} }
|
|
575
|
+
* ```
|
|
576
|
+
*/
|
|
577
|
+
declare function buildScopeMap(schema: SchemaDefinition, scopeValues: Record<string, unknown>): ScopeMap;
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Collect every unique scope field name declared across all collections.
|
|
581
|
+
*
|
|
582
|
+
* @deprecated Use {@link collectSchemaScopeValueKeys} instead.
|
|
583
|
+
*/
|
|
584
|
+
declare function collectSchemaScopeFields(schema: SchemaDefinition): string[];
|
|
585
|
+
/**
|
|
586
|
+
* Extract flat scope values from JWT (or auth) claims using schema scope field names.
|
|
587
|
+
*
|
|
588
|
+
* Resolution order for each scope field:
|
|
589
|
+
* 1. Top-level claim with the same name
|
|
590
|
+
* 2. Nested `claims.scope[field]` object
|
|
591
|
+
* 3. `userId` falls back to the standard JWT `sub` claim
|
|
592
|
+
*
|
|
593
|
+
* @param schema - Schema with per-collection scope declarations
|
|
594
|
+
* @param claims - Decoded JWT claims (unverified; client-side scope hints only)
|
|
595
|
+
* @returns Flat key-value map suitable for {@link buildScopeMap}
|
|
596
|
+
*/
|
|
597
|
+
declare function extractScopeValuesFromClaims(schema: SchemaDefinition, claims: Record<string, unknown>): Record<string, unknown>;
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* Offline-safe sequence formatting.
|
|
601
|
+
*
|
|
602
|
+
* Format tokens:
|
|
603
|
+
* - `{date}` → YYYYMMDD (current date)
|
|
604
|
+
* - `{node4}` → first 4 characters of nodeId
|
|
605
|
+
* - `{node8}` → first 8 characters of nodeId
|
|
606
|
+
* - `{seq}` → zero-padded counter (default 4 digits)
|
|
607
|
+
* - `{seq:N}` → zero-padded counter with N digits
|
|
608
|
+
*
|
|
609
|
+
* @example
|
|
610
|
+
* ```typescript
|
|
611
|
+
* formatSequenceValue('INV-{date}-{node4}-{seq}', 42, 'a1b2c3d4e5f6')
|
|
612
|
+
* // → "INV-20260508-a1b2-0042"
|
|
613
|
+
*
|
|
614
|
+
* formatSequenceValue('ORDER-{seq:6}', 7, 'node-id')
|
|
615
|
+
* // → "ORDER-000007"
|
|
616
|
+
* ```
|
|
617
|
+
*/
|
|
618
|
+
declare function formatSequenceValue(template: string, counter: number, nodeId: string, now?: Date): string;
|
|
619
|
+
/**
|
|
620
|
+
* Default format when none is provided: `{name}-{seq:4}`.
|
|
621
|
+
*/
|
|
622
|
+
declare function defaultSequenceFormat(name: string): string;
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* Transforms operations created under an older schema version so they are valid
|
|
626
|
+
* after a schema upgrade. Used during sync when client and server schema versions differ
|
|
627
|
+
* but remain within the server's supported range.
|
|
628
|
+
*/
|
|
629
|
+
interface OperationTransform {
|
|
630
|
+
/** Schema version the operation was authored against */
|
|
631
|
+
readonly fromVersion: number;
|
|
632
|
+
/** Target schema version after transformation */
|
|
633
|
+
readonly toVersion: number;
|
|
634
|
+
/**
|
|
635
|
+
* Transform a single operation. Return `null` to drop operations that cannot be migrated.
|
|
636
|
+
*/
|
|
637
|
+
transform(operation: Operation): Operation | null;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* Apply registered transforms until the operation matches `targetSchemaVersion`.
|
|
642
|
+
* Returns null when a transform drops the operation or no path exists.
|
|
643
|
+
*/
|
|
644
|
+
declare function applyOperationTransforms(operation: Operation, targetSchemaVersion: number, transforms: readonly OperationTransform[]): Operation | null;
|
|
645
|
+
|
|
646
|
+
/**
|
|
647
|
+
* Convert migration steps to SQL statements.
|
|
648
|
+
*
|
|
649
|
+
* Structural steps (addField, removeField, renameField, addIndex, removeIndex)
|
|
650
|
+
* produce SQL. Backfill steps are skipped (handled at the application layer
|
|
651
|
+
* by reading rows and applying the transform function).
|
|
652
|
+
*
|
|
653
|
+
* @param steps - The migration steps from a MigrationBuilder
|
|
654
|
+
* @returns Array of SQL statements for structural changes
|
|
655
|
+
*/
|
|
656
|
+
declare function migrationStepsToSQL(steps: readonly MigrationStep[]): string[];
|
|
657
|
+
/**
|
|
658
|
+
* Generate SQL statements to roll back a migration.
|
|
659
|
+
*
|
|
660
|
+
* Uses the migration's explicit rollback steps if available,
|
|
661
|
+
* otherwise auto-generates inverse steps from the forward steps.
|
|
662
|
+
*
|
|
663
|
+
* Backfill steps in the rollback are skipped (handled at the application layer).
|
|
664
|
+
*
|
|
665
|
+
* @param migration - The migration definition to generate rollback SQL for
|
|
666
|
+
* @returns Array of SQL statements that undo the migration's structural changes
|
|
667
|
+
*
|
|
668
|
+
* @example
|
|
669
|
+
* ```typescript
|
|
670
|
+
* const migration = migrate()
|
|
671
|
+
* .addField('todos', 'priority', t.enum(['low', 'medium', 'high']).default('medium'))
|
|
672
|
+
* .addIndex('todos', 'priority')
|
|
673
|
+
*
|
|
674
|
+
* const rollbackSQL = rollbackStepsToSQL(migration)
|
|
675
|
+
* // ['DROP INDEX IF EXISTS idx_todos_priority',
|
|
676
|
+
* // 'ALTER TABLE todos DROP COLUMN priority']
|
|
677
|
+
* ```
|
|
678
|
+
*/
|
|
679
|
+
declare function rollbackStepsToSQL(migration: MigrationDefinition): string[];
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* A migration that includes both forward (up) and backward (down) steps.
|
|
683
|
+
* Rollback steps are applied in reverse order of the forward steps.
|
|
684
|
+
*/
|
|
685
|
+
interface ReversibleMigration {
|
|
686
|
+
readonly up: readonly MigrationStep[];
|
|
687
|
+
readonly down: readonly MigrationStep[];
|
|
688
|
+
readonly fromVersion: number;
|
|
689
|
+
readonly toVersion: number;
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* Error thrown when a migration step cannot be automatically rolled back
|
|
693
|
+
* and no explicit down step has been provided.
|
|
694
|
+
*/
|
|
695
|
+
declare class MigrationRollbackError extends KoraError {
|
|
696
|
+
constructor(step: MigrationStep);
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* Determines whether a migration step can be automatically rolled back
|
|
700
|
+
* without explicit developer-provided down steps.
|
|
701
|
+
*
|
|
702
|
+
* Auto-rollback is possible when the inverse operation is deterministic:
|
|
703
|
+
* - addField -> removeField (drop the added column)
|
|
704
|
+
* - addIndex -> removeIndex (drop the added index)
|
|
705
|
+
* - removeIndex -> addIndex (re-create the index)
|
|
706
|
+
* - renameField -> renameField (swap from/to names)
|
|
707
|
+
*
|
|
708
|
+
* Steps that CANNOT auto-rollback:
|
|
709
|
+
* - removeField: the field descriptor is lost (need it to re-create the column)
|
|
710
|
+
* - backfill: data transforms are not reversible
|
|
711
|
+
*
|
|
712
|
+
* @param step - The forward migration step to check
|
|
713
|
+
* @returns true if the step can be auto-rolled back
|
|
714
|
+
*/
|
|
715
|
+
declare function canAutoRollback(step: MigrationStep): boolean;
|
|
716
|
+
/**
|
|
717
|
+
* Generates rollback steps for a list of forward migration steps.
|
|
718
|
+
* Steps are reversed in order (last forward step becomes first rollback step).
|
|
719
|
+
*
|
|
720
|
+
* For steps that cannot be auto-rolled back, throws a MigrationRollbackError.
|
|
721
|
+
* Use canAutoRollback() to check before calling, or provide explicit down steps
|
|
722
|
+
* via the MigrationBuilder .down() API.
|
|
723
|
+
*
|
|
724
|
+
* @param forwardSteps - The forward migration steps to generate rollbacks for
|
|
725
|
+
* @returns Array of rollback steps in reverse execution order
|
|
726
|
+
* @throws MigrationRollbackError if any step cannot be auto-rolled back
|
|
727
|
+
*/
|
|
728
|
+
declare function generateRollbackSteps(forwardSteps: readonly MigrationStep[]): MigrationStep[];
|
|
729
|
+
/**
|
|
730
|
+
* Create a ReversibleMigration from forward steps, explicit down steps, and version info.
|
|
731
|
+
*
|
|
732
|
+
* If explicit down steps are provided, they are used as-is.
|
|
733
|
+
* If no explicit down steps are provided, auto-generation is attempted.
|
|
734
|
+
*
|
|
735
|
+
* @param upSteps - The forward migration steps
|
|
736
|
+
* @param downSteps - Optional explicit rollback steps (overrides auto-generation)
|
|
737
|
+
* @param fromVersion - The schema version before the migration
|
|
738
|
+
* @param toVersion - The schema version after the migration
|
|
739
|
+
* @returns A complete ReversibleMigration
|
|
740
|
+
* @throws MigrationRollbackError if auto-generation fails and no explicit down steps provided
|
|
741
|
+
*/
|
|
742
|
+
declare function createReversibleMigration(upSteps: readonly MigrationStep[], downSteps: readonly MigrationStep[] | null, fromVersion: number, toVersion: number): ReversibleMigration;
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* Validates whether a transition from one state to another is allowed
|
|
746
|
+
* by the given state machine constraint.
|
|
747
|
+
*
|
|
748
|
+
* @param constraint - The state machine constraint defining allowed transitions
|
|
749
|
+
* @param fromValue - The current state value (before the transition)
|
|
750
|
+
* @param toValue - The target state value (after the transition)
|
|
751
|
+
* @returns A TransitionValidationResult describing whether the transition is valid
|
|
752
|
+
*
|
|
753
|
+
* @example
|
|
754
|
+
* ```typescript
|
|
755
|
+
* const constraint: StateMachineConstraint = {
|
|
756
|
+
* field: 'status',
|
|
757
|
+
* collection: 'orders',
|
|
758
|
+
* transitions: {
|
|
759
|
+
* draft: ['submitted', 'cancelled'],
|
|
760
|
+
* submitted: ['approved'],
|
|
761
|
+
* approved: [],
|
|
762
|
+
* },
|
|
763
|
+
* }
|
|
764
|
+
*
|
|
765
|
+
* const result = validateTransition(constraint, 'draft', 'submitted')
|
|
766
|
+
* // { valid: true, from: 'draft', to: 'submitted', field: 'status',
|
|
767
|
+
* // collection: 'orders', allowedTargets: ['submitted', 'cancelled'] }
|
|
768
|
+
* ```
|
|
769
|
+
*/
|
|
770
|
+
declare function validateTransition(constraint: StateMachineConstraint, fromValue: unknown, toValue: unknown): TransitionValidationResult;
|
|
771
|
+
/**
|
|
772
|
+
* Extracts all state machine constraints from a schema definition.
|
|
773
|
+
* Scans every collection for enum fields that have transition rules declared
|
|
774
|
+
* via the `.transitions()` builder method.
|
|
775
|
+
*
|
|
776
|
+
* @param schema - The schema definition to extract constraints from
|
|
777
|
+
* @returns An array of StateMachineConstraint objects, one per enum field with transitions
|
|
778
|
+
*
|
|
779
|
+
* @example
|
|
780
|
+
* ```typescript
|
|
781
|
+
* const schema = defineSchema({
|
|
782
|
+
* version: 1,
|
|
783
|
+
* collections: {
|
|
784
|
+
* orders: {
|
|
785
|
+
* fields: {
|
|
786
|
+
* status: t.enum(['draft', 'submitted']).transitions({
|
|
787
|
+
* draft: ['submitted'],
|
|
788
|
+
* submitted: [],
|
|
789
|
+
* }),
|
|
790
|
+
* },
|
|
791
|
+
* },
|
|
792
|
+
* },
|
|
793
|
+
* })
|
|
794
|
+
*
|
|
795
|
+
* const constraints = buildStateMachineConstraints(schema)
|
|
796
|
+
* // [{ field: 'status', collection: 'orders', transitions: { draft: ['submitted'], submitted: [] } }]
|
|
797
|
+
* ```
|
|
798
|
+
*/
|
|
799
|
+
declare function buildStateMachineConstraints(schema: SchemaDefinition): StateMachineConstraint[];
|
|
800
|
+
/**
|
|
801
|
+
* Finds the state machine constraint for a specific field in a specific collection,
|
|
802
|
+
* if one exists.
|
|
803
|
+
*
|
|
804
|
+
* @param schema - The schema definition to search
|
|
805
|
+
* @param collection - The collection name
|
|
806
|
+
* @param field - The field name
|
|
807
|
+
* @returns The TransitionMap if the field has transitions declared, or null otherwise
|
|
808
|
+
*/
|
|
809
|
+
declare function getTransitionMap(schema: SchemaDefinition, collection: string, field: string): TransitionMap | null;
|
|
810
|
+
|
|
811
|
+
/**
|
|
812
|
+
* Output of the proto definition generator.
|
|
813
|
+
* Contains the .proto file text, a type mapping, and a JSON descriptor
|
|
814
|
+
* compatible with protobufjs's `Root.fromJSON()`.
|
|
815
|
+
*/
|
|
816
|
+
interface ProtoOutput {
|
|
817
|
+
/** The generated .proto file content as a string */
|
|
818
|
+
proto: string;
|
|
819
|
+
/** TypeScript type map: field name -> protobuf type */
|
|
820
|
+
typeMap: Map<string, string>;
|
|
821
|
+
/** JSON descriptor for dynamic protobufjs usage (no .proto file needed) */
|
|
822
|
+
jsonDescriptor: Record<string, unknown>;
|
|
823
|
+
}
|
|
824
|
+
/**
|
|
825
|
+
* Generates Protocol Buffer definitions from a Kora schema.
|
|
826
|
+
*
|
|
827
|
+
* Produces three outputs:
|
|
828
|
+
* 1. A `.proto` file string conforming to proto3 syntax
|
|
829
|
+
* 2. A type map linking Kora field paths to protobuf types
|
|
830
|
+
* 3. A JSON descriptor for runtime protobufjs usage via `Root.fromJSON()`
|
|
831
|
+
*
|
|
832
|
+
* The generated definitions include:
|
|
833
|
+
* - Per-collection record messages with proper type mappings
|
|
834
|
+
* - Nested enum types for enum fields
|
|
835
|
+
* - `KoraOperation` wrapper for the sync wire format
|
|
836
|
+
* - `OperationBatch` for batched sync transfers
|
|
837
|
+
* - `HandshakeMessage` / `HandshakeResponse` for sync session initiation
|
|
838
|
+
* - `Acknowledgment` for delivery confirmation
|
|
839
|
+
*
|
|
840
|
+
* @param schema - A validated SchemaDefinition from defineSchema()
|
|
841
|
+
* @returns ProtoOutput with proto text, type map, and JSON descriptor
|
|
842
|
+
*
|
|
843
|
+
* @example
|
|
844
|
+
* ```typescript
|
|
845
|
+
* import { defineSchema, t, generateProtoDefinitions } from '@korajs/core'
|
|
846
|
+
*
|
|
847
|
+
* const schema = defineSchema({
|
|
848
|
+
* version: 1,
|
|
849
|
+
* collections: {
|
|
850
|
+
* todos: {
|
|
851
|
+
* fields: {
|
|
852
|
+
* title: t.string(),
|
|
853
|
+
* completed: t.boolean().default(false),
|
|
854
|
+
* }
|
|
855
|
+
* }
|
|
856
|
+
* }
|
|
857
|
+
* })
|
|
858
|
+
*
|
|
859
|
+
* const { proto, typeMap, jsonDescriptor } = generateProtoDefinitions(schema)
|
|
860
|
+
* // proto is a valid .proto file string
|
|
861
|
+
* // typeMap maps "todos.title" -> "string", "todos.completed" -> "bool"
|
|
862
|
+
* // jsonDescriptor can be loaded with protobuf.Root.fromJSON()
|
|
863
|
+
* ```
|
|
864
|
+
*/
|
|
865
|
+
declare function generateProtoDefinitions(schema: SchemaDefinition): ProtoOutput;
|
|
866
|
+
|
|
867
|
+
export { APPLY_FAILURE_CODES, APPLY_RESULTS, type ApplyFailureReason, type ApplyResult, ArrayFieldBuilder, AtomicOp, type AtomicOpSentinel, AtomicOpType, CausalTracker, ClockDriftError, CollectionDefinition, type CollectionInput, type ConstraintInput, CustomResolver, EnumFieldBuilder, FieldBuilder, FieldKind, type FieldKindToType, type InferFieldType, type InferInsertInput, type InferRecord, type InferUpdateInput, KORA_ERROR_FIX_SUGGESTIONS, KoraError, MergeConflictError, MigrationDefinition, MigrationRollbackError, MigrationStep, Operation, OperationError, type OperationLog, type OperationTransform, OperationType, type ProtoOutput, RandomSource, RelationDefinition, type RelationInput, type ReversibleMigration, SchemaDefinition, type SchemaInput, SchemaValidationError, type ScopeMap, StateMachineConstraint, type StateMachineInput, StorageError, SyncError, TransitionMap, TransitionValidationResult, type TypedSchemaDefinition, VersionVector, advanceVector, applyOperationTransforms, buildScopeMap, buildStateMachineConstraints, canAutoRollback, collectSchemaScopeFields, collectSchemaScopeValueKeys, computeDelta, createReversibleMigration, createVersionVector, defaultApplyFailureReason, defaultSequenceFormat, defineSchema, deserializeVector, dominates, extractScopeValuesFromClaims, extractTimestamp, formatSequenceValue, generateFullDDL, generateProtoDefinitions, generateRollbackSteps, generateSQL, generateUUIDv7, getCollectionScopeBindings, getKoraErrorFix, getTransitionMap, hasSchemaSyncRules, isApplyFailure, isAtomicOp, isCollectionSyncScoped, isValidUUIDv7, mergeVectors, migrationStepsToSQL, op, resolveAtomicOp, rollbackStepsToSQL, serializeVector, toAtomicOp, validateRecord, validateTransition, vectorsEqual };
|