@korajs/store 1.0.0-beta.12 → 1.0.0-beta.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/README.md +29 -37
  2. package/dist/adapters/better-sqlite3.cjs +90 -4
  3. package/dist/adapters/better-sqlite3.cjs.map +1 -1
  4. package/dist/adapters/better-sqlite3.d.cts +21 -12
  5. package/dist/adapters/better-sqlite3.d.ts +21 -12
  6. package/dist/adapters/better-sqlite3.js +69 -5
  7. package/dist/adapters/better-sqlite3.js.map +1 -1
  8. package/dist/adapters/indexeddb.cjs +1316 -610
  9. package/dist/adapters/indexeddb.cjs.map +1 -1
  10. package/dist/adapters/indexeddb.d.cts +29 -3
  11. package/dist/adapters/indexeddb.d.ts +29 -3
  12. package/dist/adapters/indexeddb.js +247 -221
  13. package/dist/adapters/indexeddb.js.map +1 -1
  14. package/dist/adapters/sqlite-wasm-worker.cjs +856 -63
  15. package/dist/adapters/sqlite-wasm-worker.cjs.map +1 -1
  16. package/dist/adapters/sqlite-wasm-worker.js +529 -63
  17. package/dist/adapters/sqlite-wasm-worker.js.map +1 -1
  18. package/dist/adapters/sqlite-wasm.cjs +1032 -344
  19. package/dist/adapters/sqlite-wasm.cjs.map +1 -1
  20. package/dist/adapters/sqlite-wasm.d.cts +239 -18
  21. package/dist/adapters/sqlite-wasm.d.ts +239 -18
  22. package/dist/adapters/sqlite-wasm.js +111 -5
  23. package/dist/adapters/sqlite-wasm.js.map +1 -1
  24. package/dist/backup-IHKVMUAM.js +25 -0
  25. package/dist/chunk-5AQSGMJV.js +5471 -0
  26. package/dist/chunk-5AQSGMJV.js.map +1 -0
  27. package/dist/chunk-5JMGMPUE.js +591 -0
  28. package/dist/chunk-5JMGMPUE.js.map +1 -0
  29. package/dist/{chunk-CYO3YKFL.js → chunk-5ZRRF6R2.js} +2 -2
  30. package/dist/chunk-A6GFH5IG.js +36 -0
  31. package/dist/chunk-A6GFH5IG.js.map +1 -0
  32. package/dist/chunk-BILSA6XZ.js +608 -0
  33. package/dist/chunk-BILSA6XZ.js.map +1 -0
  34. package/dist/chunk-BQSIL6TT.js +1095 -0
  35. package/dist/chunk-BQSIL6TT.js.map +1 -0
  36. package/dist/{chunk-GSJ7EEUC.js → chunk-J5AGXF3D.js} +58 -9
  37. package/dist/chunk-J5AGXF3D.js.map +1 -0
  38. package/dist/chunk-KK3GBONL.js +112 -0
  39. package/dist/chunk-KK3GBONL.js.map +1 -0
  40. package/dist/chunk-KTVNANUH.js +876 -0
  41. package/dist/chunk-KTVNANUH.js.map +1 -0
  42. package/dist/chunk-QW4UU7NG.js +1003 -0
  43. package/dist/chunk-QW4UU7NG.js.map +1 -0
  44. package/dist/chunk-SRHDJF5B.js +186 -0
  45. package/dist/chunk-SRHDJF5B.js.map +1 -0
  46. package/dist/chunk-UC2LGFPV.js +810 -0
  47. package/dist/chunk-UC2LGFPV.js.map +1 -0
  48. package/dist/{export-audit-ODGQTQIK.js → export-audit-J2QXRHD2.js} +3 -3
  49. package/dist/index.cjs +9533 -4246
  50. package/dist/index.cjs.map +1 -1
  51. package/dist/index.d.cts +1143 -110
  52. package/dist/index.d.ts +1143 -110
  53. package/dist/index.js +298 -3191
  54. package/dist/index.js.map +1 -1
  55. package/dist/internal.cjs +581 -264
  56. package/dist/internal.cjs.map +1 -1
  57. package/dist/internal.d.cts +15 -85
  58. package/dist/internal.d.ts +15 -85
  59. package/dist/internal.js +10 -8
  60. package/dist/restore-MNDPR557.js +299 -0
  61. package/dist/restore-MNDPR557.js.map +1 -0
  62. package/dist/sql-builder-D0tC3sv9.d.cts +615 -0
  63. package/dist/sql-builder-D5UMzHEo.d.ts +615 -0
  64. package/dist/sqlite-wasm-channel-DVX93Uiq.d.cts +141 -0
  65. package/dist/sqlite-wasm-channel-DVX93Uiq.d.ts +141 -0
  66. package/dist/sqlite-wasm-channel-QCCNBYCU.js +12 -0
  67. package/dist/store-HNGX4KTT.js +16 -0
  68. package/dist/store-HNGX4KTT.js.map +1 -0
  69. package/dist/{types-BslDOTk0.d.ts → types-Dh-xyUjP.d.cts} +99 -26
  70. package/dist/{types-BslDOTk0.d.cts → types-Dh-xyUjP.d.ts} +99 -26
  71. package/package.json +3 -2
  72. package/dist/backup-5S7K2Z35.js +0 -14
  73. package/dist/chunk-GSJ7EEUC.js.map +0 -1
  74. package/dist/chunk-HS6DBMST.js +0 -383
  75. package/dist/chunk-HS6DBMST.js.map +0 -1
  76. package/dist/chunk-RJIEKEN4.js +0 -751
  77. package/dist/chunk-RJIEKEN4.js.map +0 -1
  78. package/dist/chunk-TUR7RGEP.js +0 -84
  79. package/dist/chunk-TUR7RGEP.js.map +0 -1
  80. package/dist/chunk-VDBJQ75K.js +0 -78
  81. package/dist/chunk-VDBJQ75K.js.map +0 -1
  82. package/dist/chunk-YT7MRFCP.js +0 -687
  83. package/dist/chunk-YT7MRFCP.js.map +0 -1
  84. package/dist/sqlite-wasm-channel-GGHA2BXU.js +0 -12
  85. package/dist/sqlite-wasm-channel-L6o3bWQK.d.cts +0 -92
  86. package/dist/sqlite-wasm-channel-L6o3bWQK.d.ts +0 -92
  87. package/dist/types-BOO9EPt1.d.ts +0 -170
  88. package/dist/types-C_GDavG-.d.cts +0 -170
  89. /package/dist/{backup-5S7K2Z35.js.map → backup-IHKVMUAM.js.map} +0 -0
  90. /package/dist/{chunk-CYO3YKFL.js.map → chunk-5ZRRF6R2.js.map} +0 -0
  91. /package/dist/{export-audit-ODGQTQIK.js.map → export-audit-J2QXRHD2.js.map} +0 -0
  92. /package/dist/{sqlite-wasm-channel-GGHA2BXU.js.map → sqlite-wasm-channel-QCCNBYCU.js.map} +0 -0
@@ -0,0 +1,141 @@
1
+ /** Fields every request carries besides its type. */
2
+ interface RequestMeta {
3
+ /** Correlates a worker response with its request on one postMessage channel. */
4
+ id: number;
5
+ /**
6
+ * Stable id for de-duplication. A follower retrying a request (after a
7
+ * `LeaderUnresponsiveError`, say) sends the same id and the leader worker
8
+ * answers from its response cache instead of applying it twice.
9
+ */
10
+ requestId?: string;
11
+ }
12
+ /**
13
+ * Request message sent from the main thread (or a follower tab) to the SQLite
14
+ * WASM worker.
15
+ */
16
+ type WorkerRequest = (RequestMeta & {
17
+ type: 'open';
18
+ ddlStatements: string[];
19
+ dbName?: string;
20
+ /**
21
+ * `memory` opens an in-memory database without touching OPFS (the
22
+ * IndexedDB adapter persists snapshots itself). Defaults to `opfs`.
23
+ */
24
+ storage?: 'opfs' | 'memory';
25
+ /** Fail with `NOT_FOUND` instead of creating the database file. */
26
+ mustExist?: boolean;
27
+ }) | (RequestMeta & {
28
+ type: 'close';
29
+ }) | (RequestMeta & {
30
+ type: 'destroy';
31
+ }) | (RequestMeta & {
32
+ type: 'execute';
33
+ sql: string;
34
+ params?: unknown[];
35
+ }) | (RequestMeta & {
36
+ type: 'query';
37
+ sql: string;
38
+ params?: unknown[];
39
+ }) | (RequestMeta & {
40
+ type: 'begin';
41
+ }) | (RequestMeta & {
42
+ type: 'commit';
43
+ }) | (RequestMeta & {
44
+ type: 'rollback';
45
+ }) | (RequestMeta & {
46
+ type: 'migrate';
47
+ from: number;
48
+ to: number;
49
+ statements: string[];
50
+ }) | (RequestMeta & {
51
+ type: 'export';
52
+ }) | (RequestMeta & {
53
+ type: 'import';
54
+ data: Uint8Array;
55
+ }) | (RequestMeta & {
56
+ /**
57
+ * Leader worker only: answer follower RPC, pings and heartbeats on this
58
+ * BroadcastChannel directly, so a hung leader main thread never blocks
59
+ * other tabs (NEW-STORE-9).
60
+ */
61
+ type: 'serve';
62
+ channelName: string;
63
+ });
64
+ /**
65
+ * Response message sent from the worker back to the main thread.
66
+ * Matches the request `id` for correlation.
67
+ */
68
+ type WorkerResponse = {
69
+ id: number;
70
+ type: 'success';
71
+ data?: unknown;
72
+ } | {
73
+ id: number;
74
+ type: 'error';
75
+ message: string;
76
+ code: string;
77
+ context?: Record<string, unknown>;
78
+ };
79
+ /**
80
+ * Unsolicited status the worker reports while it works on a request, such as
81
+ * waiting for another holder of the OPFS pool (a blocking state the app shows).
82
+ */
83
+ type WorkerStatusEvent = {
84
+ kind: 'storage-blocked';
85
+ resource: 'pool' | 'legacy-pool';
86
+ poolName: string;
87
+ } | {
88
+ kind: 'storage-unblocked';
89
+ resource: 'pool' | 'legacy-pool';
90
+ poolName: string;
91
+ waitedMs: number;
92
+ };
93
+ /** Per-request options accepted by bridges. */
94
+ interface WorkerSendOptions {
95
+ /** Cancels the wait for the response (the worker may still apply the request). */
96
+ signal?: AbortSignal;
97
+ /** Overrides the bridge timeout for this request; `Infinity` disables it. */
98
+ timeoutMs?: number;
99
+ }
100
+ /**
101
+ * Abstraction over the communication channel with the SQLite WASM worker.
102
+ * In browsers, this is backed by a real Web Worker via MessagePort.
103
+ * In Node.js tests, this is backed by better-sqlite3 via MockWorkerBridge.
104
+ */
105
+ interface WorkerBridge {
106
+ /** Send a request to the worker and wait for a response. */
107
+ send(request: WorkerRequest, clientId?: string, options?: WorkerSendOptions): Promise<WorkerResponse>;
108
+ /** Terminate the worker. Safe to call multiple times. */
109
+ terminate(): void;
110
+ }
111
+
112
+ /** Options for {@link WebWorkerBridge}. */
113
+ interface WebWorkerBridgeOptions {
114
+ /** Receives status events (blocking state) the worker reports. */
115
+ onEvent?: (event: WorkerStatusEvent) => void;
116
+ }
117
+ /**
118
+ * WorkerBridge implementation for browser environments.
119
+ * Communicates with an actual Web Worker running SQLite WASM.
120
+ */
121
+ declare class WebWorkerBridge implements WorkerBridge {
122
+ private worker;
123
+ private pending;
124
+ private nextId;
125
+ private terminated;
126
+ private timeoutMs;
127
+ /**
128
+ * @param workerUrl - URL to the sqlite-wasm-worker script
129
+ * @param timeoutMs - Timeout for worker responses in milliseconds (default: 30000)
130
+ * @param options - Status event listener
131
+ */
132
+ constructor(workerUrl: string | URL, timeoutMs?: number, options?: WebWorkerBridgeOptions);
133
+ /** True once {@link terminate} ran. */
134
+ isTerminated(): boolean;
135
+ /** Post a request without awaiting a response (fire-and-forget control messages). */
136
+ post(request: WorkerRequest, clientId?: string): void;
137
+ send(request: WorkerRequest, clientId?: string, options?: WorkerSendOptions): Promise<WorkerResponse>;
138
+ terminate(): void;
139
+ }
140
+
141
+ export { type WorkerBridge as W, WebWorkerBridge as a, type WorkerRequest as b, type WorkerResponse as c, type WorkerSendOptions as d, type WorkerStatusEvent as e };
@@ -0,0 +1,141 @@
1
+ /** Fields every request carries besides its type. */
2
+ interface RequestMeta {
3
+ /** Correlates a worker response with its request on one postMessage channel. */
4
+ id: number;
5
+ /**
6
+ * Stable id for de-duplication. A follower retrying a request (after a
7
+ * `LeaderUnresponsiveError`, say) sends the same id and the leader worker
8
+ * answers from its response cache instead of applying it twice.
9
+ */
10
+ requestId?: string;
11
+ }
12
+ /**
13
+ * Request message sent from the main thread (or a follower tab) to the SQLite
14
+ * WASM worker.
15
+ */
16
+ type WorkerRequest = (RequestMeta & {
17
+ type: 'open';
18
+ ddlStatements: string[];
19
+ dbName?: string;
20
+ /**
21
+ * `memory` opens an in-memory database without touching OPFS (the
22
+ * IndexedDB adapter persists snapshots itself). Defaults to `opfs`.
23
+ */
24
+ storage?: 'opfs' | 'memory';
25
+ /** Fail with `NOT_FOUND` instead of creating the database file. */
26
+ mustExist?: boolean;
27
+ }) | (RequestMeta & {
28
+ type: 'close';
29
+ }) | (RequestMeta & {
30
+ type: 'destroy';
31
+ }) | (RequestMeta & {
32
+ type: 'execute';
33
+ sql: string;
34
+ params?: unknown[];
35
+ }) | (RequestMeta & {
36
+ type: 'query';
37
+ sql: string;
38
+ params?: unknown[];
39
+ }) | (RequestMeta & {
40
+ type: 'begin';
41
+ }) | (RequestMeta & {
42
+ type: 'commit';
43
+ }) | (RequestMeta & {
44
+ type: 'rollback';
45
+ }) | (RequestMeta & {
46
+ type: 'migrate';
47
+ from: number;
48
+ to: number;
49
+ statements: string[];
50
+ }) | (RequestMeta & {
51
+ type: 'export';
52
+ }) | (RequestMeta & {
53
+ type: 'import';
54
+ data: Uint8Array;
55
+ }) | (RequestMeta & {
56
+ /**
57
+ * Leader worker only: answer follower RPC, pings and heartbeats on this
58
+ * BroadcastChannel directly, so a hung leader main thread never blocks
59
+ * other tabs (NEW-STORE-9).
60
+ */
61
+ type: 'serve';
62
+ channelName: string;
63
+ });
64
+ /**
65
+ * Response message sent from the worker back to the main thread.
66
+ * Matches the request `id` for correlation.
67
+ */
68
+ type WorkerResponse = {
69
+ id: number;
70
+ type: 'success';
71
+ data?: unknown;
72
+ } | {
73
+ id: number;
74
+ type: 'error';
75
+ message: string;
76
+ code: string;
77
+ context?: Record<string, unknown>;
78
+ };
79
+ /**
80
+ * Unsolicited status the worker reports while it works on a request, such as
81
+ * waiting for another holder of the OPFS pool (a blocking state the app shows).
82
+ */
83
+ type WorkerStatusEvent = {
84
+ kind: 'storage-blocked';
85
+ resource: 'pool' | 'legacy-pool';
86
+ poolName: string;
87
+ } | {
88
+ kind: 'storage-unblocked';
89
+ resource: 'pool' | 'legacy-pool';
90
+ poolName: string;
91
+ waitedMs: number;
92
+ };
93
+ /** Per-request options accepted by bridges. */
94
+ interface WorkerSendOptions {
95
+ /** Cancels the wait for the response (the worker may still apply the request). */
96
+ signal?: AbortSignal;
97
+ /** Overrides the bridge timeout for this request; `Infinity` disables it. */
98
+ timeoutMs?: number;
99
+ }
100
+ /**
101
+ * Abstraction over the communication channel with the SQLite WASM worker.
102
+ * In browsers, this is backed by a real Web Worker via MessagePort.
103
+ * In Node.js tests, this is backed by better-sqlite3 via MockWorkerBridge.
104
+ */
105
+ interface WorkerBridge {
106
+ /** Send a request to the worker and wait for a response. */
107
+ send(request: WorkerRequest, clientId?: string, options?: WorkerSendOptions): Promise<WorkerResponse>;
108
+ /** Terminate the worker. Safe to call multiple times. */
109
+ terminate(): void;
110
+ }
111
+
112
+ /** Options for {@link WebWorkerBridge}. */
113
+ interface WebWorkerBridgeOptions {
114
+ /** Receives status events (blocking state) the worker reports. */
115
+ onEvent?: (event: WorkerStatusEvent) => void;
116
+ }
117
+ /**
118
+ * WorkerBridge implementation for browser environments.
119
+ * Communicates with an actual Web Worker running SQLite WASM.
120
+ */
121
+ declare class WebWorkerBridge implements WorkerBridge {
122
+ private worker;
123
+ private pending;
124
+ private nextId;
125
+ private terminated;
126
+ private timeoutMs;
127
+ /**
128
+ * @param workerUrl - URL to the sqlite-wasm-worker script
129
+ * @param timeoutMs - Timeout for worker responses in milliseconds (default: 30000)
130
+ * @param options - Status event listener
131
+ */
132
+ constructor(workerUrl: string | URL, timeoutMs?: number, options?: WebWorkerBridgeOptions);
133
+ /** True once {@link terminate} ran. */
134
+ isTerminated(): boolean;
135
+ /** Post a request without awaiting a response (fire-and-forget control messages). */
136
+ post(request: WorkerRequest, clientId?: string): void;
137
+ send(request: WorkerRequest, clientId?: string, options?: WorkerSendOptions): Promise<WorkerResponse>;
138
+ terminate(): void;
139
+ }
140
+
141
+ export { type WorkerBridge as W, WebWorkerBridge as a, type WorkerRequest as b, type WorkerResponse as c, type WorkerSendOptions as d, type WorkerStatusEvent as e };
@@ -0,0 +1,12 @@
1
+ import {
2
+ WebWorkerBridge
3
+ } from "./chunk-KK3GBONL.js";
4
+ import {
5
+ Mutex
6
+ } from "./chunk-TVJEC5S2.js";
7
+ import "./chunk-SRHDJF5B.js";
8
+ export {
9
+ Mutex,
10
+ WebWorkerBridge
11
+ };
12
+ //# sourceMappingURL=sqlite-wasm-channel-QCCNBYCU.js.map
@@ -0,0 +1,16 @@
1
+ import {
2
+ ReservedNodeIdError,
3
+ Store
4
+ } from "./chunk-5AQSGMJV.js";
5
+ import "./chunk-BILSA6XZ.js";
6
+ import "./chunk-A6GFH5IG.js";
7
+ import "./chunk-UC2LGFPV.js";
8
+ import "./chunk-KTVNANUH.js";
9
+ import "./chunk-QW4UU7NG.js";
10
+ import "./chunk-J5AGXF3D.js";
11
+ import "./chunk-SRHDJF5B.js";
12
+ export {
13
+ ReservedNodeIdError,
14
+ Store
15
+ };
16
+ //# sourceMappingURL=store-HNGX4KTT.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -1,4 +1,5 @@
1
- import { HLCTimestamp, SchemaDefinition, Operation, KoraEventEmitter, SecretKeyProvider } from '@korajs/core';
1
+ import * as _korajs_core from '@korajs/core';
2
+ import { HLCTimestamp, SchemaDefinition, Operation, KoraEventEmitter, SecretKeyProvider, OperationTransform } from '@korajs/core';
2
3
 
3
4
  /**
4
5
  * Transaction interface for executing multiple operations atomically.
@@ -22,6 +23,14 @@ interface StorageOpenState {
22
23
  mode: 'opfs' | 'indexeddb' | 'memory' | 'native' | 'unknown';
23
24
  /** Present when a primary storage mode degraded during open. */
24
25
  fallbackReason?: StorageFallbackReason;
26
+ /** OPFS pool that owns the database file (one pool per database), when known. */
27
+ poolName?: string;
28
+ /**
29
+ * SQLite journal mode the database actually runs with, when known (NEW-STORE-11).
30
+ * OPFS (opfs-sahpool): `delete`, the rollback journal; WAL is not available on
31
+ * that VFS. In-memory fallback: `memory`. Native better-sqlite3: `wal`.
32
+ */
33
+ journalMode?: string;
25
34
  }
26
35
  /**
27
36
  * Storage adapter interface. All storage backends must implement this.
@@ -46,39 +55,32 @@ interface StorageAdapter {
46
55
  * fallback before app code starts reading or writing user data.
47
56
  */
48
57
  getStorageOpenState?(): StorageOpenState | null;
58
+ /**
59
+ * Optional durability barrier (RT-35): resolve once every write committed before the
60
+ * call is durable, reject when it cannot be made durable. Adapters whose commits are
61
+ * durable when they resolve (SQLite on a file or OPFS) omit it; an adapter that
62
+ * persists asynchronously (the IndexedDB snapshot fallback) must implement it.
63
+ */
64
+ ensureDurable?(): Promise<void>;
49
65
  }
50
66
  /**
51
- * Buffered SQL + operation produced during a transaction (before commit).
52
- */
53
- interface TransactionBufferedEntry {
54
- operation: Operation;
55
- commands: Array<{
56
- sql: string;
57
- params: unknown[];
58
- }>;
59
- collection: string;
60
- }
61
- /**
62
- * Batch passed to {@link LocalMutationHandler.commitTransaction} on commit.
67
+ * Routes local mutations through a unified apply pipeline (korajs ApplyPipeline).
68
+ *
69
+ * Every local write — including each entry of an `app.transaction` commit — is
70
+ * built and persisted by the store's single local write path; the handler adds
71
+ * pipeline behaviour around it.
63
72
  */
64
- interface TransactionCommitBatch {
65
- entries: TransactionBufferedEntry[];
66
- transactionId: string;
67
- mutationName?: string;
68
- }
69
- interface TransactionCommitResult {
70
- operations: Operation[];
71
- affectedCollections: Set<string>;
72
- }
73
73
  interface LocalMutationHandler {
74
74
  insert(collection: string, data: Record<string, unknown>): Promise<CollectionRecord>;
75
75
  update(collection: string, id: string, data: Record<string, unknown>): Promise<CollectionRecord>;
76
76
  delete(collection: string, id: string): Promise<void>;
77
77
  /**
78
- * Commit a buffered transaction through the unified apply pipeline.
79
- * When omitted, the store uses the built-in SQL commit path.
78
+ * Called inside the write transaction for every local delete (single-record,
79
+ * transactional and cascaded), after the delete operation is built and before
80
+ * referential side effects run. Reads must go through `tx`. Throwing aborts
81
+ * the whole write.
80
82
  */
81
- commitTransaction?(batch: TransactionCommitBatch): Promise<TransactionCommitResult>;
83
+ beforeLocalDelete?(operation: Operation, tx: Transaction): Promise<void>;
82
84
  }
83
85
  type StoreIsolation = 'shared' | 'per-tab';
84
86
  interface StoreConfig {
@@ -104,6 +106,30 @@ interface StoreConfig {
104
106
  * Required only when the schema declares encrypted secret fields.
105
107
  */
106
108
  secretKeyProvider?: SecretKeyProvider;
109
+ /**
110
+ * How rows are materialized. `'fold'` (default, W7): every write merges into the
111
+ * record's per-field CRDT fold state, which re-materializes the row. `'legacy'`:
112
+ * the beta.12 per-field LWW write paths (used by `experimental.legacyMerge` for
113
+ * one beta, to compare). Switching modes on an existing database re-materializes
114
+ * it on open.
115
+ */
116
+ materialization?: 'fold' | 'legacy';
117
+ /**
118
+ * Schema transforms applied at fold time (RT-84): an operation authored under an
119
+ * older schema version is stored exactly as written and folded as
120
+ * `operationSchemaView(op, schema.version, transforms)` reads it. Pass the same list
121
+ * as the sync engine's `operationTransforms` (`createApp` does). Transforms must be
122
+ * pure and deterministic; they are part of the fold plan fingerprint, so changing
123
+ * them re-folds once.
124
+ */
125
+ operationTransforms?: readonly OperationTransform[];
126
+ /**
127
+ * Largest serialized operation a local write may produce, in bytes (RT-86). Set it to
128
+ * the sync server's `maxOperationBytes`. A write over it is refused with
129
+ * `OperationTooLargeError` and nothing is written. Default 256 KiB, the server's
130
+ * default.
131
+ */
132
+ maxOperationBytes?: number;
107
133
  }
108
134
  /**
109
135
  * Operators for where clause conditions.
@@ -150,6 +176,38 @@ interface QueryDescriptor {
150
176
  * Callback for reactive subscriptions. Receives the current result set.
151
177
  */
152
178
  type SubscriptionCallback<T> = (results: T[]) => void;
179
+ /**
180
+ * Where a subscription failure happened.
181
+ * - `initial`: the first run of the query, before any result was delivered.
182
+ * - `refresh`: a re-run after a write to a collection the query reads.
183
+ * - `callback`: the query succeeded but the subscriber's own callback threw.
184
+ */
185
+ type QueryErrorPhase = 'initial' | 'refresh' | 'callback';
186
+ /**
187
+ * A failure delivered to a subscriber's `onError` (STORE-12). The previous result
188
+ * set stays current; the subscription stays registered and is re-run on the next
189
+ * write, and a later success is delivered even if it equals the last results.
190
+ */
191
+ interface QuerySubscriptionError {
192
+ /** The error thrown by the query or the callback. */
193
+ error: Error;
194
+ phase: QueryErrorPhase;
195
+ /** The collection the query reads. */
196
+ collection: string;
197
+ /** Subscription id, the same `queryId` carried by the `query:error` event. */
198
+ queryId: string;
199
+ }
200
+ /**
201
+ * Options for `QueryBuilder.subscribe()`.
202
+ */
203
+ interface SubscribeOptions {
204
+ /**
205
+ * Called when the query fails (or the callback throws) instead of the failure
206
+ * becoming an unhandled rejection. Every failure is also emitted as a
207
+ * `query:error` event; without `onError` it is additionally logged.
208
+ */
209
+ onError?: (failure: QuerySubscriptionError) => void;
210
+ }
153
211
  /**
154
212
  * Serialized row in the operations log table.
155
213
  */
@@ -198,12 +256,27 @@ interface Subscription<T = CollectionRecord> {
198
256
  callback: SubscriptionCallback<T>;
199
257
  executeFn: () => Promise<T[]>;
200
258
  lastResults: T[];
259
+ /** Result-set comparator (structural, per field kind). */
260
+ resultsEqual?: (prev: readonly T[], next: readonly T[]) => boolean;
261
+ onError?: (failure: QuerySubscriptionError) => void;
262
+ /** Monotonic run counter: a slower, older run never overwrites a newer result. */
263
+ runsStarted?: number;
264
+ lastAppliedRun?: number;
265
+ /** True after a failure until the next successful delivery. */
266
+ errored?: boolean;
267
+ /** True once a result set has been delivered to the callback. */
268
+ delivered?: boolean;
201
269
  }
202
270
 
203
271
  /**
204
272
  * Options for applying a remote operation to materialized storage.
205
273
  */
206
274
  interface ApplyRemoteOptions {
275
+ /**
276
+ * W7: receives the merge traces of the operation (conflicting field decisions),
277
+ * after commit. The store also emits them as `merge:*` events.
278
+ */
279
+ onMergeTraces?: (traces: _korajs_core.MergeTrace[]) => void;
207
280
  /** When true, a winning remote update clears soft-delete on the row. */
208
281
  reactivateIfDeleted?: boolean;
209
282
  /**
@@ -280,4 +353,4 @@ interface VersionVectorRow {
280
353
  sequence_number: number;
281
354
  }
282
355
 
283
- export type { ApplyRemoteOptions as A, CollectionRecord as C, LocalMutationHandler as L, MigrationPlan as M, OperationRow as O, QueryDescriptor as Q, RawCollectionRow as R, StorageAdapter as S, Transaction as T, VersionVectorRow as V, WhereClause as W, StorageOpenState as a, MetaRow as b, Subscription as c, SubscriptionCallback as d, OrderByDirection as e, StoreConfig as f, MaterializedRowSnapshot as g, RowVersionState as h, OrderByClause as i, StorageFallbackReason as j, StoreIsolation as k, TransactionBufferedEntry as l, TransactionCommitBatch as m, TransactionCommitResult as n, WhereOperators as o };
356
+ export type { ApplyRemoteOptions as A, CollectionRecord as C, LocalMutationHandler as L, MigrationPlan as M, OperationRow as O, QueryDescriptor as Q, RawCollectionRow as R, StorageAdapter as S, Transaction as T, VersionVectorRow as V, WhereClause as W, StorageOpenState as a, MetaRow as b, Subscription as c, QuerySubscriptionError as d, SubscriptionCallback as e, SubscribeOptions as f, OrderByDirection as g, StoreConfig as h, MaterializedRowSnapshot as i, RowVersionState as j, OrderByClause as k, QueryErrorPhase as l, StorageFallbackReason as m, StoreIsolation as n, WhereOperators as o };
@@ -1,4 +1,5 @@
1
- import { HLCTimestamp, SchemaDefinition, Operation, KoraEventEmitter, SecretKeyProvider } from '@korajs/core';
1
+ import * as _korajs_core from '@korajs/core';
2
+ import { HLCTimestamp, SchemaDefinition, Operation, KoraEventEmitter, SecretKeyProvider, OperationTransform } from '@korajs/core';
2
3
 
3
4
  /**
4
5
  * Transaction interface for executing multiple operations atomically.
@@ -22,6 +23,14 @@ interface StorageOpenState {
22
23
  mode: 'opfs' | 'indexeddb' | 'memory' | 'native' | 'unknown';
23
24
  /** Present when a primary storage mode degraded during open. */
24
25
  fallbackReason?: StorageFallbackReason;
26
+ /** OPFS pool that owns the database file (one pool per database), when known. */
27
+ poolName?: string;
28
+ /**
29
+ * SQLite journal mode the database actually runs with, when known (NEW-STORE-11).
30
+ * OPFS (opfs-sahpool): `delete`, the rollback journal; WAL is not available on
31
+ * that VFS. In-memory fallback: `memory`. Native better-sqlite3: `wal`.
32
+ */
33
+ journalMode?: string;
25
34
  }
26
35
  /**
27
36
  * Storage adapter interface. All storage backends must implement this.
@@ -46,39 +55,32 @@ interface StorageAdapter {
46
55
  * fallback before app code starts reading or writing user data.
47
56
  */
48
57
  getStorageOpenState?(): StorageOpenState | null;
58
+ /**
59
+ * Optional durability barrier (RT-35): resolve once every write committed before the
60
+ * call is durable, reject when it cannot be made durable. Adapters whose commits are
61
+ * durable when they resolve (SQLite on a file or OPFS) omit it; an adapter that
62
+ * persists asynchronously (the IndexedDB snapshot fallback) must implement it.
63
+ */
64
+ ensureDurable?(): Promise<void>;
49
65
  }
50
66
  /**
51
- * Buffered SQL + operation produced during a transaction (before commit).
52
- */
53
- interface TransactionBufferedEntry {
54
- operation: Operation;
55
- commands: Array<{
56
- sql: string;
57
- params: unknown[];
58
- }>;
59
- collection: string;
60
- }
61
- /**
62
- * Batch passed to {@link LocalMutationHandler.commitTransaction} on commit.
67
+ * Routes local mutations through a unified apply pipeline (korajs ApplyPipeline).
68
+ *
69
+ * Every local write — including each entry of an `app.transaction` commit — is
70
+ * built and persisted by the store's single local write path; the handler adds
71
+ * pipeline behaviour around it.
63
72
  */
64
- interface TransactionCommitBatch {
65
- entries: TransactionBufferedEntry[];
66
- transactionId: string;
67
- mutationName?: string;
68
- }
69
- interface TransactionCommitResult {
70
- operations: Operation[];
71
- affectedCollections: Set<string>;
72
- }
73
73
  interface LocalMutationHandler {
74
74
  insert(collection: string, data: Record<string, unknown>): Promise<CollectionRecord>;
75
75
  update(collection: string, id: string, data: Record<string, unknown>): Promise<CollectionRecord>;
76
76
  delete(collection: string, id: string): Promise<void>;
77
77
  /**
78
- * Commit a buffered transaction through the unified apply pipeline.
79
- * When omitted, the store uses the built-in SQL commit path.
78
+ * Called inside the write transaction for every local delete (single-record,
79
+ * transactional and cascaded), after the delete operation is built and before
80
+ * referential side effects run. Reads must go through `tx`. Throwing aborts
81
+ * the whole write.
80
82
  */
81
- commitTransaction?(batch: TransactionCommitBatch): Promise<TransactionCommitResult>;
83
+ beforeLocalDelete?(operation: Operation, tx: Transaction): Promise<void>;
82
84
  }
83
85
  type StoreIsolation = 'shared' | 'per-tab';
84
86
  interface StoreConfig {
@@ -104,6 +106,30 @@ interface StoreConfig {
104
106
  * Required only when the schema declares encrypted secret fields.
105
107
  */
106
108
  secretKeyProvider?: SecretKeyProvider;
109
+ /**
110
+ * How rows are materialized. `'fold'` (default, W7): every write merges into the
111
+ * record's per-field CRDT fold state, which re-materializes the row. `'legacy'`:
112
+ * the beta.12 per-field LWW write paths (used by `experimental.legacyMerge` for
113
+ * one beta, to compare). Switching modes on an existing database re-materializes
114
+ * it on open.
115
+ */
116
+ materialization?: 'fold' | 'legacy';
117
+ /**
118
+ * Schema transforms applied at fold time (RT-84): an operation authored under an
119
+ * older schema version is stored exactly as written and folded as
120
+ * `operationSchemaView(op, schema.version, transforms)` reads it. Pass the same list
121
+ * as the sync engine's `operationTransforms` (`createApp` does). Transforms must be
122
+ * pure and deterministic; they are part of the fold plan fingerprint, so changing
123
+ * them re-folds once.
124
+ */
125
+ operationTransforms?: readonly OperationTransform[];
126
+ /**
127
+ * Largest serialized operation a local write may produce, in bytes (RT-86). Set it to
128
+ * the sync server's `maxOperationBytes`. A write over it is refused with
129
+ * `OperationTooLargeError` and nothing is written. Default 256 KiB, the server's
130
+ * default.
131
+ */
132
+ maxOperationBytes?: number;
107
133
  }
108
134
  /**
109
135
  * Operators for where clause conditions.
@@ -150,6 +176,38 @@ interface QueryDescriptor {
150
176
  * Callback for reactive subscriptions. Receives the current result set.
151
177
  */
152
178
  type SubscriptionCallback<T> = (results: T[]) => void;
179
+ /**
180
+ * Where a subscription failure happened.
181
+ * - `initial`: the first run of the query, before any result was delivered.
182
+ * - `refresh`: a re-run after a write to a collection the query reads.
183
+ * - `callback`: the query succeeded but the subscriber's own callback threw.
184
+ */
185
+ type QueryErrorPhase = 'initial' | 'refresh' | 'callback';
186
+ /**
187
+ * A failure delivered to a subscriber's `onError` (STORE-12). The previous result
188
+ * set stays current; the subscription stays registered and is re-run on the next
189
+ * write, and a later success is delivered even if it equals the last results.
190
+ */
191
+ interface QuerySubscriptionError {
192
+ /** The error thrown by the query or the callback. */
193
+ error: Error;
194
+ phase: QueryErrorPhase;
195
+ /** The collection the query reads. */
196
+ collection: string;
197
+ /** Subscription id, the same `queryId` carried by the `query:error` event. */
198
+ queryId: string;
199
+ }
200
+ /**
201
+ * Options for `QueryBuilder.subscribe()`.
202
+ */
203
+ interface SubscribeOptions {
204
+ /**
205
+ * Called when the query fails (or the callback throws) instead of the failure
206
+ * becoming an unhandled rejection. Every failure is also emitted as a
207
+ * `query:error` event; without `onError` it is additionally logged.
208
+ */
209
+ onError?: (failure: QuerySubscriptionError) => void;
210
+ }
153
211
  /**
154
212
  * Serialized row in the operations log table.
155
213
  */
@@ -198,12 +256,27 @@ interface Subscription<T = CollectionRecord> {
198
256
  callback: SubscriptionCallback<T>;
199
257
  executeFn: () => Promise<T[]>;
200
258
  lastResults: T[];
259
+ /** Result-set comparator (structural, per field kind). */
260
+ resultsEqual?: (prev: readonly T[], next: readonly T[]) => boolean;
261
+ onError?: (failure: QuerySubscriptionError) => void;
262
+ /** Monotonic run counter: a slower, older run never overwrites a newer result. */
263
+ runsStarted?: number;
264
+ lastAppliedRun?: number;
265
+ /** True after a failure until the next successful delivery. */
266
+ errored?: boolean;
267
+ /** True once a result set has been delivered to the callback. */
268
+ delivered?: boolean;
201
269
  }
202
270
 
203
271
  /**
204
272
  * Options for applying a remote operation to materialized storage.
205
273
  */
206
274
  interface ApplyRemoteOptions {
275
+ /**
276
+ * W7: receives the merge traces of the operation (conflicting field decisions),
277
+ * after commit. The store also emits them as `merge:*` events.
278
+ */
279
+ onMergeTraces?: (traces: _korajs_core.MergeTrace[]) => void;
207
280
  /** When true, a winning remote update clears soft-delete on the row. */
208
281
  reactivateIfDeleted?: boolean;
209
282
  /**
@@ -280,4 +353,4 @@ interface VersionVectorRow {
280
353
  sequence_number: number;
281
354
  }
282
355
 
283
- export type { ApplyRemoteOptions as A, CollectionRecord as C, LocalMutationHandler as L, MigrationPlan as M, OperationRow as O, QueryDescriptor as Q, RawCollectionRow as R, StorageAdapter as S, Transaction as T, VersionVectorRow as V, WhereClause as W, StorageOpenState as a, MetaRow as b, Subscription as c, SubscriptionCallback as d, OrderByDirection as e, StoreConfig as f, MaterializedRowSnapshot as g, RowVersionState as h, OrderByClause as i, StorageFallbackReason as j, StoreIsolation as k, TransactionBufferedEntry as l, TransactionCommitBatch as m, TransactionCommitResult as n, WhereOperators as o };
356
+ export type { ApplyRemoteOptions as A, CollectionRecord as C, LocalMutationHandler as L, MigrationPlan as M, OperationRow as O, QueryDescriptor as Q, RawCollectionRow as R, StorageAdapter as S, Transaction as T, VersionVectorRow as V, WhereClause as W, StorageOpenState as a, MetaRow as b, Subscription as c, QuerySubscriptionError as d, SubscriptionCallback as e, SubscribeOptions as f, OrderByDirection as g, StoreConfig as h, MaterializedRowSnapshot as i, RowVersionState as j, OrderByClause as k, QueryErrorPhase as l, StorageFallbackReason as m, StoreIsolation as n, WhereOperators as o };