@tanstack/db 0.0.11 → 0.0.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/dist/cjs/SortedMap.cjs +38 -11
  2. package/dist/cjs/SortedMap.cjs.map +1 -1
  3. package/dist/cjs/SortedMap.d.cts +10 -0
  4. package/dist/cjs/collection.cjs +476 -144
  5. package/dist/cjs/collection.cjs.map +1 -1
  6. package/dist/cjs/collection.d.cts +107 -32
  7. package/dist/cjs/index.cjs +2 -1
  8. package/dist/cjs/index.cjs.map +1 -1
  9. package/dist/cjs/index.d.cts +1 -0
  10. package/dist/cjs/optimistic-action.cjs +21 -0
  11. package/dist/cjs/optimistic-action.cjs.map +1 -0
  12. package/dist/cjs/optimistic-action.d.cts +39 -0
  13. package/dist/cjs/query/compiled-query.cjs +38 -16
  14. package/dist/cjs/query/compiled-query.cjs.map +1 -1
  15. package/dist/cjs/query/query-builder.cjs +2 -2
  16. package/dist/cjs/query/query-builder.cjs.map +1 -1
  17. package/dist/cjs/transactions.cjs +3 -1
  18. package/dist/cjs/transactions.cjs.map +1 -1
  19. package/dist/cjs/types.d.cts +83 -10
  20. package/dist/esm/SortedMap.d.ts +10 -0
  21. package/dist/esm/SortedMap.js +38 -11
  22. package/dist/esm/SortedMap.js.map +1 -1
  23. package/dist/esm/collection.d.ts +107 -32
  24. package/dist/esm/collection.js +477 -145
  25. package/dist/esm/collection.js.map +1 -1
  26. package/dist/esm/index.d.ts +1 -0
  27. package/dist/esm/index.js +3 -2
  28. package/dist/esm/index.js.map +1 -1
  29. package/dist/esm/optimistic-action.d.ts +39 -0
  30. package/dist/esm/optimistic-action.js +21 -0
  31. package/dist/esm/optimistic-action.js.map +1 -0
  32. package/dist/esm/query/compiled-query.js +38 -16
  33. package/dist/esm/query/compiled-query.js.map +1 -1
  34. package/dist/esm/query/query-builder.js +2 -2
  35. package/dist/esm/query/query-builder.js.map +1 -1
  36. package/dist/esm/transactions.js +3 -1
  37. package/dist/esm/transactions.js.map +1 -1
  38. package/dist/esm/types.d.ts +83 -10
  39. package/package.json +1 -1
  40. package/src/SortedMap.ts +46 -13
  41. package/src/collection.ts +689 -239
  42. package/src/index.ts +1 -0
  43. package/src/optimistic-action.ts +65 -0
  44. package/src/query/compiled-query.ts +79 -21
  45. package/src/query/query-builder.ts +2 -2
  46. package/src/transactions.ts +6 -1
  47. package/src/types.ts +124 -8
@@ -21,6 +21,33 @@ class SortedMap {
21
21
  if (a > b) return 1;
22
22
  return 0;
23
23
  }
24
+ /**
25
+ * Finds the index where a key-value pair should be inserted to maintain sort order.
26
+ * Uses binary search to find the correct position based on the value.
27
+ * Hence, it is in O(log n) time.
28
+ *
29
+ * @param key - The key to find position for
30
+ * @param value - The value to compare against
31
+ * @returns The index where the key should be inserted
32
+ */
33
+ indexOf(value) {
34
+ let left = 0;
35
+ let right = this.sortedKeys.length;
36
+ while (left < right) {
37
+ const mid = Math.floor((left + right) / 2);
38
+ const midKey = this.sortedKeys[mid];
39
+ const midValue = this.map.get(midKey);
40
+ const comparison = this.comparator(value, midValue);
41
+ if (comparison < 0) {
42
+ right = mid;
43
+ } else if (comparison > 0) {
44
+ left = mid + 1;
45
+ } else {
46
+ return mid;
47
+ }
48
+ }
49
+ return left;
50
+ }
24
51
  /**
25
52
  * Sets a key-value pair in the map and maintains sort order
26
53
  *
@@ -29,15 +56,14 @@ class SortedMap {
29
56
  * @returns This SortedMap instance for chaining
30
57
  */
31
58
  set(key, value) {
32
- this.map.set(key, value);
33
- if (!this.sortedKeys.includes(key)) {
34
- this.sortedKeys.push(key);
59
+ if (this.map.has(key)) {
60
+ const oldValue = this.map.get(key);
61
+ const oldIndex = this.indexOf(oldValue);
62
+ this.sortedKeys.splice(oldIndex, 1);
35
63
  }
36
- this.sortedKeys.sort((a, b) => {
37
- const valueA = this.map.get(a);
38
- const valueB = this.map.get(b);
39
- return this.comparator(valueA, valueB);
40
- });
64
+ const index = this.indexOf(value);
65
+ this.sortedKeys.splice(index, 0, key);
66
+ this.map.set(key, value);
41
67
  return this;
42
68
  }
43
69
  /**
@@ -56,10 +82,11 @@ class SortedMap {
56
82
  * @returns True if the key was found and removed, false otherwise
57
83
  */
58
84
  delete(key) {
59
- if (this.map.delete(key)) {
60
- const index = this.sortedKeys.indexOf(key);
85
+ if (this.map.has(key)) {
86
+ const oldValue = this.map.get(key);
87
+ const index = this.indexOf(oldValue);
61
88
  this.sortedKeys.splice(index, 1);
62
- return true;
89
+ return this.map.delete(key);
63
90
  }
64
91
  return false;
65
92
  }
@@ -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 * 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 this.map.set(key, value)\n\n if (!this.sortedKeys.includes(key)) {\n this.sortedKeys.push(key)\n }\n\n // Re-sort keys based on values\n this.sortedKeys.sort((a, b) => {\n const valueA = this.map.get(a)!\n const valueB = this.map.get(b)!\n return this.comparator(valueA, valueB)\n })\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.delete(key)) {\n const index = this.sortedKeys.indexOf(key)\n this.sortedKeys.splice(index, 1)\n return true\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;AACpD,SAAA,0BAAU,IAAkB;AACjC,SAAK,aAAa,CAAC;AACd,SAAA,aAAa,cAAc,KAAK;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAU/B,kBAAkB,GAAW,GAAmB;AAClD,QAAA,IAAI,EAAU,QAAA;AACd,QAAA,IAAI,EAAU,QAAA;AACX,WAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUT,IAAI,KAAW,OAAqB;AAC7B,SAAA,IAAI,IAAI,KAAK,KAAK;AAEvB,QAAI,CAAC,KAAK,WAAW,SAAS,GAAG,GAAG;AAC7B,WAAA,WAAW,KAAK,GAAG;AAAA,IAAA;AAI1B,SAAK,WAAW,KAAK,CAAC,GAAG,MAAM;AAC7B,YAAM,SAAS,KAAK,IAAI,IAAI,CAAC;AAC7B,YAAM,SAAS,KAAK,IAAI,IAAI,CAAC;AACtB,aAAA,KAAK,WAAW,QAAQ,MAAM;AAAA,IAAA,CACtC;AAEM,WAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAST,IAAI,KAA+B;AAC1B,WAAA,KAAK,IAAI,IAAI,GAAG;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASzB,OAAO,KAAoB;AACzB,QAAI,KAAK,IAAI,OAAO,GAAG,GAAG;AACxB,YAAM,QAAQ,KAAK,WAAW,QAAQ,GAAG;AACpC,WAAA,WAAW,OAAO,OAAO,CAAC;AACxB,aAAA;AAAA,IAAA;AAEF,WAAA;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAST,IAAI,KAAoB;AACf,WAAA,KAAK,IAAI,IAAI,GAAG;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMzB,QAAc;AACZ,SAAK,IAAI,MAAM;AACf,SAAK,aAAa,CAAC;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA,EAMrB,IAAI,OAAe;AACjB,WAAO,KAAK,IAAI;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQlB,EAAE,OAAO,QAAQ,IAAsC;AAC1C,eAAA,OAAO,KAAK,YAAY;AACjC,YAAM,CAAC,KAAK,KAAK,IAAI,IAAI,GAAG,CAAE;AAAA,IAAA;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQF,UAA4C;AACnC,WAAA,KAAK,OAAO,QAAQ,EAAE;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ/B,OAA+B;AAC7B,WAAO,KAAK,WAAW,OAAO,QAAQ,EAAE;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQ1C,SAAmC;AACjC,YAAO,aAA0C;AACpC,iBAAA,OAAO,KAAK,YAAY;AAC3B,cAAA,KAAK,IAAI,IAAI,GAAG;AAAA,MAAA;AAAA,IACxB,GACA,KAAK,IAAI;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQb,QACE,YACM;AACK,eAAA,OAAO,KAAK,YAAY;AACjC,iBAAW,KAAK,IAAI,IAAI,GAAG,GAAI,KAAK,KAAK,GAAG;AAAA,IAAA;AAAA,EAC9C;AAEJ;"}
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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"}
@@ -1,11 +1,17 @@
1
1
  import { Store } from '@tanstack/store';
2
2
  import { Transaction } from './transactions.js';
3
3
  import { SortedMap } from './SortedMap.js';
4
- import { ChangeListener, ChangeMessage, CollectionConfig, Fn, InsertConfig, OperationConfig, Transaction as TransactionType, UtilsRecord } from './types.js';
4
+ import { ChangeListener, ChangeMessage, CollectionConfig, CollectionStatus, Fn, InsertConfig, OperationConfig, OptimisticChangeMessage, ResolveType, Transaction as TransactionType, UtilsRecord } from './types.js';
5
+ import { StandardSchemaV1 } from '@standard-schema/spec';
5
6
  export declare const collectionsStore: Map<string, CollectionImpl<any, any>>;
7
+ interface PendingSyncedTransaction<T extends object = Record<string, unknown>> {
8
+ committed: boolean;
9
+ operations: Array<OptimisticChangeMessage<T>>;
10
+ }
6
11
  /**
7
12
  * Enhanced Collection interface that includes both data type T and utilities TUtils
8
13
  * @template T - The type of items in the collection
14
+ * @template TKey - The type of the key for the collection
9
15
  * @template TUtils - The utilities record type
10
16
  */
11
17
  export interface Collection<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TUtils extends UtilsRecord = {}> extends CollectionImpl<T, TKey> {
@@ -14,42 +20,39 @@ export interface Collection<T extends object = Record<string, unknown>, TKey ext
14
20
  /**
15
21
  * Creates a new Collection instance with the given configuration
16
22
  *
17
- * @template T - The type of items in the collection
23
+ * @template TExplicit - The explicit type of items in the collection (highest priority)
18
24
  * @template TKey - The type of the key for the collection
19
25
  * @template TUtils - The utilities record type
26
+ * @template TSchema - The schema type for validation and type inference (second priority)
27
+ * @template TFallback - The fallback type if no explicit or schema type is provided
20
28
  * @param options - Collection options with optional utilities
21
29
  * @returns A new Collection with utilities exposed both at top level and under .utils
22
- */
23
- export declare function createCollection<T extends object = Record<string, unknown>, TKey extends string | number = string | number, TUtils extends UtilsRecord = {}>(options: CollectionConfig<T, TKey> & {
24
- utils?: TUtils;
25
- }): Collection<T, TKey, TUtils>;
26
- /**
27
- * Preloads a collection with the given configuration
28
- * Returns a promise that resolves once the sync tool has done its first commit (initial sync is finished)
29
- * If the collection has already loaded, it resolves immediately
30
- *
31
- * This function is useful in route loaders or similar pre-rendering scenarios where you want
32
- * to ensure data is available before a route transition completes. It uses the same shared collection
33
- * instance that will be used by useCollection, ensuring data consistency.
34
30
  *
35
31
  * @example
36
- * ```typescript
37
- * // In a route loader
38
- * async function loader({ params }) {
39
- * await preloadCollection({
40
- * id: `users-${params.userId}`,
41
- * sync: { ... },
42
- * });
32
+ * // Using explicit type
33
+ * const todos = createCollection<Todo>({
34
+ * getKey: (todo) => todo.id,
35
+ * sync: { sync: () => {} }
36
+ * })
43
37
  *
44
- * return null;
45
- * }
46
- * ```
38
+ * // Using schema for type inference (preferred as it also gives you client side validation)
39
+ * const todoSchema = z.object({
40
+ * id: z.string(),
41
+ * title: z.string(),
42
+ * completed: z.boolean()
43
+ * })
47
44
  *
48
- * @template T - The type of items in the collection
49
- * @param config - Configuration for the collection, including id and sync
50
- * @returns Promise that resolves when the initial sync is finished
45
+ * const todos = createCollection({
46
+ * schema: todoSchema,
47
+ * getKey: (todo) => todo.id,
48
+ * sync: { sync: () => {} }
49
+ * })
50
+ *
51
+ * // Note: You must provide either an explicit type or a schema, but not both
51
52
  */
52
- export declare function preloadCollection<T extends object = Record<string, unknown>, TKey extends string | number = string | number>(config: CollectionConfig<T, TKey>): Promise<CollectionImpl<T, TKey>>;
53
+ 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> & {
54
+ utils?: TUtils;
55
+ }): Collection<ResolveType<TExplicit, TSchema, TFallback>, TKey, TUtils>;
53
56
  /**
54
57
  * Custom error class for schema validation errors
55
58
  */
@@ -65,8 +68,10 @@ export declare class SchemaValidationError extends Error {
65
68
  }>, message?: string);
66
69
  }
67
70
  export declare class CollectionImpl<T extends object = Record<string, unknown>, TKey extends string | number = string | number> {
71
+ config: CollectionConfig<T, TKey, any>;
68
72
  transactions: SortedMap<string, Transaction<any>>;
69
- syncedData: Map<TKey, T>;
73
+ pendingSyncedTransactions: Array<PendingSyncedTransaction<T>>;
74
+ syncedData: Map<TKey, T> | SortedMap<TKey, T>;
70
75
  syncedMetadata: Map<TKey, unknown>;
71
76
  derivedUpserts: Map<TKey, T>;
72
77
  derivedDeletes: Set<TKey>;
@@ -74,11 +79,17 @@ export declare class CollectionImpl<T extends object = Record<string, unknown>,
74
79
  private changeListeners;
75
80
  private changeKeyListeners;
76
81
  utils: Record<string, Fn>;
77
- private pendingSyncedTransactions;
78
82
  private syncedKeys;
79
- config: CollectionConfig<T, TKey>;
83
+ private preSyncVisibleState;
84
+ private recentlySyncedKeys;
80
85
  private hasReceivedFirstCommit;
86
+ private isCommittingSyncTransactions;
81
87
  private onFirstCommitCallbacks;
88
+ private _status;
89
+ private activeSubscribersCount;
90
+ private gcTimeoutId;
91
+ private preloadPromise;
92
+ private syncCleanupFn;
82
93
  /**
83
94
  * Register a callback to be executed on the next commit
84
95
  * Useful for preloading collections
@@ -86,13 +97,70 @@ export declare class CollectionImpl<T extends object = Record<string, unknown>,
86
97
  */
87
98
  onFirstCommit(callback: () => void): void;
88
99
  id: string;
100
+ /**
101
+ * Gets the current status of the collection
102
+ */
103
+ get status(): CollectionStatus;
104
+ /**
105
+ * Validates that the collection is in a usable state for data operations
106
+ * @private
107
+ */
108
+ private validateCollectionUsable;
109
+ /**
110
+ * Validates state transitions to prevent invalid status changes
111
+ * @private
112
+ */
113
+ private validateStatusTransition;
114
+ /**
115
+ * Safely update the collection status with validation
116
+ * @private
117
+ */
118
+ private setStatus;
89
119
  /**
90
120
  * Creates a new Collection instance
91
121
  *
92
122
  * @param config - Configuration object for the collection
93
123
  * @throws Error if sync config is missing
94
124
  */
95
- constructor(config: CollectionConfig<T, TKey>);
125
+ constructor(config: CollectionConfig<T, TKey, any>);
126
+ /**
127
+ * Start sync immediately - internal method for compiled queries
128
+ * This bypasses lazy loading for special cases like live query results
129
+ */
130
+ startSyncImmediate(): void;
131
+ /**
132
+ * Start the sync process for this collection
133
+ * This is called when the collection is first accessed or preloaded
134
+ */
135
+ private startSync;
136
+ /**
137
+ * Preload the collection data by starting sync if not already started
138
+ * Multiple concurrent calls will share the same promise
139
+ */
140
+ preload(): Promise<void>;
141
+ /**
142
+ * Clean up the collection by stopping sync and clearing data
143
+ * This can be called manually or automatically by garbage collection
144
+ */
145
+ cleanup(): Promise<void>;
146
+ /**
147
+ * Start the garbage collection timer
148
+ * Called when the collection becomes inactive (no subscribers)
149
+ */
150
+ private startGCTimer;
151
+ /**
152
+ * Cancel the garbage collection timer
153
+ * Called when the collection becomes active again
154
+ */
155
+ private cancelGCTimer;
156
+ /**
157
+ * Increment the active subscribers count and start sync if needed
158
+ */
159
+ private addSubscriber;
160
+ /**
161
+ * Decrement the active subscribers count and start GC timer if needed
162
+ */
163
+ private removeSubscriber;
96
164
  /**
97
165
  * Recompute optimistic state from active transactions
98
166
  */
@@ -145,6 +213,7 @@ export declare class CollectionImpl<T extends object = Record<string, unknown>,
145
213
  private ensureStandardSchema;
146
214
  getKeyFromItem(item: T): TKey;
147
215
  generateGlobalKey(key: any, item: any): string;
216
+ private deepEqual;
148
217
  private validateData;
149
218
  /**
150
219
  * Inserts one or more items into the collection
@@ -269,6 +338,11 @@ export declare class CollectionImpl<T extends object = Record<string, unknown>,
269
338
  subscribeChangesKey(key: TKey, listener: ChangeListener<T, TKey>, { includeInitialState }?: {
270
339
  includeInitialState?: boolean;
271
340
  }): () => void;
341
+ /**
342
+ * Capture visible state for keys that will be affected by pending sync operations
343
+ * This must be called BEFORE onTransactionStateChange clears optimistic state
344
+ */
345
+ private capturePreSyncVisibleState;
272
346
  /**
273
347
  * Trigger a recomputation when transactions change
274
348
  * This method should be called by the Transaction class when state changes
@@ -291,3 +365,4 @@ export declare class CollectionImpl<T extends object = Record<string, unknown>,
291
365
  */
292
366
  asStoreArray(): Store<Array<T>>;
293
367
  }
368
+ export {};