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