@korajs/store 0.5.0 → 1.0.0-beta.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.
Files changed (63) hide show
  1. package/LICENSE +21 -0
  2. package/dist/adapters/better-sqlite3.cjs +94 -40
  3. package/dist/adapters/better-sqlite3.cjs.map +1 -1
  4. package/dist/adapters/better-sqlite3.d.cts +9 -1
  5. package/dist/adapters/better-sqlite3.d.ts +9 -1
  6. package/dist/adapters/better-sqlite3.js +62 -41
  7. package/dist/adapters/better-sqlite3.js.map +1 -1
  8. package/dist/adapters/indexeddb.cjs +24 -13
  9. package/dist/adapters/indexeddb.cjs.map +1 -1
  10. package/dist/adapters/indexeddb.d.cts +2 -2
  11. package/dist/adapters/indexeddb.d.ts +2 -2
  12. package/dist/adapters/indexeddb.js +4 -3
  13. package/dist/adapters/indexeddb.js.map +1 -1
  14. package/dist/adapters/sqlite-wasm.cjs +24 -13
  15. package/dist/adapters/sqlite-wasm.cjs.map +1 -1
  16. package/dist/adapters/sqlite-wasm.d.cts +27 -4
  17. package/dist/adapters/sqlite-wasm.d.ts +27 -4
  18. package/dist/adapters/sqlite-wasm.js +6 -4
  19. package/dist/{backup-JN3LB4YT.js → backup-5XZ7THNJ.js} +3 -3
  20. package/dist/{chunk-EZSTD2XC.js → chunk-5R4QMY3Z.js} +2 -2
  21. package/dist/{chunk-AGN3CIPB.js → chunk-EIVXWF4B.js} +2 -2
  22. package/dist/{chunk-56LL4XP6.js → chunk-ETDJEH2I.js} +42 -1
  23. package/dist/chunk-ETDJEH2I.js.map +1 -0
  24. package/dist/{chunk-XO4LMPAH.js → chunk-IOEFNHHP.js} +2 -35
  25. package/dist/chunk-IOEFNHHP.js.map +1 -0
  26. package/dist/{chunk-7BPETY5Y.js → chunk-OUQDC5Z6.js} +105 -8
  27. package/dist/chunk-OUQDC5Z6.js.map +1 -0
  28. package/dist/{chunk-LAWV6CFH.js → chunk-QL2XVZRN.js} +12 -1
  29. package/dist/chunk-QL2XVZRN.js.map +1 -0
  30. package/dist/{chunk-6AHYGQQT.js → chunk-RBPVEAVM.js} +4 -4
  31. package/dist/chunk-TVJEC5S2.js +40 -0
  32. package/dist/chunk-TVJEC5S2.js.map +1 -0
  33. package/dist/{export-audit-5GV6VRDZ.js → export-audit-VPGQWNR6.js} +3 -3
  34. package/dist/index.cjs +1102 -104
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.cts +219 -7
  37. package/dist/index.d.ts +219 -7
  38. package/dist/index.js +905 -44
  39. package/dist/index.js.map +1 -1
  40. package/dist/internal.cjs +100 -30
  41. package/dist/internal.cjs.map +1 -1
  42. package/dist/internal.d.cts +3 -3
  43. package/dist/internal.d.ts +3 -3
  44. package/dist/internal.js +3 -3
  45. package/dist/{sqlite-wasm-channel-I9y_jObg.d.cts → sqlite-wasm-channel-DUDPclTE.d.cts} +2 -15
  46. package/dist/{sqlite-wasm-channel-I9y_jObg.d.ts → sqlite-wasm-channel-DUDPclTE.d.ts} +2 -15
  47. package/dist/sqlite-wasm-channel-H43KEZLM.js +12 -0
  48. package/dist/{types-DepnRDk_.d.cts → types-CJOVKxLw.d.cts} +43 -2
  49. package/dist/{types-DepnRDk_.d.ts → types-CJOVKxLw.d.ts} +43 -2
  50. package/dist/{types-CTr00g_d.d.cts → types-CyjXwrjW.d.cts} +1 -1
  51. package/dist/{types-BMyHmwvn.d.ts → types-DAxCladl.d.ts} +1 -1
  52. package/package.json +4 -2
  53. package/dist/chunk-56LL4XP6.js.map +0 -1
  54. package/dist/chunk-7BPETY5Y.js.map +0 -1
  55. package/dist/chunk-LAWV6CFH.js.map +0 -1
  56. package/dist/chunk-XO4LMPAH.js.map +0 -1
  57. package/dist/sqlite-wasm-channel-HDDW6UBH.js +0 -10
  58. /package/dist/{backup-JN3LB4YT.js.map → backup-5XZ7THNJ.js.map} +0 -0
  59. /package/dist/{chunk-EZSTD2XC.js.map → chunk-5R4QMY3Z.js.map} +0 -0
  60. /package/dist/{chunk-AGN3CIPB.js.map → chunk-EIVXWF4B.js.map} +0 -0
  61. /package/dist/{chunk-6AHYGQQT.js.map → chunk-RBPVEAVM.js.map} +0 -0
  62. /package/dist/{export-audit-5GV6VRDZ.js.map → export-audit-VPGQWNR6.js.map} +0 -0
  63. /package/dist/{sqlite-wasm-channel-HDDW6UBH.js.map → sqlite-wasm-channel-H43KEZLM.js.map} +0 -0
package/dist/index.d.cts CHANGED
@@ -1,9 +1,10 @@
1
- import { C as CollectionRecord, S as StorageAdapter, Q as QueryDescriptor, c as SubscriptionCallback, W as WhereClause, d as OrderByDirection, L as LocalMutationHandler, e as StoreConfig, A as ApplyRemoteOptions, f as MaterializedRowSnapshot } from './types-DepnRDk_.cjs';
2
- export { M as MigrationPlan, g as OrderByClause, h as StoreIsolation, T as Transaction, i as TransactionBufferedEntry, j as TransactionCommitBatch, k as TransactionCommitResult, l as WhereOperators } from './types-DepnRDk_.cjs';
3
- import { Operation, KoraError, VersionVector, SchemaDefinition, MergeTrace, CollectionDefinition, SequenceConfig, HybridLogicalClock, CausalTracker, OperationLog, ApplyResult, StateMachineDefinition, KoraEvent } from '@korajs/core';
1
+ import { C as CollectionRecord, S as StorageAdapter, Q as QueryDescriptor, c as SubscriptionCallback, W as WhereClause, d as OrderByDirection, L as LocalMutationHandler, e as StoreConfig, A as ApplyRemoteOptions, f as MaterializedRowSnapshot, g as RowVersionState } from './types-CJOVKxLw.cjs';
2
+ export { M as MigrationPlan, h as OrderByClause, i as StoreIsolation, T as Transaction, j as TransactionBufferedEntry, k as TransactionCommitBatch, l as TransactionCommitResult, m as WhereOperators } from './types-CJOVKxLw.cjs';
3
+ import { Operation, KoraError, VersionVector, SchemaDefinition, HLCTimestamp, MergeTrace, CollectionDefinition, SequenceConfig, HybridLogicalClock, CausalTracker, OperationLog, ApplyResult, StateMachineDefinition, KoraBytesValue, KoraEvent } from '@korajs/core';
4
4
  export { ApplyResult } from '@korajs/core';
5
- import { R as RelationEnforcer, L as LocalMutationContext } from './types-CTr00g_d.cjs';
6
- export { C as Collection } from './types-CTr00g_d.cjs';
5
+ import { R as RelationEnforcer, L as LocalMutationContext } from './types-CyjXwrjW.cjs';
6
+ export { C as Collection } from './types-CyjXwrjW.cjs';
7
+ import * as yjs from 'yjs';
7
8
 
8
9
  /**
9
10
  * In-memory materialized state at a causal cut in the operation log.
@@ -32,6 +33,18 @@ declare class QueryError extends KoraError {
32
33
  declare class RecordNotFoundError extends KoraError {
33
34
  constructor(collection: string, recordId: string);
34
35
  }
36
+ /**
37
+ * Thrown inside `applyRemoteOperation` when the row's version state changed
38
+ * between the caller's snapshot read and the guarded write (a concurrent local
39
+ * mutation landed in the window). The transaction is rolled back with nothing
40
+ * written; the caller re-reads fresh state, recomputes its merge, and retries.
41
+ * This is the optimistic-concurrency guard that keeps merge-engine results
42
+ * (richtext, add-wins arrays, constraint resolutions) from clobbering newer
43
+ * local edits they never saw.
44
+ */
45
+ declare class OptimisticLockError extends KoraError {
46
+ constructor(collection: string, recordId: string);
47
+ }
35
48
  /**
36
49
  * Thrown when a storage adapter operation fails.
37
50
  */
@@ -100,6 +113,20 @@ declare function computeAckCompactionWatermark(serverVector: VersionVector): Ver
100
113
  */
101
114
  declare function compactOperationLog(adapter: StorageAdapter, schema: SchemaDefinition, strategy: CompactionStrategy, serverVector: VersionVector): Promise<CompactionResult>;
102
115
 
116
+ /**
117
+ * Result of re-stamping unsynced operations after a clock correction.
118
+ */
119
+ interface ClockRebaseResult {
120
+ /** The rewritten operations, in causal order (original HLC total order preserved). */
121
+ operations: Operation[];
122
+ /** Maps each old (pre-rebase) operation id to its new content-addressed id. */
123
+ idMapping: Record<string, string>;
124
+ /** Number of operations that were re-stamped. */
125
+ rebasedCount: number;
126
+ /** Highest timestamp assigned during the rebase, or null when nothing was rebased. */
127
+ newMaxTimestamp: HLCTimestamp | null;
128
+ }
129
+
103
130
  declare const LAST_ACKED_SERVER_VECTOR_META_KEY = "last_acked_server_vector";
104
131
  /**
105
132
  * Serialize a version vector for `_kora_meta` storage.
@@ -714,6 +741,12 @@ declare class Store implements OperationLog {
714
741
  /**
715
742
  * Get the node ID for this store instance.
716
743
  */
744
+ /**
745
+ * Records the offset between server time and this device's clock
746
+ * (serverTime - localTime), learned at sync handshake. Lets the store's HLC
747
+ * validate remote timestamps correctly even when the local clock is wrong.
748
+ */
749
+ setClockReferenceOffset(offsetMs: number): void;
717
750
  getNodeId(): string;
718
751
  /**
719
752
  * Apply a remote operation received from sync.
@@ -721,6 +754,18 @@ declare class Store implements OperationLog {
721
754
  * and updates the version vector.
722
755
  */
723
756
  applyRemoteOperation(op: Operation, options?: ApplyRemoteOptions): Promise<ApplyResult>;
757
+ /**
758
+ * Materialize update/delete operations that were logged for a record BEFORE
759
+ * its insert arrived (reordered delivery). Runs inside the insert's write
760
+ * transaction, folding each orphan in timestamp order through the exact
761
+ * per-field LWW / LWW-delete rules the normal apply paths use, so the final
762
+ * row state equals what a device that received the operations in causal
763
+ * order computed. Called only when the insert created a previously-absent
764
+ * row — in that case every logged update/delete for the record is
765
+ * necessarily an orphan (rows are soft-deleted, never removed, so a row
766
+ * that is absent now was never materialized).
767
+ */
768
+ private foldOrphanedOperations;
724
769
  /**
725
770
  * Get operations from a node within a sequence number range.
726
771
  * Implements the OperationLog interface for computeDelta.
@@ -774,6 +819,14 @@ declare class Store implements OperationLog {
774
819
  * Load a materialized row by ID, including soft-deleted tombstones.
775
820
  */
776
821
  findMaterializedRow(collection: string, recordId: string): Promise<MaterializedRowSnapshot | null>;
822
+ /**
823
+ * Raw version state of a materialized row, for optimistic-concurrency guarded
824
+ * applies: capture this snapshot before computing a merge, pass it as
825
+ * `guardRowState`, and the apply refuses to write if the row changed since.
826
+ * Returns null when no row exists (which is itself a valid guard state — the
827
+ * apply then requires the row to still be absent).
828
+ */
829
+ getRowVersionState(collection: string, recordId: string): Promise<RowVersionState | null>;
777
830
  /**
778
831
  * Latest operation from this device for a record (used for delete-vs-update merge on sync).
779
832
  */
@@ -797,6 +850,18 @@ declare class Store implements OperationLog {
797
850
  * Local operations not yet reflected on the server version vector.
798
851
  */
799
852
  getUnsyncedOperations(serverVector: VersionVector): Promise<Operation[]>;
853
+ /**
854
+ * Re-stamp never-acknowledged local operations after a fast device clock was
855
+ * corrected, so sync can resume immediately. Safe because unacknowledged
856
+ * operations are private to this device (like unpushed git commits);
857
+ * acknowledged/shared operations are immutable and never touched.
858
+ *
859
+ * After the rewrite the store's HLC is advanced past the highest new
860
+ * timestamp so subsequent local writes keep sorting after the rebased ops.
861
+ * No data-change notifications are emitted: materialized values are
862
+ * unchanged, only version stamps move.
863
+ */
864
+ rebaseUnsyncedOperations(unsyncedOpIds: string[], correctedNowMs: number): Promise<ClockRebaseResult>;
800
865
  /**
801
866
  * Count of local operations ahead of the server version vector.
802
867
  */
@@ -896,6 +961,62 @@ interface CollectionAccessor {
896
961
  where(conditions: Record<string, unknown>): QueryBuilder;
897
962
  }
898
963
 
964
+ /**
965
+ * Bridges the async QueryBuilder.subscribe() API with synchronous snapshot reads
966
+ * required by React useSyncExternalStore, Vue composables, and Svelte stores.
967
+ *
968
+ * Uses lazy subscription: the underlying query subscription starts when the first
969
+ * listener attaches and stops when the last listener detaches.
970
+ */
971
+ declare class QueryStore<T = CollectionRecord> {
972
+ private snapshot;
973
+ private listeners;
974
+ private unsubscribeQuery;
975
+ private active;
976
+ private readonly queryBuilder;
977
+ constructor(queryBuilder: QueryBuilder<T>);
978
+ /**
979
+ * Subscribe to snapshot changes.
980
+ *
981
+ * @returns Unsubscribe function
982
+ */
983
+ subscribe: (onStoreChange: () => void) => (() => void);
984
+ /** Synchronous read of the latest query results. */
985
+ getSnapshot: () => readonly T[];
986
+ /** Tear down listeners and the underlying query subscription. */
987
+ destroy(): void;
988
+ private startSubscription;
989
+ private stopSubscription;
990
+ private notifyListeners;
991
+ }
992
+
993
+ /**
994
+ * Ensures a query builder is backed by an open store (not a legacy pending placeholder).
995
+ */
996
+ declare function assertQueryReady(query: QueryBuilder<unknown>): void;
997
+
998
+ /**
999
+ * Reference-counted cache of {@link QueryStore} instances keyed by query descriptor.
1000
+ * Identical queries from different components share one underlying subscription.
1001
+ */
1002
+ declare class QueryStoreCache {
1003
+ private readonly scopeKey;
1004
+ private entries;
1005
+ constructor(scopeKey?: string);
1006
+ getOrCreate<T>(queryBuilder: QueryBuilder<T>): QueryStore<T>;
1007
+ release(queryBuilder: QueryBuilder<unknown>): void;
1008
+ clear(): void;
1009
+ get size(): number;
1010
+ private getKey;
1011
+ }
1012
+ /**
1013
+ * Process-wide shared query store cache for framework bindings.
1014
+ *
1015
+ * @deprecated Prefer {@link KoraApp.getQueryStoreCache} via `KoraProvider` context
1016
+ * so each app instance owns an isolated cache.
1017
+ */
1018
+ declare function getSharedQueryStoreCache(): QueryStoreCache;
1019
+
899
1020
  /**
900
1021
  * Bloom filter for subscription dependency tracking.
901
1022
  *
@@ -1040,11 +1161,102 @@ declare function validateStateTransition(collectionName: string, recordId: strin
1040
1161
  */
1041
1162
  declare function validateUpdateStateMachine(collectionName: string, recordId: string, collectionDef: CollectionDefinition, currentRecord: Record<string, unknown>, updateData: Record<string, unknown>): Record<string, unknown>;
1042
1163
 
1164
+ /** User identity for collaborative cursor presence. */
1165
+ interface RichTextAwarenessUser {
1166
+ name: string;
1167
+ color: string;
1168
+ avatar?: string;
1169
+ }
1170
+ /** Remote collaborator cursor for a richtext field. */
1171
+ interface RichTextCursorInfo {
1172
+ clientId: number;
1173
+ userName: string;
1174
+ color: string;
1175
+ anchor: number;
1176
+ head: number;
1177
+ }
1178
+ /** Minimal sync surface required for richtext collaboration. */
1179
+ interface RichTextSyncEngine {
1180
+ getRichtextDocChannel?(): RichTextDocChannel;
1181
+ getAwarenessManager(): RichTextAwarenessManager;
1182
+ }
1183
+ interface RichTextDocChannel {
1184
+ shouldUseChannel(snapshotBytes: number, useDocChannel?: boolean): boolean;
1185
+ send(collection: string, recordId: string, field: string, update: Uint8Array): void;
1186
+ subscribe(collection: string, recordId: string, field: string, listener: (update: Uint8Array) => void): () => void;
1187
+ }
1188
+ interface RichTextAwarenessManager {
1189
+ clientId: number;
1190
+ getLocalState(): RichTextAwarenessState | null;
1191
+ setLocalState(state: RichTextAwarenessState): void;
1192
+ getStates(): Map<number, RichTextAwarenessState>;
1193
+ on(event: 'change', listener: () => void): () => void;
1194
+ }
1195
+ interface RichTextAwarenessState {
1196
+ user: RichTextAwarenessUser;
1197
+ cursor?: {
1198
+ collection: string;
1199
+ recordId: string;
1200
+ field: string;
1201
+ anchor: number;
1202
+ head: number;
1203
+ };
1204
+ }
1205
+ interface CreateRichTextControllerOptions {
1206
+ collection: CollectionAccessor;
1207
+ collectionName: string;
1208
+ recordId: string;
1209
+ fieldName: string;
1210
+ store: {
1211
+ collection(name: string): CollectionAccessor;
1212
+ };
1213
+ syncEngine?: RichTextSyncEngine | null;
1214
+ useDocChannel?: boolean;
1215
+ user?: RichTextAwarenessUser;
1216
+ }
1217
+ interface RichTextControllerSnapshot {
1218
+ ready: boolean;
1219
+ error: Error | null;
1220
+ canUndo: boolean;
1221
+ canRedo: boolean;
1222
+ cursors: readonly RichTextCursorInfo[];
1223
+ }
1224
+ interface RichTextController {
1225
+ readonly doc: yjs.Doc;
1226
+ readonly text: yjs.Text;
1227
+ getSnapshot(): RichTextControllerSnapshot;
1228
+ subscribe(listener: () => void): () => void;
1229
+ undo(): void;
1230
+ redo(): void;
1231
+ setCursor(anchor: number, head: number): void;
1232
+ clearCursor(): void;
1233
+ setUser(user: RichTextAwarenessUser | undefined): void;
1234
+ destroy(): void;
1235
+ }
1236
+
1237
+ /**
1238
+ * Framework-agnostic richtext binding: Yjs document lifecycle, persistence,
1239
+ * incremental doc channel, and awareness cursors.
1240
+ */
1241
+ declare function createRichTextController(options: CreateRichTextControllerOptions): RichTextController;
1242
+
1243
+ /**
1244
+ * Narrows a sync engine reference to the richtext controller surface.
1245
+ * SyncEngine satisfies this interface at runtime; types live in different packages.
1246
+ */
1247
+ declare function asRichTextSyncEngine(engine: unknown): RichTextSyncEngine | null;
1248
+
1043
1249
  type RichtextInput = string | Uint8Array | ArrayBuffer | null | undefined;
1044
1250
  /**
1045
1251
  * Encodes richtext values into Yjs document updates.
1252
+ *
1253
+ * Also accepts the tagged `{ $koraBytes }` form that binary richtext values
1254
+ * take inside `op.data` (and, for old dev databases, the pre-fix numeric-key
1255
+ * object shape), so every consumer that writes op-data values to columns
1256
+ * inherits the decoding. Plain strings keep their exact historical behavior:
1257
+ * encoded as a fresh Yjs document containing the string.
1046
1258
  */
1047
- declare function encodeRichtext(value: RichtextInput): Uint8Array | null;
1259
+ declare function encodeRichtext(value: RichtextInput | KoraBytesValue): Uint8Array | null;
1048
1260
  /**
1049
1261
  * Decodes driver-provided richtext values into Uint8Array.
1050
1262
  */
@@ -1145,4 +1357,4 @@ declare function readAuditExportManifest(data: Uint8Array): AuditExportManifest;
1145
1357
  */
1146
1358
  declare function verifyAuditExportChecksum(data: Uint8Array): Promise<boolean>;
1147
1359
 
1148
- export { AdapterError, ApplyRemoteOptions, type AuditExportManifest, type AuditExportOptions, type AuditExportPayload, type AuditExportProgress, type AuditTraceQuery, type BackupManifest, type BackupOptions, type BackupProgress, COMPACTION_BASELINE_META_KEY, type CollectionAccessor, CollectionRecord, type CompactionResult, type CompactionStrategy, InvalidStateTransitionError, LAST_ACKED_SERVER_VECTOR_META_KEY, LocalMutationHandler, MaterializedRowSnapshot, OrderByDirection, type PersistedAuditTrace, PersistenceError, QueryBuilder, QueryDescriptor, QueryError, RecordNotFoundError, type ReplaySnapshot, type RestoreOptions, type RestoreResult, SequenceManager, StorageAdapter, Store, StoreConfig, StoreNotOpenError, SubscriptionBloomFilter, SubscriptionCallback, SubscriptionManager, type SubscriptionManagerOptions, type SubscriptionStats, type TransactionCollectionAccessor, TransactionContext, type TransactionContextConfig, WhereClause, WorkerInitError, WorkerTimeoutError, collectOperationsAheadOfServer, compactOperationLog, computeAckCompactionWatermark, decodeAuditExport, decodeRichtext, deserializeVersionVectorFromMeta, encodeRichtext, exportBackup, mergeVersionVectors, persistedAuditTraceFromEvent, pluralize, readAuditExportManifest, readBackupManifest, restoreBackup, richtextStatesEqual, richtextToPlainText, serializeVersionVectorToMeta, singularize, validateStateTransition, validateUpdateStateMachine, verifyAuditExportChecksum, verifyBackupChecksum };
1360
+ export { AdapterError, ApplyRemoteOptions, type AuditExportManifest, type AuditExportOptions, type AuditExportPayload, type AuditExportProgress, type AuditTraceQuery, type BackupManifest, type BackupOptions, type BackupProgress, COMPACTION_BASELINE_META_KEY, type ClockRebaseResult, type CollectionAccessor, CollectionRecord, type CompactionResult, type CompactionStrategy, type CreateRichTextControllerOptions, InvalidStateTransitionError, LAST_ACKED_SERVER_VECTOR_META_KEY, LocalMutationHandler, MaterializedRowSnapshot, OptimisticLockError, OrderByDirection, type PersistedAuditTrace, PersistenceError, QueryBuilder, QueryDescriptor, QueryError, QueryStore, QueryStoreCache, RecordNotFoundError, type ReplaySnapshot, type RestoreOptions, type RestoreResult, type RichTextAwarenessUser, type RichTextController, type RichTextControllerSnapshot, type RichTextCursorInfo, type RichTextSyncEngine, RowVersionState, SequenceManager, StorageAdapter, Store, StoreConfig, StoreNotOpenError, SubscriptionBloomFilter, SubscriptionCallback, SubscriptionManager, type SubscriptionManagerOptions, type SubscriptionStats, type TransactionCollectionAccessor, TransactionContext, type TransactionContextConfig, WhereClause, WorkerInitError, WorkerTimeoutError, asRichTextSyncEngine, assertQueryReady, collectOperationsAheadOfServer, compactOperationLog, computeAckCompactionWatermark, createRichTextController, decodeAuditExport, decodeRichtext, deserializeVersionVectorFromMeta, encodeRichtext, exportBackup, getSharedQueryStoreCache, mergeVersionVectors, persistedAuditTraceFromEvent, pluralize, readAuditExportManifest, readBackupManifest, restoreBackup, richtextStatesEqual, richtextToPlainText, serializeVersionVectorToMeta, singularize, validateStateTransition, validateUpdateStateMachine, verifyAuditExportChecksum, verifyBackupChecksum };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,10 @@
1
- import { C as CollectionRecord, S as StorageAdapter, Q as QueryDescriptor, c as SubscriptionCallback, W as WhereClause, d as OrderByDirection, L as LocalMutationHandler, e as StoreConfig, A as ApplyRemoteOptions, f as MaterializedRowSnapshot } from './types-DepnRDk_.js';
2
- export { M as MigrationPlan, g as OrderByClause, h as StoreIsolation, T as Transaction, i as TransactionBufferedEntry, j as TransactionCommitBatch, k as TransactionCommitResult, l as WhereOperators } from './types-DepnRDk_.js';
3
- import { Operation, KoraError, VersionVector, SchemaDefinition, MergeTrace, CollectionDefinition, SequenceConfig, HybridLogicalClock, CausalTracker, OperationLog, ApplyResult, StateMachineDefinition, KoraEvent } from '@korajs/core';
1
+ import { C as CollectionRecord, S as StorageAdapter, Q as QueryDescriptor, c as SubscriptionCallback, W as WhereClause, d as OrderByDirection, L as LocalMutationHandler, e as StoreConfig, A as ApplyRemoteOptions, f as MaterializedRowSnapshot, g as RowVersionState } from './types-CJOVKxLw.js';
2
+ export { M as MigrationPlan, h as OrderByClause, i as StoreIsolation, T as Transaction, j as TransactionBufferedEntry, k as TransactionCommitBatch, l as TransactionCommitResult, m as WhereOperators } from './types-CJOVKxLw.js';
3
+ import { Operation, KoraError, VersionVector, SchemaDefinition, HLCTimestamp, MergeTrace, CollectionDefinition, SequenceConfig, HybridLogicalClock, CausalTracker, OperationLog, ApplyResult, StateMachineDefinition, KoraBytesValue, KoraEvent } from '@korajs/core';
4
4
  export { ApplyResult } from '@korajs/core';
5
- import { R as RelationEnforcer, L as LocalMutationContext } from './types-BMyHmwvn.js';
6
- export { C as Collection } from './types-BMyHmwvn.js';
5
+ import { R as RelationEnforcer, L as LocalMutationContext } from './types-DAxCladl.js';
6
+ export { C as Collection } from './types-DAxCladl.js';
7
+ import * as yjs from 'yjs';
7
8
 
8
9
  /**
9
10
  * In-memory materialized state at a causal cut in the operation log.
@@ -32,6 +33,18 @@ declare class QueryError extends KoraError {
32
33
  declare class RecordNotFoundError extends KoraError {
33
34
  constructor(collection: string, recordId: string);
34
35
  }
36
+ /**
37
+ * Thrown inside `applyRemoteOperation` when the row's version state changed
38
+ * between the caller's snapshot read and the guarded write (a concurrent local
39
+ * mutation landed in the window). The transaction is rolled back with nothing
40
+ * written; the caller re-reads fresh state, recomputes its merge, and retries.
41
+ * This is the optimistic-concurrency guard that keeps merge-engine results
42
+ * (richtext, add-wins arrays, constraint resolutions) from clobbering newer
43
+ * local edits they never saw.
44
+ */
45
+ declare class OptimisticLockError extends KoraError {
46
+ constructor(collection: string, recordId: string);
47
+ }
35
48
  /**
36
49
  * Thrown when a storage adapter operation fails.
37
50
  */
@@ -100,6 +113,20 @@ declare function computeAckCompactionWatermark(serverVector: VersionVector): Ver
100
113
  */
101
114
  declare function compactOperationLog(adapter: StorageAdapter, schema: SchemaDefinition, strategy: CompactionStrategy, serverVector: VersionVector): Promise<CompactionResult>;
102
115
 
116
+ /**
117
+ * Result of re-stamping unsynced operations after a clock correction.
118
+ */
119
+ interface ClockRebaseResult {
120
+ /** The rewritten operations, in causal order (original HLC total order preserved). */
121
+ operations: Operation[];
122
+ /** Maps each old (pre-rebase) operation id to its new content-addressed id. */
123
+ idMapping: Record<string, string>;
124
+ /** Number of operations that were re-stamped. */
125
+ rebasedCount: number;
126
+ /** Highest timestamp assigned during the rebase, or null when nothing was rebased. */
127
+ newMaxTimestamp: HLCTimestamp | null;
128
+ }
129
+
103
130
  declare const LAST_ACKED_SERVER_VECTOR_META_KEY = "last_acked_server_vector";
104
131
  /**
105
132
  * Serialize a version vector for `_kora_meta` storage.
@@ -714,6 +741,12 @@ declare class Store implements OperationLog {
714
741
  /**
715
742
  * Get the node ID for this store instance.
716
743
  */
744
+ /**
745
+ * Records the offset between server time and this device's clock
746
+ * (serverTime - localTime), learned at sync handshake. Lets the store's HLC
747
+ * validate remote timestamps correctly even when the local clock is wrong.
748
+ */
749
+ setClockReferenceOffset(offsetMs: number): void;
717
750
  getNodeId(): string;
718
751
  /**
719
752
  * Apply a remote operation received from sync.
@@ -721,6 +754,18 @@ declare class Store implements OperationLog {
721
754
  * and updates the version vector.
722
755
  */
723
756
  applyRemoteOperation(op: Operation, options?: ApplyRemoteOptions): Promise<ApplyResult>;
757
+ /**
758
+ * Materialize update/delete operations that were logged for a record BEFORE
759
+ * its insert arrived (reordered delivery). Runs inside the insert's write
760
+ * transaction, folding each orphan in timestamp order through the exact
761
+ * per-field LWW / LWW-delete rules the normal apply paths use, so the final
762
+ * row state equals what a device that received the operations in causal
763
+ * order computed. Called only when the insert created a previously-absent
764
+ * row — in that case every logged update/delete for the record is
765
+ * necessarily an orphan (rows are soft-deleted, never removed, so a row
766
+ * that is absent now was never materialized).
767
+ */
768
+ private foldOrphanedOperations;
724
769
  /**
725
770
  * Get operations from a node within a sequence number range.
726
771
  * Implements the OperationLog interface for computeDelta.
@@ -774,6 +819,14 @@ declare class Store implements OperationLog {
774
819
  * Load a materialized row by ID, including soft-deleted tombstones.
775
820
  */
776
821
  findMaterializedRow(collection: string, recordId: string): Promise<MaterializedRowSnapshot | null>;
822
+ /**
823
+ * Raw version state of a materialized row, for optimistic-concurrency guarded
824
+ * applies: capture this snapshot before computing a merge, pass it as
825
+ * `guardRowState`, and the apply refuses to write if the row changed since.
826
+ * Returns null when no row exists (which is itself a valid guard state — the
827
+ * apply then requires the row to still be absent).
828
+ */
829
+ getRowVersionState(collection: string, recordId: string): Promise<RowVersionState | null>;
777
830
  /**
778
831
  * Latest operation from this device for a record (used for delete-vs-update merge on sync).
779
832
  */
@@ -797,6 +850,18 @@ declare class Store implements OperationLog {
797
850
  * Local operations not yet reflected on the server version vector.
798
851
  */
799
852
  getUnsyncedOperations(serverVector: VersionVector): Promise<Operation[]>;
853
+ /**
854
+ * Re-stamp never-acknowledged local operations after a fast device clock was
855
+ * corrected, so sync can resume immediately. Safe because unacknowledged
856
+ * operations are private to this device (like unpushed git commits);
857
+ * acknowledged/shared operations are immutable and never touched.
858
+ *
859
+ * After the rewrite the store's HLC is advanced past the highest new
860
+ * timestamp so subsequent local writes keep sorting after the rebased ops.
861
+ * No data-change notifications are emitted: materialized values are
862
+ * unchanged, only version stamps move.
863
+ */
864
+ rebaseUnsyncedOperations(unsyncedOpIds: string[], correctedNowMs: number): Promise<ClockRebaseResult>;
800
865
  /**
801
866
  * Count of local operations ahead of the server version vector.
802
867
  */
@@ -896,6 +961,62 @@ interface CollectionAccessor {
896
961
  where(conditions: Record<string, unknown>): QueryBuilder;
897
962
  }
898
963
 
964
+ /**
965
+ * Bridges the async QueryBuilder.subscribe() API with synchronous snapshot reads
966
+ * required by React useSyncExternalStore, Vue composables, and Svelte stores.
967
+ *
968
+ * Uses lazy subscription: the underlying query subscription starts when the first
969
+ * listener attaches and stops when the last listener detaches.
970
+ */
971
+ declare class QueryStore<T = CollectionRecord> {
972
+ private snapshot;
973
+ private listeners;
974
+ private unsubscribeQuery;
975
+ private active;
976
+ private readonly queryBuilder;
977
+ constructor(queryBuilder: QueryBuilder<T>);
978
+ /**
979
+ * Subscribe to snapshot changes.
980
+ *
981
+ * @returns Unsubscribe function
982
+ */
983
+ subscribe: (onStoreChange: () => void) => (() => void);
984
+ /** Synchronous read of the latest query results. */
985
+ getSnapshot: () => readonly T[];
986
+ /** Tear down listeners and the underlying query subscription. */
987
+ destroy(): void;
988
+ private startSubscription;
989
+ private stopSubscription;
990
+ private notifyListeners;
991
+ }
992
+
993
+ /**
994
+ * Ensures a query builder is backed by an open store (not a legacy pending placeholder).
995
+ */
996
+ declare function assertQueryReady(query: QueryBuilder<unknown>): void;
997
+
998
+ /**
999
+ * Reference-counted cache of {@link QueryStore} instances keyed by query descriptor.
1000
+ * Identical queries from different components share one underlying subscription.
1001
+ */
1002
+ declare class QueryStoreCache {
1003
+ private readonly scopeKey;
1004
+ private entries;
1005
+ constructor(scopeKey?: string);
1006
+ getOrCreate<T>(queryBuilder: QueryBuilder<T>): QueryStore<T>;
1007
+ release(queryBuilder: QueryBuilder<unknown>): void;
1008
+ clear(): void;
1009
+ get size(): number;
1010
+ private getKey;
1011
+ }
1012
+ /**
1013
+ * Process-wide shared query store cache for framework bindings.
1014
+ *
1015
+ * @deprecated Prefer {@link KoraApp.getQueryStoreCache} via `KoraProvider` context
1016
+ * so each app instance owns an isolated cache.
1017
+ */
1018
+ declare function getSharedQueryStoreCache(): QueryStoreCache;
1019
+
899
1020
  /**
900
1021
  * Bloom filter for subscription dependency tracking.
901
1022
  *
@@ -1040,11 +1161,102 @@ declare function validateStateTransition(collectionName: string, recordId: strin
1040
1161
  */
1041
1162
  declare function validateUpdateStateMachine(collectionName: string, recordId: string, collectionDef: CollectionDefinition, currentRecord: Record<string, unknown>, updateData: Record<string, unknown>): Record<string, unknown>;
1042
1163
 
1164
+ /** User identity for collaborative cursor presence. */
1165
+ interface RichTextAwarenessUser {
1166
+ name: string;
1167
+ color: string;
1168
+ avatar?: string;
1169
+ }
1170
+ /** Remote collaborator cursor for a richtext field. */
1171
+ interface RichTextCursorInfo {
1172
+ clientId: number;
1173
+ userName: string;
1174
+ color: string;
1175
+ anchor: number;
1176
+ head: number;
1177
+ }
1178
+ /** Minimal sync surface required for richtext collaboration. */
1179
+ interface RichTextSyncEngine {
1180
+ getRichtextDocChannel?(): RichTextDocChannel;
1181
+ getAwarenessManager(): RichTextAwarenessManager;
1182
+ }
1183
+ interface RichTextDocChannel {
1184
+ shouldUseChannel(snapshotBytes: number, useDocChannel?: boolean): boolean;
1185
+ send(collection: string, recordId: string, field: string, update: Uint8Array): void;
1186
+ subscribe(collection: string, recordId: string, field: string, listener: (update: Uint8Array) => void): () => void;
1187
+ }
1188
+ interface RichTextAwarenessManager {
1189
+ clientId: number;
1190
+ getLocalState(): RichTextAwarenessState | null;
1191
+ setLocalState(state: RichTextAwarenessState): void;
1192
+ getStates(): Map<number, RichTextAwarenessState>;
1193
+ on(event: 'change', listener: () => void): () => void;
1194
+ }
1195
+ interface RichTextAwarenessState {
1196
+ user: RichTextAwarenessUser;
1197
+ cursor?: {
1198
+ collection: string;
1199
+ recordId: string;
1200
+ field: string;
1201
+ anchor: number;
1202
+ head: number;
1203
+ };
1204
+ }
1205
+ interface CreateRichTextControllerOptions {
1206
+ collection: CollectionAccessor;
1207
+ collectionName: string;
1208
+ recordId: string;
1209
+ fieldName: string;
1210
+ store: {
1211
+ collection(name: string): CollectionAccessor;
1212
+ };
1213
+ syncEngine?: RichTextSyncEngine | null;
1214
+ useDocChannel?: boolean;
1215
+ user?: RichTextAwarenessUser;
1216
+ }
1217
+ interface RichTextControllerSnapshot {
1218
+ ready: boolean;
1219
+ error: Error | null;
1220
+ canUndo: boolean;
1221
+ canRedo: boolean;
1222
+ cursors: readonly RichTextCursorInfo[];
1223
+ }
1224
+ interface RichTextController {
1225
+ readonly doc: yjs.Doc;
1226
+ readonly text: yjs.Text;
1227
+ getSnapshot(): RichTextControllerSnapshot;
1228
+ subscribe(listener: () => void): () => void;
1229
+ undo(): void;
1230
+ redo(): void;
1231
+ setCursor(anchor: number, head: number): void;
1232
+ clearCursor(): void;
1233
+ setUser(user: RichTextAwarenessUser | undefined): void;
1234
+ destroy(): void;
1235
+ }
1236
+
1237
+ /**
1238
+ * Framework-agnostic richtext binding: Yjs document lifecycle, persistence,
1239
+ * incremental doc channel, and awareness cursors.
1240
+ */
1241
+ declare function createRichTextController(options: CreateRichTextControllerOptions): RichTextController;
1242
+
1243
+ /**
1244
+ * Narrows a sync engine reference to the richtext controller surface.
1245
+ * SyncEngine satisfies this interface at runtime; types live in different packages.
1246
+ */
1247
+ declare function asRichTextSyncEngine(engine: unknown): RichTextSyncEngine | null;
1248
+
1043
1249
  type RichtextInput = string | Uint8Array | ArrayBuffer | null | undefined;
1044
1250
  /**
1045
1251
  * Encodes richtext values into Yjs document updates.
1252
+ *
1253
+ * Also accepts the tagged `{ $koraBytes }` form that binary richtext values
1254
+ * take inside `op.data` (and, for old dev databases, the pre-fix numeric-key
1255
+ * object shape), so every consumer that writes op-data values to columns
1256
+ * inherits the decoding. Plain strings keep their exact historical behavior:
1257
+ * encoded as a fresh Yjs document containing the string.
1046
1258
  */
1047
- declare function encodeRichtext(value: RichtextInput): Uint8Array | null;
1259
+ declare function encodeRichtext(value: RichtextInput | KoraBytesValue): Uint8Array | null;
1048
1260
  /**
1049
1261
  * Decodes driver-provided richtext values into Uint8Array.
1050
1262
  */
@@ -1145,4 +1357,4 @@ declare function readAuditExportManifest(data: Uint8Array): AuditExportManifest;
1145
1357
  */
1146
1358
  declare function verifyAuditExportChecksum(data: Uint8Array): Promise<boolean>;
1147
1359
 
1148
- export { AdapterError, ApplyRemoteOptions, type AuditExportManifest, type AuditExportOptions, type AuditExportPayload, type AuditExportProgress, type AuditTraceQuery, type BackupManifest, type BackupOptions, type BackupProgress, COMPACTION_BASELINE_META_KEY, type CollectionAccessor, CollectionRecord, type CompactionResult, type CompactionStrategy, InvalidStateTransitionError, LAST_ACKED_SERVER_VECTOR_META_KEY, LocalMutationHandler, MaterializedRowSnapshot, OrderByDirection, type PersistedAuditTrace, PersistenceError, QueryBuilder, QueryDescriptor, QueryError, RecordNotFoundError, type ReplaySnapshot, type RestoreOptions, type RestoreResult, SequenceManager, StorageAdapter, Store, StoreConfig, StoreNotOpenError, SubscriptionBloomFilter, SubscriptionCallback, SubscriptionManager, type SubscriptionManagerOptions, type SubscriptionStats, type TransactionCollectionAccessor, TransactionContext, type TransactionContextConfig, WhereClause, WorkerInitError, WorkerTimeoutError, collectOperationsAheadOfServer, compactOperationLog, computeAckCompactionWatermark, decodeAuditExport, decodeRichtext, deserializeVersionVectorFromMeta, encodeRichtext, exportBackup, mergeVersionVectors, persistedAuditTraceFromEvent, pluralize, readAuditExportManifest, readBackupManifest, restoreBackup, richtextStatesEqual, richtextToPlainText, serializeVersionVectorToMeta, singularize, validateStateTransition, validateUpdateStateMachine, verifyAuditExportChecksum, verifyBackupChecksum };
1360
+ export { AdapterError, ApplyRemoteOptions, type AuditExportManifest, type AuditExportOptions, type AuditExportPayload, type AuditExportProgress, type AuditTraceQuery, type BackupManifest, type BackupOptions, type BackupProgress, COMPACTION_BASELINE_META_KEY, type ClockRebaseResult, type CollectionAccessor, CollectionRecord, type CompactionResult, type CompactionStrategy, type CreateRichTextControllerOptions, InvalidStateTransitionError, LAST_ACKED_SERVER_VECTOR_META_KEY, LocalMutationHandler, MaterializedRowSnapshot, OptimisticLockError, OrderByDirection, type PersistedAuditTrace, PersistenceError, QueryBuilder, QueryDescriptor, QueryError, QueryStore, QueryStoreCache, RecordNotFoundError, type ReplaySnapshot, type RestoreOptions, type RestoreResult, type RichTextAwarenessUser, type RichTextController, type RichTextControllerSnapshot, type RichTextCursorInfo, type RichTextSyncEngine, RowVersionState, SequenceManager, StorageAdapter, Store, StoreConfig, StoreNotOpenError, SubscriptionBloomFilter, SubscriptionCallback, SubscriptionManager, type SubscriptionManagerOptions, type SubscriptionStats, type TransactionCollectionAccessor, TransactionContext, type TransactionContextConfig, WhereClause, WorkerInitError, WorkerTimeoutError, asRichTextSyncEngine, assertQueryReady, collectOperationsAheadOfServer, compactOperationLog, computeAckCompactionWatermark, createRichTextController, decodeAuditExport, decodeRichtext, deserializeVersionVectorFromMeta, encodeRichtext, exportBackup, getSharedQueryStoreCache, mergeVersionVectors, persistedAuditTraceFromEvent, pluralize, readAuditExportManifest, readBackupManifest, restoreBackup, richtextStatesEqual, richtextToPlainText, serializeVersionVectorToMeta, singularize, validateStateTransition, validateUpdateStateMachine, verifyAuditExportChecksum, verifyBackupChecksum };