@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/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { R as RandomSource, F as FieldKind, a as FieldDescriptor, C as CustomResolver, S as SchemaDefinition, b as CollectionDefinition, c as RelationDefinition, O as OperationType, d as Operation, V as VersionVector } from './events-7Fhdjxd2.js';
2
- export { e as CONNECTION_QUALITIES, f as ConnectionQuality, g as Constraint, H as HLCTimestamp, h as HybridLogicalClock, K as KoraEvent, i as KoraEventByType, j as KoraEventEmitter, k as KoraEventListener, l as KoraEventType, M as MERGE_STRATEGIES, m as MergeStrategy, n as MergeTrace, o as OnDeleteAction, p as OperationInput, q as RelationType, T as TimeSource, r as createOperation, s as isValidOperation, v as verifyOperationIntegrity } from './events-7Fhdjxd2.js';
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
- * Base field builder implementing the builder pattern for schema field definitions.
102
- * Each builder is immutable — modifier methods return new builder instances.
103
- *
104
- * Type parameters track field metadata at the type level for inference:
105
- * - Kind: the field kind ('string', 'number', etc.)
106
- * - Req: whether the field is required (true = required on insert)
107
- * - Auto: whether the field is auto-populated (true = excluded from insert input)
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
- * @example
174
+ * Instead of read-modify-write patterns, developers express intent:
110
175
  * ```typescript
111
- * t.string() // required string field
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
- declare class FieldBuilder<Kind extends FieldKind = FieldKind, Req extends boolean = true, Auto extends boolean = false> {
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
- * Field builder for enum fields with constrained string values.
134
- * Preserves the literal enum tuple type for inference.
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 class EnumFieldBuilder<Values extends readonly string[] = readonly string[], Req extends boolean = true, Auto extends boolean = false> extends FieldBuilder<'enum', Req, Auto> {
137
- private readonly _enumValues;
138
- constructor(values: Values, required?: Req, defaultValue?: unknown, auto?: Auto);
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
- * Field builder for array fields with a typed item kind.
146
- * Preserves the item kind type parameter for inference.
191
+ * Sentinel object returned by op.* helpers.
192
+ * Detected by Collection.update() and resolved to concrete values.
147
193
  */
148
- declare class ArrayFieldBuilder<ItemKind extends FieldKind = FieldKind, Req extends boolean = true, Auto extends boolean = false> extends FieldBuilder<'array', Req, Auto> {
149
- private readonly _itemKind;
150
- constructor(itemBuilder: FieldBuilder<ItemKind>, required?: Req, defaultValue?: unknown, auto?: Auto);
151
- optional(): ArrayFieldBuilder<ItemKind, false, Auto>;
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 builder namespace. The developer's primary interface for defining field types.
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 { t } from '@korajs/core'
162
- *
163
- * const fields = {
164
- * title: t.string(),
165
- * count: t.number(),
166
- * active: t.boolean().default(true),
167
- * notes: t.richtext(),
168
- * tags: t.array(t.string()).default([]),
169
- * priority: t.enum(['low', 'medium', 'high']).default('medium'),
170
- * createdAt: t.timestamp().auto(),
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 t: {
175
- string(): FieldBuilder<"string", true, false>;
176
- number(): FieldBuilder<"number", true, false>;
177
- boolean(): FieldBuilder<"boolean", true, false>;
178
- timestamp(): FieldBuilder<"timestamp", true, false>;
179
- richtext(): FieldBuilder<"richtext", true, false>;
180
- enum<const V extends readonly string[]>(values: V): EnumFieldBuilder<V, true, false>;
181
- array<K extends FieldKind>(itemBuilder: FieldBuilder<K>): ArrayFieldBuilder<K, true, false>;
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: Uint8Array;
432
+ richtext: string;
296
433
  enum: string;
297
434
  array: unknown[];
298
435
  }
299
436
  /**
300
- * Infers the TypeScript type for a single FieldBuilder.
301
- * Handles base fields, enums (with literal union), and arrays (with typed items).
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, infer _Req, infer _Auto> ? V[number] : F extends ArrayFieldBuilder<infer K, infer _Req, infer _Auto> ? FieldKindToType[K][] : F extends FieldBuilder<infer K, infer _Req, infer _Auto> ? FieldKindToType[K] : unknown;
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
- * Helper: extract keys where the field is required and not auto.
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 RequiredInsertKeys<Fields> & string]: InferFieldType<Fields[K]>;
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 OptionalInsertKeys<Fields> & string]?: InferFieldType<Fields[K]>;
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
- * Helper: extract keys where the field is not auto.
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 NonAutoKeys<Fields> & string]?: InferFieldType<Fields[K]>;
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
- export { ArrayFieldBuilder, ClockDriftError, CollectionDefinition, type CollectionInput, type ConstraintInput, CustomResolver, EnumFieldBuilder, FieldBuilder, FieldDescriptor, FieldKind, type FieldKindToType, type InferFieldType, type InferInsertInput, type InferRecord, type InferUpdateInput, KoraError, MergeConflictError, Operation, OperationError, type OperationLog, OperationType, RandomSource, RelationDefinition, type RelationInput, SchemaDefinition, type SchemaInput, SchemaValidationError, StorageError, SyncError, type TypedSchemaDefinition, VersionVector, advanceVector, computeDelta, createVersionVector, defineSchema, deserializeVector, dominates, extractTimestamp, generateFullDDL, generateSQL, generateUUIDv7, isValidUUIDv7, mergeVectors, serializeVector, t, validateRecord, vectorsEqual };
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 };