@tanstack/db 0.0.17 → 0.0.19
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/SortedMap.cjs.map +1 -1
- package/dist/cjs/collection.cjs +47 -6
- package/dist/cjs/collection.cjs.map +1 -1
- package/dist/cjs/collection.d.cts +150 -45
- package/dist/cjs/deferred.cjs.map +1 -1
- package/dist/cjs/errors.cjs.map +1 -1
- package/dist/cjs/optimistic-action.cjs.map +1 -1
- package/dist/cjs/proxy.cjs.map +1 -1
- package/dist/cjs/query/builder/functions.cjs.map +1 -1
- package/dist/cjs/query/builder/index.cjs.map +1 -1
- package/dist/cjs/query/builder/ref-proxy.cjs.map +1 -1
- package/dist/cjs/query/compiler/evaluators.cjs.map +1 -1
- package/dist/cjs/query/compiler/group-by.cjs.map +1 -1
- package/dist/cjs/query/compiler/index.cjs.map +1 -1
- package/dist/cjs/query/compiler/joins.cjs.map +1 -1
- package/dist/cjs/query/compiler/order-by.cjs.map +1 -1
- package/dist/cjs/query/compiler/select.cjs.map +1 -1
- package/dist/cjs/query/ir.cjs.map +1 -1
- package/dist/cjs/query/live-query-collection.cjs.map +1 -1
- package/dist/cjs/transactions.cjs +116 -0
- package/dist/cjs/transactions.cjs.map +1 -1
- package/dist/cjs/transactions.d.cts +179 -0
- package/dist/cjs/types.d.cts +169 -13
- package/dist/esm/SortedMap.js.map +1 -1
- package/dist/esm/collection.d.ts +150 -45
- package/dist/esm/collection.js +47 -6
- package/dist/esm/collection.js.map +1 -1
- package/dist/esm/deferred.js.map +1 -1
- package/dist/esm/errors.js.map +1 -1
- package/dist/esm/optimistic-action.js.map +1 -1
- package/dist/esm/proxy.js.map +1 -1
- package/dist/esm/query/builder/functions.js.map +1 -1
- package/dist/esm/query/builder/index.js.map +1 -1
- package/dist/esm/query/builder/ref-proxy.js.map +1 -1
- package/dist/esm/query/compiler/evaluators.js.map +1 -1
- package/dist/esm/query/compiler/group-by.js.map +1 -1
- package/dist/esm/query/compiler/index.js.map +1 -1
- package/dist/esm/query/compiler/joins.js.map +1 -1
- package/dist/esm/query/compiler/order-by.js.map +1 -1
- package/dist/esm/query/compiler/select.js.map +1 -1
- package/dist/esm/query/ir.js.map +1 -1
- package/dist/esm/query/live-query-collection.js.map +1 -1
- package/dist/esm/transactions.d.ts +179 -0
- package/dist/esm/transactions.js +116 -0
- package/dist/esm/transactions.js.map +1 -1
- package/dist/esm/types.d.ts +169 -13
- package/package.json +1 -1
- package/src/collection.ts +165 -50
- package/src/proxy.ts +0 -1
- package/src/transactions.ts +179 -0
- package/src/types.ts +199 -28
package/dist/cjs/types.d.cts
CHANGED
|
@@ -44,7 +44,7 @@ export interface PendingMutation<T extends object = Record<string, unknown>, TOp
|
|
|
44
44
|
syncMetadata: Record<string, unknown>;
|
|
45
45
|
createdAt: Date;
|
|
46
46
|
updatedAt: Date;
|
|
47
|
-
collection: Collection<T, any>;
|
|
47
|
+
collection: Collection<T, any, any>;
|
|
48
48
|
}
|
|
49
49
|
/**
|
|
50
50
|
* Configuration options for creating a new transaction
|
|
@@ -140,20 +140,35 @@ export interface OperationConfig {
|
|
|
140
140
|
export interface InsertConfig {
|
|
141
141
|
metadata?: Record<string, unknown>;
|
|
142
142
|
}
|
|
143
|
-
export type UpdateMutationFnParams<T extends object = Record<string, unknown>> = {
|
|
143
|
+
export type UpdateMutationFnParams<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TUtils extends UtilsRecord = Record<string, Fn>> = {
|
|
144
144
|
transaction: TransactionWithMutations<T, `update`>;
|
|
145
|
+
collection: Collection<T, TKey, TUtils>;
|
|
145
146
|
};
|
|
146
|
-
export type InsertMutationFnParams<T extends object = Record<string, unknown>> = {
|
|
147
|
+
export type InsertMutationFnParams<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TUtils extends UtilsRecord = Record<string, Fn>> = {
|
|
147
148
|
transaction: TransactionWithMutations<T, `insert`>;
|
|
149
|
+
collection: Collection<T, TKey, TUtils>;
|
|
148
150
|
};
|
|
149
|
-
export type DeleteMutationFnParams<T extends object = Record<string, unknown>> = {
|
|
151
|
+
export type DeleteMutationFnParams<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TUtils extends UtilsRecord = Record<string, Fn>> = {
|
|
150
152
|
transaction: TransactionWithMutations<T, `delete`>;
|
|
153
|
+
collection: Collection<T, TKey, TUtils>;
|
|
151
154
|
};
|
|
152
|
-
export type InsertMutationFn<T extends object = Record<string, unknown>> = (params: InsertMutationFnParams<T>) => Promise<any>;
|
|
153
|
-
export type UpdateMutationFn<T extends object = Record<string, unknown>> = (params: UpdateMutationFnParams<T>) => Promise<any>;
|
|
154
|
-
export type DeleteMutationFn<T extends object = Record<string, unknown>> = (params: DeleteMutationFnParams<T>) => Promise<any>;
|
|
155
|
+
export type InsertMutationFn<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TUtils extends UtilsRecord = Record<string, Fn>> = (params: InsertMutationFnParams<T, TKey, TUtils>) => Promise<any>;
|
|
156
|
+
export type UpdateMutationFn<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TUtils extends UtilsRecord = Record<string, Fn>> = (params: UpdateMutationFnParams<T, TKey, TUtils>) => Promise<any>;
|
|
157
|
+
export type DeleteMutationFn<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TUtils extends UtilsRecord = Record<string, Fn>> = (params: DeleteMutationFnParams<T, TKey, TUtils>) => Promise<any>;
|
|
155
158
|
/**
|
|
156
159
|
* Collection status values for lifecycle management
|
|
160
|
+
* @example
|
|
161
|
+
* // Check collection status
|
|
162
|
+
* if (collection.status === "loading") {
|
|
163
|
+
* console.log("Collection is loading initial data")
|
|
164
|
+
* } else if (collection.status === "ready") {
|
|
165
|
+
* console.log("Collection is ready for use")
|
|
166
|
+
* }
|
|
167
|
+
*
|
|
168
|
+
* @example
|
|
169
|
+
* // Status transitions
|
|
170
|
+
* // idle → loading → initialCommit → ready
|
|
171
|
+
* // Any status can transition to → error or cleaned-up
|
|
157
172
|
*/
|
|
158
173
|
export type CollectionStatus =
|
|
159
174
|
/** Collection is created but sync hasn't started yet (when startSync config is false) */
|
|
@@ -205,22 +220,132 @@ export interface CollectionConfig<T extends object = Record<string, unknown>, TK
|
|
|
205
220
|
compare?: (x: T, y: T) => number;
|
|
206
221
|
/**
|
|
207
222
|
* Optional asynchronous handler function called before an insert operation
|
|
208
|
-
* @param params Object containing transaction and
|
|
223
|
+
* @param params Object containing transaction and collection information
|
|
209
224
|
* @returns Promise resolving to any value
|
|
225
|
+
* @example
|
|
226
|
+
* // Basic insert handler
|
|
227
|
+
* onInsert: async ({ transaction, collection }) => {
|
|
228
|
+
* const newItem = transaction.mutations[0].modified
|
|
229
|
+
* await api.createTodo(newItem)
|
|
230
|
+
* }
|
|
231
|
+
*
|
|
232
|
+
* @example
|
|
233
|
+
* // Insert handler with multiple items
|
|
234
|
+
* onInsert: async ({ transaction, collection }) => {
|
|
235
|
+
* const items = transaction.mutations.map(m => m.modified)
|
|
236
|
+
* await api.createTodos(items)
|
|
237
|
+
* }
|
|
238
|
+
*
|
|
239
|
+
* @example
|
|
240
|
+
* // Insert handler with error handling
|
|
241
|
+
* onInsert: async ({ transaction, collection }) => {
|
|
242
|
+
* try {
|
|
243
|
+
* const newItem = transaction.mutations[0].modified
|
|
244
|
+
* const result = await api.createTodo(newItem)
|
|
245
|
+
* return result
|
|
246
|
+
* } catch (error) {
|
|
247
|
+
* console.error('Insert failed:', error)
|
|
248
|
+
* throw error // This will cause the transaction to fail
|
|
249
|
+
* }
|
|
250
|
+
* }
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* // Insert handler with metadata
|
|
254
|
+
* onInsert: async ({ transaction, collection }) => {
|
|
255
|
+
* const mutation = transaction.mutations[0]
|
|
256
|
+
* await api.createTodo(mutation.modified, {
|
|
257
|
+
* source: mutation.metadata?.source,
|
|
258
|
+
* timestamp: mutation.createdAt
|
|
259
|
+
* })
|
|
260
|
+
* }
|
|
210
261
|
*/
|
|
211
|
-
onInsert?: InsertMutationFn<T>;
|
|
262
|
+
onInsert?: InsertMutationFn<T, TKey>;
|
|
212
263
|
/**
|
|
213
264
|
* Optional asynchronous handler function called before an update operation
|
|
214
|
-
* @param params Object containing transaction and
|
|
265
|
+
* @param params Object containing transaction and collection information
|
|
215
266
|
* @returns Promise resolving to any value
|
|
267
|
+
* @example
|
|
268
|
+
* // Basic update handler
|
|
269
|
+
* onUpdate: async ({ transaction, collection }) => {
|
|
270
|
+
* const updatedItem = transaction.mutations[0].modified
|
|
271
|
+
* await api.updateTodo(updatedItem.id, updatedItem)
|
|
272
|
+
* }
|
|
273
|
+
*
|
|
274
|
+
* @example
|
|
275
|
+
* // Update handler with partial updates
|
|
276
|
+
* onUpdate: async ({ transaction, collection }) => {
|
|
277
|
+
* const mutation = transaction.mutations[0]
|
|
278
|
+
* const changes = mutation.changes // Only the changed fields
|
|
279
|
+
* await api.updateTodo(mutation.original.id, changes)
|
|
280
|
+
* }
|
|
281
|
+
*
|
|
282
|
+
* @example
|
|
283
|
+
* // Update handler with multiple items
|
|
284
|
+
* onUpdate: async ({ transaction, collection }) => {
|
|
285
|
+
* const updates = transaction.mutations.map(m => ({
|
|
286
|
+
* id: m.key,
|
|
287
|
+
* changes: m.changes
|
|
288
|
+
* }))
|
|
289
|
+
* await api.updateTodos(updates)
|
|
290
|
+
* }
|
|
291
|
+
*
|
|
292
|
+
* @example
|
|
293
|
+
* // Update handler with optimistic rollback
|
|
294
|
+
* onUpdate: async ({ transaction, collection }) => {
|
|
295
|
+
* const mutation = transaction.mutations[0]
|
|
296
|
+
* try {
|
|
297
|
+
* await api.updateTodo(mutation.original.id, mutation.changes)
|
|
298
|
+
* } catch (error) {
|
|
299
|
+
* // Transaction will automatically rollback optimistic changes
|
|
300
|
+
* console.error('Update failed, rolling back:', error)
|
|
301
|
+
* throw error
|
|
302
|
+
* }
|
|
303
|
+
* }
|
|
216
304
|
*/
|
|
217
|
-
onUpdate?: UpdateMutationFn<T>;
|
|
305
|
+
onUpdate?: UpdateMutationFn<T, TKey>;
|
|
218
306
|
/**
|
|
219
307
|
* Optional asynchronous handler function called before a delete operation
|
|
220
|
-
* @param params Object containing transaction and
|
|
308
|
+
* @param params Object containing transaction and collection information
|
|
221
309
|
* @returns Promise resolving to any value
|
|
310
|
+
* @example
|
|
311
|
+
* // Basic delete handler
|
|
312
|
+
* onDelete: async ({ transaction, collection }) => {
|
|
313
|
+
* const deletedKey = transaction.mutations[0].key
|
|
314
|
+
* await api.deleteTodo(deletedKey)
|
|
315
|
+
* }
|
|
316
|
+
*
|
|
317
|
+
* @example
|
|
318
|
+
* // Delete handler with multiple items
|
|
319
|
+
* onDelete: async ({ transaction, collection }) => {
|
|
320
|
+
* const keysToDelete = transaction.mutations.map(m => m.key)
|
|
321
|
+
* await api.deleteTodos(keysToDelete)
|
|
322
|
+
* }
|
|
323
|
+
*
|
|
324
|
+
* @example
|
|
325
|
+
* // Delete handler with confirmation
|
|
326
|
+
* onDelete: async ({ transaction, collection }) => {
|
|
327
|
+
* const mutation = transaction.mutations[0]
|
|
328
|
+
* const shouldDelete = await confirmDeletion(mutation.original)
|
|
329
|
+
* if (!shouldDelete) {
|
|
330
|
+
* throw new Error('Delete cancelled by user')
|
|
331
|
+
* }
|
|
332
|
+
* await api.deleteTodo(mutation.original.id)
|
|
333
|
+
* }
|
|
334
|
+
*
|
|
335
|
+
* @example
|
|
336
|
+
* // Delete handler with optimistic rollback
|
|
337
|
+
* onDelete: async ({ transaction, collection }) => {
|
|
338
|
+
* const mutation = transaction.mutations[0]
|
|
339
|
+
* try {
|
|
340
|
+
* await api.deleteTodo(mutation.original.id)
|
|
341
|
+
* } catch (error) {
|
|
342
|
+
* // Transaction will automatically rollback optimistic changes
|
|
343
|
+
* console.error('Delete failed, rolling back:', error)
|
|
344
|
+
* throw error
|
|
345
|
+
* }
|
|
346
|
+
* }
|
|
222
347
|
*/
|
|
223
|
-
onDelete?: DeleteMutationFn<T>;
|
|
348
|
+
onDelete?: DeleteMutationFn<T, TKey>;
|
|
224
349
|
}
|
|
225
350
|
export type ChangesPayload<T extends object = Record<string, unknown>> = Array<ChangeMessage<T>>;
|
|
226
351
|
/**
|
|
@@ -252,4 +377,35 @@ export type KeyedNamespacedRow = [unknown, NamespacedRow];
|
|
|
252
377
|
* a `select` clause.
|
|
253
378
|
*/
|
|
254
379
|
export type NamespacedAndKeyedStream = IStreamBuilder<KeyedNamespacedRow>;
|
|
380
|
+
/**
|
|
381
|
+
* Function type for listening to collection changes
|
|
382
|
+
* @param changes - Array of change messages describing what happened
|
|
383
|
+
* @example
|
|
384
|
+
* // Basic change listener
|
|
385
|
+
* const listener: ChangeListener = (changes) => {
|
|
386
|
+
* changes.forEach(change => {
|
|
387
|
+
* console.log(`${change.type}: ${change.key}`, change.value)
|
|
388
|
+
* })
|
|
389
|
+
* }
|
|
390
|
+
*
|
|
391
|
+
* collection.subscribeChanges(listener)
|
|
392
|
+
*
|
|
393
|
+
* @example
|
|
394
|
+
* // Handle different change types
|
|
395
|
+
* const listener: ChangeListener<Todo> = (changes) => {
|
|
396
|
+
* for (const change of changes) {
|
|
397
|
+
* switch (change.type) {
|
|
398
|
+
* case 'insert':
|
|
399
|
+
* addToUI(change.value)
|
|
400
|
+
* break
|
|
401
|
+
* case 'update':
|
|
402
|
+
* updateInUI(change.key, change.value, change.previousValue)
|
|
403
|
+
* break
|
|
404
|
+
* case 'delete':
|
|
405
|
+
* removeFromUI(change.key)
|
|
406
|
+
* break
|
|
407
|
+
* }
|
|
408
|
+
* }
|
|
409
|
+
* }
|
|
410
|
+
*/
|
|
255
411
|
export type ChangeListener<T extends object = Record<string, unknown>, TKey extends string | number = string | number> = (changes: Array<ChangeMessage<T, TKey>>) => void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SortedMap.js","sources":["../../src/SortedMap.ts"],"sourcesContent":["/**\n * A Map implementation that keeps its entries sorted based on a comparator function\n * @template TKey - The type of keys in the map\n * @template TValue - The type of values in the map\n */\nexport class SortedMap<TKey, TValue> {\n private map: Map<TKey, TValue>\n private sortedKeys: Array<TKey>\n private comparator: (a: TValue, b: TValue) => number\n\n /**\n * Creates a new SortedMap instance\n *\n * @param comparator - Optional function to compare values for sorting\n */\n constructor(comparator?: (a: TValue, b: TValue) => number) {\n this.map = new Map<TKey, TValue>()\n this.sortedKeys = []\n this.comparator = comparator || this.defaultComparator\n }\n\n /**\n * Default comparator function used when none is provided\n *\n * @param a - First value to compare\n * @param b - Second value to compare\n * @returns -1 if a < b, 1 if a > b, 0 if equal\n */\n private defaultComparator(a: TValue, b: TValue): number {\n if (a < b) return -1\n if (a > b) return 1\n return 0\n }\n\n /**\n * Finds the index where a key-value pair should be inserted to maintain sort order.\n * Uses binary search to find the correct position based on the value.\n * Hence, it is in O(log n) time.\n *\n * @param key - The key to find position for\n * @param value - The value to compare against\n * @returns The index where the key should be inserted\n */\n private indexOf(value: TValue): number {\n let left = 0\n let right = this.sortedKeys.length\n\n while (left < right) {\n const mid = Math.floor((left + right) / 2)\n const midKey = this.sortedKeys[mid]!\n const midValue = this.map.get(midKey)!\n const comparison = this.comparator(value, midValue)\n\n if (comparison < 0) {\n right = mid\n } else if (comparison > 0) {\n left = mid + 1\n } else {\n return mid\n }\n }\n\n return left\n }\n\n /**\n * Sets a key-value pair in the map and maintains sort order\n *\n * @param key - The key to set\n * @param value - The value to associate with the key\n * @returns This SortedMap instance for chaining\n */\n set(key: TKey, value: TValue): this {\n if (this.map.has(key)) {\n // Need to remove the old key from the sorted keys array\n const oldValue = this.map.get(key)!\n const oldIndex = this.indexOf(oldValue)\n this.sortedKeys.splice(oldIndex, 1)\n }\n\n // Insert the new key at the correct position\n const index = this.indexOf(value)\n this.sortedKeys.splice(index, 0, key)\n\n this.map.set(key, value)\n\n return this\n }\n\n /**\n * Gets a value by its key\n *\n * @param key - The key to look up\n * @returns The value associated with the key, or undefined if not found\n */\n get(key: TKey): TValue | undefined {\n return this.map.get(key)\n }\n\n /**\n * Removes a key-value pair from the map\n *\n * @param key - The key to remove\n * @returns True if the key was found and removed, false otherwise\n */\n delete(key: TKey): boolean {\n if (this.map.has(key)) {\n const oldValue = this.map.get(key)\n const index = this.indexOf(oldValue!)\n this.sortedKeys.splice(index, 1)\n return this.map.delete(key)\n }\n\n return false\n }\n\n /**\n * Checks if a key exists in the map\n *\n * @param key - The key to check\n * @returns True if the key exists, false otherwise\n */\n has(key: TKey): boolean {\n return this.map.has(key)\n }\n\n /**\n * Removes all key-value pairs from the map\n */\n clear(): void {\n this.map.clear()\n this.sortedKeys = []\n }\n\n /**\n * Gets the number of key-value pairs in the map\n */\n get size(): number {\n return this.map.size\n }\n\n /**\n * Default iterator that returns entries in sorted order\n *\n * @returns An iterator for the map's entries\n */\n *[Symbol.iterator](): IterableIterator<[TKey, TValue]> {\n for (const key of this.sortedKeys) {\n yield [key, this.map.get(key)!] as [TKey, TValue]\n }\n }\n\n /**\n * Returns an iterator for the map's entries in sorted order\n *\n * @returns An iterator for the map's entries\n */\n entries(): IterableIterator<[TKey, TValue]> {\n return this[Symbol.iterator]()\n }\n\n /**\n * Returns an iterator for the map's keys in sorted order\n *\n * @returns An iterator for the map's keys\n */\n keys(): IterableIterator<TKey> {\n return this.sortedKeys[Symbol.iterator]()\n }\n\n /**\n * Returns an iterator for the map's values in sorted order\n *\n * @returns An iterator for the map's values\n */\n values(): IterableIterator<TValue> {\n return function* (this: SortedMap<TKey, TValue>) {\n for (const key of this.sortedKeys) {\n yield this.map.get(key)!\n }\n }.call(this)\n }\n\n /**\n * Executes a callback function for each key-value pair in the map in sorted order\n *\n * @param callbackfn - Function to execute for each entry\n */\n forEach(\n callbackfn: (value: TValue, key: TKey, map: Map<TKey, TValue>) => void\n ): void {\n for (const key of this.sortedKeys) {\n callbackfn(this.map.get(key)!, key, this.map)\n }\n }\n}\n"],"names":[],"mappings":"AAKO,MAAM,UAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUnC,YAAY,YAA+C;
|
|
1
|
+
{"version":3,"file":"SortedMap.js","sources":["../../src/SortedMap.ts"],"sourcesContent":["/**\n * A Map implementation that keeps its entries sorted based on a comparator function\n * @template TKey - The type of keys in the map\n * @template TValue - The type of values in the map\n */\nexport class SortedMap<TKey, TValue> {\n private map: Map<TKey, TValue>\n private sortedKeys: Array<TKey>\n private comparator: (a: TValue, b: TValue) => number\n\n /**\n * Creates a new SortedMap instance\n *\n * @param comparator - Optional function to compare values for sorting\n */\n constructor(comparator?: (a: TValue, b: TValue) => number) {\n this.map = new Map<TKey, TValue>()\n this.sortedKeys = []\n this.comparator = comparator || this.defaultComparator\n }\n\n /**\n * Default comparator function used when none is provided\n *\n * @param a - First value to compare\n * @param b - Second value to compare\n * @returns -1 if a < b, 1 if a > b, 0 if equal\n */\n private defaultComparator(a: TValue, b: TValue): number {\n if (a < b) return -1\n if (a > b) return 1\n return 0\n }\n\n /**\n * Finds the index where a key-value pair should be inserted to maintain sort order.\n * Uses binary search to find the correct position based on the value.\n * Hence, it is in O(log n) time.\n *\n * @param key - The key to find position for\n * @param value - The value to compare against\n * @returns The index where the key should be inserted\n */\n private indexOf(value: TValue): number {\n let left = 0\n let right = this.sortedKeys.length\n\n while (left < right) {\n const mid = Math.floor((left + right) / 2)\n const midKey = this.sortedKeys[mid]!\n const midValue = this.map.get(midKey)!\n const comparison = this.comparator(value, midValue)\n\n if (comparison < 0) {\n right = mid\n } else if (comparison > 0) {\n left = mid + 1\n } else {\n return mid\n }\n }\n\n return left\n }\n\n /**\n * Sets a key-value pair in the map and maintains sort order\n *\n * @param key - The key to set\n * @param value - The value to associate with the key\n * @returns This SortedMap instance for chaining\n */\n set(key: TKey, value: TValue): this {\n if (this.map.has(key)) {\n // Need to remove the old key from the sorted keys array\n const oldValue = this.map.get(key)!\n const oldIndex = this.indexOf(oldValue)\n this.sortedKeys.splice(oldIndex, 1)\n }\n\n // Insert the new key at the correct position\n const index = this.indexOf(value)\n this.sortedKeys.splice(index, 0, key)\n\n this.map.set(key, value)\n\n return this\n }\n\n /**\n * Gets a value by its key\n *\n * @param key - The key to look up\n * @returns The value associated with the key, or undefined if not found\n */\n get(key: TKey): TValue | undefined {\n return this.map.get(key)\n }\n\n /**\n * Removes a key-value pair from the map\n *\n * @param key - The key to remove\n * @returns True if the key was found and removed, false otherwise\n */\n delete(key: TKey): boolean {\n if (this.map.has(key)) {\n const oldValue = this.map.get(key)\n const index = this.indexOf(oldValue!)\n this.sortedKeys.splice(index, 1)\n return this.map.delete(key)\n }\n\n return false\n }\n\n /**\n * Checks if a key exists in the map\n *\n * @param key - The key to check\n * @returns True if the key exists, false otherwise\n */\n has(key: TKey): boolean {\n return this.map.has(key)\n }\n\n /**\n * Removes all key-value pairs from the map\n */\n clear(): void {\n this.map.clear()\n this.sortedKeys = []\n }\n\n /**\n * Gets the number of key-value pairs in the map\n */\n get size(): number {\n return this.map.size\n }\n\n /**\n * Default iterator that returns entries in sorted order\n *\n * @returns An iterator for the map's entries\n */\n *[Symbol.iterator](): IterableIterator<[TKey, TValue]> {\n for (const key of this.sortedKeys) {\n yield [key, this.map.get(key)!] as [TKey, TValue]\n }\n }\n\n /**\n * Returns an iterator for the map's entries in sorted order\n *\n * @returns An iterator for the map's entries\n */\n entries(): IterableIterator<[TKey, TValue]> {\n return this[Symbol.iterator]()\n }\n\n /**\n * Returns an iterator for the map's keys in sorted order\n *\n * @returns An iterator for the map's keys\n */\n keys(): IterableIterator<TKey> {\n return this.sortedKeys[Symbol.iterator]()\n }\n\n /**\n * Returns an iterator for the map's values in sorted order\n *\n * @returns An iterator for the map's values\n */\n values(): IterableIterator<TValue> {\n return function* (this: SortedMap<TKey, TValue>) {\n for (const key of this.sortedKeys) {\n yield this.map.get(key)!\n }\n }.call(this)\n }\n\n /**\n * Executes a callback function for each key-value pair in the map in sorted order\n *\n * @param callbackfn - Function to execute for each entry\n */\n forEach(\n callbackfn: (value: TValue, key: TKey, map: Map<TKey, TValue>) => void\n ): void {\n for (const key of this.sortedKeys) {\n callbackfn(this.map.get(key)!, key, this.map)\n }\n }\n}\n"],"names":[],"mappings":"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"}
|
package/dist/esm/collection.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import { SortedMap } from './SortedMap.js';
|
|
|
2
2
|
import { Transaction } from './transactions.js';
|
|
3
3
|
import { ChangeListener, ChangeMessage, CollectionConfig, CollectionStatus, Fn, InsertConfig, OperationConfig, OptimisticChangeMessage, ResolveType, Transaction as TransactionType, UtilsRecord } from './types.js';
|
|
4
4
|
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
5
|
-
export declare const collectionsStore: Map<string, CollectionImpl<any, any>>;
|
|
5
|
+
export declare const collectionsStore: Map<string, CollectionImpl<any, any, {}>>;
|
|
6
6
|
interface PendingSyncedTransaction<T extends object = Record<string, unknown>> {
|
|
7
7
|
committed: boolean;
|
|
8
8
|
operations: Array<OptimisticChangeMessage<T>>;
|
|
@@ -28,12 +28,52 @@ export interface Collection<T extends object = Record<string, unknown>, TKey ext
|
|
|
28
28
|
* @returns A new Collection with utilities exposed both at top level and under .utils
|
|
29
29
|
*
|
|
30
30
|
* @example
|
|
31
|
-
* //
|
|
32
|
-
* const todos = createCollection
|
|
31
|
+
* // Pattern 1: With operation handlers (direct collection calls)
|
|
32
|
+
* const todos = createCollection({
|
|
33
|
+
* id: "todos",
|
|
34
|
+
* getKey: (todo) => todo.id,
|
|
35
|
+
* schema,
|
|
36
|
+
* onInsert: async ({ transaction, collection }) => {
|
|
37
|
+
* // Send to API
|
|
38
|
+
* await api.createTodo(transaction.mutations[0].modified)
|
|
39
|
+
* },
|
|
40
|
+
* onUpdate: async ({ transaction, collection }) => {
|
|
41
|
+
* await api.updateTodo(transaction.mutations[0].modified)
|
|
42
|
+
* },
|
|
43
|
+
* onDelete: async ({ transaction, collection }) => {
|
|
44
|
+
* await api.deleteTodo(transaction.mutations[0].key)
|
|
45
|
+
* },
|
|
46
|
+
* sync: { sync: () => {} }
|
|
47
|
+
* })
|
|
48
|
+
*
|
|
49
|
+
* // Direct usage (handlers manage transactions)
|
|
50
|
+
* const tx = todos.insert({ id: "1", text: "Buy milk", completed: false })
|
|
51
|
+
* await tx.isPersisted.promise
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* // Pattern 2: Manual transaction management
|
|
55
|
+
* const todos = createCollection({
|
|
33
56
|
* getKey: (todo) => todo.id,
|
|
57
|
+
* schema: todoSchema,
|
|
34
58
|
* sync: { sync: () => {} }
|
|
35
59
|
* })
|
|
36
60
|
*
|
|
61
|
+
* // Explicit transaction usage
|
|
62
|
+
* const tx = createTransaction({
|
|
63
|
+
* mutationFn: async ({ transaction }) => {
|
|
64
|
+
* // Handle all mutations in transaction
|
|
65
|
+
* await api.saveChanges(transaction.mutations)
|
|
66
|
+
* }
|
|
67
|
+
* })
|
|
68
|
+
*
|
|
69
|
+
* tx.mutate(() => {
|
|
70
|
+
* todos.insert({ id: "1", text: "Buy milk" })
|
|
71
|
+
* todos.update("2", draft => { draft.completed = true })
|
|
72
|
+
* })
|
|
73
|
+
*
|
|
74
|
+
* await tx.isPersisted.promise
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
37
77
|
* // Using schema for type inference (preferred as it also gives you client side validation)
|
|
38
78
|
* const todoSchema = z.object({
|
|
39
79
|
* id: z.string(),
|
|
@@ -47,7 +87,7 @@ export interface Collection<T extends object = Record<string, unknown>, TKey ext
|
|
|
47
87
|
* sync: { sync: () => {} }
|
|
48
88
|
* })
|
|
49
89
|
*
|
|
50
|
-
* // Note: You must provide either an explicit type or a schema, but not both
|
|
90
|
+
* // Note: You must provide either an explicit type or a schema, but not both.
|
|
51
91
|
*/
|
|
52
92
|
export declare function createCollection<TExplicit = unknown, TKey extends string | number = string | number, TUtils extends UtilsRecord = {}, TSchema extends StandardSchemaV1 = StandardSchemaV1, TFallback extends object = Record<string, unknown>>(options: CollectionConfig<ResolveType<TExplicit, TSchema, TFallback>, TKey, TSchema> & {
|
|
53
93
|
utils?: TUtils;
|
|
@@ -66,7 +106,7 @@ export declare class SchemaValidationError extends Error {
|
|
|
66
106
|
path?: ReadonlyArray<string | number | symbol>;
|
|
67
107
|
}>, message?: string);
|
|
68
108
|
}
|
|
69
|
-
export declare class CollectionImpl<T extends object = Record<string, unknown>, TKey extends string | number = string | number> {
|
|
109
|
+
export declare class CollectionImpl<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TUtils extends UtilsRecord = {}> {
|
|
70
110
|
config: CollectionConfig<T, TKey, any>;
|
|
71
111
|
transactions: SortedMap<string, Transaction<any>>;
|
|
72
112
|
pendingSyncedTransactions: Array<PendingSyncedTransaction<T>>;
|
|
@@ -95,6 +135,11 @@ export declare class CollectionImpl<T extends object = Record<string, unknown>,
|
|
|
95
135
|
* Register a callback to be executed on the next commit
|
|
96
136
|
* Useful for preloading collections
|
|
97
137
|
* @param callback Function to call after the next commit
|
|
138
|
+
* @example
|
|
139
|
+
* collection.onFirstCommit(() => {
|
|
140
|
+
* console.log('Collection has received first data')
|
|
141
|
+
* // Safe to access collection.state now
|
|
142
|
+
* })
|
|
98
143
|
*/
|
|
99
144
|
onFirstCommit(callback: () => void): void;
|
|
100
145
|
id: string;
|
|
@@ -231,60 +276,78 @@ export declare class CollectionImpl<T extends object = Record<string, unknown>,
|
|
|
231
276
|
/**
|
|
232
277
|
* Inserts one or more items into the collection
|
|
233
278
|
* @param items - Single item or array of items to insert
|
|
234
|
-
* @param config - Optional configuration including metadata
|
|
235
|
-
* @returns A
|
|
279
|
+
* @param config - Optional configuration including metadata
|
|
280
|
+
* @returns A Transaction object representing the insert operation(s)
|
|
236
281
|
* @throws {SchemaValidationError} If the data fails schema validation
|
|
237
282
|
* @example
|
|
238
|
-
* // Insert a single
|
|
239
|
-
* insert({ text: "Buy
|
|
283
|
+
* // Insert a single todo (requires onInsert handler)
|
|
284
|
+
* const tx = collection.insert({ id: "1", text: "Buy milk", completed: false })
|
|
285
|
+
* await tx.isPersisted.promise
|
|
240
286
|
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
* { text: "
|
|
287
|
+
* @example
|
|
288
|
+
* // Insert multiple todos at once
|
|
289
|
+
* const tx = collection.insert([
|
|
290
|
+
* { id: "1", text: "Buy milk", completed: false },
|
|
291
|
+
* { id: "2", text: "Walk dog", completed: true }
|
|
245
292
|
* ])
|
|
293
|
+
* await tx.isPersisted.promise
|
|
294
|
+
*
|
|
295
|
+
* @example
|
|
296
|
+
* // Insert with metadata
|
|
297
|
+
* const tx = collection.insert({ id: "1", text: "Buy groceries" },
|
|
298
|
+
* { metadata: { source: "mobile-app" } }
|
|
299
|
+
* )
|
|
300
|
+
* await tx.isPersisted.promise
|
|
246
301
|
*
|
|
247
|
-
*
|
|
248
|
-
*
|
|
302
|
+
* @example
|
|
303
|
+
* // Handle errors
|
|
304
|
+
* try {
|
|
305
|
+
* const tx = collection.insert({ id: "1", text: "New item" })
|
|
306
|
+
* await tx.isPersisted.promise
|
|
307
|
+
* console.log('Insert successful')
|
|
308
|
+
* } catch (error) {
|
|
309
|
+
* console.log('Insert failed:', error)
|
|
310
|
+
* }
|
|
249
311
|
*/
|
|
250
312
|
insert: (data: T | Array<T>, config?: InsertConfig) => Transaction<Record<string, unknown>, import('./types.js').OperationType>;
|
|
251
313
|
/**
|
|
252
314
|
* Updates one or more items in the collection using a callback function
|
|
253
|
-
* @param
|
|
315
|
+
* @param keys - Single key or array of keys to update
|
|
254
316
|
* @param configOrCallback - Either update configuration or update callback
|
|
255
317
|
* @param maybeCallback - Update callback if config was provided
|
|
256
318
|
* @returns A Transaction object representing the update operation(s)
|
|
257
319
|
* @throws {SchemaValidationError} If the updated data fails schema validation
|
|
258
320
|
* @example
|
|
259
|
-
* // Update
|
|
260
|
-
* update(todo, (draft) => {
|
|
261
|
-
*
|
|
262
|
-
* // Update multiple items
|
|
263
|
-
* update([todo1, todo2], (drafts) => {
|
|
264
|
-
* drafts.forEach(draft => { draft.completed = true })
|
|
321
|
+
* // Update single item by key
|
|
322
|
+
* const tx = collection.update("todo-1", (draft) => {
|
|
323
|
+
* draft.completed = true
|
|
265
324
|
* })
|
|
325
|
+
* await tx.isPersisted.promise
|
|
266
326
|
*
|
|
267
|
-
* // Update with metadata
|
|
268
|
-
* update(todo, { metadata: { reason: "user update" } }, (draft) => { draft.text = "Updated text" })
|
|
269
|
-
*/
|
|
270
|
-
/**
|
|
271
|
-
* Updates one or more items in the collection using a callback function
|
|
272
|
-
* @param ids - Single ID or array of IDs to update
|
|
273
|
-
* @param configOrCallback - Either update configuration or update callback
|
|
274
|
-
* @param maybeCallback - Update callback if config was provided
|
|
275
|
-
* @returns A Transaction object representing the update operation(s)
|
|
276
|
-
* @throws {SchemaValidationError} If the updated data fails schema validation
|
|
277
327
|
* @example
|
|
278
|
-
* // Update a single item
|
|
279
|
-
* update("todo-1", (draft) => { draft.completed = true })
|
|
280
|
-
*
|
|
281
328
|
* // Update multiple items
|
|
282
|
-
* update(["todo-1", "todo-2"], (drafts) => {
|
|
329
|
+
* const tx = collection.update(["todo-1", "todo-2"], (drafts) => {
|
|
283
330
|
* drafts.forEach(draft => { draft.completed = true })
|
|
284
331
|
* })
|
|
332
|
+
* await tx.isPersisted.promise
|
|
285
333
|
*
|
|
334
|
+
* @example
|
|
286
335
|
* // Update with metadata
|
|
287
|
-
* update("todo-1",
|
|
336
|
+
* const tx = collection.update("todo-1",
|
|
337
|
+
* { metadata: { reason: "user update" } },
|
|
338
|
+
* (draft) => { draft.text = "Updated text" }
|
|
339
|
+
* )
|
|
340
|
+
* await tx.isPersisted.promise
|
|
341
|
+
*
|
|
342
|
+
* @example
|
|
343
|
+
* // Handle errors
|
|
344
|
+
* try {
|
|
345
|
+
* const tx = collection.update("item-1", draft => { draft.value = "new" })
|
|
346
|
+
* await tx.isPersisted.promise
|
|
347
|
+
* console.log('Update successful')
|
|
348
|
+
* } catch (error) {
|
|
349
|
+
* console.log('Update failed:', error)
|
|
350
|
+
* }
|
|
288
351
|
*/
|
|
289
352
|
update<TItem extends object = T>(key: Array<TKey | unknown>, callback: (drafts: Array<TItem>) => void): TransactionType;
|
|
290
353
|
update<TItem extends object = T>(keys: Array<TKey | unknown>, config: OperationConfig, callback: (drafts: Array<TItem>) => void): TransactionType;
|
|
@@ -292,24 +355,50 @@ export declare class CollectionImpl<T extends object = Record<string, unknown>,
|
|
|
292
355
|
update<TItem extends object = T>(id: TKey | unknown, config: OperationConfig, callback: (draft: TItem) => void): TransactionType;
|
|
293
356
|
/**
|
|
294
357
|
* Deletes one or more items from the collection
|
|
295
|
-
* @param
|
|
358
|
+
* @param keys - Single key or array of keys to delete
|
|
296
359
|
* @param config - Optional configuration including metadata
|
|
297
|
-
* @returns A
|
|
360
|
+
* @returns A Transaction object representing the delete operation(s)
|
|
298
361
|
* @example
|
|
299
362
|
* // Delete a single item
|
|
300
|
-
* delete("todo-1")
|
|
363
|
+
* const tx = collection.delete("todo-1")
|
|
364
|
+
* await tx.isPersisted.promise
|
|
301
365
|
*
|
|
366
|
+
* @example
|
|
302
367
|
* // Delete multiple items
|
|
303
|
-
* delete(["todo-1", "todo-2"])
|
|
368
|
+
* const tx = collection.delete(["todo-1", "todo-2"])
|
|
369
|
+
* await tx.isPersisted.promise
|
|
304
370
|
*
|
|
371
|
+
* @example
|
|
305
372
|
* // Delete with metadata
|
|
306
|
-
* delete("todo-1", { metadata: { reason: "completed" } })
|
|
373
|
+
* const tx = collection.delete("todo-1", { metadata: { reason: "completed" } })
|
|
374
|
+
* await tx.isPersisted.promise
|
|
375
|
+
*
|
|
376
|
+
* @example
|
|
377
|
+
* // Handle errors
|
|
378
|
+
* try {
|
|
379
|
+
* const tx = collection.delete("item-1")
|
|
380
|
+
* await tx.isPersisted.promise
|
|
381
|
+
* console.log('Delete successful')
|
|
382
|
+
* } catch (error) {
|
|
383
|
+
* console.log('Delete failed:', error)
|
|
384
|
+
* }
|
|
307
385
|
*/
|
|
308
386
|
delete: (keys: Array<TKey> | TKey, config?: OperationConfig) => TransactionType<any>;
|
|
309
387
|
/**
|
|
310
388
|
* Gets the current state of the collection as a Map
|
|
389
|
+
* @returns Map containing all items in the collection, with keys as identifiers
|
|
390
|
+
* @example
|
|
391
|
+
* const itemsMap = collection.state
|
|
392
|
+
* console.log(`Collection has ${itemsMap.size} items`)
|
|
311
393
|
*
|
|
312
|
-
*
|
|
394
|
+
* for (const [key, item] of itemsMap) {
|
|
395
|
+
* console.log(`${key}: ${item.title}`)
|
|
396
|
+
* }
|
|
397
|
+
*
|
|
398
|
+
* // Check if specific item exists
|
|
399
|
+
* if (itemsMap.has("todo-1")) {
|
|
400
|
+
* console.log("Todo 1 exists:", itemsMap.get("todo-1"))
|
|
401
|
+
* }
|
|
313
402
|
*/
|
|
314
403
|
get state(): Map<TKey, T>;
|
|
315
404
|
/**
|
|
@@ -339,8 +428,24 @@ export declare class CollectionImpl<T extends object = Record<string, unknown>,
|
|
|
339
428
|
currentStateAsChanges(): Array<ChangeMessage<T>>;
|
|
340
429
|
/**
|
|
341
430
|
* Subscribe to changes in the collection
|
|
342
|
-
* @param callback -
|
|
343
|
-
* @
|
|
431
|
+
* @param callback - Function called when items change
|
|
432
|
+
* @param options.includeInitialState - If true, immediately calls callback with current data
|
|
433
|
+
* @returns Unsubscribe function - Call this to stop listening for changes
|
|
434
|
+
* @example
|
|
435
|
+
* // Basic subscription
|
|
436
|
+
* const unsubscribe = collection.subscribeChanges((changes) => {
|
|
437
|
+
* changes.forEach(change => {
|
|
438
|
+
* console.log(`${change.type}: ${change.key}`, change.value)
|
|
439
|
+
* })
|
|
440
|
+
* })
|
|
441
|
+
*
|
|
442
|
+
* // Later: unsubscribe()
|
|
443
|
+
*
|
|
444
|
+
* @example
|
|
445
|
+
* // Include current state immediately
|
|
446
|
+
* const unsubscribe = collection.subscribeChanges((changes) => {
|
|
447
|
+
* updateUI(changes)
|
|
448
|
+
* }, { includeInitialState: true })
|
|
344
449
|
*/
|
|
345
450
|
subscribeChanges(callback: (changes: Array<ChangeMessage<T>>) => void, { includeInitialState }?: {
|
|
346
451
|
includeInitialState?: boolean;
|
package/dist/esm/collection.js
CHANGED
|
@@ -247,7 +247,10 @@ class CollectionImpl {
|
|
|
247
247
|
} else {
|
|
248
248
|
const directOpTransaction = createTransaction({
|
|
249
249
|
mutationFn: async (params) => {
|
|
250
|
-
return this.config.onInsert(
|
|
250
|
+
return this.config.onInsert({
|
|
251
|
+
...params,
|
|
252
|
+
collection: this
|
|
253
|
+
});
|
|
251
254
|
}
|
|
252
255
|
});
|
|
253
256
|
directOpTransaction.applyMutations(mutations);
|
|
@@ -302,7 +305,10 @@ class CollectionImpl {
|
|
|
302
305
|
const directOpTransaction = createTransaction({
|
|
303
306
|
autoCommit: true,
|
|
304
307
|
mutationFn: async (params) => {
|
|
305
|
-
return this.config.onDelete(
|
|
308
|
+
return this.config.onDelete({
|
|
309
|
+
...params,
|
|
310
|
+
collection: this
|
|
311
|
+
});
|
|
306
312
|
}
|
|
307
313
|
});
|
|
308
314
|
directOpTransaction.applyMutations(mutations);
|
|
@@ -340,6 +346,11 @@ class CollectionImpl {
|
|
|
340
346
|
* Register a callback to be executed on the next commit
|
|
341
347
|
* Useful for preloading collections
|
|
342
348
|
* @param callback Function to call after the next commit
|
|
349
|
+
* @example
|
|
350
|
+
* collection.onFirstCommit(() => {
|
|
351
|
+
* console.log('Collection has received first data')
|
|
352
|
+
* // Safe to access collection.state now
|
|
353
|
+
* })
|
|
343
354
|
*/
|
|
344
355
|
onFirstCommit(callback) {
|
|
345
356
|
this.onFirstCommitCallbacks.push(callback);
|
|
@@ -1035,7 +1046,10 @@ class CollectionImpl {
|
|
|
1035
1046
|
}
|
|
1036
1047
|
const directOpTransaction = createTransaction({
|
|
1037
1048
|
mutationFn: async (params) => {
|
|
1038
|
-
return this.config.onUpdate(
|
|
1049
|
+
return this.config.onUpdate({
|
|
1050
|
+
...params,
|
|
1051
|
+
collection: this
|
|
1052
|
+
});
|
|
1039
1053
|
}
|
|
1040
1054
|
});
|
|
1041
1055
|
directOpTransaction.applyMutations(mutations);
|
|
@@ -1046,8 +1060,19 @@ class CollectionImpl {
|
|
|
1046
1060
|
}
|
|
1047
1061
|
/**
|
|
1048
1062
|
* Gets the current state of the collection as a Map
|
|
1063
|
+
* @returns Map containing all items in the collection, with keys as identifiers
|
|
1064
|
+
* @example
|
|
1065
|
+
* const itemsMap = collection.state
|
|
1066
|
+
* console.log(`Collection has ${itemsMap.size} items`)
|
|
1049
1067
|
*
|
|
1050
|
-
*
|
|
1068
|
+
* for (const [key, item] of itemsMap) {
|
|
1069
|
+
* console.log(`${key}: ${item.title}`)
|
|
1070
|
+
* }
|
|
1071
|
+
*
|
|
1072
|
+
* // Check if specific item exists
|
|
1073
|
+
* if (itemsMap.has("todo-1")) {
|
|
1074
|
+
* console.log("Todo 1 exists:", itemsMap.get("todo-1"))
|
|
1075
|
+
* }
|
|
1051
1076
|
*/
|
|
1052
1077
|
get state() {
|
|
1053
1078
|
const result = /* @__PURE__ */ new Map();
|
|
@@ -1109,8 +1134,24 @@ class CollectionImpl {
|
|
|
1109
1134
|
}
|
|
1110
1135
|
/**
|
|
1111
1136
|
* Subscribe to changes in the collection
|
|
1112
|
-
* @param callback -
|
|
1113
|
-
* @
|
|
1137
|
+
* @param callback - Function called when items change
|
|
1138
|
+
* @param options.includeInitialState - If true, immediately calls callback with current data
|
|
1139
|
+
* @returns Unsubscribe function - Call this to stop listening for changes
|
|
1140
|
+
* @example
|
|
1141
|
+
* // Basic subscription
|
|
1142
|
+
* const unsubscribe = collection.subscribeChanges((changes) => {
|
|
1143
|
+
* changes.forEach(change => {
|
|
1144
|
+
* console.log(`${change.type}: ${change.key}`, change.value)
|
|
1145
|
+
* })
|
|
1146
|
+
* })
|
|
1147
|
+
*
|
|
1148
|
+
* // Later: unsubscribe()
|
|
1149
|
+
*
|
|
1150
|
+
* @example
|
|
1151
|
+
* // Include current state immediately
|
|
1152
|
+
* const unsubscribe = collection.subscribeChanges((changes) => {
|
|
1153
|
+
* updateUI(changes)
|
|
1154
|
+
* }, { includeInitialState: true })
|
|
1114
1155
|
*/
|
|
1115
1156
|
subscribeChanges(callback, { includeInitialState = false } = {}) {
|
|
1116
1157
|
this.addSubscriber();
|