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