@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/esm/collection.js
CHANGED
|
@@ -1,62 +1,60 @@
|
|
|
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 =
|
|
6
|
-
const
|
|
7
|
-
function createCollection(
|
|
8
|
-
|
|
5
|
+
const collectionsStore = /* @__PURE__ */ new Map();
|
|
6
|
+
const loadingCollectionResolvers = /* @__PURE__ */ new Map();
|
|
7
|
+
function createCollection(options) {
|
|
8
|
+
const collection = new CollectionImpl(options);
|
|
9
|
+
if (options.utils) {
|
|
10
|
+
collection.utils = { ...options.utils };
|
|
11
|
+
} else {
|
|
12
|
+
collection.utils = {};
|
|
13
|
+
}
|
|
14
|
+
return collection;
|
|
9
15
|
}
|
|
10
16
|
function preloadCollection(config) {
|
|
11
17
|
if (!config.id) {
|
|
12
18
|
throw new Error(`The id property is required for preloadCollection`);
|
|
13
19
|
}
|
|
14
|
-
if (collectionsStore.
|
|
20
|
+
if (collectionsStore.has(config.id) && !loadingCollectionResolvers.has(config.id)) {
|
|
15
21
|
return Promise.resolve(
|
|
16
|
-
collectionsStore.
|
|
22
|
+
collectionsStore.get(config.id)
|
|
17
23
|
);
|
|
18
24
|
}
|
|
19
|
-
if (
|
|
20
|
-
return
|
|
25
|
+
if (loadingCollectionResolvers.has(config.id)) {
|
|
26
|
+
return loadingCollectionResolvers.get(config.id).promise;
|
|
21
27
|
}
|
|
22
|
-
if (!collectionsStore.
|
|
23
|
-
collectionsStore.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
config.
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
getId: config.getId,
|
|
33
|
-
sync: config.sync,
|
|
34
|
-
schema: config.schema
|
|
35
|
-
})
|
|
36
|
-
);
|
|
37
|
-
return next;
|
|
38
|
-
});
|
|
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
|
+
);
|
|
39
38
|
}
|
|
40
|
-
const collection = collectionsStore.
|
|
39
|
+
const collection = collectionsStore.get(config.id);
|
|
41
40
|
let resolveFirstCommit;
|
|
42
41
|
const firstCommitPromise = new Promise((resolve) => {
|
|
43
|
-
resolveFirstCommit =
|
|
44
|
-
|
|
45
|
-
|
|
42
|
+
resolveFirstCommit = resolve;
|
|
43
|
+
});
|
|
44
|
+
loadingCollectionResolvers.set(config.id, {
|
|
45
|
+
promise: firstCommitPromise,
|
|
46
|
+
resolve: resolveFirstCommit
|
|
46
47
|
});
|
|
47
48
|
collection.onFirstCommit(() => {
|
|
48
49
|
if (!config.id) {
|
|
49
50
|
throw new Error(`The id property is required for preloadCollection`);
|
|
50
51
|
}
|
|
51
|
-
if (
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
if (loadingCollectionResolvers.has(config.id)) {
|
|
53
|
+
const resolver = loadingCollectionResolvers.get(config.id);
|
|
54
|
+
loadingCollectionResolvers.delete(config.id);
|
|
55
|
+
resolver.resolve(collection);
|
|
54
56
|
}
|
|
55
57
|
});
|
|
56
|
-
loadingCollections.set(
|
|
57
|
-
config.id,
|
|
58
|
-
firstCommitPromise
|
|
59
|
-
);
|
|
60
58
|
return firstCommitPromise;
|
|
61
59
|
}
|
|
62
60
|
class SchemaValidationError extends Error {
|
|
@@ -68,7 +66,7 @@ class SchemaValidationError extends Error {
|
|
|
68
66
|
this.issues = issues;
|
|
69
67
|
}
|
|
70
68
|
}
|
|
71
|
-
class
|
|
69
|
+
class CollectionImpl {
|
|
72
70
|
/**
|
|
73
71
|
* Creates a new Collection instance
|
|
74
72
|
*
|
|
@@ -76,58 +74,94 @@ class Collection {
|
|
|
76
74
|
* @throws Error if sync config is missing
|
|
77
75
|
*/
|
|
78
76
|
constructor(config) {
|
|
79
|
-
this.syncedData =
|
|
80
|
-
this.syncedMetadata =
|
|
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();
|
|
84
|
+
this.utils = {};
|
|
81
85
|
this.pendingSyncedTransactions = [];
|
|
82
86
|
this.syncedKeys = /* @__PURE__ */ new Set();
|
|
83
87
|
this.hasReceivedFirstCommit = false;
|
|
84
88
|
this.onFirstCommitCallbacks = [];
|
|
85
89
|
this.id = ``;
|
|
86
90
|
this.commitPendingTransactions = () => {
|
|
87
|
-
if (!Array.from(this.transactions.
|
|
91
|
+
if (!Array.from(this.transactions.values()).some(
|
|
88
92
|
({ state }) => state === `persisting`
|
|
89
93
|
)) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
+
});
|
|
129
|
+
}
|
|
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
|
+
});
|
|
108
145
|
}
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
...operation.value
|
|
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
|
|
120
156
|
});
|
|
121
|
-
|
|
122
|
-
case `delete`:
|
|
123
|
-
prevData.delete(operation.key);
|
|
124
|
-
break;
|
|
157
|
+
}
|
|
125
158
|
}
|
|
126
|
-
|
|
127
|
-
});
|
|
159
|
+
break;
|
|
128
160
|
}
|
|
129
161
|
}
|
|
130
|
-
}
|
|
162
|
+
}
|
|
163
|
+
this._size = this.calculateSize();
|
|
164
|
+
this.emitEvents(events);
|
|
131
165
|
this.pendingSyncedTransactions = [];
|
|
132
166
|
if (!this.hasReceivedFirstCommit) {
|
|
133
167
|
this.hasReceivedFirstCommit = true;
|
|
@@ -146,22 +180,20 @@ class Collection {
|
|
|
146
180
|
}
|
|
147
181
|
const items = Array.isArray(data) ? data : [data];
|
|
148
182
|
const mutations = [];
|
|
149
|
-
|
|
150
|
-
(item) => this.generateObjectKey(this.config.getId(item), item)
|
|
151
|
-
);
|
|
152
|
-
items.forEach((item, index) => {
|
|
183
|
+
items.forEach((item) => {
|
|
153
184
|
var _a, _b;
|
|
154
185
|
const validatedData = this.validateData(item, `insert`);
|
|
155
|
-
const key =
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
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`;
|
|
159
189
|
}
|
|
190
|
+
const globalKey = this.generateGlobalKey(key, item);
|
|
160
191
|
const mutation = {
|
|
161
192
|
mutationId: crypto.randomUUID(),
|
|
162
193
|
original: {},
|
|
163
194
|
modified: validatedData,
|
|
164
195
|
changes: validatedData,
|
|
196
|
+
globalKey,
|
|
165
197
|
key,
|
|
166
198
|
metadata: config2 == null ? void 0 : config2.metadata,
|
|
167
199
|
syncMetadata: ((_b = (_a = this.config.sync).getSyncMetadata) == null ? void 0 : _b.call(_a)) || {},
|
|
@@ -174,10 +206,8 @@ class Collection {
|
|
|
174
206
|
});
|
|
175
207
|
if (ambientTransaction) {
|
|
176
208
|
ambientTransaction.applyMutations(mutations);
|
|
177
|
-
this.transactions.
|
|
178
|
-
|
|
179
|
-
return sortedMap;
|
|
180
|
-
});
|
|
209
|
+
this.transactions.set(ambientTransaction.id, ambientTransaction);
|
|
210
|
+
this.recomputeOptimisticState();
|
|
181
211
|
return ambientTransaction;
|
|
182
212
|
} else {
|
|
183
213
|
const directOpTransaction = new Transaction({
|
|
@@ -187,33 +217,34 @@ class Collection {
|
|
|
187
217
|
});
|
|
188
218
|
directOpTransaction.applyMutations(mutations);
|
|
189
219
|
directOpTransaction.commit();
|
|
190
|
-
this.transactions.
|
|
191
|
-
|
|
192
|
-
return sortedMap;
|
|
193
|
-
});
|
|
220
|
+
this.transactions.set(directOpTransaction.id, directOpTransaction);
|
|
221
|
+
this.recomputeOptimisticState();
|
|
194
222
|
return directOpTransaction;
|
|
195
223
|
}
|
|
196
224
|
};
|
|
197
|
-
this.delete = (
|
|
225
|
+
this.delete = (keys, config2) => {
|
|
198
226
|
const ambientTransaction = getActiveTransaction();
|
|
199
227
|
if (!ambientTransaction && !this.config.onDelete) {
|
|
200
228
|
throw new Error(
|
|
201
229
|
`Collection.delete called directly (not within an explicit transaction) but no 'onDelete' handler is configured.`
|
|
202
230
|
);
|
|
203
231
|
}
|
|
204
|
-
|
|
205
|
-
(
|
|
206
|
-
|
|
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];
|
|
207
236
|
const mutations = [];
|
|
208
|
-
for (const
|
|
237
|
+
for (const key of keysArray) {
|
|
238
|
+
const globalKey = this.generateGlobalKey(key, this.get(key));
|
|
209
239
|
const mutation = {
|
|
210
240
|
mutationId: crypto.randomUUID(),
|
|
211
|
-
original: this.
|
|
212
|
-
modified: this.
|
|
213
|
-
changes: this.
|
|
214
|
-
|
|
241
|
+
original: this.get(key) || {},
|
|
242
|
+
modified: this.get(key),
|
|
243
|
+
changes: this.get(key) || {},
|
|
244
|
+
globalKey,
|
|
245
|
+
key,
|
|
215
246
|
metadata: config2 == null ? void 0 : config2.metadata,
|
|
216
|
-
syncMetadata: this.syncedMetadata.
|
|
247
|
+
syncMetadata: this.syncedMetadata.get(key) || {},
|
|
217
248
|
type: `delete`,
|
|
218
249
|
createdAt: /* @__PURE__ */ new Date(),
|
|
219
250
|
updatedAt: /* @__PURE__ */ new Date(),
|
|
@@ -223,24 +254,20 @@ class Collection {
|
|
|
223
254
|
}
|
|
224
255
|
if (ambientTransaction) {
|
|
225
256
|
ambientTransaction.applyMutations(mutations);
|
|
226
|
-
this.transactions.
|
|
227
|
-
|
|
228
|
-
return sortedMap;
|
|
229
|
-
});
|
|
257
|
+
this.transactions.set(ambientTransaction.id, ambientTransaction);
|
|
258
|
+
this.recomputeOptimisticState();
|
|
230
259
|
return ambientTransaction;
|
|
231
260
|
}
|
|
232
261
|
const directOpTransaction = new Transaction({
|
|
233
262
|
autoCommit: true,
|
|
234
|
-
mutationFn: async (
|
|
235
|
-
return this.config.onDelete(
|
|
263
|
+
mutationFn: async (params) => {
|
|
264
|
+
return this.config.onDelete(params);
|
|
236
265
|
}
|
|
237
266
|
});
|
|
238
267
|
directOpTransaction.applyMutations(mutations);
|
|
239
268
|
directOpTransaction.commit();
|
|
240
|
-
this.transactions.
|
|
241
|
-
|
|
242
|
-
return sortedMap;
|
|
243
|
-
});
|
|
269
|
+
this.transactions.set(directOpTransaction.id, directOpTransaction);
|
|
270
|
+
this.recomputeOptimisticState();
|
|
244
271
|
return directOpTransaction;
|
|
245
272
|
};
|
|
246
273
|
if (!config) {
|
|
@@ -254,121 +281,10 @@ class Collection {
|
|
|
254
281
|
if (!config.sync) {
|
|
255
282
|
throw new Error(`Collection requires a sync config`);
|
|
256
283
|
}
|
|
257
|
-
this.transactions = new
|
|
258
|
-
|
|
259
|
-
(a, b) => a.createdAt.getTime() - b.createdAt.getTime()
|
|
260
|
-
)
|
|
284
|
+
this.transactions = new SortedMap(
|
|
285
|
+
(a, b) => a.createdAt.getTime() - b.createdAt.getTime()
|
|
261
286
|
);
|
|
262
|
-
this.optimisticOperations = new Derived({
|
|
263
|
-
fn: ({ currDepVals: [transactions] }) => {
|
|
264
|
-
const result = Array.from(transactions.values()).map((transaction) => {
|
|
265
|
-
const isActive = ![`completed`, `failed`].includes(
|
|
266
|
-
transaction.state
|
|
267
|
-
);
|
|
268
|
-
return transaction.mutations.filter((mutation) => mutation.collection === this).map((mutation) => {
|
|
269
|
-
const message = {
|
|
270
|
-
type: mutation.type,
|
|
271
|
-
key: mutation.key,
|
|
272
|
-
value: mutation.modified,
|
|
273
|
-
isActive
|
|
274
|
-
};
|
|
275
|
-
if (mutation.metadata !== void 0 && mutation.metadata !== null) {
|
|
276
|
-
message.metadata = mutation.metadata;
|
|
277
|
-
}
|
|
278
|
-
return message;
|
|
279
|
-
});
|
|
280
|
-
}).flat();
|
|
281
|
-
return result;
|
|
282
|
-
},
|
|
283
|
-
deps: [this.transactions]
|
|
284
|
-
});
|
|
285
|
-
this.optimisticOperations.mount();
|
|
286
|
-
this.derivedState = new Derived({
|
|
287
|
-
fn: ({ currDepVals: [syncedData, operations] }) => {
|
|
288
|
-
const combined = new Map(syncedData);
|
|
289
|
-
for (const operation of operations) {
|
|
290
|
-
if (operation.isActive) {
|
|
291
|
-
switch (operation.type) {
|
|
292
|
-
case `insert`:
|
|
293
|
-
combined.set(operation.key, operation.value);
|
|
294
|
-
break;
|
|
295
|
-
case `update`:
|
|
296
|
-
combined.set(operation.key, operation.value);
|
|
297
|
-
break;
|
|
298
|
-
case `delete`:
|
|
299
|
-
combined.delete(operation.key);
|
|
300
|
-
break;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
return combined;
|
|
305
|
-
},
|
|
306
|
-
deps: [this.syncedData, this.optimisticOperations]
|
|
307
|
-
});
|
|
308
|
-
this.derivedArray = new Derived({
|
|
309
|
-
fn: ({ currDepVals: [stateMap] }) => {
|
|
310
|
-
const array = Array.from(
|
|
311
|
-
stateMap.values()
|
|
312
|
-
);
|
|
313
|
-
if (array[0] && `_orderByIndex` in array[0]) {
|
|
314
|
-
array.sort((a, b) => {
|
|
315
|
-
if (a._orderByIndex === b._orderByIndex) {
|
|
316
|
-
return 0;
|
|
317
|
-
}
|
|
318
|
-
return a._orderByIndex < b._orderByIndex ? -1 : 1;
|
|
319
|
-
});
|
|
320
|
-
}
|
|
321
|
-
return array;
|
|
322
|
-
},
|
|
323
|
-
deps: [this.derivedState]
|
|
324
|
-
});
|
|
325
|
-
this.derivedArray.mount();
|
|
326
|
-
this.derivedChanges = new Derived({
|
|
327
|
-
fn: ({
|
|
328
|
-
currDepVals: [derivedState, optimisticOperations],
|
|
329
|
-
prevDepVals
|
|
330
|
-
}) => {
|
|
331
|
-
const prevDerivedState = (prevDepVals == null ? void 0 : prevDepVals[0]) ?? /* @__PURE__ */ new Map();
|
|
332
|
-
const prevOptimisticOperations = (prevDepVals == null ? void 0 : prevDepVals[1]) ?? [];
|
|
333
|
-
const changedKeys = new Set(this.syncedKeys);
|
|
334
|
-
optimisticOperations.flat().filter((op) => op.isActive).forEach((op) => changedKeys.add(op.key));
|
|
335
|
-
prevOptimisticOperations.flat().forEach((op) => {
|
|
336
|
-
changedKeys.add(op.key);
|
|
337
|
-
});
|
|
338
|
-
if (changedKeys.size === 0) {
|
|
339
|
-
return [];
|
|
340
|
-
}
|
|
341
|
-
const changes = [];
|
|
342
|
-
for (const key of changedKeys) {
|
|
343
|
-
if (prevDerivedState.has(key) && !derivedState.has(key)) {
|
|
344
|
-
changes.push({
|
|
345
|
-
type: `delete`,
|
|
346
|
-
key,
|
|
347
|
-
value: prevDerivedState.get(key)
|
|
348
|
-
});
|
|
349
|
-
} else if (!prevDerivedState.has(key) && derivedState.has(key)) {
|
|
350
|
-
changes.push({ type: `insert`, key, value: derivedState.get(key) });
|
|
351
|
-
} else if (prevDerivedState.has(key) && derivedState.has(key)) {
|
|
352
|
-
const value = derivedState.get(key);
|
|
353
|
-
const previousValue = prevDerivedState.get(key);
|
|
354
|
-
if (value !== previousValue) {
|
|
355
|
-
changes.push({
|
|
356
|
-
type: `update`,
|
|
357
|
-
key,
|
|
358
|
-
value,
|
|
359
|
-
previousValue
|
|
360
|
-
});
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
this.syncedKeys.clear();
|
|
365
|
-
return changes;
|
|
366
|
-
},
|
|
367
|
-
deps: [this.derivedState, this.optimisticOperations]
|
|
368
|
-
});
|
|
369
|
-
this.derivedChanges.mount();
|
|
370
287
|
this.config = config;
|
|
371
|
-
this.derivedState.mount();
|
|
372
288
|
config.sync.sync({
|
|
373
289
|
collection: this,
|
|
374
290
|
begin: () => {
|
|
@@ -387,17 +303,13 @@ class Collection {
|
|
|
387
303
|
`The pending sync transaction is already committed, you can't still write to it.`
|
|
388
304
|
);
|
|
389
305
|
}
|
|
390
|
-
const key = this.
|
|
391
|
-
this.config.getId(messageWithoutKey.value),
|
|
392
|
-
messageWithoutKey.value
|
|
393
|
-
);
|
|
306
|
+
const key = this.getKeyFromItem(messageWithoutKey.value);
|
|
394
307
|
if (messageWithoutKey.type === `insert`) {
|
|
395
|
-
if (this.syncedData.
|
|
308
|
+
if (this.syncedData.has(key) && !pendingTransaction.operations.some(
|
|
396
309
|
(op) => op.key === key && op.type === `delete`
|
|
397
310
|
)) {
|
|
398
|
-
const id = this.config.getId(messageWithoutKey.value);
|
|
399
311
|
throw new Error(
|
|
400
|
-
`Cannot insert document with
|
|
312
|
+
`Cannot insert document with key "${key}" from sync because it already exists in the collection "${this.id}"`
|
|
401
313
|
);
|
|
402
314
|
}
|
|
403
315
|
}
|
|
@@ -430,6 +342,189 @@ class Collection {
|
|
|
430
342
|
onFirstCommit(callback) {
|
|
431
343
|
this.onFirstCommitCallbacks.push(callback);
|
|
432
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
|
+
}
|
|
433
528
|
ensureStandardSchema(schema) {
|
|
434
529
|
if (schema && typeof schema === `object` && `~standard` in schema) {
|
|
435
530
|
return schema;
|
|
@@ -438,31 +533,24 @@ class Collection {
|
|
|
438
533
|
`Schema must either implement the standard-schema interface or be a Zod schema`
|
|
439
534
|
);
|
|
440
535
|
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
throw new Error(`id is undefined`);
|
|
444
|
-
}
|
|
445
|
-
if (typeof id === `string` && id.startsWith(`KEY::`)) {
|
|
446
|
-
return id;
|
|
447
|
-
} else {
|
|
448
|
-
return this.generateObjectKey(id, null);
|
|
449
|
-
}
|
|
536
|
+
getKeyFromItem(item) {
|
|
537
|
+
return this.config.getKey(item);
|
|
450
538
|
}
|
|
451
|
-
|
|
452
|
-
if (typeof
|
|
539
|
+
generateGlobalKey(key, item) {
|
|
540
|
+
if (typeof key === `undefined`) {
|
|
453
541
|
throw new Error(
|
|
454
|
-
`An object was created without a defined
|
|
542
|
+
`An object was created without a defined key: ${JSON.stringify(item)}`
|
|
455
543
|
);
|
|
456
544
|
}
|
|
457
|
-
return `KEY::${this.id}/${
|
|
545
|
+
return `KEY::${this.id}/${key}`;
|
|
458
546
|
}
|
|
459
547
|
validateData(data, type, key) {
|
|
460
548
|
if (!this.config.schema) return data;
|
|
461
549
|
const standardSchema = this.ensureStandardSchema(this.config.schema);
|
|
462
550
|
if (type === `update` && key) {
|
|
463
|
-
const existingData = this.
|
|
551
|
+
const existingData = this.get(key);
|
|
464
552
|
if (existingData && data && typeof data === `object` && typeof existingData === `object`) {
|
|
465
|
-
const mergedData = {
|
|
553
|
+
const mergedData = Object.assign({}, existingData, data);
|
|
466
554
|
const result2 = standardSchema[`~standard`].validate(mergedData);
|
|
467
555
|
if (result2 instanceof Promise) {
|
|
468
556
|
throw new TypeError(`Schema validation must be synchronous`);
|
|
@@ -496,8 +584,8 @@ class Collection {
|
|
|
496
584
|
}
|
|
497
585
|
return result.value;
|
|
498
586
|
}
|
|
499
|
-
update(
|
|
500
|
-
if (typeof
|
|
587
|
+
update(keys, configOrCallback, maybeCallback) {
|
|
588
|
+
if (typeof keys === `undefined`) {
|
|
501
589
|
throw new Error(`The first argument to update is missing`);
|
|
502
590
|
}
|
|
503
591
|
const ambientTransaction = getActiveTransaction();
|
|
@@ -506,17 +594,18 @@ class Collection {
|
|
|
506
594
|
`Collection.update called directly (not within an explicit transaction) but no 'onUpdate' handler is configured.`
|
|
507
595
|
);
|
|
508
596
|
}
|
|
509
|
-
const isArray = Array.isArray(
|
|
510
|
-
const
|
|
511
|
-
|
|
512
|
-
|
|
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
|
+
}
|
|
513
602
|
const callback = typeof configOrCallback === `function` ? configOrCallback : maybeCallback;
|
|
514
603
|
const config = typeof configOrCallback === `function` ? {} : configOrCallback;
|
|
515
|
-
const currentObjects =
|
|
516
|
-
const item = this.
|
|
604
|
+
const currentObjects = keysArray.map((key) => {
|
|
605
|
+
const item = this.get(key);
|
|
517
606
|
if (!item) {
|
|
518
607
|
throw new Error(
|
|
519
|
-
`The
|
|
608
|
+
`The key "${key}" was passed to update but an object for this key was not found in the collection`
|
|
520
609
|
);
|
|
521
610
|
}
|
|
522
611
|
return item;
|
|
@@ -534,7 +623,7 @@ class Collection {
|
|
|
534
623
|
);
|
|
535
624
|
changesArray = [result];
|
|
536
625
|
}
|
|
537
|
-
const mutations =
|
|
626
|
+
const mutations = keysArray.map((key, index) => {
|
|
538
627
|
const itemChanges = changesArray[index];
|
|
539
628
|
if (!itemChanges || Object.keys(itemChanges).length === 0) {
|
|
540
629
|
return null;
|
|
@@ -543,24 +632,30 @@ class Collection {
|
|
|
543
632
|
const validatedUpdatePayload = this.validateData(
|
|
544
633
|
itemChanges,
|
|
545
634
|
`update`,
|
|
546
|
-
|
|
635
|
+
key
|
|
547
636
|
);
|
|
548
|
-
const modifiedItem =
|
|
549
|
-
|
|
550
|
-
|
|
637
|
+
const modifiedItem = Object.assign(
|
|
638
|
+
{},
|
|
639
|
+
originalItem,
|
|
640
|
+
validatedUpdatePayload
|
|
641
|
+
);
|
|
642
|
+
const originalItemId = this.getKeyFromItem(originalItem);
|
|
643
|
+
const modifiedItemId = this.getKeyFromItem(modifiedItem);
|
|
551
644
|
if (originalItemId !== modifiedItemId) {
|
|
552
645
|
throw new Error(
|
|
553
|
-
`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.`
|
|
554
647
|
);
|
|
555
648
|
}
|
|
649
|
+
const globalKey = this.generateGlobalKey(modifiedItemId, modifiedItem);
|
|
556
650
|
return {
|
|
557
651
|
mutationId: crypto.randomUUID(),
|
|
558
652
|
original: originalItem,
|
|
559
653
|
modified: modifiedItem,
|
|
560
654
|
changes: validatedUpdatePayload,
|
|
561
|
-
|
|
655
|
+
globalKey,
|
|
656
|
+
key,
|
|
562
657
|
metadata: config.metadata,
|
|
563
|
-
syncMetadata: this.syncedMetadata.
|
|
658
|
+
syncMetadata: this.syncedMetadata.get(key) || {},
|
|
564
659
|
type: `update`,
|
|
565
660
|
createdAt: /* @__PURE__ */ new Date(),
|
|
566
661
|
updatedAt: /* @__PURE__ */ new Date(),
|
|
@@ -572,23 +667,19 @@ class Collection {
|
|
|
572
667
|
}
|
|
573
668
|
if (ambientTransaction) {
|
|
574
669
|
ambientTransaction.applyMutations(mutations);
|
|
575
|
-
this.transactions.
|
|
576
|
-
|
|
577
|
-
return sortedMap;
|
|
578
|
-
});
|
|
670
|
+
this.transactions.set(ambientTransaction.id, ambientTransaction);
|
|
671
|
+
this.recomputeOptimisticState();
|
|
579
672
|
return ambientTransaction;
|
|
580
673
|
}
|
|
581
674
|
const directOpTransaction = new Transaction({
|
|
582
|
-
mutationFn: async (
|
|
583
|
-
return this.config.onUpdate(
|
|
675
|
+
mutationFn: async (params) => {
|
|
676
|
+
return this.config.onUpdate(params);
|
|
584
677
|
}
|
|
585
678
|
});
|
|
586
679
|
directOpTransaction.applyMutations(mutations);
|
|
587
680
|
directOpTransaction.commit();
|
|
588
|
-
this.transactions.
|
|
589
|
-
|
|
590
|
-
return sortedMap;
|
|
591
|
-
});
|
|
681
|
+
this.transactions.set(directOpTransaction.id, directOpTransaction);
|
|
682
|
+
this.recomputeOptimisticState();
|
|
592
683
|
return directOpTransaction;
|
|
593
684
|
}
|
|
594
685
|
/**
|
|
@@ -597,7 +688,11 @@ class Collection {
|
|
|
597
688
|
* @returns A Map containing all items in the collection, with keys as identifiers
|
|
598
689
|
*/
|
|
599
690
|
get state() {
|
|
600
|
-
|
|
691
|
+
const result = /* @__PURE__ */ new Map();
|
|
692
|
+
for (const [key, value] of this.entries()) {
|
|
693
|
+
result.set(key, value);
|
|
694
|
+
}
|
|
695
|
+
return result;
|
|
601
696
|
}
|
|
602
697
|
/**
|
|
603
698
|
* Gets the current state of the collection as a Map, but only resolves when data is available
|
|
@@ -606,7 +701,7 @@ class Collection {
|
|
|
606
701
|
* @returns Promise that resolves to a Map containing all items in the collection
|
|
607
702
|
*/
|
|
608
703
|
stateWhenReady() {
|
|
609
|
-
if (this.
|
|
704
|
+
if (this.size > 0 || this.hasReceivedFirstCommit === true) {
|
|
610
705
|
return Promise.resolve(this.state);
|
|
611
706
|
}
|
|
612
707
|
return new Promise((resolve) => {
|
|
@@ -621,7 +716,13 @@ class Collection {
|
|
|
621
716
|
* @returns An Array containing all items in the collection
|
|
622
717
|
*/
|
|
623
718
|
get toArray() {
|
|
624
|
-
|
|
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;
|
|
625
726
|
}
|
|
626
727
|
/**
|
|
627
728
|
* Gets the current state of the collection as an Array, but only resolves when data is available
|
|
@@ -630,7 +731,7 @@ class Collection {
|
|
|
630
731
|
* @returns Promise that resolves to an Array containing all items in the collection
|
|
631
732
|
*/
|
|
632
733
|
toArrayWhenReady() {
|
|
633
|
-
if (this.
|
|
734
|
+
if (this.size > 0 || this.hasReceivedFirstCommit === true) {
|
|
634
735
|
return Promise.resolve(this.toArray);
|
|
635
736
|
}
|
|
636
737
|
return new Promise((resolve) => {
|
|
@@ -644,7 +745,7 @@ class Collection {
|
|
|
644
745
|
* @returns An array of changes
|
|
645
746
|
*/
|
|
646
747
|
currentStateAsChanges() {
|
|
647
|
-
return
|
|
748
|
+
return Array.from(this.entries()).map(([key, value]) => ({
|
|
648
749
|
type: `insert`,
|
|
649
750
|
key,
|
|
650
751
|
value
|
|
@@ -655,17 +756,82 @@ class Collection {
|
|
|
655
756
|
* @param callback - A function that will be called with the changes in the collection
|
|
656
757
|
* @returns A function that can be called to unsubscribe from the changes
|
|
657
758
|
*/
|
|
658
|
-
subscribeChanges(callback) {
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
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
|
+
}
|
|
663
792
|
}
|
|
664
|
-
}
|
|
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;
|
|
665
831
|
}
|
|
666
832
|
}
|
|
667
833
|
export {
|
|
668
|
-
|
|
834
|
+
CollectionImpl,
|
|
669
835
|
SchemaValidationError,
|
|
670
836
|
collectionsStore,
|
|
671
837
|
createCollection,
|