@tanstack/db 0.0.7 → 0.0.8
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/collection.cjs +441 -284
- package/dist/cjs/collection.cjs.map +1 -1
- package/dist/cjs/collection.d.cts +103 -30
- package/dist/cjs/proxy.cjs +2 -2
- package/dist/cjs/proxy.cjs.map +1 -1
- package/dist/cjs/query/compiled-query.cjs +23 -37
- package/dist/cjs/query/compiled-query.cjs.map +1 -1
- package/dist/cjs/query/compiled-query.d.cts +2 -2
- package/dist/cjs/query/order-by.cjs +41 -38
- package/dist/cjs/query/order-by.cjs.map +1 -1
- package/dist/cjs/query/schema.d.cts +3 -3
- package/dist/cjs/transactions.cjs +7 -6
- package/dist/cjs/transactions.cjs.map +1 -1
- package/dist/cjs/transactions.d.cts +9 -9
- package/dist/cjs/types.d.cts +28 -22
- package/dist/esm/collection.d.ts +103 -30
- package/dist/esm/collection.js +442 -285
- package/dist/esm/collection.js.map +1 -1
- package/dist/esm/proxy.js +2 -2
- package/dist/esm/proxy.js.map +1 -1
- package/dist/esm/query/compiled-query.d.ts +2 -2
- package/dist/esm/query/compiled-query.js +23 -37
- package/dist/esm/query/compiled-query.js.map +1 -1
- package/dist/esm/query/order-by.js +41 -38
- package/dist/esm/query/order-by.js.map +1 -1
- package/dist/esm/query/schema.d.ts +3 -3
- package/dist/esm/transactions.d.ts +9 -9
- package/dist/esm/transactions.js +7 -6
- package/dist/esm/transactions.js.map +1 -1
- package/dist/esm/types.d.ts +28 -22
- package/package.json +2 -2
- package/src/collection.ts +624 -372
- package/src/proxy.ts +2 -2
- package/src/query/compiled-query.ts +26 -37
- package/src/query/order-by.ts +69 -67
- package/src/query/schema.ts +3 -3
- package/src/transactions.ts +24 -22
- package/src/types.ts +44 -22
package/dist/esm/collection.js
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import { Store
|
|
1
|
+
import { Store } from "@tanstack/store";
|
|
2
2
|
import { withArrayChangeTracking, withChangeTracking } from "./proxy.js";
|
|
3
3
|
import { getActiveTransaction, Transaction } from "./transactions.js";
|
|
4
4
|
import { SortedMap } from "./SortedMap.js";
|
|
5
|
-
const collectionsStore = new
|
|
6
|
-
|
|
7
|
-
);
|
|
8
|
-
const loadingCollections = /* @__PURE__ */ new Map();
|
|
5
|
+
const collectionsStore = /* @__PURE__ */ new Map();
|
|
6
|
+
const loadingCollectionResolvers = /* @__PURE__ */ new Map();
|
|
9
7
|
function createCollection(options) {
|
|
10
8
|
const collection = new CollectionImpl(options);
|
|
11
9
|
if (options.utils) {
|
|
@@ -19,52 +17,44 @@ function preloadCollection(config) {
|
|
|
19
17
|
if (!config.id) {
|
|
20
18
|
throw new Error(`The id property is required for preloadCollection`);
|
|
21
19
|
}
|
|
22
|
-
if (collectionsStore.
|
|
20
|
+
if (collectionsStore.has(config.id) && !loadingCollectionResolvers.has(config.id)) {
|
|
23
21
|
return Promise.resolve(
|
|
24
|
-
collectionsStore.
|
|
22
|
+
collectionsStore.get(config.id)
|
|
25
23
|
);
|
|
26
24
|
}
|
|
27
|
-
if (
|
|
28
|
-
return
|
|
25
|
+
if (loadingCollectionResolvers.has(config.id)) {
|
|
26
|
+
return loadingCollectionResolvers.get(config.id).promise;
|
|
29
27
|
}
|
|
30
|
-
if (!collectionsStore.
|
|
31
|
-
collectionsStore.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
config.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
getId: config.getId,
|
|
41
|
-
sync: config.sync,
|
|
42
|
-
schema: config.schema
|
|
43
|
-
})
|
|
44
|
-
);
|
|
45
|
-
return next;
|
|
46
|
-
});
|
|
28
|
+
if (!collectionsStore.has(config.id)) {
|
|
29
|
+
collectionsStore.set(
|
|
30
|
+
config.id,
|
|
31
|
+
createCollection({
|
|
32
|
+
id: config.id,
|
|
33
|
+
getKey: config.getKey,
|
|
34
|
+
sync: config.sync,
|
|
35
|
+
schema: config.schema
|
|
36
|
+
})
|
|
37
|
+
);
|
|
47
38
|
}
|
|
48
|
-
const collection = collectionsStore.
|
|
39
|
+
const collection = collectionsStore.get(config.id);
|
|
49
40
|
let resolveFirstCommit;
|
|
50
41
|
const firstCommitPromise = new Promise((resolve) => {
|
|
51
|
-
resolveFirstCommit =
|
|
52
|
-
|
|
53
|
-
|
|
42
|
+
resolveFirstCommit = resolve;
|
|
43
|
+
});
|
|
44
|
+
loadingCollectionResolvers.set(config.id, {
|
|
45
|
+
promise: firstCommitPromise,
|
|
46
|
+
resolve: resolveFirstCommit
|
|
54
47
|
});
|
|
55
48
|
collection.onFirstCommit(() => {
|
|
56
49
|
if (!config.id) {
|
|
57
50
|
throw new Error(`The id property is required for preloadCollection`);
|
|
58
51
|
}
|
|
59
|
-
if (
|
|
60
|
-
|
|
61
|
-
|
|
52
|
+
if (loadingCollectionResolvers.has(config.id)) {
|
|
53
|
+
const resolver = loadingCollectionResolvers.get(config.id);
|
|
54
|
+
loadingCollectionResolvers.delete(config.id);
|
|
55
|
+
resolver.resolve(collection);
|
|
62
56
|
}
|
|
63
57
|
});
|
|
64
|
-
loadingCollections.set(
|
|
65
|
-
config.id,
|
|
66
|
-
firstCommitPromise
|
|
67
|
-
);
|
|
68
58
|
return firstCommitPromise;
|
|
69
59
|
}
|
|
70
60
|
class SchemaValidationError extends Error {
|
|
@@ -84,59 +74,94 @@ class CollectionImpl {
|
|
|
84
74
|
* @throws Error if sync config is missing
|
|
85
75
|
*/
|
|
86
76
|
constructor(config) {
|
|
77
|
+
this.syncedData = /* @__PURE__ */ new Map();
|
|
78
|
+
this.syncedMetadata = /* @__PURE__ */ new Map();
|
|
79
|
+
this.derivedUpserts = /* @__PURE__ */ new Map();
|
|
80
|
+
this.derivedDeletes = /* @__PURE__ */ new Set();
|
|
81
|
+
this._size = 0;
|
|
82
|
+
this.changeListeners = /* @__PURE__ */ new Set();
|
|
83
|
+
this.changeKeyListeners = /* @__PURE__ */ new Map();
|
|
87
84
|
this.utils = {};
|
|
88
|
-
this.syncedData = new Store(/* @__PURE__ */ new Map());
|
|
89
|
-
this.syncedMetadata = new Store(/* @__PURE__ */ new Map());
|
|
90
85
|
this.pendingSyncedTransactions = [];
|
|
91
86
|
this.syncedKeys = /* @__PURE__ */ new Set();
|
|
92
87
|
this.hasReceivedFirstCommit = false;
|
|
93
88
|
this.onFirstCommitCallbacks = [];
|
|
94
89
|
this.id = ``;
|
|
95
90
|
this.commitPendingTransactions = () => {
|
|
96
|
-
if (!Array.from(this.transactions.
|
|
91
|
+
if (!Array.from(this.transactions.values()).some(
|
|
97
92
|
({ state }) => state === `persisting`
|
|
98
93
|
)) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
94
|
+
const changedKeys = /* @__PURE__ */ new Set();
|
|
95
|
+
const events = [];
|
|
96
|
+
for (const transaction of this.pendingSyncedTransactions) {
|
|
97
|
+
for (const operation of transaction.operations) {
|
|
98
|
+
const key = operation.key;
|
|
99
|
+
changedKeys.add(key);
|
|
100
|
+
this.syncedKeys.add(key);
|
|
101
|
+
switch (operation.type) {
|
|
102
|
+
case `insert`:
|
|
103
|
+
this.syncedMetadata.set(key, operation.metadata);
|
|
104
|
+
break;
|
|
105
|
+
case `update`:
|
|
106
|
+
this.syncedMetadata.set(
|
|
107
|
+
key,
|
|
108
|
+
Object.assign(
|
|
109
|
+
{},
|
|
110
|
+
this.syncedMetadata.get(key),
|
|
111
|
+
operation.metadata
|
|
112
|
+
)
|
|
113
|
+
);
|
|
114
|
+
break;
|
|
115
|
+
case `delete`:
|
|
116
|
+
this.syncedMetadata.delete(key);
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
const previousValue = this.syncedData.get(key);
|
|
120
|
+
switch (operation.type) {
|
|
121
|
+
case `insert`:
|
|
122
|
+
this.syncedData.set(key, operation.value);
|
|
123
|
+
if (!this.derivedDeletes.has(key) && !this.derivedUpserts.has(key)) {
|
|
124
|
+
events.push({
|
|
125
|
+
type: `insert`,
|
|
126
|
+
key,
|
|
127
|
+
value: operation.value
|
|
128
|
+
});
|
|
117
129
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
130
|
+
break;
|
|
131
|
+
case `update`: {
|
|
132
|
+
const updatedValue = Object.assign(
|
|
133
|
+
{},
|
|
134
|
+
this.syncedData.get(key),
|
|
135
|
+
operation.value
|
|
136
|
+
);
|
|
137
|
+
this.syncedData.set(key, updatedValue);
|
|
138
|
+
if (!this.derivedDeletes.has(key) && !this.derivedUpserts.has(key)) {
|
|
139
|
+
events.push({
|
|
140
|
+
type: `update`,
|
|
141
|
+
key,
|
|
142
|
+
value: updatedValue,
|
|
143
|
+
previousValue
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
case `delete`:
|
|
149
|
+
this.syncedData.delete(key);
|
|
150
|
+
if (!this.derivedDeletes.has(key) && !this.derivedUpserts.has(key)) {
|
|
151
|
+
if (previousValue) {
|
|
152
|
+
events.push({
|
|
153
|
+
type: `delete`,
|
|
154
|
+
key,
|
|
155
|
+
value: previousValue
|
|
129
156
|
});
|
|
130
|
-
|
|
131
|
-
case `delete`:
|
|
132
|
-
prevData.delete(operation.key);
|
|
133
|
-
break;
|
|
157
|
+
}
|
|
134
158
|
}
|
|
135
|
-
|
|
136
|
-
});
|
|
159
|
+
break;
|
|
137
160
|
}
|
|
138
161
|
}
|
|
139
|
-
}
|
|
162
|
+
}
|
|
163
|
+
this._size = this.calculateSize();
|
|
164
|
+
this.emitEvents(events);
|
|
140
165
|
this.pendingSyncedTransactions = [];
|
|
141
166
|
if (!this.hasReceivedFirstCommit) {
|
|
142
167
|
this.hasReceivedFirstCommit = true;
|
|
@@ -155,22 +180,20 @@ class CollectionImpl {
|
|
|
155
180
|
}
|
|
156
181
|
const items = Array.isArray(data) ? data : [data];
|
|
157
182
|
const mutations = [];
|
|
158
|
-
|
|
159
|
-
(item) => this.generateObjectKey(this.config.getId(item), item)
|
|
160
|
-
);
|
|
161
|
-
items.forEach((item, index) => {
|
|
183
|
+
items.forEach((item) => {
|
|
162
184
|
var _a, _b;
|
|
163
185
|
const validatedData = this.validateData(item, `insert`);
|
|
164
|
-
const key =
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
throw `Cannot insert document with ID "${id}" because it already exists in the collection`;
|
|
186
|
+
const key = this.getKeyFromItem(item);
|
|
187
|
+
if (this.has(key)) {
|
|
188
|
+
throw `Cannot insert document with ID "${key}" because it already exists in the collection`;
|
|
168
189
|
}
|
|
190
|
+
const globalKey = this.generateGlobalKey(key, item);
|
|
169
191
|
const mutation = {
|
|
170
192
|
mutationId: crypto.randomUUID(),
|
|
171
193
|
original: {},
|
|
172
194
|
modified: validatedData,
|
|
173
195
|
changes: validatedData,
|
|
196
|
+
globalKey,
|
|
174
197
|
key,
|
|
175
198
|
metadata: config2 == null ? void 0 : config2.metadata,
|
|
176
199
|
syncMetadata: ((_b = (_a = this.config.sync).getSyncMetadata) == null ? void 0 : _b.call(_a)) || {},
|
|
@@ -183,10 +206,8 @@ class CollectionImpl {
|
|
|
183
206
|
});
|
|
184
207
|
if (ambientTransaction) {
|
|
185
208
|
ambientTransaction.applyMutations(mutations);
|
|
186
|
-
this.transactions.
|
|
187
|
-
|
|
188
|
-
return sortedMap;
|
|
189
|
-
});
|
|
209
|
+
this.transactions.set(ambientTransaction.id, ambientTransaction);
|
|
210
|
+
this.recomputeOptimisticState();
|
|
190
211
|
return ambientTransaction;
|
|
191
212
|
} else {
|
|
192
213
|
const directOpTransaction = new Transaction({
|
|
@@ -196,33 +217,34 @@ class CollectionImpl {
|
|
|
196
217
|
});
|
|
197
218
|
directOpTransaction.applyMutations(mutations);
|
|
198
219
|
directOpTransaction.commit();
|
|
199
|
-
this.transactions.
|
|
200
|
-
|
|
201
|
-
return sortedMap;
|
|
202
|
-
});
|
|
220
|
+
this.transactions.set(directOpTransaction.id, directOpTransaction);
|
|
221
|
+
this.recomputeOptimisticState();
|
|
203
222
|
return directOpTransaction;
|
|
204
223
|
}
|
|
205
224
|
};
|
|
206
|
-
this.delete = (
|
|
225
|
+
this.delete = (keys, config2) => {
|
|
207
226
|
const ambientTransaction = getActiveTransaction();
|
|
208
227
|
if (!ambientTransaction && !this.config.onDelete) {
|
|
209
228
|
throw new Error(
|
|
210
229
|
`Collection.delete called directly (not within an explicit transaction) but no 'onDelete' handler is configured.`
|
|
211
230
|
);
|
|
212
231
|
}
|
|
213
|
-
|
|
214
|
-
(
|
|
215
|
-
|
|
232
|
+
if (Array.isArray(keys) && keys.length === 0) {
|
|
233
|
+
throw new Error(`No keys were passed to delete`);
|
|
234
|
+
}
|
|
235
|
+
const keysArray = Array.isArray(keys) ? keys : [keys];
|
|
216
236
|
const mutations = [];
|
|
217
|
-
for (const
|
|
237
|
+
for (const key of keysArray) {
|
|
238
|
+
const globalKey = this.generateGlobalKey(key, this.get(key));
|
|
218
239
|
const mutation = {
|
|
219
240
|
mutationId: crypto.randomUUID(),
|
|
220
|
-
original: this.
|
|
221
|
-
modified: this.
|
|
222
|
-
changes: this.
|
|
223
|
-
|
|
241
|
+
original: this.get(key) || {},
|
|
242
|
+
modified: this.get(key),
|
|
243
|
+
changes: this.get(key) || {},
|
|
244
|
+
globalKey,
|
|
245
|
+
key,
|
|
224
246
|
metadata: config2 == null ? void 0 : config2.metadata,
|
|
225
|
-
syncMetadata: this.syncedMetadata.
|
|
247
|
+
syncMetadata: this.syncedMetadata.get(key) || {},
|
|
226
248
|
type: `delete`,
|
|
227
249
|
createdAt: /* @__PURE__ */ new Date(),
|
|
228
250
|
updatedAt: /* @__PURE__ */ new Date(),
|
|
@@ -232,24 +254,20 @@ class CollectionImpl {
|
|
|
232
254
|
}
|
|
233
255
|
if (ambientTransaction) {
|
|
234
256
|
ambientTransaction.applyMutations(mutations);
|
|
235
|
-
this.transactions.
|
|
236
|
-
|
|
237
|
-
return sortedMap;
|
|
238
|
-
});
|
|
257
|
+
this.transactions.set(ambientTransaction.id, ambientTransaction);
|
|
258
|
+
this.recomputeOptimisticState();
|
|
239
259
|
return ambientTransaction;
|
|
240
260
|
}
|
|
241
261
|
const directOpTransaction = new Transaction({
|
|
242
262
|
autoCommit: true,
|
|
243
|
-
mutationFn: async (
|
|
244
|
-
return this.config.onDelete(
|
|
263
|
+
mutationFn: async (params) => {
|
|
264
|
+
return this.config.onDelete(params);
|
|
245
265
|
}
|
|
246
266
|
});
|
|
247
267
|
directOpTransaction.applyMutations(mutations);
|
|
248
268
|
directOpTransaction.commit();
|
|
249
|
-
this.transactions.
|
|
250
|
-
|
|
251
|
-
return sortedMap;
|
|
252
|
-
});
|
|
269
|
+
this.transactions.set(directOpTransaction.id, directOpTransaction);
|
|
270
|
+
this.recomputeOptimisticState();
|
|
253
271
|
return directOpTransaction;
|
|
254
272
|
};
|
|
255
273
|
if (!config) {
|
|
@@ -263,121 +281,10 @@ class CollectionImpl {
|
|
|
263
281
|
if (!config.sync) {
|
|
264
282
|
throw new Error(`Collection requires a sync config`);
|
|
265
283
|
}
|
|
266
|
-
this.transactions = new
|
|
267
|
-
|
|
268
|
-
(a, b) => a.createdAt.getTime() - b.createdAt.getTime()
|
|
269
|
-
)
|
|
284
|
+
this.transactions = new SortedMap(
|
|
285
|
+
(a, b) => a.createdAt.getTime() - b.createdAt.getTime()
|
|
270
286
|
);
|
|
271
|
-
this.optimisticOperations = new Derived({
|
|
272
|
-
fn: ({ currDepVals: [transactions] }) => {
|
|
273
|
-
const result = Array.from(transactions.values()).map((transaction) => {
|
|
274
|
-
const isActive = ![`completed`, `failed`].includes(
|
|
275
|
-
transaction.state
|
|
276
|
-
);
|
|
277
|
-
return transaction.mutations.filter((mutation) => mutation.collection === this).map((mutation) => {
|
|
278
|
-
const message = {
|
|
279
|
-
type: mutation.type,
|
|
280
|
-
key: mutation.key,
|
|
281
|
-
value: mutation.modified,
|
|
282
|
-
isActive
|
|
283
|
-
};
|
|
284
|
-
if (mutation.metadata !== void 0 && mutation.metadata !== null) {
|
|
285
|
-
message.metadata = mutation.metadata;
|
|
286
|
-
}
|
|
287
|
-
return message;
|
|
288
|
-
});
|
|
289
|
-
}).flat();
|
|
290
|
-
return result;
|
|
291
|
-
},
|
|
292
|
-
deps: [this.transactions]
|
|
293
|
-
});
|
|
294
|
-
this.optimisticOperations.mount();
|
|
295
|
-
this.derivedState = new Derived({
|
|
296
|
-
fn: ({ currDepVals: [syncedData, operations] }) => {
|
|
297
|
-
const combined = new Map(syncedData);
|
|
298
|
-
for (const operation of operations) {
|
|
299
|
-
if (operation.isActive) {
|
|
300
|
-
switch (operation.type) {
|
|
301
|
-
case `insert`:
|
|
302
|
-
combined.set(operation.key, operation.value);
|
|
303
|
-
break;
|
|
304
|
-
case `update`:
|
|
305
|
-
combined.set(operation.key, operation.value);
|
|
306
|
-
break;
|
|
307
|
-
case `delete`:
|
|
308
|
-
combined.delete(operation.key);
|
|
309
|
-
break;
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
return combined;
|
|
314
|
-
},
|
|
315
|
-
deps: [this.syncedData, this.optimisticOperations]
|
|
316
|
-
});
|
|
317
|
-
this.derivedArray = new Derived({
|
|
318
|
-
fn: ({ currDepVals: [stateMap] }) => {
|
|
319
|
-
const array = Array.from(
|
|
320
|
-
stateMap.values()
|
|
321
|
-
);
|
|
322
|
-
if (array[0] && `_orderByIndex` in array[0]) {
|
|
323
|
-
array.sort((a, b) => {
|
|
324
|
-
if (a._orderByIndex === b._orderByIndex) {
|
|
325
|
-
return 0;
|
|
326
|
-
}
|
|
327
|
-
return a._orderByIndex < b._orderByIndex ? -1 : 1;
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
|
-
return array;
|
|
331
|
-
},
|
|
332
|
-
deps: [this.derivedState]
|
|
333
|
-
});
|
|
334
|
-
this.derivedArray.mount();
|
|
335
|
-
this.derivedChanges = new Derived({
|
|
336
|
-
fn: ({
|
|
337
|
-
currDepVals: [derivedState, optimisticOperations],
|
|
338
|
-
prevDepVals
|
|
339
|
-
}) => {
|
|
340
|
-
const prevDerivedState = (prevDepVals == null ? void 0 : prevDepVals[0]) ?? /* @__PURE__ */ new Map();
|
|
341
|
-
const prevOptimisticOperations = (prevDepVals == null ? void 0 : prevDepVals[1]) ?? [];
|
|
342
|
-
const changedKeys = new Set(this.syncedKeys);
|
|
343
|
-
optimisticOperations.flat().filter((op) => op.isActive).forEach((op) => changedKeys.add(op.key));
|
|
344
|
-
prevOptimisticOperations.flat().forEach((op) => {
|
|
345
|
-
changedKeys.add(op.key);
|
|
346
|
-
});
|
|
347
|
-
if (changedKeys.size === 0) {
|
|
348
|
-
return [];
|
|
349
|
-
}
|
|
350
|
-
const changes = [];
|
|
351
|
-
for (const key of changedKeys) {
|
|
352
|
-
if (prevDerivedState.has(key) && !derivedState.has(key)) {
|
|
353
|
-
changes.push({
|
|
354
|
-
type: `delete`,
|
|
355
|
-
key,
|
|
356
|
-
value: prevDerivedState.get(key)
|
|
357
|
-
});
|
|
358
|
-
} else if (!prevDerivedState.has(key) && derivedState.has(key)) {
|
|
359
|
-
changes.push({ type: `insert`, key, value: derivedState.get(key) });
|
|
360
|
-
} else if (prevDerivedState.has(key) && derivedState.has(key)) {
|
|
361
|
-
const value = derivedState.get(key);
|
|
362
|
-
const previousValue = prevDerivedState.get(key);
|
|
363
|
-
if (value !== previousValue) {
|
|
364
|
-
changes.push({
|
|
365
|
-
type: `update`,
|
|
366
|
-
key,
|
|
367
|
-
value,
|
|
368
|
-
previousValue
|
|
369
|
-
});
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
this.syncedKeys.clear();
|
|
374
|
-
return changes;
|
|
375
|
-
},
|
|
376
|
-
deps: [this.derivedState, this.optimisticOperations]
|
|
377
|
-
});
|
|
378
|
-
this.derivedChanges.mount();
|
|
379
287
|
this.config = config;
|
|
380
|
-
this.derivedState.mount();
|
|
381
288
|
config.sync.sync({
|
|
382
289
|
collection: this,
|
|
383
290
|
begin: () => {
|
|
@@ -396,17 +303,13 @@ class CollectionImpl {
|
|
|
396
303
|
`The pending sync transaction is already committed, you can't still write to it.`
|
|
397
304
|
);
|
|
398
305
|
}
|
|
399
|
-
const key = this.
|
|
400
|
-
this.config.getId(messageWithoutKey.value),
|
|
401
|
-
messageWithoutKey.value
|
|
402
|
-
);
|
|
306
|
+
const key = this.getKeyFromItem(messageWithoutKey.value);
|
|
403
307
|
if (messageWithoutKey.type === `insert`) {
|
|
404
|
-
if (this.syncedData.
|
|
308
|
+
if (this.syncedData.has(key) && !pendingTransaction.operations.some(
|
|
405
309
|
(op) => op.key === key && op.type === `delete`
|
|
406
310
|
)) {
|
|
407
|
-
const id = this.config.getId(messageWithoutKey.value);
|
|
408
311
|
throw new Error(
|
|
409
|
-
`Cannot insert document with
|
|
312
|
+
`Cannot insert document with key "${key}" from sync because it already exists in the collection "${this.id}"`
|
|
410
313
|
);
|
|
411
314
|
}
|
|
412
315
|
}
|
|
@@ -439,6 +342,189 @@ class CollectionImpl {
|
|
|
439
342
|
onFirstCommit(callback) {
|
|
440
343
|
this.onFirstCommitCallbacks.push(callback);
|
|
441
344
|
}
|
|
345
|
+
/**
|
|
346
|
+
* Recompute optimistic state from active transactions
|
|
347
|
+
*/
|
|
348
|
+
recomputeOptimisticState() {
|
|
349
|
+
const previousState = new Map(this.derivedUpserts);
|
|
350
|
+
const previousDeletes = new Set(this.derivedDeletes);
|
|
351
|
+
this.derivedUpserts.clear();
|
|
352
|
+
this.derivedDeletes.clear();
|
|
353
|
+
const activeTransactions = Array.from(this.transactions.values());
|
|
354
|
+
for (const transaction of activeTransactions) {
|
|
355
|
+
if (![`completed`, `failed`].includes(transaction.state)) {
|
|
356
|
+
for (const mutation of transaction.mutations) {
|
|
357
|
+
if (mutation.collection === this) {
|
|
358
|
+
switch (mutation.type) {
|
|
359
|
+
case `insert`:
|
|
360
|
+
case `update`:
|
|
361
|
+
this.derivedUpserts.set(mutation.key, mutation.modified);
|
|
362
|
+
this.derivedDeletes.delete(mutation.key);
|
|
363
|
+
break;
|
|
364
|
+
case `delete`:
|
|
365
|
+
this.derivedUpserts.delete(mutation.key);
|
|
366
|
+
this.derivedDeletes.add(mutation.key);
|
|
367
|
+
break;
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
this._size = this.calculateSize();
|
|
374
|
+
const events = [];
|
|
375
|
+
this.collectOptimisticChanges(previousState, previousDeletes, events);
|
|
376
|
+
this.emitEvents(events);
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Calculate the current size based on synced data and optimistic changes
|
|
380
|
+
*/
|
|
381
|
+
calculateSize() {
|
|
382
|
+
const syncedSize = this.syncedData.size;
|
|
383
|
+
const deletesFromSynced = Array.from(this.derivedDeletes).filter(
|
|
384
|
+
(key) => this.syncedData.has(key) && !this.derivedUpserts.has(key)
|
|
385
|
+
).length;
|
|
386
|
+
const upsertsNotInSynced = Array.from(this.derivedUpserts.keys()).filter(
|
|
387
|
+
(key) => !this.syncedData.has(key)
|
|
388
|
+
).length;
|
|
389
|
+
return syncedSize - deletesFromSynced + upsertsNotInSynced;
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Collect events for optimistic changes
|
|
393
|
+
*/
|
|
394
|
+
collectOptimisticChanges(previousUpserts, previousDeletes, events) {
|
|
395
|
+
const allKeys = /* @__PURE__ */ new Set([
|
|
396
|
+
...previousUpserts.keys(),
|
|
397
|
+
...this.derivedUpserts.keys(),
|
|
398
|
+
...previousDeletes,
|
|
399
|
+
...this.derivedDeletes
|
|
400
|
+
]);
|
|
401
|
+
for (const key of allKeys) {
|
|
402
|
+
const currentValue = this.get(key);
|
|
403
|
+
const previousValue = this.getPreviousValue(
|
|
404
|
+
key,
|
|
405
|
+
previousUpserts,
|
|
406
|
+
previousDeletes
|
|
407
|
+
);
|
|
408
|
+
if (previousValue !== void 0 && currentValue === void 0) {
|
|
409
|
+
events.push({ type: `delete`, key, value: previousValue });
|
|
410
|
+
} else if (previousValue === void 0 && currentValue !== void 0) {
|
|
411
|
+
events.push({ type: `insert`, key, value: currentValue });
|
|
412
|
+
} else if (previousValue !== void 0 && currentValue !== void 0 && previousValue !== currentValue) {
|
|
413
|
+
events.push({
|
|
414
|
+
type: `update`,
|
|
415
|
+
key,
|
|
416
|
+
value: currentValue,
|
|
417
|
+
previousValue
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Get the previous value for a key given previous optimistic state
|
|
424
|
+
*/
|
|
425
|
+
getPreviousValue(key, previousUpserts, previousDeletes) {
|
|
426
|
+
if (previousDeletes.has(key)) {
|
|
427
|
+
return void 0;
|
|
428
|
+
}
|
|
429
|
+
if (previousUpserts.has(key)) {
|
|
430
|
+
return previousUpserts.get(key);
|
|
431
|
+
}
|
|
432
|
+
return this.syncedData.get(key);
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Emit multiple events at once to all listeners
|
|
436
|
+
*/
|
|
437
|
+
emitEvents(changes) {
|
|
438
|
+
if (changes.length > 0) {
|
|
439
|
+
for (const listener of this.changeListeners) {
|
|
440
|
+
listener(changes);
|
|
441
|
+
}
|
|
442
|
+
if (this.changeKeyListeners.size > 0) {
|
|
443
|
+
const changesByKey = /* @__PURE__ */ new Map();
|
|
444
|
+
for (const change of changes) {
|
|
445
|
+
if (this.changeKeyListeners.has(change.key)) {
|
|
446
|
+
if (!changesByKey.has(change.key)) {
|
|
447
|
+
changesByKey.set(change.key, []);
|
|
448
|
+
}
|
|
449
|
+
changesByKey.get(change.key).push(change);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
for (const [key, keyChanges] of changesByKey) {
|
|
453
|
+
const keyListeners = this.changeKeyListeners.get(key);
|
|
454
|
+
for (const listener of keyListeners) {
|
|
455
|
+
listener(keyChanges);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Get the current value for a key (virtual derived state)
|
|
463
|
+
*/
|
|
464
|
+
get(key) {
|
|
465
|
+
if (this.derivedDeletes.has(key)) {
|
|
466
|
+
return void 0;
|
|
467
|
+
}
|
|
468
|
+
if (this.derivedUpserts.has(key)) {
|
|
469
|
+
return this.derivedUpserts.get(key);
|
|
470
|
+
}
|
|
471
|
+
return this.syncedData.get(key);
|
|
472
|
+
}
|
|
473
|
+
/**
|
|
474
|
+
* Check if a key exists in the collection (virtual derived state)
|
|
475
|
+
*/
|
|
476
|
+
has(key) {
|
|
477
|
+
if (this.derivedDeletes.has(key)) {
|
|
478
|
+
return false;
|
|
479
|
+
}
|
|
480
|
+
if (this.derivedUpserts.has(key)) {
|
|
481
|
+
return true;
|
|
482
|
+
}
|
|
483
|
+
return this.syncedData.has(key);
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Get the current size of the collection (cached)
|
|
487
|
+
*/
|
|
488
|
+
get size() {
|
|
489
|
+
return this._size;
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Get all keys (virtual derived state)
|
|
493
|
+
*/
|
|
494
|
+
*keys() {
|
|
495
|
+
for (const key of this.syncedData.keys()) {
|
|
496
|
+
if (!this.derivedDeletes.has(key)) {
|
|
497
|
+
yield key;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
for (const key of this.derivedUpserts.keys()) {
|
|
501
|
+
if (!this.syncedData.has(key) && !this.derivedDeletes.has(key)) {
|
|
502
|
+
yield key;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Get all values (virtual derived state)
|
|
508
|
+
*/
|
|
509
|
+
*values() {
|
|
510
|
+
for (const key of this.keys()) {
|
|
511
|
+
const value = this.get(key);
|
|
512
|
+
if (value !== void 0) {
|
|
513
|
+
yield value;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Get all entries (virtual derived state)
|
|
519
|
+
*/
|
|
520
|
+
*entries() {
|
|
521
|
+
for (const key of this.keys()) {
|
|
522
|
+
const value = this.get(key);
|
|
523
|
+
if (value !== void 0) {
|
|
524
|
+
yield [key, value];
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
442
528
|
ensureStandardSchema(schema) {
|
|
443
529
|
if (schema && typeof schema === `object` && `~standard` in schema) {
|
|
444
530
|
return schema;
|
|
@@ -447,31 +533,24 @@ class CollectionImpl {
|
|
|
447
533
|
`Schema must either implement the standard-schema interface or be a Zod schema`
|
|
448
534
|
);
|
|
449
535
|
}
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
throw new Error(`id is undefined`);
|
|
453
|
-
}
|
|
454
|
-
if (typeof id === `string` && id.startsWith(`KEY::`)) {
|
|
455
|
-
return id;
|
|
456
|
-
} else {
|
|
457
|
-
return this.generateObjectKey(id, null);
|
|
458
|
-
}
|
|
536
|
+
getKeyFromItem(item) {
|
|
537
|
+
return this.config.getKey(item);
|
|
459
538
|
}
|
|
460
|
-
|
|
461
|
-
if (typeof
|
|
539
|
+
generateGlobalKey(key, item) {
|
|
540
|
+
if (typeof key === `undefined`) {
|
|
462
541
|
throw new Error(
|
|
463
|
-
`An object was created without a defined
|
|
542
|
+
`An object was created without a defined key: ${JSON.stringify(item)}`
|
|
464
543
|
);
|
|
465
544
|
}
|
|
466
|
-
return `KEY::${this.id}/${
|
|
545
|
+
return `KEY::${this.id}/${key}`;
|
|
467
546
|
}
|
|
468
547
|
validateData(data, type, key) {
|
|
469
548
|
if (!this.config.schema) return data;
|
|
470
549
|
const standardSchema = this.ensureStandardSchema(this.config.schema);
|
|
471
550
|
if (type === `update` && key) {
|
|
472
|
-
const existingData = this.
|
|
551
|
+
const existingData = this.get(key);
|
|
473
552
|
if (existingData && data && typeof data === `object` && typeof existingData === `object`) {
|
|
474
|
-
const mergedData = {
|
|
553
|
+
const mergedData = Object.assign({}, existingData, data);
|
|
475
554
|
const result2 = standardSchema[`~standard`].validate(mergedData);
|
|
476
555
|
if (result2 instanceof Promise) {
|
|
477
556
|
throw new TypeError(`Schema validation must be synchronous`);
|
|
@@ -505,8 +584,8 @@ class CollectionImpl {
|
|
|
505
584
|
}
|
|
506
585
|
return result.value;
|
|
507
586
|
}
|
|
508
|
-
update(
|
|
509
|
-
if (typeof
|
|
587
|
+
update(keys, configOrCallback, maybeCallback) {
|
|
588
|
+
if (typeof keys === `undefined`) {
|
|
510
589
|
throw new Error(`The first argument to update is missing`);
|
|
511
590
|
}
|
|
512
591
|
const ambientTransaction = getActiveTransaction();
|
|
@@ -515,17 +594,18 @@ class CollectionImpl {
|
|
|
515
594
|
`Collection.update called directly (not within an explicit transaction) but no 'onUpdate' handler is configured.`
|
|
516
595
|
);
|
|
517
596
|
}
|
|
518
|
-
const isArray = Array.isArray(
|
|
519
|
-
const
|
|
520
|
-
|
|
521
|
-
|
|
597
|
+
const isArray = Array.isArray(keys);
|
|
598
|
+
const keysArray = isArray ? keys : [keys];
|
|
599
|
+
if (isArray && keysArray.length === 0) {
|
|
600
|
+
throw new Error(`No keys were passed to update`);
|
|
601
|
+
}
|
|
522
602
|
const callback = typeof configOrCallback === `function` ? configOrCallback : maybeCallback;
|
|
523
603
|
const config = typeof configOrCallback === `function` ? {} : configOrCallback;
|
|
524
|
-
const currentObjects =
|
|
525
|
-
const item = this.
|
|
604
|
+
const currentObjects = keysArray.map((key) => {
|
|
605
|
+
const item = this.get(key);
|
|
526
606
|
if (!item) {
|
|
527
607
|
throw new Error(
|
|
528
|
-
`The
|
|
608
|
+
`The key "${key}" was passed to update but an object for this key was not found in the collection`
|
|
529
609
|
);
|
|
530
610
|
}
|
|
531
611
|
return item;
|
|
@@ -543,7 +623,7 @@ class CollectionImpl {
|
|
|
543
623
|
);
|
|
544
624
|
changesArray = [result];
|
|
545
625
|
}
|
|
546
|
-
const mutations =
|
|
626
|
+
const mutations = keysArray.map((key, index) => {
|
|
547
627
|
const itemChanges = changesArray[index];
|
|
548
628
|
if (!itemChanges || Object.keys(itemChanges).length === 0) {
|
|
549
629
|
return null;
|
|
@@ -552,24 +632,30 @@ class CollectionImpl {
|
|
|
552
632
|
const validatedUpdatePayload = this.validateData(
|
|
553
633
|
itemChanges,
|
|
554
634
|
`update`,
|
|
555
|
-
|
|
635
|
+
key
|
|
636
|
+
);
|
|
637
|
+
const modifiedItem = Object.assign(
|
|
638
|
+
{},
|
|
639
|
+
originalItem,
|
|
640
|
+
validatedUpdatePayload
|
|
556
641
|
);
|
|
557
|
-
const
|
|
558
|
-
const
|
|
559
|
-
const modifiedItemId = this.config.getId(modifiedItem);
|
|
642
|
+
const originalItemId = this.getKeyFromItem(originalItem);
|
|
643
|
+
const modifiedItemId = this.getKeyFromItem(modifiedItem);
|
|
560
644
|
if (originalItemId !== modifiedItemId) {
|
|
561
645
|
throw new Error(
|
|
562
|
-
`Updating the
|
|
646
|
+
`Updating the key of an item is not allowed. Original key: "${originalItemId}", Attempted new key: "${modifiedItemId}". Please delete the old item and create a new one if a key change is necessary.`
|
|
563
647
|
);
|
|
564
648
|
}
|
|
649
|
+
const globalKey = this.generateGlobalKey(modifiedItemId, modifiedItem);
|
|
565
650
|
return {
|
|
566
651
|
mutationId: crypto.randomUUID(),
|
|
567
652
|
original: originalItem,
|
|
568
653
|
modified: modifiedItem,
|
|
569
654
|
changes: validatedUpdatePayload,
|
|
570
|
-
|
|
655
|
+
globalKey,
|
|
656
|
+
key,
|
|
571
657
|
metadata: config.metadata,
|
|
572
|
-
syncMetadata: this.syncedMetadata.
|
|
658
|
+
syncMetadata: this.syncedMetadata.get(key) || {},
|
|
573
659
|
type: `update`,
|
|
574
660
|
createdAt: /* @__PURE__ */ new Date(),
|
|
575
661
|
updatedAt: /* @__PURE__ */ new Date(),
|
|
@@ -581,23 +667,19 @@ class CollectionImpl {
|
|
|
581
667
|
}
|
|
582
668
|
if (ambientTransaction) {
|
|
583
669
|
ambientTransaction.applyMutations(mutations);
|
|
584
|
-
this.transactions.
|
|
585
|
-
|
|
586
|
-
return sortedMap;
|
|
587
|
-
});
|
|
670
|
+
this.transactions.set(ambientTransaction.id, ambientTransaction);
|
|
671
|
+
this.recomputeOptimisticState();
|
|
588
672
|
return ambientTransaction;
|
|
589
673
|
}
|
|
590
674
|
const directOpTransaction = new Transaction({
|
|
591
|
-
mutationFn: async (
|
|
592
|
-
return this.config.onUpdate(
|
|
675
|
+
mutationFn: async (params) => {
|
|
676
|
+
return this.config.onUpdate(params);
|
|
593
677
|
}
|
|
594
678
|
});
|
|
595
679
|
directOpTransaction.applyMutations(mutations);
|
|
596
680
|
directOpTransaction.commit();
|
|
597
|
-
this.transactions.
|
|
598
|
-
|
|
599
|
-
return sortedMap;
|
|
600
|
-
});
|
|
681
|
+
this.transactions.set(directOpTransaction.id, directOpTransaction);
|
|
682
|
+
this.recomputeOptimisticState();
|
|
601
683
|
return directOpTransaction;
|
|
602
684
|
}
|
|
603
685
|
/**
|
|
@@ -606,7 +688,11 @@ class CollectionImpl {
|
|
|
606
688
|
* @returns A Map containing all items in the collection, with keys as identifiers
|
|
607
689
|
*/
|
|
608
690
|
get state() {
|
|
609
|
-
|
|
691
|
+
const result = /* @__PURE__ */ new Map();
|
|
692
|
+
for (const [key, value] of this.entries()) {
|
|
693
|
+
result.set(key, value);
|
|
694
|
+
}
|
|
695
|
+
return result;
|
|
610
696
|
}
|
|
611
697
|
/**
|
|
612
698
|
* Gets the current state of the collection as a Map, but only resolves when data is available
|
|
@@ -615,7 +701,7 @@ class CollectionImpl {
|
|
|
615
701
|
* @returns Promise that resolves to a Map containing all items in the collection
|
|
616
702
|
*/
|
|
617
703
|
stateWhenReady() {
|
|
618
|
-
if (this.
|
|
704
|
+
if (this.size > 0 || this.hasReceivedFirstCommit === true) {
|
|
619
705
|
return Promise.resolve(this.state);
|
|
620
706
|
}
|
|
621
707
|
return new Promise((resolve) => {
|
|
@@ -630,7 +716,13 @@ class CollectionImpl {
|
|
|
630
716
|
* @returns An Array containing all items in the collection
|
|
631
717
|
*/
|
|
632
718
|
get toArray() {
|
|
633
|
-
|
|
719
|
+
const array = Array.from(this.values());
|
|
720
|
+
if (array[0] && array[0]._orderByIndex) {
|
|
721
|
+
return array.sort(
|
|
722
|
+
(a, b) => a._orderByIndex - b._orderByIndex
|
|
723
|
+
);
|
|
724
|
+
}
|
|
725
|
+
return array;
|
|
634
726
|
}
|
|
635
727
|
/**
|
|
636
728
|
* Gets the current state of the collection as an Array, but only resolves when data is available
|
|
@@ -639,7 +731,7 @@ class CollectionImpl {
|
|
|
639
731
|
* @returns Promise that resolves to an Array containing all items in the collection
|
|
640
732
|
*/
|
|
641
733
|
toArrayWhenReady() {
|
|
642
|
-
if (this.
|
|
734
|
+
if (this.size > 0 || this.hasReceivedFirstCommit === true) {
|
|
643
735
|
return Promise.resolve(this.toArray);
|
|
644
736
|
}
|
|
645
737
|
return new Promise((resolve) => {
|
|
@@ -653,7 +745,7 @@ class CollectionImpl {
|
|
|
653
745
|
* @returns An array of changes
|
|
654
746
|
*/
|
|
655
747
|
currentStateAsChanges() {
|
|
656
|
-
return
|
|
748
|
+
return Array.from(this.entries()).map(([key, value]) => ({
|
|
657
749
|
type: `insert`,
|
|
658
750
|
key,
|
|
659
751
|
value
|
|
@@ -664,13 +756,78 @@ class CollectionImpl {
|
|
|
664
756
|
* @param callback - A function that will be called with the changes in the collection
|
|
665
757
|
* @returns A function that can be called to unsubscribe from the changes
|
|
666
758
|
*/
|
|
667
|
-
subscribeChanges(callback) {
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
759
|
+
subscribeChanges(callback, { includeInitialState = false } = {}) {
|
|
760
|
+
if (includeInitialState) {
|
|
761
|
+
callback(this.currentStateAsChanges());
|
|
762
|
+
}
|
|
763
|
+
this.changeListeners.add(callback);
|
|
764
|
+
return () => {
|
|
765
|
+
this.changeListeners.delete(callback);
|
|
766
|
+
};
|
|
767
|
+
}
|
|
768
|
+
/**
|
|
769
|
+
* Subscribe to changes for a specific key
|
|
770
|
+
*/
|
|
771
|
+
subscribeChangesKey(key, listener, { includeInitialState = false } = {}) {
|
|
772
|
+
if (!this.changeKeyListeners.has(key)) {
|
|
773
|
+
this.changeKeyListeners.set(key, /* @__PURE__ */ new Set());
|
|
774
|
+
}
|
|
775
|
+
if (includeInitialState) {
|
|
776
|
+
listener([
|
|
777
|
+
{
|
|
778
|
+
type: `insert`,
|
|
779
|
+
key,
|
|
780
|
+
value: this.get(key)
|
|
781
|
+
}
|
|
782
|
+
]);
|
|
783
|
+
}
|
|
784
|
+
this.changeKeyListeners.get(key).add(listener);
|
|
785
|
+
return () => {
|
|
786
|
+
const listeners = this.changeKeyListeners.get(key);
|
|
787
|
+
if (listeners) {
|
|
788
|
+
listeners.delete(listener);
|
|
789
|
+
if (listeners.size === 0) {
|
|
790
|
+
this.changeKeyListeners.delete(key);
|
|
791
|
+
}
|
|
672
792
|
}
|
|
673
|
-
}
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
/**
|
|
796
|
+
* Trigger a recomputation when transactions change
|
|
797
|
+
* This method should be called by the Transaction class when state changes
|
|
798
|
+
*/
|
|
799
|
+
onTransactionStateChange() {
|
|
800
|
+
this.recomputeOptimisticState();
|
|
801
|
+
}
|
|
802
|
+
/**
|
|
803
|
+
* Returns a Tanstack Store Map that is updated when the collection changes
|
|
804
|
+
* This is a temporary solution to enable the existing framework hooks to work
|
|
805
|
+
* with the new internals of Collection until they are rewritten.
|
|
806
|
+
* TODO: Remove this once the framework hooks are rewritten.
|
|
807
|
+
*/
|
|
808
|
+
asStoreMap() {
|
|
809
|
+
if (!this._storeMap) {
|
|
810
|
+
this._storeMap = new Store(new Map(this.entries()));
|
|
811
|
+
this.subscribeChanges(() => {
|
|
812
|
+
this._storeMap.setState(() => new Map(this.entries()));
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
return this._storeMap;
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Returns a Tanstack Store Array that is updated when the collection changes
|
|
819
|
+
* This is a temporary solution to enable the existing framework hooks to work
|
|
820
|
+
* with the new internals of Collection until they are rewritten.
|
|
821
|
+
* TODO: Remove this once the framework hooks are rewritten.
|
|
822
|
+
*/
|
|
823
|
+
asStoreArray() {
|
|
824
|
+
if (!this._storeArray) {
|
|
825
|
+
this._storeArray = new Store(this.toArray);
|
|
826
|
+
this.subscribeChanges(() => {
|
|
827
|
+
this._storeArray.setState(() => this.toArray);
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
return this._storeArray;
|
|
674
831
|
}
|
|
675
832
|
}
|
|
676
833
|
export {
|