@tanstack/db 0.3.1 → 0.4.0
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 → collection/change-events.cjs} +13 -42
- package/dist/cjs/collection/change-events.cjs.map +1 -0
- package/dist/{esm/change-events.d.ts → cjs/collection/change-events.d.cts} +6 -6
- package/dist/cjs/collection/changes.cjs +108 -0
- package/dist/cjs/collection/changes.cjs.map +1 -0
- package/dist/cjs/collection/changes.d.cts +53 -0
- package/dist/cjs/collection/events.cjs +90 -0
- package/dist/cjs/collection/events.cjs.map +1 -0
- package/dist/cjs/collection/events.d.cts +53 -0
- package/dist/cjs/collection/index.cjs +417 -0
- package/dist/cjs/collection/index.cjs.map +1 -0
- package/dist/{esm/collection.d.ts → cjs/collection/index.d.cts} +56 -172
- package/dist/cjs/collection/indexes.cjs +124 -0
- package/dist/cjs/collection/indexes.cjs.map +1 -0
- package/dist/cjs/collection/indexes.d.cts +47 -0
- package/dist/cjs/collection/lifecycle.cjs +150 -0
- package/dist/cjs/collection/lifecycle.cjs.map +1 -0
- package/dist/cjs/collection/lifecycle.d.cts +70 -0
- package/dist/cjs/collection/mutations.cjs +315 -0
- package/dist/cjs/collection/mutations.cjs.map +1 -0
- package/dist/cjs/collection/mutations.d.cts +33 -0
- package/dist/cjs/collection/state.cjs +597 -0
- package/dist/cjs/collection/state.cjs.map +1 -0
- package/dist/cjs/collection/state.d.cts +122 -0
- package/dist/cjs/collection/subscription.cjs +160 -0
- package/dist/cjs/collection/subscription.cjs.map +1 -0
- package/dist/cjs/collection/subscription.d.cts +57 -0
- package/dist/cjs/collection/sync.cjs +154 -0
- package/dist/cjs/collection/sync.cjs.map +1 -0
- package/dist/cjs/collection/sync.d.cts +34 -0
- package/dist/cjs/index.cjs +8 -8
- package/dist/cjs/index.d.cts +2 -2
- package/dist/cjs/indexes/auto-index.cjs.map +1 -1
- package/dist/cjs/indexes/auto-index.d.cts +1 -1
- package/dist/cjs/indexes/base-index.cjs.map +1 -1
- package/dist/cjs/indexes/base-index.d.cts +2 -2
- package/dist/cjs/indexes/btree-index.cjs +2 -2
- package/dist/cjs/indexes/btree-index.cjs.map +1 -1
- package/dist/cjs/indexes/btree-index.d.cts +1 -1
- package/dist/cjs/query/builder/index.cjs +2 -2
- package/dist/cjs/query/builder/index.cjs.map +1 -1
- package/dist/cjs/query/builder/types.d.cts +1 -1
- package/dist/cjs/query/compiler/index.cjs +5 -2
- package/dist/cjs/query/compiler/index.cjs.map +1 -1
- package/dist/cjs/query/compiler/index.d.cts +3 -2
- package/dist/cjs/query/compiler/joins.cjs +22 -24
- package/dist/cjs/query/compiler/joins.cjs.map +1 -1
- package/dist/cjs/query/compiler/joins.d.cts +3 -2
- package/dist/cjs/query/compiler/order-by.cjs.map +1 -1
- package/dist/cjs/query/compiler/order-by.d.cts +1 -1
- package/dist/cjs/query/ir.cjs.map +1 -1
- package/dist/cjs/query/ir.d.cts +1 -1
- package/dist/cjs/query/live/collection-config-builder.cjs +29 -12
- package/dist/cjs/query/live/collection-config-builder.cjs.map +1 -1
- package/dist/cjs/query/live/collection-config-builder.d.cts +3 -0
- package/dist/cjs/query/live/collection-subscriber.cjs +43 -104
- package/dist/cjs/query/live/collection-subscriber.cjs.map +1 -1
- package/dist/cjs/query/live/collection-subscriber.d.cts +4 -7
- package/dist/cjs/query/live-query-collection.cjs +2 -2
- package/dist/cjs/query/live-query-collection.cjs.map +1 -1
- package/dist/cjs/query/live-query-collection.d.cts +1 -1
- package/dist/cjs/transactions.cjs +3 -3
- package/dist/cjs/transactions.cjs.map +1 -1
- package/dist/cjs/types.d.cts +12 -10
- package/dist/{cjs/change-events.d.cts → esm/collection/change-events.d.ts} +6 -6
- package/dist/esm/{change-events.js → collection/change-events.js} +13 -42
- package/dist/esm/collection/change-events.js.map +1 -0
- package/dist/esm/collection/changes.d.ts +53 -0
- package/dist/esm/collection/changes.js +108 -0
- package/dist/esm/collection/changes.js.map +1 -0
- package/dist/esm/collection/events.d.ts +53 -0
- package/dist/esm/collection/events.js +90 -0
- package/dist/esm/collection/events.js.map +1 -0
- package/dist/{cjs/collection.d.cts → esm/collection/index.d.ts} +56 -172
- package/dist/esm/collection/index.js +417 -0
- package/dist/esm/collection/index.js.map +1 -0
- package/dist/esm/collection/indexes.d.ts +47 -0
- package/dist/esm/collection/indexes.js +124 -0
- package/dist/esm/collection/indexes.js.map +1 -0
- package/dist/esm/collection/lifecycle.d.ts +70 -0
- package/dist/esm/collection/lifecycle.js +150 -0
- package/dist/esm/collection/lifecycle.js.map +1 -0
- package/dist/esm/collection/mutations.d.ts +33 -0
- package/dist/esm/collection/mutations.js +315 -0
- package/dist/esm/collection/mutations.js.map +1 -0
- package/dist/esm/collection/state.d.ts +122 -0
- package/dist/esm/collection/state.js +597 -0
- package/dist/esm/collection/state.js.map +1 -0
- package/dist/esm/collection/subscription.d.ts +57 -0
- package/dist/esm/collection/subscription.js +160 -0
- package/dist/esm/collection/subscription.js.map +1 -0
- package/dist/esm/collection/sync.d.ts +34 -0
- package/dist/esm/collection/sync.js +154 -0
- package/dist/esm/collection/sync.js.map +1 -0
- package/dist/esm/index.d.ts +2 -2
- package/dist/esm/index.js +1 -1
- package/dist/esm/indexes/auto-index.d.ts +1 -1
- package/dist/esm/indexes/auto-index.js.map +1 -1
- package/dist/esm/indexes/base-index.d.ts +2 -2
- package/dist/esm/indexes/base-index.js.map +1 -1
- package/dist/esm/indexes/btree-index.d.ts +1 -1
- package/dist/esm/indexes/btree-index.js +2 -2
- package/dist/esm/indexes/btree-index.js.map +1 -1
- package/dist/esm/proxy.js +1 -1
- package/dist/esm/query/builder/index.js +1 -1
- package/dist/esm/query/builder/index.js.map +1 -1
- package/dist/esm/query/builder/types.d.ts +1 -1
- package/dist/esm/query/compiler/index.d.ts +3 -2
- package/dist/esm/query/compiler/index.js +5 -2
- package/dist/esm/query/compiler/index.js.map +1 -1
- package/dist/esm/query/compiler/joins.d.ts +3 -2
- package/dist/esm/query/compiler/joins.js +22 -24
- package/dist/esm/query/compiler/joins.js.map +1 -1
- package/dist/esm/query/compiler/order-by.d.ts +1 -1
- package/dist/esm/query/compiler/order-by.js.map +1 -1
- package/dist/esm/query/ir.d.ts +1 -1
- package/dist/esm/query/ir.js.map +1 -1
- package/dist/esm/query/live/collection-config-builder.d.ts +3 -0
- package/dist/esm/query/live/collection-config-builder.js +29 -12
- package/dist/esm/query/live/collection-config-builder.js.map +1 -1
- package/dist/esm/query/live/collection-subscriber.d.ts +4 -7
- package/dist/esm/query/live/collection-subscriber.js +43 -104
- package/dist/esm/query/live/collection-subscriber.js.map +1 -1
- package/dist/esm/query/live-query-collection.d.ts +1 -1
- package/dist/esm/query/live-query-collection.js +1 -1
- package/dist/esm/query/live-query-collection.js.map +1 -1
- package/dist/esm/transactions.js +3 -3
- package/dist/esm/transactions.js.map +1 -1
- package/dist/esm/types.d.ts +12 -10
- package/package.json +2 -2
- package/src/{change-events.ts → collection/change-events.ts} +25 -39
- package/src/collection/changes.ts +163 -0
- package/src/collection/events.ts +171 -0
- package/src/collection/index.ts +808 -0
- package/src/collection/indexes.ts +172 -0
- package/src/collection/lifecycle.ts +221 -0
- package/src/collection/mutations.ts +535 -0
- package/src/collection/state.ts +866 -0
- package/src/collection/subscription.ts +239 -0
- package/src/collection/sync.ts +235 -0
- package/src/index.ts +2 -2
- package/src/indexes/auto-index.ts +1 -1
- package/src/indexes/base-index.ts +3 -3
- package/src/indexes/btree-index.ts +2 -2
- package/src/query/builder/index.ts +1 -1
- package/src/query/builder/types.ts +1 -1
- package/src/query/compiler/index.ts +7 -1
- package/src/query/compiler/joins.ts +28 -41
- package/src/query/compiler/order-by.ts +1 -1
- package/src/query/ir.ts +1 -1
- package/src/query/live/collection-config-builder.ts +48 -22
- package/src/query/live/collection-subscriber.ts +63 -168
- package/src/query/live-query-collection.ts +2 -2
- package/src/transactions.ts +3 -3
- package/src/types.ts +14 -15
- package/dist/cjs/change-events.cjs.map +0 -1
- package/dist/cjs/collection.cjs +0 -1580
- package/dist/cjs/collection.cjs.map +0 -1
- package/dist/esm/change-events.js.map +0 -1
- package/dist/esm/collection.js +0 -1580
- package/dist/esm/collection.js.map +0 -1
- package/src/collection.ts +0 -2488
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const indexOptimization = require("./utils/index-optimization.cjs");
|
|
3
|
+
const evaluators = require("../query/compiler/evaluators.cjs");
|
|
4
|
+
const indexOptimization = require("../utils/index-optimization.cjs");
|
|
6
5
|
function currentStateAsChanges(collection, options = {}) {
|
|
7
6
|
const collectFilteredResults = (filterFn) => {
|
|
8
7
|
const result = [];
|
|
@@ -17,20 +16,11 @@ function currentStateAsChanges(collection, options = {}) {
|
|
|
17
16
|
}
|
|
18
17
|
return result;
|
|
19
18
|
};
|
|
20
|
-
if (!options.where
|
|
19
|
+
if (!options.where) {
|
|
21
20
|
return collectFilteredResults();
|
|
22
21
|
}
|
|
23
22
|
try {
|
|
24
|
-
|
|
25
|
-
if (options.whereExpression) {
|
|
26
|
-
expression = options.whereExpression;
|
|
27
|
-
} else if (options.where) {
|
|
28
|
-
const singleRowRefProxy = refProxy.createSingleRowRefProxy();
|
|
29
|
-
const whereExpression = options.where(singleRowRefProxy);
|
|
30
|
-
expression = refProxy.toExpression(whereExpression);
|
|
31
|
-
} else {
|
|
32
|
-
return [];
|
|
33
|
-
}
|
|
23
|
+
const expression = options.where;
|
|
34
24
|
const optimizationResult = indexOptimization.optimizeExpressionWithIndexes(
|
|
35
25
|
expression,
|
|
36
26
|
collection.indexes
|
|
@@ -49,7 +39,10 @@ function currentStateAsChanges(collection, options = {}) {
|
|
|
49
39
|
}
|
|
50
40
|
return result;
|
|
51
41
|
} else {
|
|
52
|
-
|
|
42
|
+
if (options.optimizedOnly) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const filterFn = createFilterFunctionFromExpression(expression);
|
|
53
46
|
return collectFilteredResults(filterFn);
|
|
54
47
|
}
|
|
55
48
|
} catch (error) {
|
|
@@ -57,34 +50,13 @@ function currentStateAsChanges(collection, options = {}) {
|
|
|
57
50
|
`Error processing where clause, falling back to full scan:`,
|
|
58
51
|
error
|
|
59
52
|
);
|
|
60
|
-
const filterFn =
|
|
53
|
+
const filterFn = createFilterFunctionFromExpression(options.where);
|
|
54
|
+
if (options.optimizedOnly) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
61
57
|
return collectFilteredResults(filterFn);
|
|
62
58
|
}
|
|
63
59
|
}
|
|
64
|
-
function createFilterFunction(whereCallback) {
|
|
65
|
-
return (item) => {
|
|
66
|
-
try {
|
|
67
|
-
const singleRowRefProxy = refProxy.createSingleRowRefProxy();
|
|
68
|
-
const whereExpression = whereCallback(singleRowRefProxy);
|
|
69
|
-
const expression = refProxy.toExpression(whereExpression);
|
|
70
|
-
const evaluator = evaluators.compileSingleRowExpression(expression);
|
|
71
|
-
const result = evaluator(item);
|
|
72
|
-
return result;
|
|
73
|
-
} catch {
|
|
74
|
-
try {
|
|
75
|
-
const simpleProxy = new Proxy(item, {
|
|
76
|
-
get(target, prop) {
|
|
77
|
-
return target[prop];
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
const result = whereCallback(simpleProxy);
|
|
81
|
-
return result;
|
|
82
|
-
} catch {
|
|
83
|
-
return false;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
60
|
function createFilterFunctionFromExpression(expression) {
|
|
89
61
|
return (item) => {
|
|
90
62
|
try {
|
|
@@ -97,7 +69,7 @@ function createFilterFunctionFromExpression(expression) {
|
|
|
97
69
|
};
|
|
98
70
|
}
|
|
99
71
|
function createFilteredCallback(originalCallback, options) {
|
|
100
|
-
const filterFn =
|
|
72
|
+
const filterFn = createFilterFunctionFromExpression(options.whereExpression);
|
|
101
73
|
return (changes) => {
|
|
102
74
|
const filteredChanges = [];
|
|
103
75
|
for (const change of changes) {
|
|
@@ -134,7 +106,6 @@ function createFilteredCallback(originalCallback, options) {
|
|
|
134
106
|
}
|
|
135
107
|
};
|
|
136
108
|
}
|
|
137
|
-
exports.createFilterFunction = createFilterFunction;
|
|
138
109
|
exports.createFilterFunctionFromExpression = createFilterFunctionFromExpression;
|
|
139
110
|
exports.createFilteredCallback = createFilteredCallback;
|
|
140
111
|
exports.currentStateAsChanges = currentStateAsChanges;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"change-events.cjs","sources":["../../../src/collection/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 \"./index.js\"\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 = {}\n): Array<ChangeMessage<T>> | void {\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 // TODO: handle orderBy and limit options\n // by calling optimizeOrderedLimit\n\n if (!options.where) {\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 const expression: BasicExpression<boolean> = options.where\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 if (options.optimizedOnly) {\n return\n }\n\n const filterFn = createFilterFunctionFromExpression(expression)\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 = createFilterFunctionFromExpression(options.where)\n\n if (options.optimizedOnly) {\n return\n }\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\n): (changes: Array<ChangeMessage<T>>) => void {\n const filterFn = createFilterFunctionFromExpression(options.whereExpression!)\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":["optimizeExpressionWithIndexes","compileSingleRowExpression"],"mappings":";;;;AA2CO,SAAS,sBAId,YACA,UAAwC,IACR;AAEhC,QAAM,yBAAyB,CAC7B,aAC4B;AAC5B,UAAM,SAAkC,CAAA;AACxC,eAAW,CAAC,KAAK,KAAK,KAAK,WAAW,WAAW;AAE/C,WAAI,qCAAW,WAAU,MAAM;AAC7B,eAAO,KAAK;AAAA,UACV,MAAM;AAAA,UACN;AAAA,UACA;AAAA,QAAA,CACD;AAAA,MACH;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAKA,MAAI,CAAC,QAAQ,OAAO;AAElB,WAAO,uBAAA;AAAA,EACT;AAGA,MAAI;AACF,UAAM,aAAuC,QAAQ;AAGrD,UAAM,qBAAqBA,kBAAAA;AAAAA,MACzB;AAAA,MACA,WAAW;AAAA,IAAA;AAGb,QAAI,mBAAmB,aAAa;AAElC,YAAM,SAAkC,CAAA;AACxC,iBAAW,OAAO,mBAAmB,cAAc;AACjD,cAAM,QAAQ,WAAW,IAAI,GAAG;AAChC,YAAI,UAAU,QAAW;AACvB,iBAAO,KAAK;AAAA,YACV,MAAM;AAAA,YACN;AAAA,YACA;AAAA,UAAA,CACD;AAAA,QACH;AAAA,MACF;AACA,aAAO;AAAA,IACT,OAAO;AACL,UAAI,QAAQ,eAAe;AACzB;AAAA,MACF;AAEA,YAAM,WAAW,mCAAmC,UAAU;AAC9D,aAAO,uBAAuB,QAAQ;AAAA,IACxC;AAAA,EACF,SAAS,OAAO;AAEd,YAAQ;AAAA,MACN;AAAA,MACA;AAAA,IAAA;AAGF,UAAM,WAAW,mCAAmC,QAAQ,KAAK;AAEjE,QAAI,QAAQ,eAAe;AACzB;AAAA,IACF;AAEA,WAAO,uBAAuB,QAAQ;AAAA,EACxC;AACF;AA6CO,SAAS,mCACd,YACsB;AACtB,SAAO,CAAC,SAAqB;AAC3B,QAAI;AACF,YAAM,YAAYC,WAAAA,2BAA2B,UAAU;AACvD,YAAM,SAAS,UAAU,IAA+B;AACxD,aAAO,QAAQ,MAAM;AAAA,IACvB,QAAQ;AAEN,aAAO;AAAA,IACT;AAAA,EACF;AACF;AAQO,SAAS,uBACd,kBACA,SAC4C;AAC5C,QAAM,WAAW,mCAAmC,QAAQ,eAAgB;AAE5E,SAAO,CAAC,YAAqC;AAC3C,UAAM,kBAA2C,CAAA;AAEjD,eAAW,UAAU,SAAS;AAC5B,UAAI,OAAO,SAAS,UAAU;AAE5B,YAAI,SAAS,OAAO,KAAK,GAAG;AAC1B,0BAAgB,KAAK,MAAM;AAAA,QAC7B;AAAA,MACF,WAAW,OAAO,SAAS,UAAU;AAEnC,cAAM,kBAAkB,SAAS,OAAO,KAAK;AAC7C,cAAM,kBAAkB,OAAO,gBAC3B,SAAS,OAAO,aAAa,IAC7B;AAEJ,YAAI,mBAAmB,iBAAiB;AAEtC,0BAAgB,KAAK,MAAM;AAAA,QAC7B,WAAW,mBAAmB,CAAC,iBAAiB;AAE9C,0BAAgB,KAAK;AAAA,YACnB,GAAG;AAAA,YACH,MAAM;AAAA,UAAA,CACP;AAAA,QACH,WAAW,CAAC,mBAAmB,iBAAiB;AAE9C,0BAAgB,KAAK;AAAA,YACnB,GAAG;AAAA,YACH,MAAM;AAAA,YACN,OAAO,OAAO;AAAA;AAAA,UAAA,CACf;AAAA,QACH;AAAA,MAEF,OAAO;AAGL,YAAI,SAAS,OAAO,KAAK,GAAG;AAC1B,0BAAgB,KAAK,MAAM;AAAA,QAC7B;AAAA,MACF;AAAA,IACF;AAIA,QAAI,gBAAgB,SAAS,KAAK,QAAQ,WAAW,GAAG;AACtD,uBAAiB,eAAe;AAAA,IAClC;AAAA,EACF;AACF;;;;"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { ChangeMessage, CurrentStateAsChangesOptions, SubscribeChangesOptions } from '
|
|
2
|
-
import { Collection } from './
|
|
3
|
-
import { SingleRowRefProxy } from '
|
|
4
|
-
import { BasicExpression } from '
|
|
1
|
+
import { ChangeMessage, CurrentStateAsChangesOptions, SubscribeChangesOptions } from '../types.cjs';
|
|
2
|
+
import { Collection } from './index.js';
|
|
3
|
+
import { SingleRowRefProxy } from '../query/builder/ref-proxy.cjs';
|
|
4
|
+
import { BasicExpression } from '../query/ir.js';
|
|
5
5
|
/**
|
|
6
6
|
* Interface for a collection-like object that provides the necessary methods
|
|
7
7
|
* for the change events system to work
|
|
@@ -27,7 +27,7 @@ export interface CollectionLike<T extends object = Record<string, unknown>, TKey
|
|
|
27
27
|
* whereExpression: eq(row.status, 'active')
|
|
28
28
|
* })
|
|
29
29
|
*/
|
|
30
|
-
export declare function currentStateAsChanges<T extends object, TKey extends string | number>(collection: CollectionLike<T, TKey>, options?: CurrentStateAsChangesOptions
|
|
30
|
+
export declare function currentStateAsChanges<T extends object, TKey extends string | number>(collection: CollectionLike<T, TKey>, options?: CurrentStateAsChangesOptions): Array<ChangeMessage<T>> | void;
|
|
31
31
|
/**
|
|
32
32
|
* Creates a filter function from a where callback
|
|
33
33
|
* @param whereCallback - The callback function that defines the filter condition
|
|
@@ -46,4 +46,4 @@ export declare function createFilterFunctionFromExpression<T extends object>(exp
|
|
|
46
46
|
* @param options - The subscription options containing the where clause
|
|
47
47
|
* @returns A filtered callback function
|
|
48
48
|
*/
|
|
49
|
-
export declare function createFilteredCallback<T extends object>(originalCallback: (changes: Array<ChangeMessage<T>>) => void, options: SubscribeChangesOptions
|
|
49
|
+
export declare function createFilteredCallback<T extends object>(originalCallback: (changes: Array<ChangeMessage<T>>) => void, options: SubscribeChangesOptions): (changes: Array<ChangeMessage<T>>) => void;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const errors = require("../errors.cjs");
|
|
4
|
+
const subscription = require("./subscription.cjs");
|
|
5
|
+
class CollectionChangesManager {
|
|
6
|
+
/**
|
|
7
|
+
* Creates a new CollectionChangesManager instance
|
|
8
|
+
*/
|
|
9
|
+
constructor() {
|
|
10
|
+
this.activeSubscribersCount = 0;
|
|
11
|
+
this.changeSubscriptions = /* @__PURE__ */ new Set();
|
|
12
|
+
this.batchedEvents = [];
|
|
13
|
+
this.shouldBatchEvents = false;
|
|
14
|
+
}
|
|
15
|
+
setDeps(deps) {
|
|
16
|
+
this.lifecycle = deps.lifecycle;
|
|
17
|
+
this.sync = deps.sync;
|
|
18
|
+
this.events = deps.events;
|
|
19
|
+
this.collection = deps.collection;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Emit an empty ready event to notify subscribers that the collection is ready
|
|
23
|
+
* This bypasses the normal empty array check in emitEvents
|
|
24
|
+
*/
|
|
25
|
+
emitEmptyReadyEvent() {
|
|
26
|
+
for (const subscription2 of this.changeSubscriptions) {
|
|
27
|
+
subscription2.emitEvents([]);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Emit events either immediately or batch them for later emission
|
|
32
|
+
*/
|
|
33
|
+
emitEvents(changes, forceEmit = false) {
|
|
34
|
+
if (this.shouldBatchEvents && !forceEmit) {
|
|
35
|
+
this.batchedEvents.push(...changes);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
let eventsToEmit = changes;
|
|
39
|
+
if (this.batchedEvents.length > 0 && forceEmit) {
|
|
40
|
+
eventsToEmit = [...this.batchedEvents, ...changes];
|
|
41
|
+
this.batchedEvents = [];
|
|
42
|
+
this.shouldBatchEvents = false;
|
|
43
|
+
}
|
|
44
|
+
if (eventsToEmit.length === 0) return;
|
|
45
|
+
for (const subscription2 of this.changeSubscriptions) {
|
|
46
|
+
subscription2.emitEvents(eventsToEmit);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Subscribe to changes in the collection
|
|
51
|
+
*/
|
|
52
|
+
subscribeChanges(callback, options = {}) {
|
|
53
|
+
this.addSubscriber();
|
|
54
|
+
const subscription$1 = new subscription.CollectionSubscription(this.collection, callback, {
|
|
55
|
+
...options,
|
|
56
|
+
onUnsubscribe: () => {
|
|
57
|
+
this.removeSubscriber();
|
|
58
|
+
this.changeSubscriptions.delete(subscription$1);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
if (options.includeInitialState) {
|
|
62
|
+
subscription$1.requestSnapshot();
|
|
63
|
+
}
|
|
64
|
+
this.changeSubscriptions.add(subscription$1);
|
|
65
|
+
return subscription$1;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Increment the active subscribers count and start sync if needed
|
|
69
|
+
*/
|
|
70
|
+
addSubscriber() {
|
|
71
|
+
const previousSubscriberCount = this.activeSubscribersCount;
|
|
72
|
+
this.activeSubscribersCount++;
|
|
73
|
+
this.lifecycle.cancelGCTimer();
|
|
74
|
+
if (this.lifecycle.status === `cleaned-up` || this.lifecycle.status === `idle`) {
|
|
75
|
+
this.sync.startSync();
|
|
76
|
+
}
|
|
77
|
+
this.events.emitSubscribersChange(
|
|
78
|
+
this.activeSubscribersCount,
|
|
79
|
+
previousSubscriberCount
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Decrement the active subscribers count and start GC timer if needed
|
|
84
|
+
*/
|
|
85
|
+
removeSubscriber() {
|
|
86
|
+
const previousSubscriberCount = this.activeSubscribersCount;
|
|
87
|
+
this.activeSubscribersCount--;
|
|
88
|
+
if (this.activeSubscribersCount === 0) {
|
|
89
|
+
this.lifecycle.startGCTimer();
|
|
90
|
+
} else if (this.activeSubscribersCount < 0) {
|
|
91
|
+
throw new errors.NegativeActiveSubscribersError();
|
|
92
|
+
}
|
|
93
|
+
this.events.emitSubscribersChange(
|
|
94
|
+
this.activeSubscribersCount,
|
|
95
|
+
previousSubscriberCount
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Clean up the collection by stopping sync and clearing data
|
|
100
|
+
* This can be called manually or automatically by garbage collection
|
|
101
|
+
*/
|
|
102
|
+
cleanup() {
|
|
103
|
+
this.batchedEvents = [];
|
|
104
|
+
this.shouldBatchEvents = false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
exports.CollectionChangesManager = CollectionChangesManager;
|
|
108
|
+
//# sourceMappingURL=changes.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"changes.cjs","sources":["../../../src/collection/changes.ts"],"sourcesContent":["import { NegativeActiveSubscribersError } from \"../errors\"\nimport { CollectionSubscription } from \"./subscription.js\"\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\"\nimport type { ChangeMessage, SubscribeChangesOptions } from \"../types\"\nimport type { CollectionLifecycleManager } from \"./lifecycle.js\"\nimport type { CollectionSyncManager } from \"./sync.js\"\nimport type { CollectionEventsManager } from \"./events.js\"\nimport type { CollectionImpl } from \"./index.js\"\n\nexport class CollectionChangesManager<\n TOutput extends object = Record<string, unknown>,\n TKey extends string | number = string | number,\n TSchema extends StandardSchemaV1 = StandardSchemaV1,\n TInput extends object = TOutput,\n> {\n private lifecycle!: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>\n private sync!: CollectionSyncManager<TOutput, TKey, TSchema, TInput>\n private events!: CollectionEventsManager\n private collection!: CollectionImpl<TOutput, TKey, any, TSchema, TInput>\n\n public activeSubscribersCount = 0\n public changeSubscriptions = new Set<CollectionSubscription>()\n public batchedEvents: Array<ChangeMessage<TOutput, TKey>> = []\n public shouldBatchEvents = false\n\n /**\n * Creates a new CollectionChangesManager instance\n */\n constructor() {}\n\n public setDeps(deps: {\n lifecycle: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>\n sync: CollectionSyncManager<TOutput, TKey, TSchema, TInput>\n events: CollectionEventsManager\n collection: CollectionImpl<TOutput, TKey, any, TSchema, TInput>\n }) {\n this.lifecycle = deps.lifecycle\n this.sync = deps.sync\n this.events = deps.events\n this.collection = deps.collection\n }\n\n /**\n * Emit an empty ready event to notify subscribers that the collection is ready\n * This bypasses the normal empty array check in emitEvents\n */\n public emitEmptyReadyEvent(): void {\n // Emit empty array directly to all subscribers\n for (const subscription of this.changeSubscriptions) {\n subscription.emitEvents([])\n }\n }\n\n /**\n * Emit events either immediately or batch them for later emission\n */\n public emitEvents(\n changes: Array<ChangeMessage<TOutput, TKey>>,\n forceEmit = false\n ): void {\n // Skip batching for user actions (forceEmit=true) to keep UI responsive\n if (this.shouldBatchEvents && !forceEmit) {\n // Add events to the batch\n this.batchedEvents.push(...changes)\n return\n }\n\n // Either we're not batching, or we're forcing emission (user action or ending batch cycle)\n let eventsToEmit = changes\n\n // If we have batched events and this is a forced emit, combine them\n if (this.batchedEvents.length > 0 && forceEmit) {\n eventsToEmit = [...this.batchedEvents, ...changes]\n this.batchedEvents = []\n this.shouldBatchEvents = false\n }\n\n if (eventsToEmit.length === 0) return\n\n // Emit to all listeners\n for (const subscription of this.changeSubscriptions) {\n subscription.emitEvents(eventsToEmit)\n }\n }\n\n /**\n * Subscribe to changes in the collection\n */\n public subscribeChanges(\n callback: (changes: Array<ChangeMessage<TOutput>>) => void,\n options: SubscribeChangesOptions = {}\n ): CollectionSubscription {\n // Start sync and track subscriber\n this.addSubscriber()\n\n const subscription = new CollectionSubscription(this.collection, callback, {\n ...options,\n onUnsubscribe: () => {\n this.removeSubscriber()\n this.changeSubscriptions.delete(subscription)\n },\n })\n\n if (options.includeInitialState) {\n subscription.requestSnapshot()\n }\n\n // Add to batched listeners\n this.changeSubscriptions.add(subscription)\n\n return subscription\n }\n\n /**\n * Increment the active subscribers count and start sync if needed\n */\n private addSubscriber(): void {\n const previousSubscriberCount = this.activeSubscribersCount\n this.activeSubscribersCount++\n this.lifecycle.cancelGCTimer()\n\n // Start sync if collection was cleaned up\n if (\n this.lifecycle.status === `cleaned-up` ||\n this.lifecycle.status === `idle`\n ) {\n this.sync.startSync()\n }\n\n this.events.emitSubscribersChange(\n this.activeSubscribersCount,\n previousSubscriberCount\n )\n }\n\n /**\n * Decrement the active subscribers count and start GC timer if needed\n */\n private removeSubscriber(): void {\n const previousSubscriberCount = this.activeSubscribersCount\n this.activeSubscribersCount--\n\n if (this.activeSubscribersCount === 0) {\n this.lifecycle.startGCTimer()\n } else if (this.activeSubscribersCount < 0) {\n throw new NegativeActiveSubscribersError()\n }\n\n this.events.emitSubscribersChange(\n this.activeSubscribersCount,\n previousSubscriberCount\n )\n }\n\n /**\n * Clean up the collection by stopping sync and clearing data\n * This can be called manually or automatically by garbage collection\n */\n public cleanup(): void {\n this.batchedEvents = []\n this.shouldBatchEvents = false\n }\n}\n"],"names":["subscription","CollectionSubscription","NegativeActiveSubscribersError"],"mappings":";;;;AASO,MAAM,yBAKX;AAAA;AAAA;AAAA;AAAA,EAcA,cAAc;AARd,SAAO,yBAAyB;AAChC,SAAO,0CAA0B,IAAA;AACjC,SAAO,gBAAqD,CAAA;AAC5D,SAAO,oBAAoB;AAAA,EAKZ;AAAA,EAER,QAAQ,MAKZ;AACD,SAAK,YAAY,KAAK;AACtB,SAAK,OAAO,KAAK;AACjB,SAAK,SAAS,KAAK;AACnB,SAAK,aAAa,KAAK;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,sBAA4B;AAEjC,eAAWA,iBAAgB,KAAK,qBAAqB;AACnD,MAAAA,cAAa,WAAW,EAAE;AAAA,IAC5B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKO,WACL,SACA,YAAY,OACN;AAEN,QAAI,KAAK,qBAAqB,CAAC,WAAW;AAExC,WAAK,cAAc,KAAK,GAAG,OAAO;AAClC;AAAA,IACF;AAGA,QAAI,eAAe;AAGnB,QAAI,KAAK,cAAc,SAAS,KAAK,WAAW;AAC9C,qBAAe,CAAC,GAAG,KAAK,eAAe,GAAG,OAAO;AACjD,WAAK,gBAAgB,CAAA;AACrB,WAAK,oBAAoB;AAAA,IAC3B;AAEA,QAAI,aAAa,WAAW,EAAG;AAG/B,eAAWA,iBAAgB,KAAK,qBAAqB;AACnD,MAAAA,cAAa,WAAW,YAAY;AAAA,IACtC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKO,iBACL,UACA,UAAmC,IACX;AAExB,SAAK,cAAA;AAEL,UAAMA,iBAAe,IAAIC,aAAAA,uBAAuB,KAAK,YAAY,UAAU;AAAA,MACzE,GAAG;AAAA,MACH,eAAe,MAAM;AACnB,aAAK,iBAAA;AACL,aAAK,oBAAoB,OAAOD,cAAY;AAAA,MAC9C;AAAA,IAAA,CACD;AAED,QAAI,QAAQ,qBAAqB;AAC/BA,qBAAa,gBAAA;AAAA,IACf;AAGA,SAAK,oBAAoB,IAAIA,cAAY;AAEzC,WAAOA;AAAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAsB;AAC5B,UAAM,0BAA0B,KAAK;AACrC,SAAK;AACL,SAAK,UAAU,cAAA;AAGf,QACE,KAAK,UAAU,WAAW,gBAC1B,KAAK,UAAU,WAAW,QAC1B;AACA,WAAK,KAAK,UAAA;AAAA,IACZ;AAEA,SAAK,OAAO;AAAA,MACV,KAAK;AAAA,MACL;AAAA,IAAA;AAAA,EAEJ;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAAyB;AAC/B,UAAM,0BAA0B,KAAK;AACrC,SAAK;AAEL,QAAI,KAAK,2BAA2B,GAAG;AACrC,WAAK,UAAU,aAAA;AAAA,IACjB,WAAW,KAAK,yBAAyB,GAAG;AAC1C,YAAM,IAAIE,OAAAA,+BAAA;AAAA,IACZ;AAEA,SAAK,OAAO;AAAA,MACV,KAAK;AAAA,MACL;AAAA,IAAA;AAAA,EAEJ;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,UAAgB;AACrB,SAAK,gBAAgB,CAAA;AACrB,SAAK,oBAAoB;AAAA,EAC3B;AACF;;"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { CollectionSubscription } from './subscription.js';
|
|
2
|
+
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
3
|
+
import { ChangeMessage, SubscribeChangesOptions } from '../types.cjs';
|
|
4
|
+
import { CollectionLifecycleManager } from './lifecycle.js';
|
|
5
|
+
import { CollectionSyncManager } from './sync.js';
|
|
6
|
+
import { CollectionEventsManager } from './events.js';
|
|
7
|
+
import { CollectionImpl } from './index.js';
|
|
8
|
+
export declare class CollectionChangesManager<TOutput extends object = Record<string, unknown>, TKey extends string | number = string | number, TSchema extends StandardSchemaV1 = StandardSchemaV1, TInput extends object = TOutput> {
|
|
9
|
+
private lifecycle;
|
|
10
|
+
private sync;
|
|
11
|
+
private events;
|
|
12
|
+
private collection;
|
|
13
|
+
activeSubscribersCount: number;
|
|
14
|
+
changeSubscriptions: Set<CollectionSubscription>;
|
|
15
|
+
batchedEvents: Array<ChangeMessage<TOutput, TKey>>;
|
|
16
|
+
shouldBatchEvents: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Creates a new CollectionChangesManager instance
|
|
19
|
+
*/
|
|
20
|
+
constructor();
|
|
21
|
+
setDeps(deps: {
|
|
22
|
+
lifecycle: CollectionLifecycleManager<TOutput, TKey, TSchema, TInput>;
|
|
23
|
+
sync: CollectionSyncManager<TOutput, TKey, TSchema, TInput>;
|
|
24
|
+
events: CollectionEventsManager;
|
|
25
|
+
collection: CollectionImpl<TOutput, TKey, any, TSchema, TInput>;
|
|
26
|
+
}): void;
|
|
27
|
+
/**
|
|
28
|
+
* Emit an empty ready event to notify subscribers that the collection is ready
|
|
29
|
+
* This bypasses the normal empty array check in emitEvents
|
|
30
|
+
*/
|
|
31
|
+
emitEmptyReadyEvent(): void;
|
|
32
|
+
/**
|
|
33
|
+
* Emit events either immediately or batch them for later emission
|
|
34
|
+
*/
|
|
35
|
+
emitEvents(changes: Array<ChangeMessage<TOutput, TKey>>, forceEmit?: boolean): void;
|
|
36
|
+
/**
|
|
37
|
+
* Subscribe to changes in the collection
|
|
38
|
+
*/
|
|
39
|
+
subscribeChanges(callback: (changes: Array<ChangeMessage<TOutput>>) => void, options?: SubscribeChangesOptions): CollectionSubscription;
|
|
40
|
+
/**
|
|
41
|
+
* Increment the active subscribers count and start sync if needed
|
|
42
|
+
*/
|
|
43
|
+
private addSubscriber;
|
|
44
|
+
/**
|
|
45
|
+
* Decrement the active subscribers count and start GC timer if needed
|
|
46
|
+
*/
|
|
47
|
+
private removeSubscriber;
|
|
48
|
+
/**
|
|
49
|
+
* Clean up the collection by stopping sync and clearing data
|
|
50
|
+
* This can be called manually or automatically by garbage collection
|
|
51
|
+
*/
|
|
52
|
+
cleanup(): void;
|
|
53
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
class CollectionEventsManager {
|
|
4
|
+
constructor() {
|
|
5
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
6
|
+
}
|
|
7
|
+
setDeps(deps) {
|
|
8
|
+
this.collection = deps.collection;
|
|
9
|
+
}
|
|
10
|
+
on(event, callback) {
|
|
11
|
+
if (!this.listeners.has(event)) {
|
|
12
|
+
this.listeners.set(event, /* @__PURE__ */ new Set());
|
|
13
|
+
}
|
|
14
|
+
this.listeners.get(event).add(callback);
|
|
15
|
+
return () => {
|
|
16
|
+
this.listeners.get(event).delete(callback);
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
once(event, callback) {
|
|
20
|
+
const unsubscribe = this.on(event, (eventPayload) => {
|
|
21
|
+
callback(eventPayload);
|
|
22
|
+
unsubscribe();
|
|
23
|
+
});
|
|
24
|
+
return unsubscribe;
|
|
25
|
+
}
|
|
26
|
+
off(event, callback) {
|
|
27
|
+
var _a;
|
|
28
|
+
(_a = this.listeners.get(event)) == null ? void 0 : _a.delete(callback);
|
|
29
|
+
}
|
|
30
|
+
waitFor(event, timeout) {
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
32
|
+
let timeoutId;
|
|
33
|
+
const unsubscribe = this.on(event, (eventPayload) => {
|
|
34
|
+
if (timeoutId) {
|
|
35
|
+
clearTimeout(timeoutId);
|
|
36
|
+
timeoutId = void 0;
|
|
37
|
+
}
|
|
38
|
+
resolve(eventPayload);
|
|
39
|
+
unsubscribe();
|
|
40
|
+
});
|
|
41
|
+
if (timeout) {
|
|
42
|
+
timeoutId = setTimeout(() => {
|
|
43
|
+
timeoutId = void 0;
|
|
44
|
+
unsubscribe();
|
|
45
|
+
reject(new Error(`Timeout waiting for event ${event}`));
|
|
46
|
+
}, timeout);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
emit(event, eventPayload) {
|
|
51
|
+
var _a;
|
|
52
|
+
(_a = this.listeners.get(event)) == null ? void 0 : _a.forEach((listener) => {
|
|
53
|
+
try {
|
|
54
|
+
listener(eventPayload);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
queueMicrotask(() => {
|
|
57
|
+
throw error;
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
emitStatusChange(status, previousStatus) {
|
|
63
|
+
this.emit(`status:change`, {
|
|
64
|
+
type: `status:change`,
|
|
65
|
+
collection: this.collection,
|
|
66
|
+
previousStatus,
|
|
67
|
+
status
|
|
68
|
+
});
|
|
69
|
+
const eventKey = `status:${status}`;
|
|
70
|
+
this.emit(eventKey, {
|
|
71
|
+
type: eventKey,
|
|
72
|
+
collection: this.collection,
|
|
73
|
+
previousStatus,
|
|
74
|
+
status
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
emitSubscribersChange(subscriberCount, previousSubscriberCount) {
|
|
78
|
+
this.emit(`subscribers:change`, {
|
|
79
|
+
type: `subscribers:change`,
|
|
80
|
+
collection: this.collection,
|
|
81
|
+
previousSubscriberCount,
|
|
82
|
+
subscriberCount
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
cleanup() {
|
|
86
|
+
this.listeners.clear();
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
exports.CollectionEventsManager = CollectionEventsManager;
|
|
90
|
+
//# sourceMappingURL=events.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.cjs","sources":["../../../src/collection/events.ts"],"sourcesContent":["import type { Collection } from \"./index.js\"\nimport type { CollectionStatus } from \"../types.js\"\n\n/**\n * Event emitted when the collection status changes\n */\nexport interface CollectionStatusChangeEvent {\n type: `status:change`\n collection: Collection\n previousStatus: CollectionStatus\n status: CollectionStatus\n}\n\n/**\n * Event emitted when the collection status changes to a specific status\n */\nexport interface CollectionStatusEvent<T extends CollectionStatus> {\n type: `status:${T}`\n collection: Collection\n previousStatus: CollectionStatus\n status: T\n}\n\n/**\n * Event emitted when the number of subscribers to the collection changes\n */\nexport interface CollectionSubscribersChangeEvent {\n type: `subscribers:change`\n collection: Collection\n previousSubscriberCount: number\n subscriberCount: number\n}\n\nexport type AllCollectionEvents = {\n \"status:change\": CollectionStatusChangeEvent\n \"subscribers:change\": CollectionSubscribersChangeEvent\n} & {\n [K in CollectionStatus as `status:${K}`]: CollectionStatusEvent<K>\n}\n\nexport type CollectionEvent =\n | AllCollectionEvents[keyof AllCollectionEvents]\n | CollectionStatusChangeEvent\n | CollectionSubscribersChangeEvent\n\nexport type CollectionEventHandler<T extends keyof AllCollectionEvents> = (\n event: AllCollectionEvents[T]\n) => void\n\nexport class CollectionEventsManager {\n private collection!: Collection<any, any, any, any, any>\n private listeners = new Map<\n keyof AllCollectionEvents,\n Set<CollectionEventHandler<any>>\n >()\n\n constructor() {}\n\n setDeps(deps: { collection: Collection<any, any, any, any, any> }) {\n this.collection = deps.collection\n }\n\n on<T extends keyof AllCollectionEvents>(\n event: T,\n callback: CollectionEventHandler<T>\n ) {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set())\n }\n this.listeners.get(event)!.add(callback)\n\n return () => {\n this.listeners.get(event)!.delete(callback)\n }\n }\n\n once<T extends keyof AllCollectionEvents>(\n event: T,\n callback: CollectionEventHandler<T>\n ) {\n const unsubscribe = this.on(event, (eventPayload) => {\n callback(eventPayload)\n unsubscribe()\n })\n return unsubscribe\n }\n\n off<T extends keyof AllCollectionEvents>(\n event: T,\n callback: CollectionEventHandler<T>\n ) {\n this.listeners.get(event)?.delete(callback)\n }\n\n waitFor<T extends keyof AllCollectionEvents>(\n event: T,\n timeout?: number\n ): Promise<AllCollectionEvents[T]> {\n return new Promise((resolve, reject) => {\n let timeoutId: NodeJS.Timeout | undefined\n const unsubscribe = this.on(event, (eventPayload) => {\n if (timeoutId) {\n clearTimeout(timeoutId)\n timeoutId = undefined\n }\n resolve(eventPayload)\n unsubscribe()\n })\n if (timeout) {\n timeoutId = setTimeout(() => {\n timeoutId = undefined\n unsubscribe()\n reject(new Error(`Timeout waiting for event ${event}`))\n }, timeout)\n }\n })\n }\n\n emit<T extends keyof AllCollectionEvents>(\n event: T,\n eventPayload: AllCollectionEvents[T]\n ) {\n this.listeners.get(event)?.forEach((listener) => {\n try {\n listener(eventPayload)\n } catch (error) {\n // Re-throw in a microtask to surface the error\n queueMicrotask(() => {\n throw error\n })\n }\n })\n }\n\n emitStatusChange<T extends CollectionStatus>(\n status: T,\n previousStatus: CollectionStatus\n ) {\n this.emit(`status:change`, {\n type: `status:change`,\n collection: this.collection,\n previousStatus,\n status,\n })\n\n // Emit specific status event using type assertion\n const eventKey: `status:${T}` = `status:${status}`\n this.emit(eventKey, {\n type: eventKey,\n collection: this.collection,\n previousStatus,\n status,\n } as AllCollectionEvents[`status:${T}`])\n }\n\n emitSubscribersChange(\n subscriberCount: number,\n previousSubscriberCount: number\n ) {\n this.emit(`subscribers:change`, {\n type: `subscribers:change`,\n collection: this.collection,\n previousSubscriberCount,\n subscriberCount,\n })\n }\n\n cleanup() {\n this.listeners.clear()\n }\n}\n"],"names":[],"mappings":";;AAiDO,MAAM,wBAAwB;AAAA,EAOnC,cAAc;AALd,SAAQ,gCAAgB,IAAA;AAAA,EAKT;AAAA,EAEf,QAAQ,MAA2D;AACjE,SAAK,aAAa,KAAK;AAAA,EACzB;AAAA,EAEA,GACE,OACA,UACA;AACA,QAAI,CAAC,KAAK,UAAU,IAAI,KAAK,GAAG;AAC9B,WAAK,UAAU,IAAI,OAAO,oBAAI,KAAK;AAAA,IACrC;AACA,SAAK,UAAU,IAAI,KAAK,EAAG,IAAI,QAAQ;AAEvC,WAAO,MAAM;AACX,WAAK,UAAU,IAAI,KAAK,EAAG,OAAO,QAAQ;AAAA,IAC5C;AAAA,EACF;AAAA,EAEA,KACE,OACA,UACA;AACA,UAAM,cAAc,KAAK,GAAG,OAAO,CAAC,iBAAiB;AACnD,eAAS,YAAY;AACrB,kBAAA;AAAA,IACF,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEA,IACE,OACA,UACA;;AACA,eAAK,UAAU,IAAI,KAAK,MAAxB,mBAA2B,OAAO;AAAA,EACpC;AAAA,EAEA,QACE,OACA,SACiC;AACjC,WAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAI;AACJ,YAAM,cAAc,KAAK,GAAG,OAAO,CAAC,iBAAiB;AACnD,YAAI,WAAW;AACb,uBAAa,SAAS;AACtB,sBAAY;AAAA,QACd;AACA,gBAAQ,YAAY;AACpB,oBAAA;AAAA,MACF,CAAC;AACD,UAAI,SAAS;AACX,oBAAY,WAAW,MAAM;AAC3B,sBAAY;AACZ,sBAAA;AACA,iBAAO,IAAI,MAAM,6BAA6B,KAAK,EAAE,CAAC;AAAA,QACxD,GAAG,OAAO;AAAA,MACZ;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,KACE,OACA,cACA;;AACA,eAAK,UAAU,IAAI,KAAK,MAAxB,mBAA2B,QAAQ,CAAC,aAAa;AAC/C,UAAI;AACF,iBAAS,YAAY;AAAA,MACvB,SAAS,OAAO;AAEd,uBAAe,MAAM;AACnB,gBAAM;AAAA,QACR,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA,EAEA,iBACE,QACA,gBACA;AACA,SAAK,KAAK,iBAAiB;AAAA,MACzB,MAAM;AAAA,MACN,YAAY,KAAK;AAAA,MACjB;AAAA,MACA;AAAA,IAAA,CACD;AAGD,UAAM,WAA0B,UAAU,MAAM;AAChD,SAAK,KAAK,UAAU;AAAA,MAClB,MAAM;AAAA,MACN,YAAY,KAAK;AAAA,MACjB;AAAA,MACA;AAAA,IAAA,CACqC;AAAA,EACzC;AAAA,EAEA,sBACE,iBACA,yBACA;AACA,SAAK,KAAK,sBAAsB;AAAA,MAC9B,MAAM;AAAA,MACN,YAAY,KAAK;AAAA,MACjB;AAAA,MACA;AAAA,IAAA,CACD;AAAA,EACH;AAAA,EAEA,UAAU;AACR,SAAK,UAAU,MAAA;AAAA,EACjB;AACF;;"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { Collection } from './index.js';
|
|
2
|
+
import { CollectionStatus } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Event emitted when the collection status changes
|
|
5
|
+
*/
|
|
6
|
+
export interface CollectionStatusChangeEvent {
|
|
7
|
+
type: `status:change`;
|
|
8
|
+
collection: Collection;
|
|
9
|
+
previousStatus: CollectionStatus;
|
|
10
|
+
status: CollectionStatus;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Event emitted when the collection status changes to a specific status
|
|
14
|
+
*/
|
|
15
|
+
export interface CollectionStatusEvent<T extends CollectionStatus> {
|
|
16
|
+
type: `status:${T}`;
|
|
17
|
+
collection: Collection;
|
|
18
|
+
previousStatus: CollectionStatus;
|
|
19
|
+
status: T;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Event emitted when the number of subscribers to the collection changes
|
|
23
|
+
*/
|
|
24
|
+
export interface CollectionSubscribersChangeEvent {
|
|
25
|
+
type: `subscribers:change`;
|
|
26
|
+
collection: Collection;
|
|
27
|
+
previousSubscriberCount: number;
|
|
28
|
+
subscriberCount: number;
|
|
29
|
+
}
|
|
30
|
+
export type AllCollectionEvents = {
|
|
31
|
+
"status:change": CollectionStatusChangeEvent;
|
|
32
|
+
"subscribers:change": CollectionSubscribersChangeEvent;
|
|
33
|
+
} & {
|
|
34
|
+
[K in CollectionStatus as `status:${K}`]: CollectionStatusEvent<K>;
|
|
35
|
+
};
|
|
36
|
+
export type CollectionEvent = AllCollectionEvents[keyof AllCollectionEvents] | CollectionStatusChangeEvent | CollectionSubscribersChangeEvent;
|
|
37
|
+
export type CollectionEventHandler<T extends keyof AllCollectionEvents> = (event: AllCollectionEvents[T]) => void;
|
|
38
|
+
export declare class CollectionEventsManager {
|
|
39
|
+
private collection;
|
|
40
|
+
private listeners;
|
|
41
|
+
constructor();
|
|
42
|
+
setDeps(deps: {
|
|
43
|
+
collection: Collection<any, any, any, any, any>;
|
|
44
|
+
}): void;
|
|
45
|
+
on<T extends keyof AllCollectionEvents>(event: T, callback: CollectionEventHandler<T>): () => void;
|
|
46
|
+
once<T extends keyof AllCollectionEvents>(event: T, callback: CollectionEventHandler<T>): () => void;
|
|
47
|
+
off<T extends keyof AllCollectionEvents>(event: T, callback: CollectionEventHandler<T>): void;
|
|
48
|
+
waitFor<T extends keyof AllCollectionEvents>(event: T, timeout?: number): Promise<AllCollectionEvents[T]>;
|
|
49
|
+
emit<T extends keyof AllCollectionEvents>(event: T, eventPayload: AllCollectionEvents[T]): void;
|
|
50
|
+
emitStatusChange<T extends CollectionStatus>(status: T, previousStatus: CollectionStatus): void;
|
|
51
|
+
emitSubscribersChange(subscriberCount: number, previousSubscriberCount: number): void;
|
|
52
|
+
cleanup(): void;
|
|
53
|
+
}
|