@tanstack/db 0.0.4 → 0.0.5
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 +113 -94
- package/dist/cjs/collection.cjs.map +1 -1
- package/dist/cjs/collection.d.cts +38 -11
- package/dist/cjs/index.cjs +1 -0
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/proxy.cjs +87 -248
- package/dist/cjs/proxy.cjs.map +1 -1
- package/dist/cjs/proxy.d.cts +5 -5
- package/dist/cjs/query/compiled-query.cjs +23 -14
- package/dist/cjs/query/compiled-query.cjs.map +1 -1
- package/dist/cjs/query/compiled-query.d.cts +3 -1
- package/dist/cjs/query/evaluators.cjs +20 -20
- package/dist/cjs/query/evaluators.cjs.map +1 -1
- package/dist/cjs/query/evaluators.d.cts +3 -2
- package/dist/cjs/query/extractors.cjs +20 -20
- package/dist/cjs/query/extractors.cjs.map +1 -1
- package/dist/cjs/query/extractors.d.cts +3 -3
- package/dist/cjs/query/group-by.cjs +12 -15
- package/dist/cjs/query/group-by.cjs.map +1 -1
- package/dist/cjs/query/group-by.d.cts +7 -7
- package/dist/cjs/query/joins.cjs +41 -55
- package/dist/cjs/query/joins.cjs.map +1 -1
- package/dist/cjs/query/joins.d.cts +3 -3
- package/dist/cjs/query/order-by.cjs +37 -84
- package/dist/cjs/query/order-by.cjs.map +1 -1
- package/dist/cjs/query/order-by.d.cts +2 -2
- package/dist/cjs/query/pipeline-compiler.cjs +13 -18
- package/dist/cjs/query/pipeline-compiler.cjs.map +1 -1
- package/dist/cjs/query/pipeline-compiler.d.cts +2 -1
- package/dist/cjs/query/query-builder.cjs +0 -12
- package/dist/cjs/query/query-builder.cjs.map +1 -1
- package/dist/cjs/query/query-builder.d.cts +4 -8
- package/dist/cjs/query/schema.d.cts +1 -6
- package/dist/cjs/query/select.cjs +35 -24
- package/dist/cjs/query/select.cjs.map +1 -1
- package/dist/cjs/query/select.d.cts +2 -2
- package/dist/cjs/query/types.d.cts +1 -0
- package/dist/cjs/transactions.cjs +17 -8
- package/dist/cjs/transactions.cjs.map +1 -1
- package/dist/cjs/types.d.cts +41 -7
- package/dist/esm/collection.d.ts +38 -11
- package/dist/esm/collection.js +113 -94
- package/dist/esm/collection.js.map +1 -1
- package/dist/esm/index.js +2 -1
- package/dist/esm/proxy.d.ts +5 -5
- package/dist/esm/proxy.js +87 -248
- package/dist/esm/proxy.js.map +1 -1
- package/dist/esm/query/compiled-query.d.ts +3 -1
- package/dist/esm/query/compiled-query.js +23 -14
- package/dist/esm/query/compiled-query.js.map +1 -1
- package/dist/esm/query/evaluators.d.ts +3 -2
- package/dist/esm/query/evaluators.js +21 -21
- package/dist/esm/query/evaluators.js.map +1 -1
- package/dist/esm/query/extractors.d.ts +3 -3
- package/dist/esm/query/extractors.js +20 -20
- package/dist/esm/query/extractors.js.map +1 -1
- package/dist/esm/query/group-by.d.ts +7 -7
- package/dist/esm/query/group-by.js +14 -17
- package/dist/esm/query/group-by.js.map +1 -1
- package/dist/esm/query/joins.d.ts +3 -3
- package/dist/esm/query/joins.js +42 -56
- package/dist/esm/query/joins.js.map +1 -1
- package/dist/esm/query/order-by.d.ts +2 -2
- package/dist/esm/query/order-by.js +39 -86
- package/dist/esm/query/order-by.js.map +1 -1
- package/dist/esm/query/pipeline-compiler.d.ts +2 -1
- package/dist/esm/query/pipeline-compiler.js +14 -19
- package/dist/esm/query/pipeline-compiler.js.map +1 -1
- package/dist/esm/query/query-builder.d.ts +4 -8
- package/dist/esm/query/query-builder.js +0 -12
- package/dist/esm/query/query-builder.js.map +1 -1
- package/dist/esm/query/schema.d.ts +1 -6
- package/dist/esm/query/select.d.ts +2 -2
- package/dist/esm/query/select.js +36 -25
- package/dist/esm/query/select.js.map +1 -1
- package/dist/esm/query/types.d.ts +1 -0
- package/dist/esm/transactions.js +17 -8
- package/dist/esm/transactions.js.map +1 -1
- package/dist/esm/types.d.ts +41 -7
- package/package.json +2 -2
- package/src/collection.ts +174 -121
- package/src/proxy.ts +141 -358
- package/src/query/compiled-query.ts +30 -15
- package/src/query/evaluators.ts +22 -21
- package/src/query/extractors.ts +24 -21
- package/src/query/group-by.ts +24 -22
- package/src/query/joins.ts +88 -75
- package/src/query/order-by.ts +56 -106
- package/src/query/pipeline-compiler.ts +34 -37
- package/src/query/query-builder.ts +9 -23
- package/src/query/schema.ts +1 -10
- package/src/query/select.ts +44 -32
- package/src/query/types.ts +1 -0
- package/src/transactions.ts +22 -13
- package/src/types.ts +48 -7
- package/dist/cjs/query/key-by.cjs +0 -43
- package/dist/cjs/query/key-by.cjs.map +0 -1
- package/dist/cjs/query/key-by.d.cts +0 -3
- package/dist/esm/query/key-by.d.ts +0 -3
- package/dist/esm/query/key-by.js +0 -43
- package/dist/esm/query/key-by.js.map +0 -1
- package/src/query/key-by.ts +0 -61
package/dist/cjs/collection.cjs
CHANGED
|
@@ -6,7 +6,13 @@ const transactions = require("./transactions.cjs");
|
|
|
6
6
|
const SortedMap = require("./SortedMap.cjs");
|
|
7
7
|
const collectionsStore = new store.Store(/* @__PURE__ */ new Map());
|
|
8
8
|
const loadingCollections = /* @__PURE__ */ new Map();
|
|
9
|
+
function createCollection(config) {
|
|
10
|
+
return new Collection(config);
|
|
11
|
+
}
|
|
9
12
|
function preloadCollection(config) {
|
|
13
|
+
if (!config.id) {
|
|
14
|
+
throw new Error(`The id property is required for preloadCollection`);
|
|
15
|
+
}
|
|
10
16
|
if (collectionsStore.state.has(config.id) && !loadingCollections.has(config.id)) {
|
|
11
17
|
return Promise.resolve(
|
|
12
18
|
collectionsStore.state.get(config.id)
|
|
@@ -18,10 +24,14 @@ function preloadCollection(config) {
|
|
|
18
24
|
if (!collectionsStore.state.has(config.id)) {
|
|
19
25
|
collectionsStore.setState((prev) => {
|
|
20
26
|
const next = new Map(prev);
|
|
27
|
+
if (!config.id) {
|
|
28
|
+
throw new Error(`The id property is required for preloadCollection`);
|
|
29
|
+
}
|
|
21
30
|
next.set(
|
|
22
31
|
config.id,
|
|
23
32
|
new Collection({
|
|
24
33
|
id: config.id,
|
|
34
|
+
getId: config.getId,
|
|
25
35
|
sync: config.sync,
|
|
26
36
|
schema: config.schema
|
|
27
37
|
})
|
|
@@ -37,6 +47,9 @@ function preloadCollection(config) {
|
|
|
37
47
|
};
|
|
38
48
|
});
|
|
39
49
|
collection.onFirstCommit(() => {
|
|
50
|
+
if (!config.id) {
|
|
51
|
+
throw new Error(`The id property is required for preloadCollection`);
|
|
52
|
+
}
|
|
40
53
|
if (loadingCollections.has(config.id)) {
|
|
41
54
|
loadingCollections.delete(config.id);
|
|
42
55
|
resolveFirstCommit();
|
|
@@ -70,18 +83,15 @@ class Collection {
|
|
|
70
83
|
this.pendingSyncedTransactions = [];
|
|
71
84
|
this.syncedKeys = /* @__PURE__ */ new Set();
|
|
72
85
|
this.hasReceivedFirstCommit = false;
|
|
73
|
-
this.objectKeyMap = /* @__PURE__ */ new WeakMap();
|
|
74
86
|
this.onFirstCommitCallbacks = [];
|
|
75
|
-
this.id =
|
|
87
|
+
this.id = ``;
|
|
76
88
|
this.commitPendingTransactions = () => {
|
|
77
89
|
if (!Array.from(this.transactions.state.values()).some(
|
|
78
90
|
({ state }) => state === `persisting`
|
|
79
91
|
)) {
|
|
80
|
-
const keys = /* @__PURE__ */ new Set();
|
|
81
92
|
store.batch(() => {
|
|
82
93
|
for (const transaction of this.pendingSyncedTransactions) {
|
|
83
94
|
for (const operation of transaction.operations) {
|
|
84
|
-
keys.add(operation.key);
|
|
85
95
|
this.syncedKeys.add(operation.key);
|
|
86
96
|
this.syncedMetadata.setState((prevData) => {
|
|
87
97
|
switch (operation.type) {
|
|
@@ -120,12 +130,6 @@ class Collection {
|
|
|
120
130
|
}
|
|
121
131
|
}
|
|
122
132
|
});
|
|
123
|
-
keys.forEach((key) => {
|
|
124
|
-
const curValue = this.state.get(key);
|
|
125
|
-
if (curValue) {
|
|
126
|
-
this.objectKeyMap.set(curValue, key);
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
133
|
this.pendingSyncedTransactions = [];
|
|
130
134
|
if (!this.hasReceivedFirstCommit) {
|
|
131
135
|
this.hasReceivedFirstCommit = true;
|
|
@@ -142,20 +146,17 @@ class Collection {
|
|
|
142
146
|
}
|
|
143
147
|
const items = Array.isArray(data) ? data : [data];
|
|
144
148
|
const mutations = [];
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
if (Array.isArray(config2.key) && configKeys.length > items.length) {
|
|
149
|
-
throw new Error(`More keys provided than items to insert`);
|
|
150
|
-
}
|
|
151
|
-
keys = items.map((_, i) => configKeys[i] ?? this.generateKey(items[i]));
|
|
152
|
-
} else {
|
|
153
|
-
keys = items.map((item) => this.generateKey(item));
|
|
154
|
-
}
|
|
149
|
+
const keys = items.map(
|
|
150
|
+
(item) => this.generateObjectKey(this.config.getId(item), item)
|
|
151
|
+
);
|
|
155
152
|
items.forEach((item, index) => {
|
|
156
153
|
var _a, _b;
|
|
157
154
|
const validatedData = this.validateData(item, `insert`);
|
|
158
155
|
const key = keys[index];
|
|
156
|
+
const id = this.config.getId(item);
|
|
157
|
+
if (this.state.has(this.getKeyFromId(id))) {
|
|
158
|
+
throw `Cannot insert document with ID "${id}" because it already exists in the collection`;
|
|
159
|
+
}
|
|
159
160
|
const mutation = {
|
|
160
161
|
mutationId: crypto.randomUUID(),
|
|
161
162
|
original: {},
|
|
@@ -178,38 +179,24 @@ class Collection {
|
|
|
178
179
|
});
|
|
179
180
|
return transaction;
|
|
180
181
|
};
|
|
181
|
-
this.delete = (
|
|
182
|
+
this.delete = (ids, config2) => {
|
|
182
183
|
const transaction = transactions.getActiveTransaction();
|
|
183
184
|
if (typeof transaction === `undefined`) {
|
|
184
185
|
throw `no transaction found when calling collection.delete`;
|
|
185
186
|
}
|
|
186
|
-
const
|
|
187
|
+
const idsArray = (Array.isArray(ids) ? ids : [ids]).map(
|
|
188
|
+
(id) => this.getKeyFromId(id)
|
|
189
|
+
);
|
|
187
190
|
const mutations = [];
|
|
188
|
-
for (const
|
|
189
|
-
let key;
|
|
190
|
-
if (typeof item === `object` && item !== null) {
|
|
191
|
-
const objectKey = this.objectKeyMap.get(item);
|
|
192
|
-
if (objectKey === void 0) {
|
|
193
|
-
throw new Error(
|
|
194
|
-
`Object not found in collection: ${JSON.stringify(item)}`
|
|
195
|
-
);
|
|
196
|
-
}
|
|
197
|
-
key = objectKey;
|
|
198
|
-
} else if (typeof item === `string`) {
|
|
199
|
-
key = item;
|
|
200
|
-
} else {
|
|
201
|
-
throw new Error(
|
|
202
|
-
`Invalid item type for delete - must be an object or string key`
|
|
203
|
-
);
|
|
204
|
-
}
|
|
191
|
+
for (const id of idsArray) {
|
|
205
192
|
const mutation = {
|
|
206
193
|
mutationId: crypto.randomUUID(),
|
|
207
|
-
original: this.state.get(
|
|
208
|
-
modified:
|
|
209
|
-
changes:
|
|
210
|
-
key,
|
|
194
|
+
original: this.state.get(id) || {},
|
|
195
|
+
modified: this.state.get(id) || {},
|
|
196
|
+
changes: this.state.get(id) || {},
|
|
197
|
+
key: id,
|
|
211
198
|
metadata: config2 == null ? void 0 : config2.metadata,
|
|
212
|
-
syncMetadata: this.syncedMetadata.state.get(
|
|
199
|
+
syncMetadata: this.syncedMetadata.state.get(id) || {},
|
|
213
200
|
type: `delete`,
|
|
214
201
|
createdAt: /* @__PURE__ */ new Date(),
|
|
215
202
|
updatedAt: /* @__PURE__ */ new Date(),
|
|
@@ -217,12 +204,6 @@ class Collection {
|
|
|
217
204
|
};
|
|
218
205
|
mutations.push(mutation);
|
|
219
206
|
}
|
|
220
|
-
mutations.forEach((mutation) => {
|
|
221
|
-
const curValue = this.state.get(mutation.key);
|
|
222
|
-
if (curValue) {
|
|
223
|
-
this.objectKeyMap.delete(curValue);
|
|
224
|
-
}
|
|
225
|
-
});
|
|
226
207
|
transaction.applyMutations(mutations);
|
|
227
208
|
this.transactions.setState((sortedMap) => {
|
|
228
209
|
sortedMap.set(transaction.id, transaction);
|
|
@@ -230,7 +211,15 @@ class Collection {
|
|
|
230
211
|
});
|
|
231
212
|
return transaction;
|
|
232
213
|
};
|
|
233
|
-
if (!
|
|
214
|
+
if (!config) {
|
|
215
|
+
throw new Error(`Collection requires a config`);
|
|
216
|
+
}
|
|
217
|
+
if (config.id) {
|
|
218
|
+
this.id = config.id;
|
|
219
|
+
} else {
|
|
220
|
+
this.id = crypto.randomUUID();
|
|
221
|
+
}
|
|
222
|
+
if (!config.sync) {
|
|
234
223
|
throw new Error(`Collection requires a sync config`);
|
|
235
224
|
}
|
|
236
225
|
this.transactions = new store.Store(
|
|
@@ -265,9 +254,7 @@ class Collection {
|
|
|
265
254
|
this.derivedState = new store.Derived({
|
|
266
255
|
fn: ({ currDepVals: [syncedData, operations] }) => {
|
|
267
256
|
const combined = new Map(syncedData);
|
|
268
|
-
const optimisticKeys = /* @__PURE__ */ new Set();
|
|
269
257
|
for (const operation of operations) {
|
|
270
|
-
optimisticKeys.add(operation.key);
|
|
271
258
|
if (operation.isActive) {
|
|
272
259
|
switch (operation.type) {
|
|
273
260
|
case `insert`:
|
|
@@ -282,11 +269,6 @@ class Collection {
|
|
|
282
269
|
}
|
|
283
270
|
}
|
|
284
271
|
}
|
|
285
|
-
optimisticKeys.forEach((key) => {
|
|
286
|
-
if (combined.has(key)) {
|
|
287
|
-
this.objectKeyMap.set(combined.get(key), key);
|
|
288
|
-
}
|
|
289
|
-
});
|
|
290
272
|
return combined;
|
|
291
273
|
},
|
|
292
274
|
deps: [this.syncedData, this.optimisticOperations]
|
|
@@ -363,7 +345,7 @@ class Collection {
|
|
|
363
345
|
operations: []
|
|
364
346
|
});
|
|
365
347
|
},
|
|
366
|
-
write: (
|
|
348
|
+
write: (messageWithoutKey) => {
|
|
367
349
|
const pendingTransaction = this.pendingSyncedTransactions[this.pendingSyncedTransactions.length - 1];
|
|
368
350
|
if (!pendingTransaction) {
|
|
369
351
|
throw new Error(`No pending sync transaction to write to`);
|
|
@@ -373,6 +355,24 @@ class Collection {
|
|
|
373
355
|
`The pending sync transaction is already committed, you can't still write to it.`
|
|
374
356
|
);
|
|
375
357
|
}
|
|
358
|
+
const key = this.generateObjectKey(
|
|
359
|
+
this.config.getId(messageWithoutKey.value),
|
|
360
|
+
messageWithoutKey.value
|
|
361
|
+
);
|
|
362
|
+
if (messageWithoutKey.type === `insert`) {
|
|
363
|
+
if (this.syncedData.state.has(key) && !pendingTransaction.operations.some(
|
|
364
|
+
(op) => op.key === key && op.type === `delete`
|
|
365
|
+
)) {
|
|
366
|
+
const id = this.config.getId(messageWithoutKey.value);
|
|
367
|
+
throw new Error(
|
|
368
|
+
`Cannot insert document with ID "${id}" from sync because it already exists in the collection "${this.id}"`
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
const message = {
|
|
373
|
+
...messageWithoutKey,
|
|
374
|
+
key
|
|
375
|
+
};
|
|
376
376
|
pendingTransaction.operations.push(message);
|
|
377
377
|
},
|
|
378
378
|
commit: () => {
|
|
@@ -406,6 +406,24 @@ class Collection {
|
|
|
406
406
|
`Schema must either implement the standard-schema interface or be a Zod schema`
|
|
407
407
|
);
|
|
408
408
|
}
|
|
409
|
+
getKeyFromId(id) {
|
|
410
|
+
if (typeof id === `undefined`) {
|
|
411
|
+
throw new Error(`id is undefined`);
|
|
412
|
+
}
|
|
413
|
+
if (typeof id === `string` && id.startsWith(`KEY::`)) {
|
|
414
|
+
return id;
|
|
415
|
+
} else {
|
|
416
|
+
return this.generateObjectKey(id, null);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
generateObjectKey(id, item) {
|
|
420
|
+
if (typeof id === `undefined`) {
|
|
421
|
+
throw new Error(
|
|
422
|
+
`An object was created without a defined id: ${JSON.stringify(item)}`
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
return `KEY::${this.id}/${id}`;
|
|
426
|
+
}
|
|
409
427
|
validateData(data, type, key) {
|
|
410
428
|
if (!this.config.schema) return data;
|
|
411
429
|
const standardSchema = this.ensureStandardSchema(this.config.schema);
|
|
@@ -446,39 +464,29 @@ class Collection {
|
|
|
446
464
|
}
|
|
447
465
|
return result.value;
|
|
448
466
|
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
let h = 0;
|
|
452
|
-
for (let i = 0; i < str.length; i++) {
|
|
453
|
-
h = Math.imul(31, h) + str.charCodeAt(i) | 0;
|
|
454
|
-
}
|
|
455
|
-
return `${this.id}/${Math.abs(h).toString(36)}`;
|
|
456
|
-
}
|
|
457
|
-
update(items, configOrCallback, maybeCallback) {
|
|
458
|
-
if (typeof items === `undefined`) {
|
|
467
|
+
update(ids, configOrCallback, maybeCallback) {
|
|
468
|
+
if (typeof ids === `undefined`) {
|
|
459
469
|
throw new Error(`The first argument to update is missing`);
|
|
460
470
|
}
|
|
461
471
|
const transaction = transactions.getActiveTransaction();
|
|
462
472
|
if (typeof transaction === `undefined`) {
|
|
463
473
|
throw `no transaction found when calling collection.update`;
|
|
464
474
|
}
|
|
465
|
-
const isArray = Array.isArray(
|
|
466
|
-
const
|
|
475
|
+
const isArray = Array.isArray(ids);
|
|
476
|
+
const idsArray = (Array.isArray(ids) ? ids : [ids]).map(
|
|
477
|
+
(id) => this.getKeyFromId(id)
|
|
478
|
+
);
|
|
467
479
|
const callback = typeof configOrCallback === `function` ? configOrCallback : maybeCallback;
|
|
468
480
|
const config = typeof configOrCallback === `function` ? {} : configOrCallback;
|
|
469
|
-
const
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
return key;
|
|
481
|
+
const currentObjects = idsArray.map((id) => {
|
|
482
|
+
const item = this.state.get(id);
|
|
483
|
+
if (!item) {
|
|
484
|
+
throw new Error(
|
|
485
|
+
`The id "${id}" was passed to update but an object for this ID was not found in the collection`
|
|
486
|
+
);
|
|
476
487
|
}
|
|
477
|
-
|
|
488
|
+
return item;
|
|
478
489
|
});
|
|
479
|
-
const currentObjects = keys.map((key) => ({
|
|
480
|
-
...this.state.get(key) || {}
|
|
481
|
-
}));
|
|
482
490
|
let changesArray;
|
|
483
491
|
if (isArray) {
|
|
484
492
|
changesArray = proxy.withArrayChangeTracking(
|
|
@@ -492,23 +500,33 @@ class Collection {
|
|
|
492
500
|
);
|
|
493
501
|
changesArray = [result];
|
|
494
502
|
}
|
|
495
|
-
const mutations =
|
|
496
|
-
const
|
|
497
|
-
if (!
|
|
503
|
+
const mutations = idsArray.map((id, index) => {
|
|
504
|
+
const itemChanges = changesArray[index];
|
|
505
|
+
if (!itemChanges || Object.keys(itemChanges).length === 0) {
|
|
498
506
|
return null;
|
|
499
507
|
}
|
|
500
|
-
const
|
|
508
|
+
const originalItem = currentObjects[index];
|
|
509
|
+
const validatedUpdatePayload = this.validateData(
|
|
510
|
+
itemChanges,
|
|
511
|
+
`update`,
|
|
512
|
+
id
|
|
513
|
+
);
|
|
514
|
+
const modifiedItem = { ...originalItem, ...validatedUpdatePayload };
|
|
515
|
+
const originalItemId = this.config.getId(originalItem);
|
|
516
|
+
const modifiedItemId = this.config.getId(modifiedItem);
|
|
517
|
+
if (originalItemId !== modifiedItemId) {
|
|
518
|
+
throw new Error(
|
|
519
|
+
`Updating the ID of an item is not allowed. Original ID: "${originalItemId}", Attempted new ID: "${modifiedItemId}". Please delete the old item and create a new one if an ID change is necessary.`
|
|
520
|
+
);
|
|
521
|
+
}
|
|
501
522
|
return {
|
|
502
523
|
mutationId: crypto.randomUUID(),
|
|
503
|
-
original:
|
|
504
|
-
modified:
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
},
|
|
508
|
-
changes: validatedData,
|
|
509
|
-
key,
|
|
524
|
+
original: originalItem,
|
|
525
|
+
modified: modifiedItem,
|
|
526
|
+
changes: validatedUpdatePayload,
|
|
527
|
+
key: id,
|
|
510
528
|
metadata: config.metadata,
|
|
511
|
-
syncMetadata: this.syncedMetadata.state.get(
|
|
529
|
+
syncMetadata: this.syncedMetadata.state.get(id) || {},
|
|
512
530
|
type: `update`,
|
|
513
531
|
createdAt: /* @__PURE__ */ new Date(),
|
|
514
532
|
updatedAt: /* @__PURE__ */ new Date(),
|
|
@@ -601,5 +619,6 @@ class Collection {
|
|
|
601
619
|
exports.Collection = Collection;
|
|
602
620
|
exports.SchemaValidationError = SchemaValidationError;
|
|
603
621
|
exports.collectionsStore = collectionsStore;
|
|
622
|
+
exports.createCollection = createCollection;
|
|
604
623
|
exports.preloadCollection = preloadCollection;
|
|
605
624
|
//# sourceMappingURL=collection.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collection.cjs","sources":["../../src/collection.ts"],"sourcesContent":["import { Derived, Store, batch } from \"@tanstack/store\"\nimport { withArrayChangeTracking, withChangeTracking } from \"./proxy\"\nimport { getActiveTransaction } from \"./transactions\"\nimport { SortedMap } from \"./SortedMap\"\nimport type {\n ChangeMessage,\n CollectionConfig,\n InsertConfig,\n OperationConfig,\n OptimisticChangeMessage,\n PendingMutation,\n StandardSchema,\n Transaction,\n} from \"./types\"\n\n// Store collections in memory using Tanstack store\nexport const collectionsStore = new Store(new Map<string, Collection<any>>())\n\n// Map to track loading collections\n\nconst loadingCollections = new Map<\n string,\n Promise<Collection<Record<string, unknown>>>\n>()\n\ninterface PendingSyncedTransaction<T extends object = Record<string, unknown>> {\n committed: boolean\n operations: Array<OptimisticChangeMessage<T>>\n}\n\n/**\n * Preloads a collection with the given configuration\n * Returns a promise that resolves once the sync tool has done its first commit (initial sync is finished)\n * If the collection has already loaded, it resolves immediately\n *\n * This function is useful in route loaders or similar pre-rendering scenarios where you want\n * to ensure data is available before a route transition completes. It uses the same shared collection\n * instance that will be used by useCollection, ensuring data consistency.\n *\n * @example\n * ```typescript\n * // In a route loader\n * async function loader({ params }) {\n * await preloadCollection({\n * id: `users-${params.userId}`,\n * sync: { ... },\n * });\n *\n * return null;\n * }\n * ```\n *\n * @template T - The type of items in the collection\n * @param config - Configuration for the collection, including id and sync\n * @returns Promise that resolves when the initial sync is finished\n */\nexport function preloadCollection<T extends object = Record<string, unknown>>(\n config: CollectionConfig<T>\n): Promise<Collection<T>> {\n // If the collection is already fully loaded, return a resolved promise\n if (\n collectionsStore.state.has(config.id) &&\n !loadingCollections.has(config.id)\n ) {\n return Promise.resolve(\n collectionsStore.state.get(config.id)! as Collection<T>\n )\n }\n\n // If the collection is in the process of loading, return its promise\n if (loadingCollections.has(config.id)) {\n return loadingCollections.get(config.id)! as Promise<Collection<T>>\n }\n\n // Create a new collection instance if it doesn't exist\n if (!collectionsStore.state.has(config.id)) {\n collectionsStore.setState((prev) => {\n const next = new Map(prev)\n next.set(\n config.id,\n new Collection<T>({\n id: config.id,\n sync: config.sync,\n schema: config.schema,\n })\n )\n return next\n })\n }\n\n const collection = collectionsStore.state.get(config.id)! as Collection<T>\n\n // Create a promise that will resolve after the first commit\n let resolveFirstCommit: () => void\n const firstCommitPromise = new Promise<Collection<T>>((resolve) => {\n resolveFirstCommit = () => {\n resolve(collection)\n }\n })\n\n // Register a one-time listener for the first commit\n collection.onFirstCommit(() => {\n if (loadingCollections.has(config.id)) {\n loadingCollections.delete(config.id)\n resolveFirstCommit()\n }\n })\n\n // Store the loading promise\n loadingCollections.set(\n config.id,\n firstCommitPromise as Promise<Collection<Record<string, unknown>>>\n )\n\n return firstCommitPromise\n}\n\n/**\n * Custom error class for schema validation errors\n */\nexport class SchemaValidationError extends Error {\n type: `insert` | `update`\n issues: ReadonlyArray<{\n message: string\n path?: ReadonlyArray<string | number | symbol>\n }>\n\n constructor(\n type: `insert` | `update`,\n issues: ReadonlyArray<{\n message: string\n path?: ReadonlyArray<string | number | symbol>\n }>,\n message?: string\n ) {\n const defaultMessage = `${type === `insert` ? `Insert` : `Update`} validation failed: ${issues\n .map((issue) => issue.message)\n .join(`, `)}`\n\n super(message || defaultMessage)\n this.name = `SchemaValidationError`\n this.type = type\n this.issues = issues\n }\n}\n\nexport class Collection<T extends object = Record<string, unknown>> {\n public transactions: Store<SortedMap<string, Transaction>>\n public optimisticOperations: Derived<Array<OptimisticChangeMessage<T>>>\n public derivedState: Derived<Map<string, T>>\n public derivedArray: Derived<Array<T>>\n public derivedChanges: Derived<Array<ChangeMessage<T>>>\n public syncedData = new Store<Map<string, T>>(new Map())\n public syncedMetadata = new Store(new Map<string, unknown>())\n private pendingSyncedTransactions: Array<PendingSyncedTransaction<T>> = []\n private syncedKeys = new Set<string>()\n public config: CollectionConfig<T>\n private hasReceivedFirstCommit = false\n\n // WeakMap to associate objects with their keys\n public objectKeyMap = new WeakMap<object, string>()\n\n // Array to store one-time commit listeners\n private onFirstCommitCallbacks: Array<() => void> = []\n\n /**\n * Register a callback to be executed on the next commit\n * Useful for preloading collections\n * @param callback Function to call after the next commit\n */\n public onFirstCommit(callback: () => void): void {\n this.onFirstCommitCallbacks.push(callback)\n }\n\n public id = crypto.randomUUID()\n\n /**\n * Creates a new Collection instance\n *\n * @param config - Configuration object for the collection\n * @throws Error if sync config is missing\n */\n constructor(config?: CollectionConfig<T>) {\n if (!config?.sync) {\n throw new Error(`Collection requires a sync config`)\n }\n\n this.transactions = new Store(\n new SortedMap<string, Transaction>(\n (a, b) => a.createdAt.getTime() - b.createdAt.getTime()\n )\n )\n\n // Copies of live mutations are stored here and removed once the transaction completes.\n this.optimisticOperations = new Derived({\n fn: ({ currDepVals: [transactions] }) => {\n const result = Array.from(transactions.values())\n .map((transaction) => {\n const isActive = ![`completed`, `failed`].includes(\n transaction.state\n )\n return transaction.mutations\n .filter((mutation) => mutation.collection === this)\n .map((mutation) => {\n const message: OptimisticChangeMessage<T> = {\n type: mutation.type,\n key: mutation.key,\n value: mutation.modified as T,\n isActive,\n }\n if (\n mutation.metadata !== undefined &&\n mutation.metadata !== null\n ) {\n message.metadata = mutation.metadata as Record<\n string,\n unknown\n >\n }\n return message\n })\n })\n .flat()\n\n return result\n },\n deps: [this.transactions],\n })\n this.optimisticOperations.mount()\n\n // Combine together synced data & optimistic operations.\n this.derivedState = new Derived({\n fn: ({ currDepVals: [syncedData, operations] }) => {\n const combined = new Map<string, T>(syncedData)\n const optimisticKeys = new Set<string>()\n\n // Apply the optimistic operations on top of the synced state.\n for (const operation of operations) {\n optimisticKeys.add(operation.key)\n if (operation.isActive) {\n switch (operation.type) {\n case `insert`:\n combined.set(operation.key, operation.value)\n break\n case `update`:\n combined.set(operation.key, operation.value)\n break\n case `delete`:\n combined.delete(operation.key)\n break\n }\n }\n }\n\n // Update object => key mappings\n optimisticKeys.forEach((key) => {\n if (combined.has(key)) {\n this.objectKeyMap.set(combined.get(key)!, key)\n }\n })\n\n return combined\n },\n deps: [this.syncedData, this.optimisticOperations],\n })\n\n // Create a derived array from the map to avoid recalculating it\n this.derivedArray = new Derived({\n fn: ({ currDepVals: [stateMap] }) => {\n // Collections returned by a query that has an orderBy are annotated\n // with the _orderByIndex field.\n // This is used to sort the array when it's derived.\n const array: Array<T & { _orderByIndex?: number }> = Array.from(\n stateMap.values()\n )\n if (array[0] && `_orderByIndex` in array[0]) {\n ;(array as Array<T & { _orderByIndex: number }>).sort((a, b) => {\n if (a._orderByIndex === b._orderByIndex) {\n return 0\n }\n return a._orderByIndex < b._orderByIndex ? -1 : 1\n })\n }\n return array\n },\n deps: [this.derivedState],\n })\n this.derivedArray.mount()\n\n this.derivedChanges = new Derived({\n fn: ({\n currDepVals: [derivedState, optimisticOperations],\n prevDepVals,\n }) => {\n const prevDerivedState = prevDepVals?.[0] ?? new Map<string, T>()\n const prevOptimisticOperations = prevDepVals?.[1] ?? []\n const changedKeys = new Set(this.syncedKeys)\n optimisticOperations\n .flat()\n .filter((op) => op.isActive)\n .forEach((op) => changedKeys.add(op.key))\n prevOptimisticOperations.flat().forEach((op) => {\n changedKeys.add(op.key)\n })\n\n if (changedKeys.size === 0) {\n return []\n }\n\n const changes: Array<ChangeMessage<T>> = []\n for (const key of changedKeys) {\n if (prevDerivedState.has(key) && !derivedState.has(key)) {\n changes.push({\n type: `delete`,\n key,\n value: prevDerivedState.get(key)!,\n })\n } else if (!prevDerivedState.has(key) && derivedState.has(key)) {\n changes.push({ type: `insert`, key, value: derivedState.get(key)! })\n } else if (prevDerivedState.has(key) && derivedState.has(key)) {\n const value = derivedState.get(key)!\n const previousValue = prevDerivedState.get(key)\n if (value !== previousValue) {\n // Comparing objects by reference as records are not mutated\n changes.push({\n type: `update`,\n key,\n value,\n previousValue,\n })\n }\n }\n }\n\n this.syncedKeys.clear()\n\n return changes\n },\n deps: [this.derivedState, this.optimisticOperations],\n })\n this.derivedChanges.mount()\n\n this.config = config\n\n this.derivedState.mount()\n\n // Start the sync process\n config.sync.sync({\n collection: this,\n begin: () => {\n this.pendingSyncedTransactions.push({\n committed: false,\n operations: [],\n })\n },\n write: (message: ChangeMessage<T>) => {\n const pendingTransaction =\n this.pendingSyncedTransactions[\n this.pendingSyncedTransactions.length - 1\n ]\n if (!pendingTransaction) {\n throw new Error(`No pending sync transaction to write to`)\n }\n if (pendingTransaction.committed) {\n throw new Error(\n `The pending sync transaction is already committed, you can't still write to it.`\n )\n }\n pendingTransaction.operations.push(message)\n },\n commit: () => {\n const pendingTransaction =\n this.pendingSyncedTransactions[\n this.pendingSyncedTransactions.length - 1\n ]\n if (!pendingTransaction) {\n throw new Error(`No pending sync transaction to commit`)\n }\n if (pendingTransaction.committed) {\n throw new Error(\n `The pending sync transaction is already committed, you can't commit it again.`\n )\n }\n\n pendingTransaction.committed = true\n\n this.commitPendingTransactions()\n },\n })\n }\n\n /**\n * Attempts to commit pending synced transactions if there are no active transactions\n * This method processes operations from pending transactions and applies them to the synced data\n */\n commitPendingTransactions = () => {\n if (\n !Array.from(this.transactions.state.values()).some(\n ({ state }) => state === `persisting`\n )\n ) {\n const keys = new Set<string>()\n batch(() => {\n for (const transaction of this.pendingSyncedTransactions) {\n for (const operation of transaction.operations) {\n keys.add(operation.key)\n this.syncedKeys.add(operation.key)\n this.syncedMetadata.setState((prevData) => {\n switch (operation.type) {\n case `insert`:\n prevData.set(operation.key, operation.metadata)\n break\n case `update`:\n prevData.set(operation.key, {\n ...prevData.get(operation.key)!,\n ...operation.metadata,\n })\n break\n case `delete`:\n prevData.delete(operation.key)\n break\n }\n return prevData\n })\n this.syncedData.setState((prevData) => {\n switch (operation.type) {\n case `insert`:\n prevData.set(operation.key, operation.value)\n break\n case `update`:\n prevData.set(operation.key, {\n ...prevData.get(operation.key)!,\n ...operation.value,\n })\n break\n case `delete`:\n prevData.delete(operation.key)\n break\n }\n return prevData\n })\n }\n }\n })\n\n keys.forEach((key) => {\n const curValue = this.state.get(key)\n if (curValue) {\n this.objectKeyMap.set(curValue, key)\n }\n })\n\n this.pendingSyncedTransactions = []\n\n // Call any registered one-time commit listeners\n if (!this.hasReceivedFirstCommit) {\n this.hasReceivedFirstCommit = true\n const callbacks = [...this.onFirstCommitCallbacks]\n this.onFirstCommitCallbacks = []\n callbacks.forEach((callback) => callback())\n }\n }\n }\n\n private ensureStandardSchema(schema: unknown): StandardSchema<T> {\n // If the schema already implements the standard-schema interface, return it\n if (schema && typeof schema === `object` && `~standard` in schema) {\n return schema as StandardSchema<T>\n }\n\n throw new Error(\n `Schema must either implement the standard-schema interface or be a Zod schema`\n )\n }\n\n private validateData(\n data: unknown,\n type: `insert` | `update`,\n key?: string\n ): T | never {\n if (!this.config.schema) return data as T\n\n const standardSchema = this.ensureStandardSchema(this.config.schema)\n\n // For updates, we need to merge with the existing data before validation\n if (type === `update` && key) {\n // Get the existing data for this key\n const existingData = this.state.get(key)\n\n if (\n existingData &&\n data &&\n typeof data === `object` &&\n typeof existingData === `object`\n ) {\n // Merge the update with the existing data\n const mergedData = { ...existingData, ...data }\n\n // Validate the merged data\n const result = standardSchema[`~standard`].validate(mergedData)\n\n // Ensure validation is synchronous\n if (result instanceof Promise) {\n throw new TypeError(`Schema validation must be synchronous`)\n }\n\n // If validation fails, throw a SchemaValidationError with the issues\n if (`issues` in result && result.issues) {\n const typedIssues = result.issues.map((issue) => ({\n message: issue.message,\n path: issue.path?.map((p) => String(p)),\n }))\n throw new SchemaValidationError(type, typedIssues)\n }\n\n // Return the original update data, not the merged data\n // We only used the merged data for validation\n return data as T\n }\n }\n\n // For inserts or updates without existing data, validate the data directly\n const result = standardSchema[`~standard`].validate(data)\n\n // Ensure validation is synchronous\n if (result instanceof Promise) {\n throw new TypeError(`Schema validation must be synchronous`)\n }\n\n // If validation fails, throw a SchemaValidationError with the issues\n if (`issues` in result && result.issues) {\n const typedIssues = result.issues.map((issue) => ({\n message: issue.message,\n path: issue.path?.map((p) => String(p)),\n }))\n throw new SchemaValidationError(type, typedIssues)\n }\n\n return result.value as T\n }\n\n private generateKey(data: unknown): string {\n const str = JSON.stringify(data)\n let h = 0\n\n for (let i = 0; i < str.length; i++) {\n h = (Math.imul(31, h) + str.charCodeAt(i)) | 0\n }\n\n return `${this.id}/${Math.abs(h).toString(36)}`\n }\n\n /**\n * Inserts one or more items into the collection\n * @param items - Single item or array of items to insert\n * @param config - Optional configuration including metadata and custom keys\n * @returns A Transaction object representing the insert operation(s)\n * @throws {SchemaValidationError} If the data fails schema validation\n * @example\n * // Insert a single item\n * insert({ text: \"Buy groceries\", completed: false })\n *\n * // Insert multiple items\n * insert([\n * { text: \"Buy groceries\", completed: false },\n * { text: \"Walk dog\", completed: false }\n * ])\n *\n * // Insert with custom key\n * insert({ text: \"Buy groceries\" }, { key: \"grocery-task\" })\n */\n insert = (data: T | Array<T>, config?: InsertConfig) => {\n const transaction = getActiveTransaction()\n if (typeof transaction === `undefined`) {\n throw `no transaction found when calling collection.insert`\n }\n\n const items = Array.isArray(data) ? data : [data]\n const mutations: Array<PendingMutation<T>> = []\n\n // Handle keys - convert to array if string, or generate if not provided\n let keys: Array<string>\n if (config?.key) {\n const configKeys = Array.isArray(config.key) ? config.key : [config.key]\n // If keys are provided, ensure we have the right number or allow sparse array\n if (Array.isArray(config.key) && configKeys.length > items.length) {\n throw new Error(`More keys provided than items to insert`)\n }\n keys = items.map((_, i) => configKeys[i] ?? this.generateKey(items[i]))\n } else {\n // No keys provided, generate for all items\n keys = items.map((item) => this.generateKey(item))\n }\n\n // Create mutations for each item\n items.forEach((item, index) => {\n // Validate the data against the schema if one exists\n const validatedData = this.validateData(item, `insert`)\n const key = keys[index]!\n\n const mutation: PendingMutation<T> = {\n mutationId: crypto.randomUUID(),\n original: {},\n modified: validatedData as Record<string, unknown>,\n changes: validatedData as Record<string, unknown>,\n key,\n metadata: config?.metadata as unknown,\n syncMetadata: this.config.sync.getSyncMetadata?.() || {},\n type: `insert`,\n createdAt: new Date(),\n updatedAt: new Date(),\n collection: this,\n }\n\n mutations.push(mutation)\n })\n\n transaction.applyMutations(mutations)\n\n this.transactions.setState((sortedMap) => {\n sortedMap.set(transaction.id, transaction)\n return sortedMap\n })\n\n return transaction\n }\n\n /**\n * Updates one or more items in the collection using a callback function\n * @param items - Single item/key or array of items/keys to update\n * @param configOrCallback - Either update configuration or update callback\n * @param maybeCallback - Update callback if config was provided\n * @returns A Transaction object representing the update operation(s)\n * @throws {SchemaValidationError} If the updated data fails schema validation\n * @example\n * // Update a single item\n * update(todo, (draft) => { draft.completed = true })\n *\n * // Update multiple items\n * update([todo1, todo2], (drafts) => {\n * drafts.forEach(draft => { draft.completed = true })\n * })\n *\n * // Update with metadata\n * update(todo, { metadata: { reason: \"user update\" } }, (draft) => { draft.text = \"Updated text\" })\n */\n\n update<TItem extends object = T>(\n item: TItem,\n configOrCallback: ((draft: TItem) => void) | OperationConfig,\n maybeCallback?: (draft: TItem) => void\n ): Transaction\n\n update<TItem extends object = T>(\n items: Array<TItem>,\n configOrCallback: ((draft: Array<TItem>) => void) | OperationConfig,\n maybeCallback?: (draft: Array<TItem>) => void\n ): Transaction\n\n update<TItem extends object = T>(\n items: TItem | Array<TItem>,\n configOrCallback: ((draft: TItem | Array<TItem>) => void) | OperationConfig,\n maybeCallback?: (draft: TItem | Array<TItem>) => void\n ) {\n if (typeof items === `undefined`) {\n throw new Error(`The first argument to update is missing`)\n }\n\n const transaction = getActiveTransaction()\n if (typeof transaction === `undefined`) {\n throw `no transaction found when calling collection.update`\n }\n\n const isArray = Array.isArray(items)\n const itemsArray = Array.isArray(items) ? items : [items]\n const callback =\n typeof configOrCallback === `function` ? configOrCallback : maybeCallback!\n const config =\n typeof configOrCallback === `function` ? {} : configOrCallback\n\n const keys = itemsArray.map((item) => {\n if (typeof item === `object` && (item as unknown) !== null) {\n const key = this.objectKeyMap.get(item)\n if (key === undefined) {\n throw new Error(`Object not found in collection`)\n }\n return key\n }\n throw new Error(`Invalid item type for update - must be an object`)\n })\n\n // Get the current objects or empty objects if they don't exist\n const currentObjects = keys.map((key) => ({\n ...(this.state.get(key) || {}),\n })) as Array<TItem>\n\n let changesArray\n if (isArray) {\n // Use the proxy to track changes for all objects\n changesArray = withArrayChangeTracking(\n currentObjects,\n callback as (draft: Array<TItem>) => void\n )\n } else {\n const result = withChangeTracking(\n currentObjects[0] as TItem,\n callback as (draft: TItem) => void\n )\n changesArray = [result]\n }\n\n // Create mutations for each object that has changes\n const mutations: Array<PendingMutation<T>> = keys\n .map((key, index) => {\n const changes = changesArray[index]\n\n // Skip items with no changes\n if (!changes || Object.keys(changes).length === 0) {\n return null\n }\n\n // Validate the changes for this item\n const validatedData = this.validateData(changes, `update`, key)\n\n return {\n mutationId: crypto.randomUUID(),\n original: (this.state.get(key) || {}) as Record<string, unknown>,\n modified: {\n ...(this.state.get(key) || {}),\n ...validatedData,\n } as Record<string, unknown>,\n changes: validatedData as Record<string, unknown>,\n key,\n metadata: config.metadata as unknown,\n syncMetadata: (this.syncedMetadata.state.get(key) || {}) as Record<\n string,\n unknown\n >,\n type: `update`,\n createdAt: new Date(),\n updatedAt: new Date(),\n collection: this,\n }\n })\n .filter(Boolean) as Array<PendingMutation<T>>\n\n // If no changes were made, return early\n if (mutations.length === 0) {\n throw new Error(`No changes were made to any of the objects`)\n }\n\n transaction.applyMutations(mutations)\n\n this.transactions.setState((sortedMap) => {\n sortedMap.set(transaction.id, transaction)\n return sortedMap\n })\n\n return transaction\n }\n\n /**\n * Deletes one or more items from the collection\n * @param items - Single item/key or array of items/keys to delete\n * @param config - Optional configuration including metadata\n * @returns A Transaction object representing the delete operation(s)\n * @example\n * // Delete a single item\n * delete(todo)\n *\n * // Delete multiple items\n * delete([todo1, todo2])\n *\n * // Delete with metadata\n * delete(todo, { metadata: { reason: \"completed\" } })\n */\n delete = (\n items: Array<T | string> | T | string,\n config?: OperationConfig\n ) => {\n const transaction = getActiveTransaction()\n if (typeof transaction === `undefined`) {\n throw `no transaction found when calling collection.delete`\n }\n\n const itemsArray = Array.isArray(items) ? items : [items]\n const mutations: Array<PendingMutation<T>> = []\n\n for (const item of itemsArray) {\n let key: string\n if (typeof item === `object` && (item as unknown) !== null) {\n const objectKey = this.objectKeyMap.get(item)\n if (objectKey === undefined) {\n throw new Error(\n `Object not found in collection: ${JSON.stringify(item)}`\n )\n }\n key = objectKey\n } else if (typeof item === `string`) {\n key = item\n } else {\n throw new Error(\n `Invalid item type for delete - must be an object or string key`\n )\n }\n\n const mutation: PendingMutation<T> = {\n mutationId: crypto.randomUUID(),\n original: (this.state.get(key) || {}) as Record<string, unknown>,\n modified: { _deleted: true },\n changes: { _deleted: true },\n key,\n metadata: config?.metadata as unknown,\n syncMetadata: (this.syncedMetadata.state.get(key) || {}) as Record<\n string,\n unknown\n >,\n type: `delete`,\n createdAt: new Date(),\n updatedAt: new Date(),\n collection: this,\n }\n\n mutations.push(mutation)\n }\n\n // Delete object => key mapping.\n mutations.forEach((mutation) => {\n const curValue = this.state.get(mutation.key)\n if (curValue) {\n this.objectKeyMap.delete(curValue)\n }\n })\n\n transaction.applyMutations(mutations)\n\n this.transactions.setState((sortedMap) => {\n sortedMap.set(transaction.id, transaction)\n return sortedMap\n })\n\n return transaction\n }\n\n /**\n * Gets the current state of the collection as a Map\n *\n * @returns A Map containing all items in the collection, with keys as identifiers\n */\n get state() {\n return this.derivedState.state\n }\n\n /**\n * Gets the current state of the collection as a Map, but only resolves when data is available\n * Waits for the first sync commit to complete before resolving\n *\n * @returns Promise that resolves to a Map containing all items in the collection\n */\n stateWhenReady(): Promise<Map<string, T>> {\n // If we already have data or there are no loading collections, resolve immediately\n if (this.state.size > 0 || this.hasReceivedFirstCommit === true) {\n return Promise.resolve(this.state)\n }\n\n // Otherwise, wait for the first commit\n return new Promise<Map<string, T>>((resolve) => {\n this.onFirstCommit(() => {\n resolve(this.state)\n })\n })\n }\n\n /**\n * Gets the current state of the collection as an Array\n *\n * @returns An Array containing all items in the collection\n */\n get toArray() {\n return this.derivedArray.state\n }\n\n /**\n * Gets the current state of the collection as an Array, but only resolves when data is available\n * Waits for the first sync commit to complete before resolving\n *\n * @returns Promise that resolves to an Array containing all items in the collection\n */\n toArrayWhenReady(): Promise<Array<T>> {\n // If we already have data or there are no loading collections, resolve immediately\n if (this.toArray.length > 0 || this.hasReceivedFirstCommit === true) {\n return Promise.resolve(this.toArray)\n }\n\n // Otherwise, wait for the first commit\n return new Promise<Array<T>>((resolve) => {\n this.onFirstCommit(() => {\n resolve(this.toArray)\n })\n })\n }\n\n /**\n * Returns the current state of the collection as an array of changes\n * @returns An array of changes\n */\n public currentStateAsChanges(): Array<ChangeMessage<T>> {\n return [...this.state.entries()].map(([key, value]) => ({\n type: `insert`,\n key,\n value,\n }))\n }\n\n /**\n * Subscribe to changes in the collection\n * @param callback - A function that will be called with the changes in the collection\n * @returns A function that can be called to unsubscribe from the changes\n */\n public subscribeChanges(\n callback: (changes: Array<ChangeMessage<T>>) => void\n ): () => void {\n // First send the current state as changes\n callback(this.currentStateAsChanges())\n\n // Then subscribe to changes, this returns an unsubscribe function\n return this.derivedChanges.subscribe((changes) => {\n if (changes.currentVal.length > 0) {\n callback(changes.currentVal)\n }\n })\n }\n}\n"],"names":["Store","batch","config","getActiveTransaction","SortedMap","Derived","transactions","result","withArrayChangeTracking","withChangeTracking"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"collection.cjs","sources":["../../src/collection.ts"],"sourcesContent":["import { Derived, Store, batch } from \"@tanstack/store\"\nimport { withArrayChangeTracking, withChangeTracking } from \"./proxy\"\nimport { getActiveTransaction } from \"./transactions\"\nimport { SortedMap } from \"./SortedMap\"\nimport type {\n ChangeMessage,\n CollectionConfig,\n InsertConfig,\n OperationConfig,\n OptimisticChangeMessage,\n PendingMutation,\n StandardSchema,\n Transaction,\n} from \"./types\"\n\n// Store collections in memory using Tanstack store\nexport const collectionsStore = new Store(new Map<string, Collection<any>>())\n\n// Map to track loading collections\n\nconst loadingCollections = new Map<\n string,\n Promise<Collection<Record<string, unknown>>>\n>()\n\ninterface PendingSyncedTransaction<T extends object = Record<string, unknown>> {\n committed: boolean\n operations: Array<OptimisticChangeMessage<T>>\n}\n\n/**\n * Creates a new Collection instance with the given configuration\n *\n * @template T - The type of items in the collection\n * @param config - Configuration for the collection, including id and sync\n * @returns A new Collection instance\n */\nexport function createCollection<T extends object = Record<string, unknown>>(\n config: CollectionConfig<T>\n): Collection<T> {\n return new Collection<T>(config)\n}\n\n/**\n * Preloads a collection with the given configuration\n * Returns a promise that resolves once the sync tool has done its first commit (initial sync is finished)\n * If the collection has already loaded, it resolves immediately\n *\n * This function is useful in route loaders or similar pre-rendering scenarios where you want\n * to ensure data is available before a route transition completes. It uses the same shared collection\n * instance that will be used by useCollection, ensuring data consistency.\n *\n * @example\n * ```typescript\n * // In a route loader\n * async function loader({ params }) {\n * await preloadCollection({\n * id: `users-${params.userId}`,\n * sync: { ... },\n * });\n *\n * return null;\n * }\n * ```\n *\n * @template T - The type of items in the collection\n * @param config - Configuration for the collection, including id and sync\n * @returns Promise that resolves when the initial sync is finished\n */\nexport function preloadCollection<T extends object = Record<string, unknown>>(\n config: CollectionConfig<T>\n): Promise<Collection<T>> {\n if (!config.id) {\n throw new Error(`The id property is required for preloadCollection`)\n }\n\n // If the collection is already fully loaded, return a resolved promise\n if (\n collectionsStore.state.has(config.id) &&\n !loadingCollections.has(config.id)\n ) {\n return Promise.resolve(\n collectionsStore.state.get(config.id)! as Collection<T>\n )\n }\n\n // If the collection is in the process of loading, return its promise\n if (loadingCollections.has(config.id)) {\n return loadingCollections.get(config.id)! as Promise<Collection<T>>\n }\n\n // Create a new collection instance if it doesn't exist\n if (!collectionsStore.state.has(config.id)) {\n collectionsStore.setState((prev) => {\n const next = new Map(prev)\n if (!config.id) {\n throw new Error(`The id property is required for preloadCollection`)\n }\n next.set(\n config.id,\n new Collection<T>({\n id: config.id,\n getId: config.getId,\n sync: config.sync,\n schema: config.schema,\n })\n )\n return next\n })\n }\n\n const collection = collectionsStore.state.get(config.id)! as Collection<T>\n\n // Create a promise that will resolve after the first commit\n let resolveFirstCommit: () => void\n const firstCommitPromise = new Promise<Collection<T>>((resolve) => {\n resolveFirstCommit = () => {\n resolve(collection)\n }\n })\n\n // Register a one-time listener for the first commit\n collection.onFirstCommit(() => {\n if (!config.id) {\n throw new Error(`The id property is required for preloadCollection`)\n }\n if (loadingCollections.has(config.id)) {\n loadingCollections.delete(config.id)\n resolveFirstCommit()\n }\n })\n\n // Store the loading promise\n loadingCollections.set(\n config.id,\n firstCommitPromise as Promise<Collection<Record<string, unknown>>>\n )\n\n return firstCommitPromise\n}\n\n/**\n * Custom error class for schema validation errors\n */\nexport class SchemaValidationError extends Error {\n type: `insert` | `update`\n issues: ReadonlyArray<{\n message: string\n path?: ReadonlyArray<string | number | symbol>\n }>\n\n constructor(\n type: `insert` | `update`,\n issues: ReadonlyArray<{\n message: string\n path?: ReadonlyArray<string | number | symbol>\n }>,\n message?: string\n ) {\n const defaultMessage = `${type === `insert` ? `Insert` : `Update`} validation failed: ${issues\n .map((issue) => issue.message)\n .join(`, `)}`\n\n super(message || defaultMessage)\n this.name = `SchemaValidationError`\n this.type = type\n this.issues = issues\n }\n}\n\nexport class Collection<T extends object = Record<string, unknown>> {\n public transactions: Store<SortedMap<string, Transaction>>\n public optimisticOperations: Derived<Array<OptimisticChangeMessage<T>>>\n public derivedState: Derived<Map<string, T>>\n public derivedArray: Derived<Array<T>>\n public derivedChanges: Derived<Array<ChangeMessage<T>>>\n public syncedData = new Store<Map<string, T>>(new Map())\n public syncedMetadata = new Store(new Map<string, unknown>())\n private pendingSyncedTransactions: Array<PendingSyncedTransaction<T>> = []\n private syncedKeys = new Set<string>()\n public config: CollectionConfig<T>\n private hasReceivedFirstCommit = false\n\n // Array to store one-time commit listeners\n private onFirstCommitCallbacks: Array<() => void> = []\n\n /**\n * Register a callback to be executed on the next commit\n * Useful for preloading collections\n * @param callback Function to call after the next commit\n */\n public onFirstCommit(callback: () => void): void {\n this.onFirstCommitCallbacks.push(callback)\n }\n\n public id = ``\n\n /**\n * Creates a new Collection instance\n *\n * @param config - Configuration object for the collection\n * @throws Error if sync config is missing\n */\n constructor(config: CollectionConfig<T>) {\n // eslint-disable-next-line\n if (!config) {\n throw new Error(`Collection requires a config`)\n }\n if (config.id) {\n this.id = config.id\n } else {\n this.id = crypto.randomUUID()\n }\n\n // eslint-disable-next-line\n if (!config.sync) {\n throw new Error(`Collection requires a sync config`)\n }\n\n this.transactions = new Store(\n new SortedMap<string, Transaction>(\n (a, b) => a.createdAt.getTime() - b.createdAt.getTime()\n )\n )\n\n // Copies of live mutations are stored here and removed once the transaction completes.\n this.optimisticOperations = new Derived({\n fn: ({ currDepVals: [transactions] }) => {\n const result = Array.from(transactions.values())\n .map((transaction) => {\n const isActive = ![`completed`, `failed`].includes(\n transaction.state\n )\n return transaction.mutations\n .filter((mutation) => mutation.collection === this)\n .map((mutation) => {\n const message: OptimisticChangeMessage<T> = {\n type: mutation.type,\n key: mutation.key,\n value: mutation.modified as T,\n isActive,\n }\n if (\n mutation.metadata !== undefined &&\n mutation.metadata !== null\n ) {\n message.metadata = mutation.metadata as Record<\n string,\n unknown\n >\n }\n return message\n })\n })\n .flat()\n\n return result\n },\n deps: [this.transactions],\n })\n this.optimisticOperations.mount()\n\n // Combine together synced data & optimistic operations.\n this.derivedState = new Derived({\n fn: ({ currDepVals: [syncedData, operations] }) => {\n const combined = new Map<string, T>(syncedData)\n\n // Apply the optimistic operations on top of the synced state.\n for (const operation of operations) {\n if (operation.isActive) {\n switch (operation.type) {\n case `insert`:\n combined.set(operation.key, operation.value)\n break\n case `update`:\n combined.set(operation.key, operation.value)\n break\n case `delete`:\n combined.delete(operation.key)\n break\n }\n }\n }\n\n return combined\n },\n deps: [this.syncedData, this.optimisticOperations],\n })\n\n // Create a derived array from the map to avoid recalculating it\n this.derivedArray = new Derived({\n fn: ({ currDepVals: [stateMap] }) => {\n // Collections returned by a query that has an orderBy are annotated\n // with the _orderByIndex field.\n // This is used to sort the array when it's derived.\n const array: Array<T & { _orderByIndex?: number }> = Array.from(\n stateMap.values()\n )\n if (array[0] && `_orderByIndex` in array[0]) {\n ;(array as Array<T & { _orderByIndex: number }>).sort((a, b) => {\n if (a._orderByIndex === b._orderByIndex) {\n return 0\n }\n return a._orderByIndex < b._orderByIndex ? -1 : 1\n })\n }\n return array\n },\n deps: [this.derivedState],\n })\n this.derivedArray.mount()\n\n this.derivedChanges = new Derived({\n fn: ({\n currDepVals: [derivedState, optimisticOperations],\n prevDepVals,\n }) => {\n const prevDerivedState = prevDepVals?.[0] ?? new Map<string, T>()\n const prevOptimisticOperations = prevDepVals?.[1] ?? []\n const changedKeys = new Set(this.syncedKeys)\n optimisticOperations\n .flat()\n .filter((op) => op.isActive)\n .forEach((op) => changedKeys.add(op.key))\n prevOptimisticOperations.flat().forEach((op) => {\n changedKeys.add(op.key)\n })\n\n if (changedKeys.size === 0) {\n return []\n }\n\n const changes: Array<ChangeMessage<T>> = []\n for (const key of changedKeys) {\n if (prevDerivedState.has(key) && !derivedState.has(key)) {\n changes.push({\n type: `delete`,\n key,\n value: prevDerivedState.get(key)!,\n })\n } else if (!prevDerivedState.has(key) && derivedState.has(key)) {\n changes.push({ type: `insert`, key, value: derivedState.get(key)! })\n } else if (prevDerivedState.has(key) && derivedState.has(key)) {\n const value = derivedState.get(key)!\n const previousValue = prevDerivedState.get(key)\n if (value !== previousValue) {\n // Comparing objects by reference as records are not mutated\n changes.push({\n type: `update`,\n key,\n value,\n previousValue,\n })\n }\n }\n }\n\n this.syncedKeys.clear()\n\n return changes\n },\n deps: [this.derivedState, this.optimisticOperations],\n })\n this.derivedChanges.mount()\n\n this.config = config\n\n this.derivedState.mount()\n\n // Start the sync process\n config.sync.sync({\n collection: this,\n begin: () => {\n this.pendingSyncedTransactions.push({\n committed: false,\n operations: [],\n })\n },\n write: (messageWithoutKey: Omit<ChangeMessage<T>, `key`>) => {\n const pendingTransaction =\n this.pendingSyncedTransactions[\n this.pendingSyncedTransactions.length - 1\n ]\n if (!pendingTransaction) {\n throw new Error(`No pending sync transaction to write to`)\n }\n if (pendingTransaction.committed) {\n throw new Error(\n `The pending sync transaction is already committed, you can't still write to it.`\n )\n }\n const key = this.generateObjectKey(\n this.config.getId(messageWithoutKey.value),\n messageWithoutKey.value\n )\n\n // Check if an item with this ID already exists when inserting\n if (messageWithoutKey.type === `insert`) {\n if (\n this.syncedData.state.has(key) &&\n !pendingTransaction.operations.some(\n (op) => op.key === key && op.type === `delete`\n )\n ) {\n const id = this.config.getId(messageWithoutKey.value)\n throw new Error(\n `Cannot insert document with ID \"${id}\" from sync because it already exists in the collection \"${this.id}\"`\n )\n }\n }\n\n const message: ChangeMessage<T> = {\n ...messageWithoutKey,\n key,\n }\n pendingTransaction.operations.push(message)\n },\n commit: () => {\n const pendingTransaction =\n this.pendingSyncedTransactions[\n this.pendingSyncedTransactions.length - 1\n ]\n if (!pendingTransaction) {\n throw new Error(`No pending sync transaction to commit`)\n }\n if (pendingTransaction.committed) {\n throw new Error(\n `The pending sync transaction is already committed, you can't commit it again.`\n )\n }\n\n pendingTransaction.committed = true\n\n this.commitPendingTransactions()\n },\n })\n }\n\n /**\n * Attempts to commit pending synced transactions if there are no active transactions\n * This method processes operations from pending transactions and applies them to the synced data\n */\n commitPendingTransactions = () => {\n if (\n !Array.from(this.transactions.state.values()).some(\n ({ state }) => state === `persisting`\n )\n ) {\n batch(() => {\n for (const transaction of this.pendingSyncedTransactions) {\n for (const operation of transaction.operations) {\n this.syncedKeys.add(operation.key)\n this.syncedMetadata.setState((prevData) => {\n switch (operation.type) {\n case `insert`:\n prevData.set(operation.key, operation.metadata)\n break\n case `update`:\n prevData.set(operation.key, {\n ...prevData.get(operation.key)!,\n ...operation.metadata,\n })\n break\n case `delete`:\n prevData.delete(operation.key)\n break\n }\n return prevData\n })\n this.syncedData.setState((prevData) => {\n switch (operation.type) {\n case `insert`:\n prevData.set(operation.key, operation.value)\n break\n case `update`:\n prevData.set(operation.key, {\n ...prevData.get(operation.key)!,\n ...operation.value,\n })\n break\n case `delete`:\n prevData.delete(operation.key)\n break\n }\n return prevData\n })\n }\n }\n })\n\n this.pendingSyncedTransactions = []\n\n // Call any registered one-time commit listeners\n if (!this.hasReceivedFirstCommit) {\n this.hasReceivedFirstCommit = true\n const callbacks = [...this.onFirstCommitCallbacks]\n this.onFirstCommitCallbacks = []\n callbacks.forEach((callback) => callback())\n }\n }\n }\n\n private ensureStandardSchema(schema: unknown): StandardSchema<T> {\n // If the schema already implements the standard-schema interface, return it\n if (schema && typeof schema === `object` && `~standard` in schema) {\n return schema as StandardSchema<T>\n }\n\n throw new Error(\n `Schema must either implement the standard-schema interface or be a Zod schema`\n )\n }\n\n private getKeyFromId(id: unknown): string {\n if (typeof id === `undefined`) {\n throw new Error(`id is undefined`)\n }\n if (typeof id === `string` && id.startsWith(`KEY::`)) {\n return id\n } else {\n // if it's not a string, then it's some other\n // primitive type and needs turned into a key.\n return this.generateObjectKey(id, null)\n }\n }\n\n public generateObjectKey(id: any, item: any): string {\n if (typeof id === `undefined`) {\n throw new Error(\n `An object was created without a defined id: ${JSON.stringify(item)}`\n )\n }\n\n return `KEY::${this.id}/${id}`\n }\n\n private validateData(\n data: unknown,\n type: `insert` | `update`,\n key?: string\n ): T | never {\n if (!this.config.schema) return data as T\n\n const standardSchema = this.ensureStandardSchema(this.config.schema)\n\n // For updates, we need to merge with the existing data before validation\n if (type === `update` && key) {\n // Get the existing data for this key\n const existingData = this.state.get(key)\n\n if (\n existingData &&\n data &&\n typeof data === `object` &&\n typeof existingData === `object`\n ) {\n // Merge the update with the existing data\n const mergedData = { ...existingData, ...data }\n\n // Validate the merged data\n const result = standardSchema[`~standard`].validate(mergedData)\n\n // Ensure validation is synchronous\n if (result instanceof Promise) {\n throw new TypeError(`Schema validation must be synchronous`)\n }\n\n // If validation fails, throw a SchemaValidationError with the issues\n if (`issues` in result && result.issues) {\n const typedIssues = result.issues.map((issue) => ({\n message: issue.message,\n path: issue.path?.map((p) => String(p)),\n }))\n throw new SchemaValidationError(type, typedIssues)\n }\n\n // Return the original update data, not the merged data\n // We only used the merged data for validation\n return data as T\n }\n }\n\n // For inserts or updates without existing data, validate the data directly\n const result = standardSchema[`~standard`].validate(data)\n\n // Ensure validation is synchronous\n if (result instanceof Promise) {\n throw new TypeError(`Schema validation must be synchronous`)\n }\n\n // If validation fails, throw a SchemaValidationError with the issues\n if (`issues` in result && result.issues) {\n const typedIssues = result.issues.map((issue) => ({\n message: issue.message,\n path: issue.path?.map((p) => String(p)),\n }))\n throw new SchemaValidationError(type, typedIssues)\n }\n\n return result.value as T\n }\n\n /**\n * Inserts one or more items into the collection\n * @param items - Single item or array of items to insert\n * @param config - Optional configuration including metadata and custom keys\n * @returns A Transaction object representing the insert operation(s)\n * @throws {SchemaValidationError} If the data fails schema validation\n * @example\n * // Insert a single item\n * insert({ text: \"Buy groceries\", completed: false })\n *\n * // Insert multiple items\n * insert([\n * { text: \"Buy groceries\", completed: false },\n * { text: \"Walk dog\", completed: false }\n * ])\n *\n * // Insert with custom key\n * insert({ text: \"Buy groceries\" }, { key: \"grocery-task\" })\n */\n insert = (data: T | Array<T>, config?: InsertConfig) => {\n const transaction = getActiveTransaction()\n if (typeof transaction === `undefined`) {\n throw `no transaction found when calling collection.insert`\n }\n\n const items = Array.isArray(data) ? data : [data]\n const mutations: Array<PendingMutation<T>> = []\n\n // Handle keys - convert to array if string, or generate if not provided\n const keys: Array<unknown> = items.map((item) =>\n this.generateObjectKey(this.config.getId(item), item)\n )\n\n // Create mutations for each item\n items.forEach((item, index) => {\n // Validate the data against the schema if one exists\n const validatedData = this.validateData(item, `insert`)\n const key = keys[index]!\n\n // Check if an item with this ID already exists in the collection\n const id = this.config.getId(item)\n if (this.state.has(this.getKeyFromId(id))) {\n throw `Cannot insert document with ID \"${id}\" because it already exists in the collection`\n }\n\n const mutation: PendingMutation<T> = {\n mutationId: crypto.randomUUID(),\n original: {},\n modified: validatedData as Record<string, unknown>,\n changes: validatedData as Record<string, unknown>,\n key,\n metadata: config?.metadata as unknown,\n syncMetadata: this.config.sync.getSyncMetadata?.() || {},\n type: `insert`,\n createdAt: new Date(),\n updatedAt: new Date(),\n collection: this,\n }\n\n mutations.push(mutation)\n })\n\n transaction.applyMutations(mutations)\n\n this.transactions.setState((sortedMap) => {\n sortedMap.set(transaction.id, transaction)\n return sortedMap\n })\n\n return transaction\n }\n\n /**\n * Updates one or more items in the collection using a callback function\n * @param items - Single item/key or array of items/keys to update\n * @param configOrCallback - Either update configuration or update callback\n * @param maybeCallback - Update callback if config was provided\n * @returns A Transaction object representing the update operation(s)\n * @throws {SchemaValidationError} If the updated data fails schema validation\n * @example\n * // Update a single item\n * update(todo, (draft) => { draft.completed = true })\n *\n * // Update multiple items\n * update([todo1, todo2], (drafts) => {\n * drafts.forEach(draft => { draft.completed = true })\n * })\n *\n * // Update with metadata\n * update(todo, { metadata: { reason: \"user update\" } }, (draft) => { draft.text = \"Updated text\" })\n */\n\n /**\n * Updates one or more items in the collection using a callback function\n * @param ids - Single ID or array of IDs to update\n * @param configOrCallback - Either update configuration or update callback\n * @param maybeCallback - Update callback if config was provided\n * @returns A Transaction object representing the update operation(s)\n * @throws {SchemaValidationError} If the updated data fails schema validation\n * @example\n * // Update a single item\n * update(\"todo-1\", (draft) => { draft.completed = true })\n *\n * // Update multiple items\n * update([\"todo-1\", \"todo-2\"], (drafts) => {\n * drafts.forEach(draft => { draft.completed = true })\n * })\n *\n * // Update with metadata\n * update(\"todo-1\", { metadata: { reason: \"user update\" } }, (draft) => { draft.text = \"Updated text\" })\n */\n update<TItem extends object = T>(\n id: unknown,\n configOrCallback: ((draft: TItem) => void) | OperationConfig,\n maybeCallback?: (draft: TItem) => void\n ): Transaction\n\n update<TItem extends object = T>(\n ids: Array<unknown>,\n configOrCallback: ((draft: Array<TItem>) => void) | OperationConfig,\n maybeCallback?: (draft: Array<TItem>) => void\n ): Transaction\n\n update<TItem extends object = T>(\n ids: unknown | Array<unknown>,\n configOrCallback: ((draft: TItem | Array<TItem>) => void) | OperationConfig,\n maybeCallback?: (draft: TItem | Array<TItem>) => void\n ) {\n if (typeof ids === `undefined`) {\n throw new Error(`The first argument to update is missing`)\n }\n\n const transaction = getActiveTransaction()\n if (typeof transaction === `undefined`) {\n throw `no transaction found when calling collection.update`\n }\n\n const isArray = Array.isArray(ids)\n const idsArray = (Array.isArray(ids) ? ids : [ids]).map((id) =>\n this.getKeyFromId(id)\n )\n const callback =\n typeof configOrCallback === `function` ? configOrCallback : maybeCallback!\n const config =\n typeof configOrCallback === `function` ? {} : configOrCallback\n\n // Get the current objects or empty objects if they don't exist\n const currentObjects = idsArray.map((id) => {\n const item = this.state.get(id)\n if (!item) {\n throw new Error(\n `The id \"${id}\" was passed to update but an object for this ID was not found in the collection`\n )\n }\n\n return item\n }) as unknown as Array<TItem>\n\n let changesArray\n if (isArray) {\n // Use the proxy to track changes for all objects\n changesArray = withArrayChangeTracking(\n currentObjects,\n callback as (draft: Array<TItem>) => void\n )\n } else {\n const result = withChangeTracking(\n currentObjects[0] as TItem,\n callback as (draft: TItem) => void\n )\n changesArray = [result]\n }\n\n // Create mutations for each object that has changes\n const mutations: Array<PendingMutation<T>> = idsArray\n .map((id, index) => {\n const itemChanges = changesArray[index] // User-provided changes for this specific item\n\n // Skip items with no changes\n if (!itemChanges || Object.keys(itemChanges).length === 0) {\n return null\n }\n\n const originalItem = currentObjects[index] as unknown as T\n // Validate the user-provided changes for this item\n const validatedUpdatePayload = this.validateData(\n itemChanges,\n `update`,\n id\n )\n\n // Construct the full modified item by applying the validated update payload to the original item\n const modifiedItem = { ...originalItem, ...validatedUpdatePayload }\n\n // Check if the ID of the item is being changed\n const originalItemId = this.config.getId(originalItem)\n const modifiedItemId = this.config.getId(modifiedItem)\n\n if (originalItemId !== modifiedItemId) {\n throw new Error(\n `Updating the ID of an item is not allowed. Original ID: \"${originalItemId}\", Attempted new ID: \"${modifiedItemId}\". Please delete the old item and create a new one if an ID change is necessary.`\n )\n }\n\n return {\n mutationId: crypto.randomUUID(),\n original: originalItem as Record<string, unknown>,\n modified: modifiedItem as Record<string, unknown>,\n changes: validatedUpdatePayload as Record<string, unknown>,\n key: id,\n metadata: config.metadata as unknown,\n syncMetadata: (this.syncedMetadata.state.get(id) || {}) as Record<\n string,\n unknown\n >,\n type: `update`,\n createdAt: new Date(),\n updatedAt: new Date(),\n collection: this,\n }\n })\n .filter(Boolean) as Array<PendingMutation<T>>\n\n // If no changes were made, return early\n if (mutations.length === 0) {\n throw new Error(`No changes were made to any of the objects`)\n }\n\n transaction.applyMutations(mutations)\n\n this.transactions.setState((sortedMap) => {\n sortedMap.set(transaction.id, transaction)\n return sortedMap\n })\n\n return transaction\n }\n\n /**\n * Deletes one or more items from the collection\n * @param ids - Single ID or array of IDs to delete\n * @param config - Optional configuration including metadata\n * @returns A Transaction object representing the delete operation(s)\n * @example\n * // Delete a single item\n * delete(\"todo-1\")\n *\n * // Delete multiple items\n * delete([\"todo-1\", \"todo-2\"])\n *\n * // Delete with metadata\n * delete(\"todo-1\", { metadata: { reason: \"completed\" } })\n */\n delete = (ids: Array<string> | string, config?: OperationConfig) => {\n const transaction = getActiveTransaction()\n if (typeof transaction === `undefined`) {\n throw `no transaction found when calling collection.delete`\n }\n\n const idsArray = (Array.isArray(ids) ? ids : [ids]).map((id) =>\n this.getKeyFromId(id)\n )\n const mutations: Array<PendingMutation<T>> = []\n\n for (const id of idsArray) {\n const mutation: PendingMutation<T> = {\n mutationId: crypto.randomUUID(),\n original: (this.state.get(id) || {}) as Record<string, unknown>,\n modified: (this.state.get(id) || {}) as Record<string, unknown>,\n changes: (this.state.get(id) || {}) as Record<string, unknown>,\n key: id,\n metadata: config?.metadata as unknown,\n syncMetadata: (this.syncedMetadata.state.get(id) || {}) as Record<\n string,\n unknown\n >,\n type: `delete`,\n createdAt: new Date(),\n updatedAt: new Date(),\n collection: this,\n }\n\n mutations.push(mutation)\n }\n\n transaction.applyMutations(mutations)\n\n this.transactions.setState((sortedMap) => {\n sortedMap.set(transaction.id, transaction)\n return sortedMap\n })\n\n return transaction\n }\n\n /**\n * Gets the current state of the collection as a Map\n *\n * @returns A Map containing all items in the collection, with keys as identifiers\n */\n get state() {\n return this.derivedState.state\n }\n\n /**\n * Gets the current state of the collection as a Map, but only resolves when data is available\n * Waits for the first sync commit to complete before resolving\n *\n * @returns Promise that resolves to a Map containing all items in the collection\n */\n stateWhenReady(): Promise<Map<string, T>> {\n // If we already have data or there are no loading collections, resolve immediately\n if (this.state.size > 0 || this.hasReceivedFirstCommit === true) {\n return Promise.resolve(this.state)\n }\n\n // Otherwise, wait for the first commit\n return new Promise<Map<string, T>>((resolve) => {\n this.onFirstCommit(() => {\n resolve(this.state)\n })\n })\n }\n\n /**\n * Gets the current state of the collection as an Array\n *\n * @returns An Array containing all items in the collection\n */\n get toArray() {\n return this.derivedArray.state\n }\n\n /**\n * Gets the current state of the collection as an Array, but only resolves when data is available\n * Waits for the first sync commit to complete before resolving\n *\n * @returns Promise that resolves to an Array containing all items in the collection\n */\n toArrayWhenReady(): Promise<Array<T>> {\n // If we already have data or there are no loading collections, resolve immediately\n if (this.toArray.length > 0 || this.hasReceivedFirstCommit === true) {\n return Promise.resolve(this.toArray)\n }\n\n // Otherwise, wait for the first commit\n return new Promise<Array<T>>((resolve) => {\n this.onFirstCommit(() => {\n resolve(this.toArray)\n })\n })\n }\n\n /**\n * Returns the current state of the collection as an array of changes\n * @returns An array of changes\n */\n public currentStateAsChanges(): Array<ChangeMessage<T>> {\n return [...this.state.entries()].map(([key, value]) => ({\n type: `insert`,\n key,\n value,\n }))\n }\n\n /**\n * Subscribe to changes in the collection\n * @param callback - A function that will be called with the changes in the collection\n * @returns A function that can be called to unsubscribe from the changes\n */\n public subscribeChanges(\n callback: (changes: Array<ChangeMessage<T>>) => void\n ): () => void {\n // First send the current state as changes\n callback(this.currentStateAsChanges())\n\n // Then subscribe to changes, this returns an unsubscribe function\n return this.derivedChanges.subscribe((changes) => {\n if (changes.currentVal.length > 0) {\n callback(changes.currentVal)\n }\n })\n }\n}\n"],"names":["Store","batch","config","getActiveTransaction","SortedMap","Derived","transactions","result","withArrayChangeTracking","withChangeTracking"],"mappings":"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"}
|