@tanstack/db 0.0.1

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.
Files changed (154) hide show
  1. package/README.md +37 -0
  2. package/dist/cjs/SortedMap.cjs +140 -0
  3. package/dist/cjs/SortedMap.cjs.map +1 -0
  4. package/dist/cjs/SortedMap.d.cts +91 -0
  5. package/dist/cjs/collection.cjs +597 -0
  6. package/dist/cjs/collection.cjs.map +1 -0
  7. package/dist/cjs/collection.d.cts +176 -0
  8. package/dist/cjs/deferred.cjs +25 -0
  9. package/dist/cjs/deferred.cjs.map +1 -0
  10. package/dist/cjs/deferred.d.cts +20 -0
  11. package/dist/cjs/errors.cjs +10 -0
  12. package/dist/cjs/errors.cjs.map +1 -0
  13. package/dist/cjs/errors.d.cts +3 -0
  14. package/dist/cjs/index.cjs +33 -0
  15. package/dist/cjs/index.cjs.map +1 -0
  16. package/dist/cjs/index.d.cts +9 -0
  17. package/dist/cjs/proxy.cjs +654 -0
  18. package/dist/cjs/proxy.cjs.map +1 -0
  19. package/dist/cjs/proxy.d.cts +59 -0
  20. package/dist/cjs/query/compiled-query.cjs +162 -0
  21. package/dist/cjs/query/compiled-query.cjs.map +1 -0
  22. package/dist/cjs/query/compiled-query.d.cts +22 -0
  23. package/dist/cjs/query/evaluators.cjs +146 -0
  24. package/dist/cjs/query/evaluators.cjs.map +1 -0
  25. package/dist/cjs/query/evaluators.d.cts +9 -0
  26. package/dist/cjs/query/extractors.cjs +122 -0
  27. package/dist/cjs/query/extractors.cjs.map +1 -0
  28. package/dist/cjs/query/extractors.d.cts +22 -0
  29. package/dist/cjs/query/functions.cjs +152 -0
  30. package/dist/cjs/query/functions.cjs.map +1 -0
  31. package/dist/cjs/query/functions.d.cts +21 -0
  32. package/dist/cjs/query/group-by.cjs +91 -0
  33. package/dist/cjs/query/group-by.cjs.map +1 -0
  34. package/dist/cjs/query/group-by.d.cts +40 -0
  35. package/dist/cjs/query/index.d.cts +5 -0
  36. package/dist/cjs/query/joins.cjs +155 -0
  37. package/dist/cjs/query/joins.cjs.map +1 -0
  38. package/dist/cjs/query/joins.d.cts +14 -0
  39. package/dist/cjs/query/key-by.cjs +43 -0
  40. package/dist/cjs/query/key-by.cjs.map +1 -0
  41. package/dist/cjs/query/key-by.d.cts +3 -0
  42. package/dist/cjs/query/order-by.cjs +229 -0
  43. package/dist/cjs/query/order-by.cjs.map +1 -0
  44. package/dist/cjs/query/order-by.d.cts +3 -0
  45. package/dist/cjs/query/pipeline-compiler.cjs +94 -0
  46. package/dist/cjs/query/pipeline-compiler.cjs.map +1 -0
  47. package/dist/cjs/query/pipeline-compiler.d.cts +9 -0
  48. package/dist/cjs/query/query-builder.cjs +314 -0
  49. package/dist/cjs/query/query-builder.cjs.map +1 -0
  50. package/dist/cjs/query/query-builder.d.cts +219 -0
  51. package/dist/cjs/query/schema.d.cts +98 -0
  52. package/dist/cjs/query/select.cjs +107 -0
  53. package/dist/cjs/query/select.cjs.map +1 -0
  54. package/dist/cjs/query/select.d.cts +3 -0
  55. package/dist/cjs/query/types.d.cts +188 -0
  56. package/dist/cjs/query/utils.cjs +154 -0
  57. package/dist/cjs/query/utils.cjs.map +1 -0
  58. package/dist/cjs/query/utils.d.cts +37 -0
  59. package/dist/cjs/transactions.cjs +137 -0
  60. package/dist/cjs/transactions.cjs.map +1 -0
  61. package/dist/cjs/transactions.d.cts +27 -0
  62. package/dist/cjs/types.d.cts +94 -0
  63. package/dist/cjs/utils.cjs +17 -0
  64. package/dist/cjs/utils.cjs.map +1 -0
  65. package/dist/cjs/utils.d.cts +3 -0
  66. package/dist/esm/SortedMap.d.ts +91 -0
  67. package/dist/esm/SortedMap.js +140 -0
  68. package/dist/esm/SortedMap.js.map +1 -0
  69. package/dist/esm/collection.d.ts +176 -0
  70. package/dist/esm/collection.js +597 -0
  71. package/dist/esm/collection.js.map +1 -0
  72. package/dist/esm/deferred.d.ts +20 -0
  73. package/dist/esm/deferred.js +25 -0
  74. package/dist/esm/deferred.js.map +1 -0
  75. package/dist/esm/errors.d.ts +3 -0
  76. package/dist/esm/errors.js +10 -0
  77. package/dist/esm/errors.js.map +1 -0
  78. package/dist/esm/index.d.ts +9 -0
  79. package/dist/esm/index.js +33 -0
  80. package/dist/esm/index.js.map +1 -0
  81. package/dist/esm/proxy.d.ts +59 -0
  82. package/dist/esm/proxy.js +654 -0
  83. package/dist/esm/proxy.js.map +1 -0
  84. package/dist/esm/query/compiled-query.d.ts +22 -0
  85. package/dist/esm/query/compiled-query.js +162 -0
  86. package/dist/esm/query/compiled-query.js.map +1 -0
  87. package/dist/esm/query/evaluators.d.ts +9 -0
  88. package/dist/esm/query/evaluators.js +146 -0
  89. package/dist/esm/query/evaluators.js.map +1 -0
  90. package/dist/esm/query/extractors.d.ts +22 -0
  91. package/dist/esm/query/extractors.js +122 -0
  92. package/dist/esm/query/extractors.js.map +1 -0
  93. package/dist/esm/query/functions.d.ts +21 -0
  94. package/dist/esm/query/functions.js +152 -0
  95. package/dist/esm/query/functions.js.map +1 -0
  96. package/dist/esm/query/group-by.d.ts +40 -0
  97. package/dist/esm/query/group-by.js +91 -0
  98. package/dist/esm/query/group-by.js.map +1 -0
  99. package/dist/esm/query/index.d.ts +5 -0
  100. package/dist/esm/query/joins.d.ts +14 -0
  101. package/dist/esm/query/joins.js +155 -0
  102. package/dist/esm/query/joins.js.map +1 -0
  103. package/dist/esm/query/key-by.d.ts +3 -0
  104. package/dist/esm/query/key-by.js +43 -0
  105. package/dist/esm/query/key-by.js.map +1 -0
  106. package/dist/esm/query/order-by.d.ts +3 -0
  107. package/dist/esm/query/order-by.js +229 -0
  108. package/dist/esm/query/order-by.js.map +1 -0
  109. package/dist/esm/query/pipeline-compiler.d.ts +9 -0
  110. package/dist/esm/query/pipeline-compiler.js +94 -0
  111. package/dist/esm/query/pipeline-compiler.js.map +1 -0
  112. package/dist/esm/query/query-builder.d.ts +219 -0
  113. package/dist/esm/query/query-builder.js +314 -0
  114. package/dist/esm/query/query-builder.js.map +1 -0
  115. package/dist/esm/query/schema.d.ts +98 -0
  116. package/dist/esm/query/select.d.ts +3 -0
  117. package/dist/esm/query/select.js +107 -0
  118. package/dist/esm/query/select.js.map +1 -0
  119. package/dist/esm/query/types.d.ts +188 -0
  120. package/dist/esm/query/utils.d.ts +37 -0
  121. package/dist/esm/query/utils.js +154 -0
  122. package/dist/esm/query/utils.js.map +1 -0
  123. package/dist/esm/transactions.d.ts +27 -0
  124. package/dist/esm/transactions.js +137 -0
  125. package/dist/esm/transactions.js.map +1 -0
  126. package/dist/esm/types.d.ts +94 -0
  127. package/dist/esm/utils.d.ts +3 -0
  128. package/dist/esm/utils.js +17 -0
  129. package/dist/esm/utils.js.map +1 -0
  130. package/package.json +57 -0
  131. package/src/SortedMap.ts +163 -0
  132. package/src/collection.ts +919 -0
  133. package/src/deferred.ts +47 -0
  134. package/src/errors.ts +6 -0
  135. package/src/index.ts +12 -0
  136. package/src/proxy.ts +1104 -0
  137. package/src/query/compiled-query.ts +193 -0
  138. package/src/query/evaluators.ts +222 -0
  139. package/src/query/extractors.ts +211 -0
  140. package/src/query/functions.ts +297 -0
  141. package/src/query/group-by.ts +137 -0
  142. package/src/query/index.ts +5 -0
  143. package/src/query/joins.ts +247 -0
  144. package/src/query/key-by.ts +61 -0
  145. package/src/query/order-by.ts +312 -0
  146. package/src/query/pipeline-compiler.ts +152 -0
  147. package/src/query/query-builder.ts +898 -0
  148. package/src/query/schema.ts +255 -0
  149. package/src/query/select.ts +173 -0
  150. package/src/query/types.ts +417 -0
  151. package/src/query/utils.ts +245 -0
  152. package/src/transactions.ts +198 -0
  153. package/src/types.ts +125 -0
  154. package/src/utils.ts +15 -0
@@ -0,0 +1,597 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const store = require("@tanstack/store");
4
+ const proxy = require("./proxy.cjs");
5
+ const transactions = require("./transactions.cjs");
6
+ const SortedMap = require("./SortedMap.cjs");
7
+ const collectionsStore = new store.Store(/* @__PURE__ */ new Map());
8
+ const loadingCollections = /* @__PURE__ */ new Map();
9
+ function preloadCollection(config) {
10
+ if (collectionsStore.state.has(config.id) && !loadingCollections.has(config.id)) {
11
+ return Promise.resolve(
12
+ collectionsStore.state.get(config.id)
13
+ );
14
+ }
15
+ if (loadingCollections.has(config.id)) {
16
+ return loadingCollections.get(config.id);
17
+ }
18
+ if (!collectionsStore.state.has(config.id)) {
19
+ collectionsStore.setState((prev) => {
20
+ const next = new Map(prev);
21
+ next.set(
22
+ config.id,
23
+ new Collection({
24
+ id: config.id,
25
+ sync: config.sync,
26
+ schema: config.schema
27
+ })
28
+ );
29
+ return next;
30
+ });
31
+ }
32
+ const collection = collectionsStore.state.get(config.id);
33
+ let resolveFirstCommit;
34
+ const firstCommitPromise = new Promise((resolve) => {
35
+ resolveFirstCommit = () => {
36
+ resolve(collection);
37
+ };
38
+ });
39
+ collection.onFirstCommit(() => {
40
+ if (loadingCollections.has(config.id)) {
41
+ loadingCollections.delete(config.id);
42
+ resolveFirstCommit();
43
+ }
44
+ });
45
+ loadingCollections.set(
46
+ config.id,
47
+ firstCommitPromise
48
+ );
49
+ return firstCommitPromise;
50
+ }
51
+ class SchemaValidationError extends Error {
52
+ constructor(type, issues, message) {
53
+ const defaultMessage = `${type === `insert` ? `Insert` : `Update`} validation failed: ${issues.map((issue) => issue.message).join(`, `)}`;
54
+ super(message || defaultMessage);
55
+ this.name = `SchemaValidationError`;
56
+ this.type = type;
57
+ this.issues = issues;
58
+ }
59
+ }
60
+ class Collection {
61
+ /**
62
+ * Creates a new Collection instance
63
+ *
64
+ * @param config - Configuration object for the collection
65
+ * @throws Error if sync config is missing
66
+ */
67
+ constructor(config) {
68
+ this.syncedData = new store.Store(/* @__PURE__ */ new Map());
69
+ this.syncedMetadata = new store.Store(/* @__PURE__ */ new Map());
70
+ this.pendingSyncedTransactions = [];
71
+ this.syncedKeys = /* @__PURE__ */ new Set();
72
+ this.hasReceivedFirstCommit = false;
73
+ this.objectKeyMap = /* @__PURE__ */ new WeakMap();
74
+ this.onFirstCommitCallbacks = [];
75
+ this.id = crypto.randomUUID();
76
+ this.commitPendingTransactions = () => {
77
+ if (!Array.from(this.transactions.state.values()).some(
78
+ ({ state }) => state === `persisting`
79
+ )) {
80
+ const keys = /* @__PURE__ */ new Set();
81
+ store.batch(() => {
82
+ for (const transaction of this.pendingSyncedTransactions) {
83
+ for (const operation of transaction.operations) {
84
+ keys.add(operation.key);
85
+ this.syncedKeys.add(operation.key);
86
+ this.syncedMetadata.setState((prevData) => {
87
+ switch (operation.type) {
88
+ case `insert`:
89
+ prevData.set(operation.key, operation.metadata);
90
+ break;
91
+ case `update`:
92
+ prevData.set(operation.key, {
93
+ ...prevData.get(operation.key),
94
+ ...operation.metadata
95
+ });
96
+ break;
97
+ case `delete`:
98
+ prevData.delete(operation.key);
99
+ break;
100
+ }
101
+ return prevData;
102
+ });
103
+ this.syncedData.setState((prevData) => {
104
+ switch (operation.type) {
105
+ case `insert`:
106
+ prevData.set(operation.key, operation.value);
107
+ break;
108
+ case `update`:
109
+ prevData.set(operation.key, {
110
+ ...prevData.get(operation.key),
111
+ ...operation.value
112
+ });
113
+ break;
114
+ case `delete`:
115
+ prevData.delete(operation.key);
116
+ break;
117
+ }
118
+ return prevData;
119
+ });
120
+ }
121
+ }
122
+ });
123
+ keys.forEach((key) => {
124
+ const curValue = this.state.get(key);
125
+ if (curValue) {
126
+ this.objectKeyMap.set(curValue, key);
127
+ }
128
+ });
129
+ this.pendingSyncedTransactions = [];
130
+ if (!this.hasReceivedFirstCommit) {
131
+ this.hasReceivedFirstCommit = true;
132
+ const callbacks = [...this.onFirstCommitCallbacks];
133
+ this.onFirstCommitCallbacks = [];
134
+ callbacks.forEach((callback) => callback());
135
+ }
136
+ }
137
+ };
138
+ this.insert = (data, config2) => {
139
+ const transaction = transactions.getActiveTransaction();
140
+ if (typeof transaction === `undefined`) {
141
+ throw `no transaction found when calling collection.insert`;
142
+ }
143
+ const items = Array.isArray(data) ? data : [data];
144
+ const mutations = [];
145
+ let keys;
146
+ if (config2 == null ? void 0 : config2.key) {
147
+ const configKeys = Array.isArray(config2.key) ? config2.key : [config2.key];
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
+ }
155
+ items.forEach((item, index) => {
156
+ var _a, _b;
157
+ const validatedData = this.validateData(item, `insert`);
158
+ const key = keys[index];
159
+ const mutation = {
160
+ mutationId: crypto.randomUUID(),
161
+ original: {},
162
+ modified: validatedData,
163
+ changes: validatedData,
164
+ key,
165
+ metadata: config2 == null ? void 0 : config2.metadata,
166
+ syncMetadata: ((_b = (_a = this.config.sync).getSyncMetadata) == null ? void 0 : _b.call(_a)) || {},
167
+ type: `insert`,
168
+ createdAt: /* @__PURE__ */ new Date(),
169
+ updatedAt: /* @__PURE__ */ new Date(),
170
+ collection: this
171
+ };
172
+ mutations.push(mutation);
173
+ });
174
+ transaction.applyMutations(mutations);
175
+ this.transactions.setState((sortedMap) => {
176
+ sortedMap.set(transaction.id, transaction);
177
+ return sortedMap;
178
+ });
179
+ return transaction;
180
+ };
181
+ this.delete = (items, config2) => {
182
+ const transaction = transactions.getActiveTransaction();
183
+ if (typeof transaction === `undefined`) {
184
+ throw `no transaction found when calling collection.delete`;
185
+ }
186
+ const itemsArray = Array.isArray(items) ? items : [items];
187
+ const mutations = [];
188
+ for (const item of itemsArray) {
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
+ }
205
+ const mutation = {
206
+ mutationId: crypto.randomUUID(),
207
+ original: this.state.get(key) || {},
208
+ modified: { _deleted: true },
209
+ changes: { _deleted: true },
210
+ key,
211
+ metadata: config2 == null ? void 0 : config2.metadata,
212
+ syncMetadata: this.syncedMetadata.state.get(key) || {},
213
+ type: `delete`,
214
+ createdAt: /* @__PURE__ */ new Date(),
215
+ updatedAt: /* @__PURE__ */ new Date(),
216
+ collection: this
217
+ };
218
+ mutations.push(mutation);
219
+ }
220
+ mutations.forEach((mutation) => {
221
+ const curValue = this.state.get(mutation.key);
222
+ if (curValue) {
223
+ this.objectKeyMap.delete(curValue);
224
+ }
225
+ });
226
+ transaction.applyMutations(mutations);
227
+ this.transactions.setState((sortedMap) => {
228
+ sortedMap.set(transaction.id, transaction);
229
+ return sortedMap;
230
+ });
231
+ return transaction;
232
+ };
233
+ if (!(config == null ? void 0 : config.sync)) {
234
+ throw new Error(`Collection requires a sync config`);
235
+ }
236
+ this.transactions = new store.Store(
237
+ new SortedMap.SortedMap(
238
+ (a, b) => a.createdAt.getTime() - b.createdAt.getTime()
239
+ )
240
+ );
241
+ this.optimisticOperations = new store.Derived({
242
+ fn: ({ currDepVals: [transactions2] }) => {
243
+ const result = Array.from(transactions2.values()).map((transaction) => {
244
+ const isActive = ![`completed`, `failed`].includes(
245
+ transaction.state
246
+ );
247
+ return transaction.mutations.map((mutation) => {
248
+ const message = {
249
+ type: mutation.type,
250
+ key: mutation.key,
251
+ value: mutation.modified,
252
+ isActive
253
+ };
254
+ if (mutation.metadata !== void 0 && mutation.metadata !== null) {
255
+ message.metadata = mutation.metadata;
256
+ }
257
+ return message;
258
+ });
259
+ }).flat();
260
+ return result;
261
+ },
262
+ deps: [this.transactions]
263
+ });
264
+ this.optimisticOperations.mount();
265
+ this.derivedState = new store.Derived({
266
+ fn: ({ currDepVals: [syncedData, operations] }) => {
267
+ const combined = new Map(syncedData);
268
+ const optimisticKeys = /* @__PURE__ */ new Set();
269
+ for (const operation of operations) {
270
+ optimisticKeys.add(operation.key);
271
+ if (operation.isActive) {
272
+ switch (operation.type) {
273
+ case `insert`:
274
+ combined.set(operation.key, operation.value);
275
+ break;
276
+ case `update`:
277
+ combined.set(operation.key, operation.value);
278
+ break;
279
+ case `delete`:
280
+ combined.delete(operation.key);
281
+ break;
282
+ }
283
+ }
284
+ }
285
+ optimisticKeys.forEach((key) => {
286
+ if (combined.has(key)) {
287
+ this.objectKeyMap.set(combined.get(key), key);
288
+ }
289
+ });
290
+ return combined;
291
+ },
292
+ deps: [this.syncedData, this.optimisticOperations]
293
+ });
294
+ this.derivedArray = new store.Derived({
295
+ fn: ({ currDepVals: [stateMap] }) => {
296
+ const array = Array.from(
297
+ stateMap.values()
298
+ );
299
+ if (array[0] && `_orderByIndex` in array[0]) {
300
+ array.sort((a, b) => {
301
+ if (a._orderByIndex === b._orderByIndex) {
302
+ return 0;
303
+ }
304
+ return a._orderByIndex < b._orderByIndex ? -1 : 1;
305
+ });
306
+ }
307
+ return array;
308
+ },
309
+ deps: [this.derivedState]
310
+ });
311
+ this.derivedArray.mount();
312
+ this.derivedChanges = new store.Derived({
313
+ fn: ({
314
+ currDepVals: [derivedState, optimisticOperations],
315
+ prevDepVals
316
+ }) => {
317
+ const prevDerivedState = (prevDepVals == null ? void 0 : prevDepVals[0]) ?? /* @__PURE__ */ new Map();
318
+ const changedKeys = new Set(this.syncedKeys);
319
+ optimisticOperations.flat().filter((op) => op.isActive).forEach((op) => changedKeys.add(op.key));
320
+ if (changedKeys.size === 0) {
321
+ return [];
322
+ }
323
+ const changes = [];
324
+ for (const key of changedKeys) {
325
+ if (prevDerivedState.has(key) && !derivedState.has(key)) {
326
+ changes.push({
327
+ type: `delete`,
328
+ key,
329
+ value: prevDerivedState.get(key)
330
+ });
331
+ } else if (!prevDerivedState.has(key) && derivedState.has(key)) {
332
+ changes.push({ type: `insert`, key, value: derivedState.get(key) });
333
+ } else if (prevDerivedState.has(key) && derivedState.has(key)) {
334
+ changes.push({
335
+ type: `update`,
336
+ key,
337
+ value: derivedState.get(key),
338
+ previousValue: prevDerivedState.get(key)
339
+ });
340
+ }
341
+ }
342
+ this.syncedKeys.clear();
343
+ return changes;
344
+ },
345
+ deps: [this.derivedState, this.optimisticOperations]
346
+ });
347
+ this.derivedChanges.mount();
348
+ this.config = config;
349
+ this.derivedState.mount();
350
+ config.sync.sync({
351
+ collection: this,
352
+ begin: () => {
353
+ this.pendingSyncedTransactions.push({
354
+ committed: false,
355
+ operations: []
356
+ });
357
+ },
358
+ write: (message) => {
359
+ const pendingTransaction = this.pendingSyncedTransactions[this.pendingSyncedTransactions.length - 1];
360
+ if (!pendingTransaction) {
361
+ throw new Error(`No pending sync transaction to write to`);
362
+ }
363
+ if (pendingTransaction.committed) {
364
+ throw new Error(
365
+ `The pending sync transaction is already committed, you can't still write to it.`
366
+ );
367
+ }
368
+ pendingTransaction.operations.push(message);
369
+ },
370
+ commit: () => {
371
+ const pendingTransaction = this.pendingSyncedTransactions[this.pendingSyncedTransactions.length - 1];
372
+ if (!pendingTransaction) {
373
+ throw new Error(`No pending sync transaction to commit`);
374
+ }
375
+ if (pendingTransaction.committed) {
376
+ throw new Error(
377
+ `The pending sync transaction is already committed, you can't commit it again.`
378
+ );
379
+ }
380
+ pendingTransaction.committed = true;
381
+ this.commitPendingTransactions();
382
+ }
383
+ });
384
+ }
385
+ /**
386
+ * Register a callback to be executed on the next commit
387
+ * Useful for preloading collections
388
+ * @param callback Function to call after the next commit
389
+ */
390
+ onFirstCommit(callback) {
391
+ this.onFirstCommitCallbacks.push(callback);
392
+ }
393
+ ensureStandardSchema(schema) {
394
+ if (schema && typeof schema === `object` && `~standard` in schema) {
395
+ return schema;
396
+ }
397
+ throw new Error(
398
+ `Schema must either implement the standard-schema interface or be a Zod schema`
399
+ );
400
+ }
401
+ validateData(data, type, key) {
402
+ if (!this.config.schema) return data;
403
+ const standardSchema = this.ensureStandardSchema(this.config.schema);
404
+ if (type === `update` && key) {
405
+ const existingData = this.state.get(key);
406
+ if (existingData && data && typeof data === `object` && typeof existingData === `object`) {
407
+ const mergedData = { ...existingData, ...data };
408
+ const result2 = standardSchema[`~standard`].validate(mergedData);
409
+ if (result2 instanceof Promise) {
410
+ throw new TypeError(`Schema validation must be synchronous`);
411
+ }
412
+ if (`issues` in result2 && result2.issues) {
413
+ const typedIssues = result2.issues.map((issue) => {
414
+ var _a;
415
+ return {
416
+ message: issue.message,
417
+ path: (_a = issue.path) == null ? void 0 : _a.map((p) => String(p))
418
+ };
419
+ });
420
+ throw new SchemaValidationError(type, typedIssues);
421
+ }
422
+ return data;
423
+ }
424
+ }
425
+ const result = standardSchema[`~standard`].validate(data);
426
+ if (result instanceof Promise) {
427
+ throw new TypeError(`Schema validation must be synchronous`);
428
+ }
429
+ if (`issues` in result && result.issues) {
430
+ const typedIssues = result.issues.map((issue) => {
431
+ var _a;
432
+ return {
433
+ message: issue.message,
434
+ path: (_a = issue.path) == null ? void 0 : _a.map((p) => String(p))
435
+ };
436
+ });
437
+ throw new SchemaValidationError(type, typedIssues);
438
+ }
439
+ return result.value;
440
+ }
441
+ generateKey(data) {
442
+ const str = JSON.stringify(data);
443
+ let h = 0;
444
+ for (let i = 0; i < str.length; i++) {
445
+ h = Math.imul(31, h) + str.charCodeAt(i) | 0;
446
+ }
447
+ return `${this.id}/${Math.abs(h).toString(36)}`;
448
+ }
449
+ update(items, configOrCallback, maybeCallback) {
450
+ if (typeof items === `undefined`) {
451
+ throw new Error(`The first argument to update is missing`);
452
+ }
453
+ const transaction = transactions.getActiveTransaction();
454
+ if (typeof transaction === `undefined`) {
455
+ throw `no transaction found when calling collection.update`;
456
+ }
457
+ const isArray = Array.isArray(items);
458
+ const itemsArray = Array.isArray(items) ? items : [items];
459
+ const callback = typeof configOrCallback === `function` ? configOrCallback : maybeCallback;
460
+ const config = typeof configOrCallback === `function` ? {} : configOrCallback;
461
+ const keys = itemsArray.map((item) => {
462
+ if (typeof item === `object` && item !== null) {
463
+ const key = this.objectKeyMap.get(item);
464
+ if (key === void 0) {
465
+ throw new Error(`Object not found in collection`);
466
+ }
467
+ return key;
468
+ }
469
+ throw new Error(`Invalid item type for update - must be an object`);
470
+ });
471
+ const currentObjects = keys.map((key) => ({
472
+ ...this.state.get(key) || {}
473
+ }));
474
+ let changesArray;
475
+ if (isArray) {
476
+ changesArray = proxy.withArrayChangeTracking(
477
+ currentObjects,
478
+ callback
479
+ );
480
+ } else {
481
+ const result = proxy.withChangeTracking(
482
+ currentObjects[0],
483
+ callback
484
+ );
485
+ changesArray = [result];
486
+ }
487
+ const mutations = keys.map((key, index) => {
488
+ const changes = changesArray[index];
489
+ if (!changes || Object.keys(changes).length === 0) {
490
+ return null;
491
+ }
492
+ const validatedData = this.validateData(changes, `update`, key);
493
+ return {
494
+ mutationId: crypto.randomUUID(),
495
+ original: this.state.get(key) || {},
496
+ modified: {
497
+ ...this.state.get(key) || {},
498
+ ...validatedData
499
+ },
500
+ changes: validatedData,
501
+ key,
502
+ metadata: config.metadata,
503
+ syncMetadata: this.syncedMetadata.state.get(key) || {},
504
+ type: `update`,
505
+ createdAt: /* @__PURE__ */ new Date(),
506
+ updatedAt: /* @__PURE__ */ new Date(),
507
+ collection: this
508
+ };
509
+ }).filter(Boolean);
510
+ if (mutations.length === 0) {
511
+ throw new Error(`No changes were made to any of the objects`);
512
+ }
513
+ transaction.applyMutations(mutations);
514
+ this.transactions.setState((sortedMap) => {
515
+ sortedMap.set(transaction.id, transaction);
516
+ return sortedMap;
517
+ });
518
+ return transaction;
519
+ }
520
+ /**
521
+ * Gets the current state of the collection as a Map
522
+ *
523
+ * @returns A Map containing all items in the collection, with keys as identifiers
524
+ */
525
+ get state() {
526
+ return this.derivedState.state;
527
+ }
528
+ /**
529
+ * Gets the current state of the collection as a Map, but only resolves when data is available
530
+ * Waits for the first sync commit to complete before resolving
531
+ *
532
+ * @returns Promise that resolves to a Map containing all items in the collection
533
+ */
534
+ stateWhenReady() {
535
+ if (this.state.size > 0 || this.hasReceivedFirstCommit === true) {
536
+ return Promise.resolve(this.state);
537
+ }
538
+ return new Promise((resolve) => {
539
+ this.onFirstCommit(() => {
540
+ resolve(this.state);
541
+ });
542
+ });
543
+ }
544
+ /**
545
+ * Gets the current state of the collection as an Array
546
+ *
547
+ * @returns An Array containing all items in the collection
548
+ */
549
+ get toArray() {
550
+ return this.derivedArray.state;
551
+ }
552
+ /**
553
+ * Gets the current state of the collection as an Array, but only resolves when data is available
554
+ * Waits for the first sync commit to complete before resolving
555
+ *
556
+ * @returns Promise that resolves to an Array containing all items in the collection
557
+ */
558
+ toArrayWhenReady() {
559
+ if (this.toArray.length > 0 || this.hasReceivedFirstCommit === true) {
560
+ return Promise.resolve(this.toArray);
561
+ }
562
+ return new Promise((resolve) => {
563
+ this.onFirstCommit(() => {
564
+ resolve(this.toArray);
565
+ });
566
+ });
567
+ }
568
+ /**
569
+ * Returns the current state of the collection as an array of changes
570
+ * @returns An array of changes
571
+ */
572
+ currentStateAsChanges() {
573
+ return [...this.state.entries()].map(([key, value]) => ({
574
+ type: `insert`,
575
+ key,
576
+ value
577
+ }));
578
+ }
579
+ /**
580
+ * Subscribe to changes in the collection
581
+ * @param callback - A function that will be called with the changes in the collection
582
+ * @returns A function that can be called to unsubscribe from the changes
583
+ */
584
+ subscribeChanges(callback) {
585
+ callback(this.currentStateAsChanges());
586
+ return this.derivedChanges.subscribe((changes) => {
587
+ if (changes.currentVal.length > 0) {
588
+ callback(changes.currentVal);
589
+ }
590
+ });
591
+ }
592
+ }
593
+ exports.Collection = Collection;
594
+ exports.SchemaValidationError = SchemaValidationError;
595
+ exports.collectionsStore = collectionsStore;
596
+ exports.preloadCollection = preloadCollection;
597
+ //# sourceMappingURL=collection.cjs.map