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