@tanstack/db 0.0.30 → 0.0.32
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/change-events.cjs +1 -1
- package/dist/cjs/change-events.cjs.map +1 -1
- package/dist/cjs/collection.cjs +36 -23
- package/dist/cjs/collection.cjs.map +1 -1
- package/dist/cjs/collection.d.cts +5 -1
- package/dist/cjs/index.cjs +0 -1
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/change-events.js +1 -1
- package/dist/esm/change-events.js.map +1 -1
- package/dist/esm/collection.d.ts +5 -1
- package/dist/esm/collection.js +36 -23
- package/dist/esm/collection.js.map +1 -1
- package/dist/esm/index.js +1 -2
- package/package.json +1 -1
- package/src/change-events.ts +3 -1
- package/src/collection.ts +53 -28
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"change-events.cjs","sources":["../../src/change-events.ts"],"sourcesContent":["import {\n createSingleRowRefProxy,\n toExpression,\n} from \"./query/builder/ref-proxy\"\nimport { compileSingleRowExpression } from \"./query/compiler/evaluators.js\"\nimport { optimizeExpressionWithIndexes } from \"./utils/index-optimization.js\"\nimport type {\n ChangeMessage,\n CurrentStateAsChangesOptions,\n SubscribeChangesOptions,\n} from \"./types\"\nimport type { Collection } from \"./collection\"\nimport type { SingleRowRefProxy } from \"./query/builder/ref-proxy\"\nimport type { BasicExpression } from \"./query/ir.js\"\n\n/**\n * Interface for a collection-like object that provides the necessary methods\n * for the change events system to work\n */\nexport interface CollectionLike<\n T extends object = Record<string, unknown>,\n TKey extends string | number = string | number,\n> extends Pick<Collection<T, TKey>, `get` | `has` | `entries` | `indexes`> {}\n\n/**\n * Returns the current state of the collection as an array of changes\n * @param collection - The collection to get changes from\n * @param options - Options including optional where filter\n * @returns An array of changes\n * @example\n * // Get all items as changes\n * const allChanges = currentStateAsChanges(collection)\n *\n * // Get only items matching a condition\n * const activeChanges = currentStateAsChanges(collection, {\n * where: (row) => row.status === 'active'\n * })\n *\n * // Get only items using a pre-compiled expression\n * const activeChanges = currentStateAsChanges(collection, {\n * whereExpression: eq(row.status, 'active')\n * })\n */\nexport function currentStateAsChanges<\n T extends object,\n TKey extends string | number,\n>(\n collection: CollectionLike<T, TKey>,\n options: CurrentStateAsChangesOptions<T> = {}\n): Array<ChangeMessage<T>> {\n // Helper function to collect filtered results\n const collectFilteredResults = (\n filterFn?: (value: T) => boolean\n ): Array<ChangeMessage<T>> => {\n const result: Array<ChangeMessage<T>> = []\n for (const [key, value] of collection.entries()) {\n // If no filter function is provided, include all items\n if (filterFn?.(value) ?? true) {\n result.push({\n type: `insert`,\n key,\n value,\n })\n }\n }\n return result\n }\n\n if (!options.where && !options.whereExpression) {\n // No filtering, return all items\n return collectFilteredResults()\n }\n\n // There's a where clause, let's see if we can use an index\n try {\n let expression: BasicExpression<boolean>\n\n if (options.whereExpression) {\n // Use the pre-compiled expression directly\n expression = options.whereExpression\n } else if (options.where) {\n // Create the single-row refProxy for the callback\n const singleRowRefProxy = createSingleRowRefProxy<T>()\n\n // Execute the callback to get the expression\n const whereExpression = options.where(singleRowRefProxy)\n\n // Convert the result to a BasicExpression\n expression = toExpression(whereExpression)\n } else {\n // This should never happen due to the check above, but TypeScript needs it\n return []\n }\n\n // Try to optimize the query using indexes\n const optimizationResult = optimizeExpressionWithIndexes(\n expression,\n collection.indexes\n )\n\n if (optimizationResult.canOptimize) {\n // Use index optimization\n const result: Array<ChangeMessage<T>> = []\n for (const key of optimizationResult.matchingKeys) {\n const value = collection.get(key)\n if (value !== undefined) {\n result.push({\n type: `insert`,\n key,\n value,\n })\n }\n }\n return result\n } else {\n // No index found or complex expression, fall back to full scan with filter\n const filterFn = options.where\n ? createFilterFunction(options.where)\n : createFilterFunctionFromExpression(expression)\n\n return collectFilteredResults(filterFn)\n }\n } catch (error) {\n // If anything goes wrong with the where clause, fall back to full scan\n console.warn(\n `Error processing where clause, falling back to full scan:`,\n error\n )\n\n const filterFn = options.where\n ? createFilterFunction(options.where)\n : createFilterFunctionFromExpression(options.whereExpression!)\n\n return collectFilteredResults(filterFn)\n }\n}\n\n/**\n * Creates a filter function from a where callback\n * @param whereCallback - The callback function that defines the filter condition\n * @returns A function that takes an item and returns true if it matches the filter\n */\nexport function createFilterFunction<T extends object>(\n whereCallback: (row: SingleRowRefProxy<T>) => any\n): (item: T) => boolean {\n return (item: T): boolean => {\n try {\n // First try the RefProxy approach for query builder functions\n const singleRowRefProxy = createSingleRowRefProxy<T>()\n const whereExpression = whereCallback(singleRowRefProxy)\n const expression = toExpression(whereExpression)\n const evaluator = compileSingleRowExpression(expression)\n const result = evaluator(item as Record<string, unknown>)\n // WHERE clauses should always evaluate to boolean predicates (Kevin's feedback)\n return result\n } catch {\n // If RefProxy approach fails (e.g., arithmetic operations), fall back to direct evaluation\n try {\n // Create a simple proxy that returns actual values for arithmetic operations\n const simpleProxy = new Proxy(item as any, {\n get(target, prop) {\n return target[prop]\n },\n }) as SingleRowRefProxy<T>\n\n const result = whereCallback(simpleProxy)\n return result\n } catch {\n // If both approaches fail, exclude the item\n return false\n }\n }\n }\n}\n\n/**\n * Creates a filter function from a pre-compiled expression\n * @param expression - The pre-compiled expression to evaluate\n * @returns A function that takes an item and returns true if it matches the filter\n */\nexport function createFilterFunctionFromExpression<T extends object>(\n expression: BasicExpression<boolean>\n): (item: T) => boolean {\n return (item: T): boolean => {\n try {\n const evaluator = compileSingleRowExpression(expression)\n const result = evaluator(item as Record<string, unknown>)\n return Boolean(result)\n } catch {\n // If evaluation fails, exclude the item\n return false\n }\n }\n}\n\n/**\n * Creates a filtered callback that only calls the original callback with changes that match the where clause\n * @param originalCallback - The original callback to filter\n * @param options - The subscription options containing the where clause\n * @returns A filtered callback function\n */\nexport function createFilteredCallback<T extends object>(\n originalCallback: (changes: Array<ChangeMessage<T>>) => void,\n options: SubscribeChangesOptions<T>\n): (changes: Array<ChangeMessage<T>>) => void {\n const filterFn = options.whereExpression\n ? createFilterFunctionFromExpression(options.whereExpression)\n : createFilterFunction(options.where!)\n\n return (changes: Array<ChangeMessage<T>>) => {\n const filteredChanges: Array<ChangeMessage<T>> = []\n\n for (const change of changes) {\n if (change.type === `insert`) {\n // For inserts, check if the new value matches the filter\n if (filterFn(change.value)) {\n filteredChanges.push(change)\n }\n } else if (change.type === `update`) {\n // For updates, we need to check both old and new values\n const newValueMatches = filterFn(change.value)\n const oldValueMatches = change.previousValue\n ? filterFn(change.previousValue)\n : false\n\n if (newValueMatches && oldValueMatches) {\n // Both old and new match: emit update\n filteredChanges.push(change)\n } else if (newValueMatches && !oldValueMatches) {\n // New matches but old didn't: emit insert\n filteredChanges.push({\n ...change,\n type: `insert`,\n })\n } else if (!newValueMatches && oldValueMatches) {\n // Old matched but new doesn't: emit delete\n filteredChanges.push({\n ...change,\n type: `delete`,\n value: change.previousValue!, // Use the previous value for the delete\n })\n }\n // If neither matches, don't emit anything\n } else {\n // For deletes, include if the previous value would have matched\n // (so subscribers know something they were tracking was deleted)\n if (filterFn(change.value)) {\n filteredChanges.push(change)\n }\n }\n }\n\n if (filteredChanges.length > 0) {\n originalCallback(filteredChanges)\n }\n }\n}\n"],"names":["createSingleRowRefProxy","toExpression","optimizeExpressionWithIndexes","compileSingleRowExpression"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"change-events.cjs","sources":["../../src/change-events.ts"],"sourcesContent":["import {\n createSingleRowRefProxy,\n toExpression,\n} from \"./query/builder/ref-proxy\"\nimport { compileSingleRowExpression } from \"./query/compiler/evaluators.js\"\nimport { optimizeExpressionWithIndexes } from \"./utils/index-optimization.js\"\nimport type {\n ChangeMessage,\n CurrentStateAsChangesOptions,\n SubscribeChangesOptions,\n} from \"./types\"\nimport type { Collection } from \"./collection\"\nimport type { SingleRowRefProxy } from \"./query/builder/ref-proxy\"\nimport type { BasicExpression } from \"./query/ir.js\"\n\n/**\n * Interface for a collection-like object that provides the necessary methods\n * for the change events system to work\n */\nexport interface CollectionLike<\n T extends object = Record<string, unknown>,\n TKey extends string | number = string | number,\n> extends Pick<Collection<T, TKey>, `get` | `has` | `entries` | `indexes`> {}\n\n/**\n * Returns the current state of the collection as an array of changes\n * @param collection - The collection to get changes from\n * @param options - Options including optional where filter\n * @returns An array of changes\n * @example\n * // Get all items as changes\n * const allChanges = currentStateAsChanges(collection)\n *\n * // Get only items matching a condition\n * const activeChanges = currentStateAsChanges(collection, {\n * where: (row) => row.status === 'active'\n * })\n *\n * // Get only items using a pre-compiled expression\n * const activeChanges = currentStateAsChanges(collection, {\n * whereExpression: eq(row.status, 'active')\n * })\n */\nexport function currentStateAsChanges<\n T extends object,\n TKey extends string | number,\n>(\n collection: CollectionLike<T, TKey>,\n options: CurrentStateAsChangesOptions<T> = {}\n): Array<ChangeMessage<T>> {\n // Helper function to collect filtered results\n const collectFilteredResults = (\n filterFn?: (value: T) => boolean\n ): Array<ChangeMessage<T>> => {\n const result: Array<ChangeMessage<T>> = []\n for (const [key, value] of collection.entries()) {\n // If no filter function is provided, include all items\n if (filterFn?.(value) ?? true) {\n result.push({\n type: `insert`,\n key,\n value,\n })\n }\n }\n return result\n }\n\n if (!options.where && !options.whereExpression) {\n // No filtering, return all items\n return collectFilteredResults()\n }\n\n // There's a where clause, let's see if we can use an index\n try {\n let expression: BasicExpression<boolean>\n\n if (options.whereExpression) {\n // Use the pre-compiled expression directly\n expression = options.whereExpression\n } else if (options.where) {\n // Create the single-row refProxy for the callback\n const singleRowRefProxy = createSingleRowRefProxy<T>()\n\n // Execute the callback to get the expression\n const whereExpression = options.where(singleRowRefProxy)\n\n // Convert the result to a BasicExpression\n expression = toExpression(whereExpression)\n } else {\n // This should never happen due to the check above, but TypeScript needs it\n return []\n }\n\n // Try to optimize the query using indexes\n const optimizationResult = optimizeExpressionWithIndexes(\n expression,\n collection.indexes\n )\n\n if (optimizationResult.canOptimize) {\n // Use index optimization\n const result: Array<ChangeMessage<T>> = []\n for (const key of optimizationResult.matchingKeys) {\n const value = collection.get(key)\n if (value !== undefined) {\n result.push({\n type: `insert`,\n key,\n value,\n })\n }\n }\n return result\n } else {\n // No index found or complex expression, fall back to full scan with filter\n const filterFn = options.where\n ? createFilterFunction(options.where)\n : createFilterFunctionFromExpression(expression)\n\n return collectFilteredResults(filterFn)\n }\n } catch (error) {\n // If anything goes wrong with the where clause, fall back to full scan\n console.warn(\n `Error processing where clause, falling back to full scan:`,\n error\n )\n\n const filterFn = options.where\n ? createFilterFunction(options.where)\n : createFilterFunctionFromExpression(options.whereExpression!)\n\n return collectFilteredResults(filterFn)\n }\n}\n\n/**\n * Creates a filter function from a where callback\n * @param whereCallback - The callback function that defines the filter condition\n * @returns A function that takes an item and returns true if it matches the filter\n */\nexport function createFilterFunction<T extends object>(\n whereCallback: (row: SingleRowRefProxy<T>) => any\n): (item: T) => boolean {\n return (item: T): boolean => {\n try {\n // First try the RefProxy approach for query builder functions\n const singleRowRefProxy = createSingleRowRefProxy<T>()\n const whereExpression = whereCallback(singleRowRefProxy)\n const expression = toExpression(whereExpression)\n const evaluator = compileSingleRowExpression(expression)\n const result = evaluator(item as Record<string, unknown>)\n // WHERE clauses should always evaluate to boolean predicates (Kevin's feedback)\n return result\n } catch {\n // If RefProxy approach fails (e.g., arithmetic operations), fall back to direct evaluation\n try {\n // Create a simple proxy that returns actual values for arithmetic operations\n const simpleProxy = new Proxy(item as any, {\n get(target, prop) {\n return target[prop]\n },\n }) as SingleRowRefProxy<T>\n\n const result = whereCallback(simpleProxy)\n return result\n } catch {\n // If both approaches fail, exclude the item\n return false\n }\n }\n }\n}\n\n/**\n * Creates a filter function from a pre-compiled expression\n * @param expression - The pre-compiled expression to evaluate\n * @returns A function that takes an item and returns true if it matches the filter\n */\nexport function createFilterFunctionFromExpression<T extends object>(\n expression: BasicExpression<boolean>\n): (item: T) => boolean {\n return (item: T): boolean => {\n try {\n const evaluator = compileSingleRowExpression(expression)\n const result = evaluator(item as Record<string, unknown>)\n return Boolean(result)\n } catch {\n // If evaluation fails, exclude the item\n return false\n }\n }\n}\n\n/**\n * Creates a filtered callback that only calls the original callback with changes that match the where clause\n * @param originalCallback - The original callback to filter\n * @param options - The subscription options containing the where clause\n * @returns A filtered callback function\n */\nexport function createFilteredCallback<T extends object>(\n originalCallback: (changes: Array<ChangeMessage<T>>) => void,\n options: SubscribeChangesOptions<T>\n): (changes: Array<ChangeMessage<T>>) => void {\n const filterFn = options.whereExpression\n ? createFilterFunctionFromExpression(options.whereExpression)\n : createFilterFunction(options.where!)\n\n return (changes: Array<ChangeMessage<T>>) => {\n const filteredChanges: Array<ChangeMessage<T>> = []\n\n for (const change of changes) {\n if (change.type === `insert`) {\n // For inserts, check if the new value matches the filter\n if (filterFn(change.value)) {\n filteredChanges.push(change)\n }\n } else if (change.type === `update`) {\n // For updates, we need to check both old and new values\n const newValueMatches = filterFn(change.value)\n const oldValueMatches = change.previousValue\n ? filterFn(change.previousValue)\n : false\n\n if (newValueMatches && oldValueMatches) {\n // Both old and new match: emit update\n filteredChanges.push(change)\n } else if (newValueMatches && !oldValueMatches) {\n // New matches but old didn't: emit insert\n filteredChanges.push({\n ...change,\n type: `insert`,\n })\n } else if (!newValueMatches && oldValueMatches) {\n // Old matched but new doesn't: emit delete\n filteredChanges.push({\n ...change,\n type: `delete`,\n value: change.previousValue!, // Use the previous value for the delete\n })\n }\n // If neither matches, don't emit anything\n } else {\n // For deletes, include if the previous value would have matched\n // (so subscribers know something they were tracking was deleted)\n if (filterFn(change.value)) {\n filteredChanges.push(change)\n }\n }\n }\n\n // Always call the original callback if we have filtered changes OR\n // if the original changes array was empty (which indicates a ready signal)\n if (filteredChanges.length > 0 || changes.length === 0) {\n originalCallback(filteredChanges)\n }\n }\n}\n"],"names":["createSingleRowRefProxy","toExpression","optimizeExpressionWithIndexes","compileSingleRowExpression"],"mappings":"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"}
|
package/dist/cjs/collection.cjs
CHANGED
|
@@ -9,7 +9,6 @@ const autoIndex = require("./indexes/auto-index.cjs");
|
|
|
9
9
|
const transactions = require("./transactions.cjs");
|
|
10
10
|
const errors = require("./errors.cjs");
|
|
11
11
|
const changeEvents = require("./change-events.cjs");
|
|
12
|
-
const collectionsStore = /* @__PURE__ */ new Map();
|
|
13
12
|
function createCollection(options) {
|
|
14
13
|
const collection = new CollectionImpl(options);
|
|
15
14
|
if (options.utils) {
|
|
@@ -255,7 +254,7 @@ class CollectionImpl {
|
|
|
255
254
|
if (ambientTransaction) {
|
|
256
255
|
ambientTransaction.applyMutations(mutations);
|
|
257
256
|
this.transactions.set(ambientTransaction.id, ambientTransaction);
|
|
258
|
-
this.recomputeOptimisticState();
|
|
257
|
+
this.recomputeOptimisticState(true);
|
|
259
258
|
return ambientTransaction;
|
|
260
259
|
} else {
|
|
261
260
|
const directOpTransaction = transactions.createTransaction({
|
|
@@ -269,7 +268,7 @@ class CollectionImpl {
|
|
|
269
268
|
directOpTransaction.applyMutations(mutations);
|
|
270
269
|
directOpTransaction.commit();
|
|
271
270
|
this.transactions.set(directOpTransaction.id, directOpTransaction);
|
|
272
|
-
this.recomputeOptimisticState();
|
|
271
|
+
this.recomputeOptimisticState(true);
|
|
273
272
|
return directOpTransaction;
|
|
274
273
|
}
|
|
275
274
|
};
|
|
@@ -309,7 +308,7 @@ class CollectionImpl {
|
|
|
309
308
|
if (ambientTransaction) {
|
|
310
309
|
ambientTransaction.applyMutations(mutations);
|
|
311
310
|
this.transactions.set(ambientTransaction.id, ambientTransaction);
|
|
312
|
-
this.recomputeOptimisticState();
|
|
311
|
+
this.recomputeOptimisticState(true);
|
|
313
312
|
return ambientTransaction;
|
|
314
313
|
}
|
|
315
314
|
const directOpTransaction = transactions.createTransaction({
|
|
@@ -324,7 +323,7 @@ class CollectionImpl {
|
|
|
324
323
|
directOpTransaction.applyMutations(mutations);
|
|
325
324
|
directOpTransaction.commit();
|
|
326
325
|
this.transactions.set(directOpTransaction.id, directOpTransaction);
|
|
327
|
-
this.recomputeOptimisticState();
|
|
326
|
+
this.recomputeOptimisticState(true);
|
|
328
327
|
return directOpTransaction;
|
|
329
328
|
};
|
|
330
329
|
if (!config) {
|
|
@@ -345,7 +344,6 @@ class CollectionImpl {
|
|
|
345
344
|
...config,
|
|
346
345
|
autoIndex: config.autoIndex ?? `eager`
|
|
347
346
|
};
|
|
348
|
-
collectionsStore.set(this.id, this);
|
|
349
347
|
if (this.config.compare) {
|
|
350
348
|
this.syncedData = new SortedMap.SortedMap(this.config.compare);
|
|
351
349
|
} else {
|
|
@@ -404,6 +402,9 @@ class CollectionImpl {
|
|
|
404
402
|
const callbacks = [...this.onFirstReadyCallbacks];
|
|
405
403
|
this.onFirstReadyCallbacks = [];
|
|
406
404
|
callbacks.forEach((callback) => callback());
|
|
405
|
+
if (this.size === 0 && this.changeListeners.size > 0) {
|
|
406
|
+
this.emitEmptyReadyEvent();
|
|
407
|
+
}
|
|
407
408
|
}
|
|
408
409
|
}
|
|
409
410
|
}
|
|
@@ -653,7 +654,7 @@ class CollectionImpl {
|
|
|
653
654
|
/**
|
|
654
655
|
* Recompute optimistic state from active transactions
|
|
655
656
|
*/
|
|
656
|
-
recomputeOptimisticState() {
|
|
657
|
+
recomputeOptimisticState(triggeredByUserAction = false) {
|
|
657
658
|
if (this.isCommittingSyncTransactions) {
|
|
658
659
|
return;
|
|
659
660
|
}
|
|
@@ -690,10 +691,16 @@ class CollectionImpl {
|
|
|
690
691
|
this._size = this.calculateSize();
|
|
691
692
|
const events = [];
|
|
692
693
|
this.collectOptimisticChanges(previousState, previousDeletes, events);
|
|
693
|
-
const filteredEventsBySyncStatus = events.filter(
|
|
694
|
-
(
|
|
695
|
-
|
|
696
|
-
|
|
694
|
+
const filteredEventsBySyncStatus = events.filter((event) => {
|
|
695
|
+
if (!this.recentlySyncedKeys.has(event.key)) {
|
|
696
|
+
return true;
|
|
697
|
+
}
|
|
698
|
+
if (triggeredByUserAction) {
|
|
699
|
+
return true;
|
|
700
|
+
}
|
|
701
|
+
return false;
|
|
702
|
+
});
|
|
703
|
+
if (this.pendingSyncedTransactions.length > 0 && !triggeredByUserAction) {
|
|
697
704
|
const pendingSyncKeys = /* @__PURE__ */ new Set();
|
|
698
705
|
const completedTransactionMutations = /* @__PURE__ */ new Set();
|
|
699
706
|
for (const transaction of this.pendingSyncedTransactions) {
|
|
@@ -724,12 +731,12 @@ class CollectionImpl {
|
|
|
724
731
|
if (filteredEvents.length > 0) {
|
|
725
732
|
this.updateIndexes(filteredEvents);
|
|
726
733
|
}
|
|
727
|
-
this.emitEvents(filteredEvents);
|
|
734
|
+
this.emitEvents(filteredEvents, triggeredByUserAction);
|
|
728
735
|
} else {
|
|
729
736
|
if (filteredEventsBySyncStatus.length > 0) {
|
|
730
737
|
this.updateIndexes(filteredEventsBySyncStatus);
|
|
731
738
|
}
|
|
732
|
-
this.emitEvents(filteredEventsBySyncStatus);
|
|
739
|
+
this.emitEvents(filteredEventsBySyncStatus, triggeredByUserAction);
|
|
733
740
|
}
|
|
734
741
|
}
|
|
735
742
|
/**
|
|
@@ -788,19 +795,26 @@ class CollectionImpl {
|
|
|
788
795
|
}
|
|
789
796
|
return this.syncedData.get(key);
|
|
790
797
|
}
|
|
798
|
+
/**
|
|
799
|
+
* Emit an empty ready event to notify subscribers that the collection is ready
|
|
800
|
+
* This bypasses the normal empty array check in emitEvents
|
|
801
|
+
*/
|
|
802
|
+
emitEmptyReadyEvent() {
|
|
803
|
+
for (const listener of this.changeListeners) {
|
|
804
|
+
listener([]);
|
|
805
|
+
}
|
|
806
|
+
}
|
|
791
807
|
/**
|
|
792
808
|
* Emit events either immediately or batch them for later emission
|
|
793
809
|
*/
|
|
794
|
-
emitEvents(changes,
|
|
795
|
-
if (this.shouldBatchEvents && !
|
|
810
|
+
emitEvents(changes, forceEmit = false) {
|
|
811
|
+
if (this.shouldBatchEvents && !forceEmit) {
|
|
796
812
|
this.batchedEvents.push(...changes);
|
|
797
813
|
return;
|
|
798
814
|
}
|
|
799
815
|
let eventsToEmit = changes;
|
|
800
|
-
if (
|
|
801
|
-
|
|
802
|
-
eventsToEmit = [...this.batchedEvents, ...changes];
|
|
803
|
-
}
|
|
816
|
+
if (this.batchedEvents.length > 0 && forceEmit) {
|
|
817
|
+
eventsToEmit = [...this.batchedEvents, ...changes];
|
|
804
818
|
this.batchedEvents = [];
|
|
805
819
|
this.shouldBatchEvents = false;
|
|
806
820
|
}
|
|
@@ -1206,7 +1220,7 @@ class CollectionImpl {
|
|
|
1206
1220
|
if (ambientTransaction) {
|
|
1207
1221
|
ambientTransaction.applyMutations(mutations);
|
|
1208
1222
|
this.transactions.set(ambientTransaction.id, ambientTransaction);
|
|
1209
|
-
this.recomputeOptimisticState();
|
|
1223
|
+
this.recomputeOptimisticState(true);
|
|
1210
1224
|
return ambientTransaction;
|
|
1211
1225
|
}
|
|
1212
1226
|
const directOpTransaction = transactions.createTransaction({
|
|
@@ -1220,7 +1234,7 @@ class CollectionImpl {
|
|
|
1220
1234
|
directOpTransaction.applyMutations(mutations);
|
|
1221
1235
|
directOpTransaction.commit();
|
|
1222
1236
|
this.transactions.set(directOpTransaction.id, directOpTransaction);
|
|
1223
|
-
this.recomputeOptimisticState();
|
|
1237
|
+
this.recomputeOptimisticState(true);
|
|
1224
1238
|
return directOpTransaction;
|
|
1225
1239
|
}
|
|
1226
1240
|
/**
|
|
@@ -1424,10 +1438,9 @@ class CollectionImpl {
|
|
|
1424
1438
|
onTransactionStateChange() {
|
|
1425
1439
|
this.shouldBatchEvents = this.pendingSyncedTransactions.length > 0;
|
|
1426
1440
|
this.capturePreSyncVisibleState();
|
|
1427
|
-
this.recomputeOptimisticState();
|
|
1441
|
+
this.recomputeOptimisticState(false);
|
|
1428
1442
|
}
|
|
1429
1443
|
}
|
|
1430
1444
|
exports.CollectionImpl = CollectionImpl;
|
|
1431
|
-
exports.collectionsStore = collectionsStore;
|
|
1432
1445
|
exports.createCollection = createCollection;
|
|
1433
1446
|
//# sourceMappingURL=collection.cjs.map
|