@tanstack/db 0.3.1 → 0.4.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 (162) hide show
  1. package/dist/cjs/{change-events.cjs → collection/change-events.cjs} +13 -42
  2. package/dist/cjs/collection/change-events.cjs.map +1 -0
  3. package/dist/{esm/change-events.d.ts → cjs/collection/change-events.d.cts} +6 -6
  4. package/dist/cjs/collection/changes.cjs +108 -0
  5. package/dist/cjs/collection/changes.cjs.map +1 -0
  6. package/dist/cjs/collection/changes.d.cts +53 -0
  7. package/dist/cjs/collection/events.cjs +90 -0
  8. package/dist/cjs/collection/events.cjs.map +1 -0
  9. package/dist/cjs/collection/events.d.cts +53 -0
  10. package/dist/cjs/collection/index.cjs +417 -0
  11. package/dist/cjs/collection/index.cjs.map +1 -0
  12. package/dist/{esm/collection.d.ts → cjs/collection/index.d.cts} +56 -172
  13. package/dist/cjs/collection/indexes.cjs +124 -0
  14. package/dist/cjs/collection/indexes.cjs.map +1 -0
  15. package/dist/cjs/collection/indexes.d.cts +47 -0
  16. package/dist/cjs/collection/lifecycle.cjs +150 -0
  17. package/dist/cjs/collection/lifecycle.cjs.map +1 -0
  18. package/dist/cjs/collection/lifecycle.d.cts +70 -0
  19. package/dist/cjs/collection/mutations.cjs +315 -0
  20. package/dist/cjs/collection/mutations.cjs.map +1 -0
  21. package/dist/cjs/collection/mutations.d.cts +33 -0
  22. package/dist/cjs/collection/state.cjs +597 -0
  23. package/dist/cjs/collection/state.cjs.map +1 -0
  24. package/dist/cjs/collection/state.d.cts +122 -0
  25. package/dist/cjs/collection/subscription.cjs +160 -0
  26. package/dist/cjs/collection/subscription.cjs.map +1 -0
  27. package/dist/cjs/collection/subscription.d.cts +57 -0
  28. package/dist/cjs/collection/sync.cjs +154 -0
  29. package/dist/cjs/collection/sync.cjs.map +1 -0
  30. package/dist/cjs/collection/sync.d.cts +34 -0
  31. package/dist/cjs/index.cjs +8 -8
  32. package/dist/cjs/index.d.cts +2 -2
  33. package/dist/cjs/indexes/auto-index.cjs.map +1 -1
  34. package/dist/cjs/indexes/auto-index.d.cts +1 -1
  35. package/dist/cjs/indexes/base-index.cjs.map +1 -1
  36. package/dist/cjs/indexes/base-index.d.cts +2 -2
  37. package/dist/cjs/indexes/btree-index.cjs +2 -2
  38. package/dist/cjs/indexes/btree-index.cjs.map +1 -1
  39. package/dist/cjs/indexes/btree-index.d.cts +1 -1
  40. package/dist/cjs/query/builder/index.cjs +2 -2
  41. package/dist/cjs/query/builder/index.cjs.map +1 -1
  42. package/dist/cjs/query/builder/types.d.cts +1 -1
  43. package/dist/cjs/query/compiler/index.cjs +5 -2
  44. package/dist/cjs/query/compiler/index.cjs.map +1 -1
  45. package/dist/cjs/query/compiler/index.d.cts +3 -2
  46. package/dist/cjs/query/compiler/joins.cjs +22 -24
  47. package/dist/cjs/query/compiler/joins.cjs.map +1 -1
  48. package/dist/cjs/query/compiler/joins.d.cts +3 -2
  49. package/dist/cjs/query/compiler/order-by.cjs.map +1 -1
  50. package/dist/cjs/query/compiler/order-by.d.cts +1 -1
  51. package/dist/cjs/query/ir.cjs.map +1 -1
  52. package/dist/cjs/query/ir.d.cts +1 -1
  53. package/dist/cjs/query/live/collection-config-builder.cjs +29 -12
  54. package/dist/cjs/query/live/collection-config-builder.cjs.map +1 -1
  55. package/dist/cjs/query/live/collection-config-builder.d.cts +3 -0
  56. package/dist/cjs/query/live/collection-subscriber.cjs +43 -104
  57. package/dist/cjs/query/live/collection-subscriber.cjs.map +1 -1
  58. package/dist/cjs/query/live/collection-subscriber.d.cts +4 -7
  59. package/dist/cjs/query/live-query-collection.cjs +2 -2
  60. package/dist/cjs/query/live-query-collection.cjs.map +1 -1
  61. package/dist/cjs/query/live-query-collection.d.cts +1 -1
  62. package/dist/cjs/transactions.cjs +3 -3
  63. package/dist/cjs/transactions.cjs.map +1 -1
  64. package/dist/cjs/types.d.cts +12 -10
  65. package/dist/{cjs/change-events.d.cts → esm/collection/change-events.d.ts} +6 -6
  66. package/dist/esm/{change-events.js → collection/change-events.js} +13 -42
  67. package/dist/esm/collection/change-events.js.map +1 -0
  68. package/dist/esm/collection/changes.d.ts +53 -0
  69. package/dist/esm/collection/changes.js +108 -0
  70. package/dist/esm/collection/changes.js.map +1 -0
  71. package/dist/esm/collection/events.d.ts +53 -0
  72. package/dist/esm/collection/events.js +90 -0
  73. package/dist/esm/collection/events.js.map +1 -0
  74. package/dist/{cjs/collection.d.cts → esm/collection/index.d.ts} +56 -172
  75. package/dist/esm/collection/index.js +417 -0
  76. package/dist/esm/collection/index.js.map +1 -0
  77. package/dist/esm/collection/indexes.d.ts +47 -0
  78. package/dist/esm/collection/indexes.js +124 -0
  79. package/dist/esm/collection/indexes.js.map +1 -0
  80. package/dist/esm/collection/lifecycle.d.ts +70 -0
  81. package/dist/esm/collection/lifecycle.js +150 -0
  82. package/dist/esm/collection/lifecycle.js.map +1 -0
  83. package/dist/esm/collection/mutations.d.ts +33 -0
  84. package/dist/esm/collection/mutations.js +315 -0
  85. package/dist/esm/collection/mutations.js.map +1 -0
  86. package/dist/esm/collection/state.d.ts +122 -0
  87. package/dist/esm/collection/state.js +597 -0
  88. package/dist/esm/collection/state.js.map +1 -0
  89. package/dist/esm/collection/subscription.d.ts +57 -0
  90. package/dist/esm/collection/subscription.js +160 -0
  91. package/dist/esm/collection/subscription.js.map +1 -0
  92. package/dist/esm/collection/sync.d.ts +34 -0
  93. package/dist/esm/collection/sync.js +154 -0
  94. package/dist/esm/collection/sync.js.map +1 -0
  95. package/dist/esm/index.d.ts +2 -2
  96. package/dist/esm/index.js +1 -1
  97. package/dist/esm/indexes/auto-index.d.ts +1 -1
  98. package/dist/esm/indexes/auto-index.js.map +1 -1
  99. package/dist/esm/indexes/base-index.d.ts +2 -2
  100. package/dist/esm/indexes/base-index.js.map +1 -1
  101. package/dist/esm/indexes/btree-index.d.ts +1 -1
  102. package/dist/esm/indexes/btree-index.js +2 -2
  103. package/dist/esm/indexes/btree-index.js.map +1 -1
  104. package/dist/esm/proxy.js +1 -1
  105. package/dist/esm/query/builder/index.js +1 -1
  106. package/dist/esm/query/builder/index.js.map +1 -1
  107. package/dist/esm/query/builder/types.d.ts +1 -1
  108. package/dist/esm/query/compiler/index.d.ts +3 -2
  109. package/dist/esm/query/compiler/index.js +5 -2
  110. package/dist/esm/query/compiler/index.js.map +1 -1
  111. package/dist/esm/query/compiler/joins.d.ts +3 -2
  112. package/dist/esm/query/compiler/joins.js +22 -24
  113. package/dist/esm/query/compiler/joins.js.map +1 -1
  114. package/dist/esm/query/compiler/order-by.d.ts +1 -1
  115. package/dist/esm/query/compiler/order-by.js.map +1 -1
  116. package/dist/esm/query/ir.d.ts +1 -1
  117. package/dist/esm/query/ir.js.map +1 -1
  118. package/dist/esm/query/live/collection-config-builder.d.ts +3 -0
  119. package/dist/esm/query/live/collection-config-builder.js +29 -12
  120. package/dist/esm/query/live/collection-config-builder.js.map +1 -1
  121. package/dist/esm/query/live/collection-subscriber.d.ts +4 -7
  122. package/dist/esm/query/live/collection-subscriber.js +43 -104
  123. package/dist/esm/query/live/collection-subscriber.js.map +1 -1
  124. package/dist/esm/query/live-query-collection.d.ts +1 -1
  125. package/dist/esm/query/live-query-collection.js +1 -1
  126. package/dist/esm/query/live-query-collection.js.map +1 -1
  127. package/dist/esm/transactions.js +3 -3
  128. package/dist/esm/transactions.js.map +1 -1
  129. package/dist/esm/types.d.ts +12 -10
  130. package/package.json +2 -2
  131. package/src/{change-events.ts → collection/change-events.ts} +25 -39
  132. package/src/collection/changes.ts +163 -0
  133. package/src/collection/events.ts +171 -0
  134. package/src/collection/index.ts +808 -0
  135. package/src/collection/indexes.ts +172 -0
  136. package/src/collection/lifecycle.ts +221 -0
  137. package/src/collection/mutations.ts +535 -0
  138. package/src/collection/state.ts +866 -0
  139. package/src/collection/subscription.ts +239 -0
  140. package/src/collection/sync.ts +235 -0
  141. package/src/index.ts +2 -2
  142. package/src/indexes/auto-index.ts +1 -1
  143. package/src/indexes/base-index.ts +3 -3
  144. package/src/indexes/btree-index.ts +2 -2
  145. package/src/query/builder/index.ts +1 -1
  146. package/src/query/builder/types.ts +1 -1
  147. package/src/query/compiler/index.ts +7 -1
  148. package/src/query/compiler/joins.ts +28 -41
  149. package/src/query/compiler/order-by.ts +1 -1
  150. package/src/query/ir.ts +1 -1
  151. package/src/query/live/collection-config-builder.ts +48 -22
  152. package/src/query/live/collection-subscriber.ts +63 -168
  153. package/src/query/live-query-collection.ts +2 -2
  154. package/src/transactions.ts +3 -3
  155. package/src/types.ts +14 -15
  156. package/dist/cjs/change-events.cjs.map +0 -1
  157. package/dist/cjs/collection.cjs +0 -1580
  158. package/dist/cjs/collection.cjs.map +0 -1
  159. package/dist/esm/change-events.js.map +0 -1
  160. package/dist/esm/collection.js +0 -1580
  161. package/dist/esm/collection.js.map +0 -1
  162. package/src/collection.ts +0 -2488
@@ -0,0 +1,597 @@
1
+ import { deepEquals } from "../utils.js";
2
+ import { SortedMap } from "../SortedMap.js";
3
+ class CollectionStateManager {
4
+ /**
5
+ * Creates a new CollectionState manager
6
+ */
7
+ constructor(config) {
8
+ this.pendingSyncedTransactions = [];
9
+ this.syncedMetadata = /* @__PURE__ */ new Map();
10
+ this.optimisticUpserts = /* @__PURE__ */ new Map();
11
+ this.optimisticDeletes = /* @__PURE__ */ new Set();
12
+ this.size = 0;
13
+ this.syncedKeys = /* @__PURE__ */ new Set();
14
+ this.preSyncVisibleState = /* @__PURE__ */ new Map();
15
+ this.recentlySyncedKeys = /* @__PURE__ */ new Set();
16
+ this.hasReceivedFirstCommit = false;
17
+ this.isCommittingSyncTransactions = false;
18
+ this.commitPendingTransactions = () => {
19
+ let hasPersistingTransaction = false;
20
+ for (const transaction of this.transactions.values()) {
21
+ if (transaction.state === `persisting`) {
22
+ hasPersistingTransaction = true;
23
+ break;
24
+ }
25
+ }
26
+ const {
27
+ committedSyncedTransactions,
28
+ uncommittedSyncedTransactions,
29
+ hasTruncateSync
30
+ } = this.pendingSyncedTransactions.reduce(
31
+ (acc, t) => {
32
+ if (t.committed) {
33
+ acc.committedSyncedTransactions.push(t);
34
+ if (t.truncate === true) {
35
+ acc.hasTruncateSync = true;
36
+ }
37
+ } else {
38
+ acc.uncommittedSyncedTransactions.push(t);
39
+ }
40
+ return acc;
41
+ },
42
+ {
43
+ committedSyncedTransactions: [],
44
+ uncommittedSyncedTransactions: [],
45
+ hasTruncateSync: false
46
+ }
47
+ );
48
+ if (!hasPersistingTransaction || hasTruncateSync) {
49
+ this.isCommittingSyncTransactions = true;
50
+ const changedKeys = /* @__PURE__ */ new Set();
51
+ for (const transaction of committedSyncedTransactions) {
52
+ for (const operation of transaction.operations) {
53
+ changedKeys.add(operation.key);
54
+ }
55
+ }
56
+ let currentVisibleState = this.preSyncVisibleState;
57
+ if (currentVisibleState.size === 0) {
58
+ currentVisibleState = /* @__PURE__ */ new Map();
59
+ for (const key of changedKeys) {
60
+ const currentValue = this.get(key);
61
+ if (currentValue !== void 0) {
62
+ currentVisibleState.set(key, currentValue);
63
+ }
64
+ }
65
+ }
66
+ const events = [];
67
+ const rowUpdateMode = this.config.sync.rowUpdateMode || `partial`;
68
+ for (const transaction of committedSyncedTransactions) {
69
+ if (transaction.truncate) {
70
+ for (const key of this.syncedData.keys()) {
71
+ if (this.optimisticDeletes.has(key)) continue;
72
+ const previousValue = this.optimisticUpserts.get(key) || this.syncedData.get(key);
73
+ if (previousValue !== void 0) {
74
+ events.push({ type: `delete`, key, value: previousValue });
75
+ }
76
+ }
77
+ this.syncedData.clear();
78
+ this.syncedMetadata.clear();
79
+ this.syncedKeys.clear();
80
+ for (const key of changedKeys) {
81
+ currentVisibleState.delete(key);
82
+ }
83
+ }
84
+ for (const operation of transaction.operations) {
85
+ const key = operation.key;
86
+ this.syncedKeys.add(key);
87
+ switch (operation.type) {
88
+ case `insert`:
89
+ this.syncedMetadata.set(key, operation.metadata);
90
+ break;
91
+ case `update`:
92
+ this.syncedMetadata.set(
93
+ key,
94
+ Object.assign(
95
+ {},
96
+ this.syncedMetadata.get(key),
97
+ operation.metadata
98
+ )
99
+ );
100
+ break;
101
+ case `delete`:
102
+ this.syncedMetadata.delete(key);
103
+ break;
104
+ }
105
+ switch (operation.type) {
106
+ case `insert`:
107
+ this.syncedData.set(key, operation.value);
108
+ break;
109
+ case `update`: {
110
+ if (rowUpdateMode === `partial`) {
111
+ const updatedValue = Object.assign(
112
+ {},
113
+ this.syncedData.get(key),
114
+ operation.value
115
+ );
116
+ this.syncedData.set(key, updatedValue);
117
+ } else {
118
+ this.syncedData.set(key, operation.value);
119
+ }
120
+ break;
121
+ }
122
+ case `delete`:
123
+ this.syncedData.delete(key);
124
+ break;
125
+ }
126
+ }
127
+ }
128
+ if (hasTruncateSync) {
129
+ const syncedInsertedOrUpdatedKeys = /* @__PURE__ */ new Set();
130
+ for (const t of committedSyncedTransactions) {
131
+ for (const op of t.operations) {
132
+ if (op.type === `insert` || op.type === `update`) {
133
+ syncedInsertedOrUpdatedKeys.add(op.key);
134
+ }
135
+ }
136
+ }
137
+ const reapplyUpserts = /* @__PURE__ */ new Map();
138
+ const reapplyDeletes = /* @__PURE__ */ new Set();
139
+ for (const tx of this.transactions.values()) {
140
+ if ([`completed`, `failed`].includes(tx.state)) continue;
141
+ for (const mutation of tx.mutations) {
142
+ if (!this.isThisCollection(mutation.collection) || !mutation.optimistic)
143
+ continue;
144
+ const key = mutation.key;
145
+ switch (mutation.type) {
146
+ case `insert`:
147
+ reapplyUpserts.set(key, mutation.modified);
148
+ reapplyDeletes.delete(key);
149
+ break;
150
+ case `update`: {
151
+ const base = this.syncedData.get(key);
152
+ const next = base ? Object.assign({}, base, mutation.changes) : mutation.modified;
153
+ reapplyUpserts.set(key, next);
154
+ reapplyDeletes.delete(key);
155
+ break;
156
+ }
157
+ case `delete`:
158
+ reapplyUpserts.delete(key);
159
+ reapplyDeletes.add(key);
160
+ break;
161
+ }
162
+ }
163
+ }
164
+ for (const [key, value] of reapplyUpserts) {
165
+ if (reapplyDeletes.has(key)) continue;
166
+ if (syncedInsertedOrUpdatedKeys.has(key)) {
167
+ let foundInsert = false;
168
+ for (let i = events.length - 1; i >= 0; i--) {
169
+ const evt = events[i];
170
+ if (evt.key === key && evt.type === `insert`) {
171
+ evt.value = value;
172
+ foundInsert = true;
173
+ break;
174
+ }
175
+ }
176
+ if (!foundInsert) {
177
+ events.push({ type: `insert`, key, value });
178
+ }
179
+ } else {
180
+ events.push({ type: `insert`, key, value });
181
+ }
182
+ }
183
+ if (events.length > 0 && reapplyDeletes.size > 0) {
184
+ const filtered = [];
185
+ for (const evt of events) {
186
+ if (evt.type === `insert` && reapplyDeletes.has(evt.key)) {
187
+ continue;
188
+ }
189
+ filtered.push(evt);
190
+ }
191
+ events.length = 0;
192
+ events.push(...filtered);
193
+ }
194
+ if (this.lifecycle.status !== `ready`) {
195
+ this.lifecycle.setStatus(`ready`);
196
+ }
197
+ }
198
+ this.optimisticUpserts.clear();
199
+ this.optimisticDeletes.clear();
200
+ this.isCommittingSyncTransactions = false;
201
+ for (const transaction of this.transactions.values()) {
202
+ if (![`completed`, `failed`].includes(transaction.state)) {
203
+ for (const mutation of transaction.mutations) {
204
+ if (this.isThisCollection(mutation.collection) && mutation.optimistic) {
205
+ switch (mutation.type) {
206
+ case `insert`:
207
+ case `update`:
208
+ this.optimisticUpserts.set(
209
+ mutation.key,
210
+ mutation.modified
211
+ );
212
+ this.optimisticDeletes.delete(mutation.key);
213
+ break;
214
+ case `delete`:
215
+ this.optimisticUpserts.delete(mutation.key);
216
+ this.optimisticDeletes.add(mutation.key);
217
+ break;
218
+ }
219
+ }
220
+ }
221
+ }
222
+ }
223
+ const completedOptimisticOps = /* @__PURE__ */ new Map();
224
+ for (const transaction of this.transactions.values()) {
225
+ if (transaction.state === `completed`) {
226
+ for (const mutation of transaction.mutations) {
227
+ if (this.isThisCollection(mutation.collection) && changedKeys.has(mutation.key)) {
228
+ completedOptimisticOps.set(mutation.key, {
229
+ type: mutation.type,
230
+ value: mutation.modified
231
+ });
232
+ }
233
+ }
234
+ }
235
+ }
236
+ for (const key of changedKeys) {
237
+ const previousVisibleValue = currentVisibleState.get(key);
238
+ const newVisibleValue = this.get(key);
239
+ const completedOp = completedOptimisticOps.get(key);
240
+ const isRedundantSync = completedOp && newVisibleValue !== void 0 && deepEquals(completedOp.value, newVisibleValue);
241
+ if (!isRedundantSync) {
242
+ if (previousVisibleValue === void 0 && newVisibleValue !== void 0) {
243
+ events.push({
244
+ type: `insert`,
245
+ key,
246
+ value: newVisibleValue
247
+ });
248
+ } else if (previousVisibleValue !== void 0 && newVisibleValue === void 0) {
249
+ events.push({
250
+ type: `delete`,
251
+ key,
252
+ value: previousVisibleValue
253
+ });
254
+ } else if (previousVisibleValue !== void 0 && newVisibleValue !== void 0 && !deepEquals(previousVisibleValue, newVisibleValue)) {
255
+ events.push({
256
+ type: `update`,
257
+ key,
258
+ value: newVisibleValue,
259
+ previousValue: previousVisibleValue
260
+ });
261
+ }
262
+ }
263
+ }
264
+ this.size = this.calculateSize();
265
+ if (events.length > 0) {
266
+ this.indexes.updateIndexes(events);
267
+ }
268
+ this.changes.emitEvents(events, true);
269
+ this.pendingSyncedTransactions = uncommittedSyncedTransactions;
270
+ this.preSyncVisibleState.clear();
271
+ Promise.resolve().then(() => {
272
+ this.recentlySyncedKeys.clear();
273
+ });
274
+ if (!this.hasReceivedFirstCommit) {
275
+ this.hasReceivedFirstCommit = true;
276
+ const callbacks = [...this.lifecycle.onFirstReadyCallbacks];
277
+ this.lifecycle.onFirstReadyCallbacks = [];
278
+ callbacks.forEach((callback) => callback());
279
+ }
280
+ }
281
+ };
282
+ this.config = config;
283
+ this.transactions = new SortedMap(
284
+ (a, b) => a.compareCreatedAt(b)
285
+ );
286
+ if (config.compare) {
287
+ this.syncedData = new SortedMap(config.compare);
288
+ } else {
289
+ this.syncedData = /* @__PURE__ */ new Map();
290
+ }
291
+ }
292
+ setDeps(deps) {
293
+ this.collection = deps.collection;
294
+ this.lifecycle = deps.lifecycle;
295
+ this.changes = deps.changes;
296
+ this.indexes = deps.indexes;
297
+ }
298
+ /**
299
+ * Get the current value for a key (virtual derived state)
300
+ */
301
+ get(key) {
302
+ const { optimisticDeletes, optimisticUpserts, syncedData } = this;
303
+ if (optimisticDeletes.has(key)) {
304
+ return void 0;
305
+ }
306
+ if (optimisticUpserts.has(key)) {
307
+ return optimisticUpserts.get(key);
308
+ }
309
+ return syncedData.get(key);
310
+ }
311
+ /**
312
+ * Check if a key exists in the collection (virtual derived state)
313
+ */
314
+ has(key) {
315
+ const { optimisticDeletes, optimisticUpserts, syncedData } = this;
316
+ if (optimisticDeletes.has(key)) {
317
+ return false;
318
+ }
319
+ if (optimisticUpserts.has(key)) {
320
+ return true;
321
+ }
322
+ return syncedData.has(key);
323
+ }
324
+ /**
325
+ * Get all keys (virtual derived state)
326
+ */
327
+ *keys() {
328
+ const { syncedData, optimisticDeletes, optimisticUpserts } = this;
329
+ for (const key of syncedData.keys()) {
330
+ if (!optimisticDeletes.has(key)) {
331
+ yield key;
332
+ }
333
+ }
334
+ for (const key of optimisticUpserts.keys()) {
335
+ if (!syncedData.has(key) && !optimisticDeletes.has(key)) {
336
+ yield key;
337
+ }
338
+ }
339
+ }
340
+ /**
341
+ * Get all values (virtual derived state)
342
+ */
343
+ *values() {
344
+ for (const key of this.keys()) {
345
+ const value = this.get(key);
346
+ if (value !== void 0) {
347
+ yield value;
348
+ }
349
+ }
350
+ }
351
+ /**
352
+ * Get all entries (virtual derived state)
353
+ */
354
+ *entries() {
355
+ for (const key of this.keys()) {
356
+ const value = this.get(key);
357
+ if (value !== void 0) {
358
+ yield [key, value];
359
+ }
360
+ }
361
+ }
362
+ /**
363
+ * Get all entries (virtual derived state)
364
+ */
365
+ *[Symbol.iterator]() {
366
+ for (const [key, value] of this.entries()) {
367
+ yield [key, value];
368
+ }
369
+ }
370
+ /**
371
+ * Execute a callback for each entry in the collection
372
+ */
373
+ forEach(callbackfn) {
374
+ let index = 0;
375
+ for (const [key, value] of this.entries()) {
376
+ callbackfn(value, key, index++);
377
+ }
378
+ }
379
+ /**
380
+ * Create a new array with the results of calling a function for each entry in the collection
381
+ */
382
+ map(callbackfn) {
383
+ const result = [];
384
+ let index = 0;
385
+ for (const [key, value] of this.entries()) {
386
+ result.push(callbackfn(value, key, index++));
387
+ }
388
+ return result;
389
+ }
390
+ /**
391
+ * Check if the given collection is this collection
392
+ * @param collection The collection to check
393
+ * @returns True if the given collection is this collection, false otherwise
394
+ */
395
+ isThisCollection(collection) {
396
+ return collection === this.collection;
397
+ }
398
+ /**
399
+ * Recompute optimistic state from active transactions
400
+ */
401
+ recomputeOptimisticState(triggeredByUserAction = false) {
402
+ if (this.isCommittingSyncTransactions) {
403
+ return;
404
+ }
405
+ const previousState = new Map(this.optimisticUpserts);
406
+ const previousDeletes = new Set(this.optimisticDeletes);
407
+ this.optimisticUpserts.clear();
408
+ this.optimisticDeletes.clear();
409
+ const activeTransactions = [];
410
+ for (const transaction of this.transactions.values()) {
411
+ if (![`completed`, `failed`].includes(transaction.state)) {
412
+ activeTransactions.push(transaction);
413
+ }
414
+ }
415
+ for (const transaction of activeTransactions) {
416
+ for (const mutation of transaction.mutations) {
417
+ if (this.isThisCollection(mutation.collection) && mutation.optimistic) {
418
+ switch (mutation.type) {
419
+ case `insert`:
420
+ case `update`:
421
+ this.optimisticUpserts.set(
422
+ mutation.key,
423
+ mutation.modified
424
+ );
425
+ this.optimisticDeletes.delete(mutation.key);
426
+ break;
427
+ case `delete`:
428
+ this.optimisticUpserts.delete(mutation.key);
429
+ this.optimisticDeletes.add(mutation.key);
430
+ break;
431
+ }
432
+ }
433
+ }
434
+ }
435
+ this.size = this.calculateSize();
436
+ const events = [];
437
+ this.collectOptimisticChanges(previousState, previousDeletes, events);
438
+ const filteredEventsBySyncStatus = events.filter((event) => {
439
+ if (!this.recentlySyncedKeys.has(event.key)) {
440
+ return true;
441
+ }
442
+ if (triggeredByUserAction) {
443
+ return true;
444
+ }
445
+ return false;
446
+ });
447
+ if (this.pendingSyncedTransactions.length > 0 && !triggeredByUserAction) {
448
+ const pendingSyncKeys = /* @__PURE__ */ new Set();
449
+ for (const transaction of this.pendingSyncedTransactions) {
450
+ for (const operation of transaction.operations) {
451
+ pendingSyncKeys.add(operation.key);
452
+ }
453
+ }
454
+ const filteredEvents = filteredEventsBySyncStatus.filter((event) => {
455
+ if (event.type === `delete` && pendingSyncKeys.has(event.key)) {
456
+ const hasActiveOptimisticMutation = activeTransactions.some(
457
+ (tx) => tx.mutations.some(
458
+ (m) => this.isThisCollection(m.collection) && m.key === event.key
459
+ )
460
+ );
461
+ if (!hasActiveOptimisticMutation) {
462
+ return false;
463
+ }
464
+ }
465
+ return true;
466
+ });
467
+ if (filteredEvents.length > 0) {
468
+ this.indexes.updateIndexes(filteredEvents);
469
+ }
470
+ this.changes.emitEvents(filteredEvents, triggeredByUserAction);
471
+ } else {
472
+ if (filteredEventsBySyncStatus.length > 0) {
473
+ this.indexes.updateIndexes(filteredEventsBySyncStatus);
474
+ }
475
+ this.changes.emitEvents(filteredEventsBySyncStatus, triggeredByUserAction);
476
+ }
477
+ }
478
+ /**
479
+ * Calculate the current size based on synced data and optimistic changes
480
+ */
481
+ calculateSize() {
482
+ const syncedSize = this.syncedData.size;
483
+ const deletesFromSynced = Array.from(this.optimisticDeletes).filter(
484
+ (key) => this.syncedData.has(key) && !this.optimisticUpserts.has(key)
485
+ ).length;
486
+ const upsertsNotInSynced = Array.from(this.optimisticUpserts.keys()).filter(
487
+ (key) => !this.syncedData.has(key)
488
+ ).length;
489
+ return syncedSize - deletesFromSynced + upsertsNotInSynced;
490
+ }
491
+ /**
492
+ * Collect events for optimistic changes
493
+ */
494
+ collectOptimisticChanges(previousUpserts, previousDeletes, events) {
495
+ const allKeys = /* @__PURE__ */ new Set([
496
+ ...previousUpserts.keys(),
497
+ ...this.optimisticUpserts.keys(),
498
+ ...previousDeletes,
499
+ ...this.optimisticDeletes
500
+ ]);
501
+ for (const key of allKeys) {
502
+ const currentValue = this.get(key);
503
+ const previousValue = this.getPreviousValue(
504
+ key,
505
+ previousUpserts,
506
+ previousDeletes
507
+ );
508
+ if (previousValue !== void 0 && currentValue === void 0) {
509
+ events.push({ type: `delete`, key, value: previousValue });
510
+ } else if (previousValue === void 0 && currentValue !== void 0) {
511
+ events.push({ type: `insert`, key, value: currentValue });
512
+ } else if (previousValue !== void 0 && currentValue !== void 0 && previousValue !== currentValue) {
513
+ events.push({
514
+ type: `update`,
515
+ key,
516
+ value: currentValue,
517
+ previousValue
518
+ });
519
+ }
520
+ }
521
+ }
522
+ /**
523
+ * Get the previous value for a key given previous optimistic state
524
+ */
525
+ getPreviousValue(key, previousUpserts, previousDeletes) {
526
+ if (previousDeletes.has(key)) {
527
+ return void 0;
528
+ }
529
+ if (previousUpserts.has(key)) {
530
+ return previousUpserts.get(key);
531
+ }
532
+ return this.syncedData.get(key);
533
+ }
534
+ /**
535
+ * Schedule cleanup of a transaction when it completes
536
+ */
537
+ scheduleTransactionCleanup(transaction) {
538
+ if (transaction.state === `completed`) {
539
+ this.transactions.delete(transaction.id);
540
+ return;
541
+ }
542
+ transaction.isPersisted.promise.then(() => {
543
+ this.transactions.delete(transaction.id);
544
+ }).catch(() => {
545
+ });
546
+ }
547
+ /**
548
+ * Capture visible state for keys that will be affected by pending sync operations
549
+ * This must be called BEFORE onTransactionStateChange clears optimistic state
550
+ */
551
+ capturePreSyncVisibleState() {
552
+ if (this.pendingSyncedTransactions.length === 0) return;
553
+ this.preSyncVisibleState.clear();
554
+ const syncedKeys = /* @__PURE__ */ new Set();
555
+ for (const transaction of this.pendingSyncedTransactions) {
556
+ for (const operation of transaction.operations) {
557
+ syncedKeys.add(operation.key);
558
+ }
559
+ }
560
+ for (const key of syncedKeys) {
561
+ this.recentlySyncedKeys.add(key);
562
+ }
563
+ for (const key of syncedKeys) {
564
+ const currentValue = this.get(key);
565
+ if (currentValue !== void 0) {
566
+ this.preSyncVisibleState.set(key, currentValue);
567
+ }
568
+ }
569
+ }
570
+ /**
571
+ * Trigger a recomputation when transactions change
572
+ * This method should be called by the Transaction class when state changes
573
+ */
574
+ onTransactionStateChange() {
575
+ this.changes.shouldBatchEvents = this.pendingSyncedTransactions.length > 0;
576
+ this.capturePreSyncVisibleState();
577
+ this.recomputeOptimisticState(false);
578
+ }
579
+ /**
580
+ * Clean up the collection by stopping sync and clearing data
581
+ * This can be called manually or automatically by garbage collection
582
+ */
583
+ cleanup() {
584
+ this.syncedData.clear();
585
+ this.syncedMetadata.clear();
586
+ this.optimisticUpserts.clear();
587
+ this.optimisticDeletes.clear();
588
+ this.size = 0;
589
+ this.pendingSyncedTransactions = [];
590
+ this.syncedKeys.clear();
591
+ this.hasReceivedFirstCommit = false;
592
+ }
593
+ }
594
+ export {
595
+ CollectionStateManager
596
+ };
597
+ //# sourceMappingURL=state.js.map