@tanstack/db 0.5.33 → 0.6.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.
Files changed (273) hide show
  1. package/dist/cjs/collection/change-events.cjs.map +1 -1
  2. package/dist/cjs/collection/change-events.d.cts +3 -2
  3. package/dist/cjs/collection/changes.cjs +13 -4
  4. package/dist/cjs/collection/changes.cjs.map +1 -1
  5. package/dist/cjs/collection/changes.d.cts +10 -1
  6. package/dist/cjs/collection/cleanup-queue.cjs +89 -0
  7. package/dist/cjs/collection/cleanup-queue.cjs.map +1 -0
  8. package/dist/cjs/collection/cleanup-queue.d.cts +30 -0
  9. package/dist/cjs/collection/events.cjs +14 -0
  10. package/dist/cjs/collection/events.cjs.map +1 -1
  11. package/dist/cjs/collection/events.d.cts +39 -1
  12. package/dist/cjs/collection/index.cjs +66 -28
  13. package/dist/cjs/collection/index.cjs.map +1 -1
  14. package/dist/cjs/collection/index.d.cts +49 -36
  15. package/dist/cjs/collection/indexes.cjs +211 -62
  16. package/dist/cjs/collection/indexes.cjs.map +1 -1
  17. package/dist/cjs/collection/indexes.d.cts +27 -17
  18. package/dist/cjs/collection/lifecycle.cjs +5 -22
  19. package/dist/cjs/collection/lifecycle.cjs.map +1 -1
  20. package/dist/cjs/collection/lifecycle.d.cts +0 -1
  21. package/dist/cjs/collection/mutations.cjs +18 -0
  22. package/dist/cjs/collection/mutations.cjs.map +1 -1
  23. package/dist/cjs/collection/mutations.d.cts +1 -0
  24. package/dist/cjs/collection/state.cjs +381 -53
  25. package/dist/cjs/collection/state.cjs.map +1 -1
  26. package/dist/cjs/collection/state.d.cts +65 -1
  27. package/dist/cjs/collection/subscription.cjs +6 -0
  28. package/dist/cjs/collection/subscription.cjs.map +1 -1
  29. package/dist/cjs/collection/subscription.d.cts +4 -0
  30. package/dist/cjs/collection/sync.cjs +108 -1
  31. package/dist/cjs/collection/sync.cjs.map +1 -1
  32. package/dist/cjs/collection/sync.d.cts +2 -0
  33. package/dist/cjs/collection/transaction-metadata.cjs +5 -0
  34. package/dist/cjs/collection/transaction-metadata.cjs.map +1 -0
  35. package/dist/cjs/collection/transaction-metadata.d.cts +1 -0
  36. package/dist/cjs/errors.cjs +8 -0
  37. package/dist/cjs/errors.cjs.map +1 -1
  38. package/dist/cjs/errors.d.cts +3 -0
  39. package/dist/cjs/index.cjs +22 -4
  40. package/dist/cjs/index.cjs.map +1 -1
  41. package/dist/cjs/index.d.cts +11 -3
  42. package/dist/cjs/indexes/auto-index.cjs +13 -6
  43. package/dist/cjs/indexes/auto-index.cjs.map +1 -1
  44. package/dist/cjs/indexes/base-index.cjs +0 -3
  45. package/dist/cjs/indexes/base-index.cjs.map +1 -1
  46. package/dist/cjs/indexes/base-index.d.cts +2 -6
  47. package/dist/cjs/indexes/basic-index.cjs +361 -0
  48. package/dist/cjs/indexes/basic-index.cjs.map +1 -0
  49. package/dist/cjs/indexes/basic-index.d.cts +102 -0
  50. package/dist/cjs/indexes/btree-index.cjs.map +1 -1
  51. package/dist/cjs/indexes/btree-index.d.cts +1 -1
  52. package/dist/cjs/indexes/index-options.d.cts +8 -9
  53. package/dist/cjs/indexes/index-registry.cjs +89 -0
  54. package/dist/cjs/indexes/index-registry.cjs.map +1 -0
  55. package/dist/cjs/indexes/index-registry.d.cts +61 -0
  56. package/dist/cjs/local-only.cjs +5 -0
  57. package/dist/cjs/local-only.cjs.map +1 -1
  58. package/dist/cjs/query/builder/functions.cjs +27 -11
  59. package/dist/cjs/query/builder/functions.cjs.map +1 -1
  60. package/dist/cjs/query/builder/functions.d.cts +25 -3
  61. package/dist/cjs/query/builder/index.cjs +200 -39
  62. package/dist/cjs/query/builder/index.cjs.map +1 -1
  63. package/dist/cjs/query/builder/index.d.cts +4 -3
  64. package/dist/cjs/query/builder/ref-proxy.cjs.map +1 -1
  65. package/dist/cjs/query/builder/ref-proxy.d.cts +14 -3
  66. package/dist/cjs/query/builder/types.d.cts +84 -19
  67. package/dist/cjs/query/compiler/evaluators.cjs +51 -0
  68. package/dist/cjs/query/compiler/evaluators.cjs.map +1 -1
  69. package/dist/cjs/query/compiler/group-by.cjs +100 -28
  70. package/dist/cjs/query/compiler/group-by.cjs.map +1 -1
  71. package/dist/cjs/query/compiler/group-by.d.cts +4 -2
  72. package/dist/cjs/query/compiler/index.cjs +283 -11
  73. package/dist/cjs/query/compiler/index.cjs.map +1 -1
  74. package/dist/cjs/query/compiler/index.d.cts +30 -2
  75. package/dist/cjs/query/compiler/order-by.cjs +29 -10
  76. package/dist/cjs/query/compiler/order-by.cjs.map +1 -1
  77. package/dist/cjs/query/compiler/order-by.d.cts +1 -1
  78. package/dist/cjs/query/compiler/select.cjs +8 -0
  79. package/dist/cjs/query/compiler/select.cjs.map +1 -1
  80. package/dist/cjs/query/index.d.cts +2 -1
  81. package/dist/cjs/query/ir.cjs +18 -1
  82. package/dist/cjs/query/ir.cjs.map +1 -1
  83. package/dist/cjs/query/ir.d.cts +21 -1
  84. package/dist/cjs/query/live/collection-config-builder.cjs +501 -5
  85. package/dist/cjs/query/live/collection-config-builder.cjs.map +1 -1
  86. package/dist/cjs/query/live/collection-config-builder.d.cts +7 -0
  87. package/dist/cjs/query/live/types.d.cts +3 -3
  88. package/dist/cjs/query/live/utils.cjs +43 -3
  89. package/dist/cjs/query/live/utils.cjs.map +1 -1
  90. package/dist/cjs/query/live/utils.d.cts +1 -0
  91. package/dist/cjs/query/live-query-collection.cjs.map +1 -1
  92. package/dist/cjs/query/live-query-collection.d.cts +9 -6
  93. package/dist/cjs/query/query-once.cjs.map +1 -1
  94. package/dist/cjs/query/query-once.d.cts +7 -5
  95. package/dist/cjs/query/subset-dedupe.cjs +9 -3
  96. package/dist/cjs/query/subset-dedupe.cjs.map +1 -1
  97. package/dist/cjs/types.d.cts +42 -8
  98. package/dist/cjs/utils/array-utils.cjs +27 -0
  99. package/dist/cjs/utils/array-utils.cjs.map +1 -0
  100. package/dist/cjs/utils/array-utils.d.cts +16 -0
  101. package/dist/cjs/utils/comparison.cjs +11 -0
  102. package/dist/cjs/utils/comparison.cjs.map +1 -1
  103. package/dist/cjs/utils/index-optimization.cjs +4 -0
  104. package/dist/cjs/utils/index-optimization.cjs.map +1 -1
  105. package/dist/cjs/utils.cjs +7 -9
  106. package/dist/cjs/utils.cjs.map +1 -1
  107. package/dist/cjs/utils.d.cts +6 -1
  108. package/dist/cjs/virtual-props.cjs +33 -0
  109. package/dist/cjs/virtual-props.cjs.map +1 -0
  110. package/dist/cjs/virtual-props.d.cts +196 -0
  111. package/dist/esm/collection/change-events.d.ts +3 -2
  112. package/dist/esm/collection/change-events.js.map +1 -1
  113. package/dist/esm/collection/changes.d.ts +10 -1
  114. package/dist/esm/collection/changes.js +13 -4
  115. package/dist/esm/collection/changes.js.map +1 -1
  116. package/dist/esm/collection/cleanup-queue.d.ts +30 -0
  117. package/dist/esm/collection/cleanup-queue.js +89 -0
  118. package/dist/esm/collection/cleanup-queue.js.map +1 -0
  119. package/dist/esm/collection/events.d.ts +39 -1
  120. package/dist/esm/collection/events.js +14 -0
  121. package/dist/esm/collection/events.js.map +1 -1
  122. package/dist/esm/collection/index.d.ts +49 -36
  123. package/dist/esm/collection/index.js +67 -29
  124. package/dist/esm/collection/index.js.map +1 -1
  125. package/dist/esm/collection/indexes.d.ts +27 -17
  126. package/dist/esm/collection/indexes.js +211 -62
  127. package/dist/esm/collection/indexes.js.map +1 -1
  128. package/dist/esm/collection/lifecycle.d.ts +0 -1
  129. package/dist/esm/collection/lifecycle.js +5 -22
  130. package/dist/esm/collection/lifecycle.js.map +1 -1
  131. package/dist/esm/collection/mutations.d.ts +1 -0
  132. package/dist/esm/collection/mutations.js +18 -0
  133. package/dist/esm/collection/mutations.js.map +1 -1
  134. package/dist/esm/collection/state.d.ts +65 -1
  135. package/dist/esm/collection/state.js +381 -53
  136. package/dist/esm/collection/state.js.map +1 -1
  137. package/dist/esm/collection/subscription.d.ts +4 -0
  138. package/dist/esm/collection/subscription.js +6 -0
  139. package/dist/esm/collection/subscription.js.map +1 -1
  140. package/dist/esm/collection/sync.d.ts +2 -0
  141. package/dist/esm/collection/sync.js +108 -1
  142. package/dist/esm/collection/sync.js.map +1 -1
  143. package/dist/esm/collection/transaction-metadata.d.ts +1 -0
  144. package/dist/esm/collection/transaction-metadata.js +5 -0
  145. package/dist/esm/collection/transaction-metadata.js.map +1 -0
  146. package/dist/esm/errors.d.ts +3 -0
  147. package/dist/esm/errors.js +8 -0
  148. package/dist/esm/errors.js.map +1 -1
  149. package/dist/esm/index.d.ts +11 -3
  150. package/dist/esm/index.js +25 -7
  151. package/dist/esm/index.js.map +1 -1
  152. package/dist/esm/indexes/auto-index.js +13 -6
  153. package/dist/esm/indexes/auto-index.js.map +1 -1
  154. package/dist/esm/indexes/base-index.d.ts +2 -6
  155. package/dist/esm/indexes/base-index.js +1 -4
  156. package/dist/esm/indexes/base-index.js.map +1 -1
  157. package/dist/esm/indexes/basic-index.d.ts +102 -0
  158. package/dist/esm/indexes/basic-index.js +361 -0
  159. package/dist/esm/indexes/basic-index.js.map +1 -0
  160. package/dist/esm/indexes/btree-index.d.ts +1 -1
  161. package/dist/esm/indexes/btree-index.js.map +1 -1
  162. package/dist/esm/indexes/index-options.d.ts +8 -9
  163. package/dist/esm/indexes/index-registry.d.ts +61 -0
  164. package/dist/esm/indexes/index-registry.js +89 -0
  165. package/dist/esm/indexes/index-registry.js.map +1 -0
  166. package/dist/esm/local-only.js +5 -0
  167. package/dist/esm/local-only.js.map +1 -1
  168. package/dist/esm/query/builder/functions.d.ts +25 -3
  169. package/dist/esm/query/builder/functions.js +27 -11
  170. package/dist/esm/query/builder/functions.js.map +1 -1
  171. package/dist/esm/query/builder/index.d.ts +4 -3
  172. package/dist/esm/query/builder/index.js +201 -40
  173. package/dist/esm/query/builder/index.js.map +1 -1
  174. package/dist/esm/query/builder/ref-proxy.d.ts +14 -3
  175. package/dist/esm/query/builder/ref-proxy.js.map +1 -1
  176. package/dist/esm/query/builder/types.d.ts +84 -19
  177. package/dist/esm/query/compiler/evaluators.js +51 -0
  178. package/dist/esm/query/compiler/evaluators.js.map +1 -1
  179. package/dist/esm/query/compiler/group-by.d.ts +4 -2
  180. package/dist/esm/query/compiler/group-by.js +101 -29
  181. package/dist/esm/query/compiler/group-by.js.map +1 -1
  182. package/dist/esm/query/compiler/index.d.ts +30 -2
  183. package/dist/esm/query/compiler/index.js +285 -13
  184. package/dist/esm/query/compiler/index.js.map +1 -1
  185. package/dist/esm/query/compiler/order-by.d.ts +1 -1
  186. package/dist/esm/query/compiler/order-by.js +30 -11
  187. package/dist/esm/query/compiler/order-by.js.map +1 -1
  188. package/dist/esm/query/compiler/select.js +8 -0
  189. package/dist/esm/query/compiler/select.js.map +1 -1
  190. package/dist/esm/query/index.d.ts +2 -1
  191. package/dist/esm/query/ir.d.ts +21 -1
  192. package/dist/esm/query/ir.js +18 -1
  193. package/dist/esm/query/ir.js.map +1 -1
  194. package/dist/esm/query/live/collection-config-builder.d.ts +7 -0
  195. package/dist/esm/query/live/collection-config-builder.js +503 -7
  196. package/dist/esm/query/live/collection-config-builder.js.map +1 -1
  197. package/dist/esm/query/live/types.d.ts +3 -3
  198. package/dist/esm/query/live/utils.d.ts +1 -0
  199. package/dist/esm/query/live/utils.js +43 -3
  200. package/dist/esm/query/live/utils.js.map +1 -1
  201. package/dist/esm/query/live-query-collection.d.ts +9 -6
  202. package/dist/esm/query/live-query-collection.js.map +1 -1
  203. package/dist/esm/query/query-once.d.ts +7 -5
  204. package/dist/esm/query/query-once.js.map +1 -1
  205. package/dist/esm/query/subset-dedupe.js +9 -3
  206. package/dist/esm/query/subset-dedupe.js.map +1 -1
  207. package/dist/esm/types.d.ts +42 -8
  208. package/dist/esm/utils/array-utils.d.ts +16 -0
  209. package/dist/esm/utils/array-utils.js +27 -0
  210. package/dist/esm/utils/array-utils.js.map +1 -0
  211. package/dist/esm/utils/comparison.js +11 -0
  212. package/dist/esm/utils/comparison.js.map +1 -1
  213. package/dist/esm/utils/index-optimization.js +4 -0
  214. package/dist/esm/utils/index-optimization.js.map +1 -1
  215. package/dist/esm/utils.d.ts +6 -1
  216. package/dist/esm/utils.js +7 -9
  217. package/dist/esm/utils.js.map +1 -1
  218. package/dist/esm/virtual-props.d.ts +196 -0
  219. package/dist/esm/virtual-props.js +33 -0
  220. package/dist/esm/virtual-props.js.map +1 -0
  221. package/package.json +2 -2
  222. package/skills/db-core/collection-setup/references/electric-adapter.md +1 -1
  223. package/src/collection/change-events.ts +13 -9
  224. package/src/collection/changes.ts +30 -7
  225. package/src/collection/cleanup-queue.ts +105 -0
  226. package/src/collection/events.ts +65 -0
  227. package/src/collection/index.ts +110 -45
  228. package/src/collection/indexes.ts +283 -76
  229. package/src/collection/lifecycle.ts +5 -26
  230. package/src/collection/mutations.ts +21 -0
  231. package/src/collection/state.ts +545 -71
  232. package/src/collection/subscription.ts +7 -0
  233. package/src/collection/sync.ts +137 -0
  234. package/src/collection/transaction-metadata.ts +1 -0
  235. package/src/errors.ts +9 -0
  236. package/src/index.ts +46 -3
  237. package/src/indexes/auto-index.ts +18 -8
  238. package/src/indexes/base-index.ts +2 -10
  239. package/src/indexes/basic-index.ts +507 -0
  240. package/src/indexes/btree-index.ts +1 -1
  241. package/src/indexes/index-options.ts +17 -37
  242. package/src/indexes/index-registry.ts +174 -0
  243. package/src/local-only.ts +7 -0
  244. package/src/query/builder/functions.ts +84 -7
  245. package/src/query/builder/index.ts +329 -9
  246. package/src/query/builder/ref-proxy.ts +22 -4
  247. package/src/query/builder/types.ts +257 -62
  248. package/src/query/compiler/evaluators.ts +57 -0
  249. package/src/query/compiler/group-by.ts +156 -35
  250. package/src/query/compiler/index.ts +445 -15
  251. package/src/query/compiler/order-by.ts +51 -12
  252. package/src/query/compiler/select.ts +9 -0
  253. package/src/query/index.ts +7 -0
  254. package/src/query/ir.ts +23 -2
  255. package/src/query/live/collection-config-builder.ts +809 -9
  256. package/src/query/live/types.ts +10 -4
  257. package/src/query/live/utils.ts +64 -3
  258. package/src/query/live-query-collection.ts +43 -18
  259. package/src/query/query-once.ts +31 -12
  260. package/src/query/subset-dedupe.ts +11 -7
  261. package/src/types.ts +49 -9
  262. package/src/utils/array-utils.ts +49 -0
  263. package/src/utils/comparison.ts +14 -0
  264. package/src/utils/index-optimization.ts +4 -0
  265. package/src/utils.ts +12 -9
  266. package/src/virtual-props.ts +282 -0
  267. package/dist/cjs/indexes/lazy-index.cjs +0 -190
  268. package/dist/cjs/indexes/lazy-index.cjs.map +0 -1
  269. package/dist/cjs/indexes/lazy-index.d.cts +0 -96
  270. package/dist/esm/indexes/lazy-index.d.ts +0 -96
  271. package/dist/esm/indexes/lazy-index.js +0 -190
  272. package/dist/esm/indexes/lazy-index.js.map +0 -1
  273. package/src/indexes/lazy-index.ts +0 -251
@@ -0,0 +1,89 @@
1
+ const _CleanupQueue = class _CleanupQueue {
2
+ constructor() {
3
+ this.tasks = /* @__PURE__ */ new Map();
4
+ this.timeoutId = null;
5
+ this.microtaskScheduled = false;
6
+ }
7
+ static getInstance() {
8
+ if (!_CleanupQueue.instance) {
9
+ _CleanupQueue.instance = new _CleanupQueue();
10
+ }
11
+ return _CleanupQueue.instance;
12
+ }
13
+ /**
14
+ * Queues a cleanup task and defers timeout selection to a microtask so
15
+ * multiple synchronous registrations can share one root timer.
16
+ */
17
+ schedule(key, gcTime, callback) {
18
+ const executeAt = Date.now() + gcTime;
19
+ this.tasks.set(key, { executeAt, callback });
20
+ if (!this.microtaskScheduled) {
21
+ this.microtaskScheduled = true;
22
+ Promise.resolve().then(() => {
23
+ this.microtaskScheduled = false;
24
+ this.updateTimeout();
25
+ });
26
+ }
27
+ }
28
+ cancel(key) {
29
+ this.tasks.delete(key);
30
+ }
31
+ /**
32
+ * Keeps only one active timeout: whichever task is due next.
33
+ */
34
+ updateTimeout() {
35
+ if (this.timeoutId !== null) {
36
+ clearTimeout(this.timeoutId);
37
+ this.timeoutId = null;
38
+ }
39
+ if (this.tasks.size === 0) {
40
+ return;
41
+ }
42
+ let earliestTime = Infinity;
43
+ for (const task of this.tasks.values()) {
44
+ if (task.executeAt < earliestTime) {
45
+ earliestTime = task.executeAt;
46
+ }
47
+ }
48
+ const delay = Math.max(0, earliestTime - Date.now());
49
+ this.timeoutId = setTimeout(() => this.process(), delay);
50
+ }
51
+ /**
52
+ * Runs every task whose deadline has passed, then schedules the next wakeup
53
+ * if there is still pending work.
54
+ */
55
+ process() {
56
+ this.timeoutId = null;
57
+ const now = Date.now();
58
+ for (const [key, task] of this.tasks.entries()) {
59
+ if (now >= task.executeAt) {
60
+ this.tasks.delete(key);
61
+ try {
62
+ task.callback();
63
+ } catch (error) {
64
+ console.error("Error in CleanupQueue task:", error);
65
+ }
66
+ }
67
+ }
68
+ if (this.tasks.size > 0) {
69
+ this.updateTimeout();
70
+ }
71
+ }
72
+ /**
73
+ * Resets the singleton instance for tests.
74
+ */
75
+ static resetInstance() {
76
+ if (_CleanupQueue.instance) {
77
+ if (_CleanupQueue.instance.timeoutId !== null) {
78
+ clearTimeout(_CleanupQueue.instance.timeoutId);
79
+ }
80
+ _CleanupQueue.instance = null;
81
+ }
82
+ }
83
+ };
84
+ _CleanupQueue.instance = null;
85
+ let CleanupQueue = _CleanupQueue;
86
+ export {
87
+ CleanupQueue
88
+ };
89
+ //# sourceMappingURL=cleanup-queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cleanup-queue.js","sources":["../../../src/collection/cleanup-queue.ts"],"sourcesContent":["type CleanupTask = {\n executeAt: number\n callback: () => void\n}\n\n/**\n * Batches many GC registrations behind a single shared timeout.\n */\nexport class CleanupQueue {\n private static instance: CleanupQueue | null = null\n\n private tasks: Map<unknown, CleanupTask> = new Map()\n\n private timeoutId: ReturnType<typeof setTimeout> | null = null\n private microtaskScheduled = false\n\n private constructor() {}\n\n public static getInstance(): CleanupQueue {\n if (!CleanupQueue.instance) {\n CleanupQueue.instance = new CleanupQueue()\n }\n return CleanupQueue.instance\n }\n\n /**\n * Queues a cleanup task and defers timeout selection to a microtask so\n * multiple synchronous registrations can share one root timer.\n */\n public schedule(key: unknown, gcTime: number, callback: () => void): void {\n const executeAt = Date.now() + gcTime\n this.tasks.set(key, { executeAt, callback })\n\n if (!this.microtaskScheduled) {\n this.microtaskScheduled = true\n Promise.resolve().then(() => {\n this.microtaskScheduled = false\n this.updateTimeout()\n })\n }\n }\n\n public cancel(key: unknown): void {\n this.tasks.delete(key)\n }\n\n /**\n * Keeps only one active timeout: whichever task is due next.\n */\n private updateTimeout(): void {\n if (this.timeoutId !== null) {\n clearTimeout(this.timeoutId)\n this.timeoutId = null\n }\n\n if (this.tasks.size === 0) {\n return\n }\n\n let earliestTime = Infinity\n for (const task of this.tasks.values()) {\n if (task.executeAt < earliestTime) {\n earliestTime = task.executeAt\n }\n }\n\n const delay = Math.max(0, earliestTime - Date.now())\n this.timeoutId = setTimeout(() => this.process(), delay)\n }\n\n /**\n * Runs every task whose deadline has passed, then schedules the next wakeup\n * if there is still pending work.\n */\n private process(): void {\n this.timeoutId = null\n const now = Date.now()\n for (const [key, task] of this.tasks.entries()) {\n if (now >= task.executeAt) {\n this.tasks.delete(key)\n try {\n task.callback()\n } catch (error) {\n console.error('Error in CleanupQueue task:', error)\n }\n }\n }\n\n if (this.tasks.size > 0) {\n this.updateTimeout()\n }\n }\n\n /**\n * Resets the singleton instance for tests.\n */\n public static resetInstance(): void {\n if (CleanupQueue.instance) {\n if (CleanupQueue.instance.timeoutId !== null) {\n clearTimeout(CleanupQueue.instance.timeoutId)\n }\n CleanupQueue.instance = null\n }\n }\n}\n"],"names":[],"mappings":"AAQO,MAAM,gBAAN,MAAM,cAAa;AAAA,EAQhB,cAAc;AALtB,SAAQ,4BAAuC,IAAA;AAE/C,SAAQ,YAAkD;AAC1D,SAAQ,qBAAqB;AAAA,EAEN;AAAA,EAEvB,OAAc,cAA4B;AACxC,QAAI,CAAC,cAAa,UAAU;AAC1B,oBAAa,WAAW,IAAI,cAAA;AAAA,IAC9B;AACA,WAAO,cAAa;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMO,SAAS,KAAc,QAAgB,UAA4B;AACxE,UAAM,YAAY,KAAK,IAAA,IAAQ;AAC/B,SAAK,MAAM,IAAI,KAAK,EAAE,WAAW,UAAU;AAE3C,QAAI,CAAC,KAAK,oBAAoB;AAC5B,WAAK,qBAAqB;AAC1B,cAAQ,UAAU,KAAK,MAAM;AAC3B,aAAK,qBAAqB;AAC1B,aAAK,cAAA;AAAA,MACP,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEO,OAAO,KAAoB;AAChC,SAAK,MAAM,OAAO,GAAG;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA,EAKQ,gBAAsB;AAC5B,QAAI,KAAK,cAAc,MAAM;AAC3B,mBAAa,KAAK,SAAS;AAC3B,WAAK,YAAY;AAAA,IACnB;AAEA,QAAI,KAAK,MAAM,SAAS,GAAG;AACzB;AAAA,IACF;AAEA,QAAI,eAAe;AACnB,eAAW,QAAQ,KAAK,MAAM,OAAA,GAAU;AACtC,UAAI,KAAK,YAAY,cAAc;AACjC,uBAAe,KAAK;AAAA,MACtB;AAAA,IACF;AAEA,UAAM,QAAQ,KAAK,IAAI,GAAG,eAAe,KAAK,KAAK;AACnD,SAAK,YAAY,WAAW,MAAM,KAAK,QAAA,GAAW,KAAK;AAAA,EACzD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,UAAgB;AACtB,SAAK,YAAY;AACjB,UAAM,MAAM,KAAK,IAAA;AACjB,eAAW,CAAC,KAAK,IAAI,KAAK,KAAK,MAAM,WAAW;AAC9C,UAAI,OAAO,KAAK,WAAW;AACzB,aAAK,MAAM,OAAO,GAAG;AACrB,YAAI;AACF,eAAK,SAAA;AAAA,QACP,SAAS,OAAO;AACd,kBAAQ,MAAM,+BAA+B,KAAK;AAAA,QACpD;AAAA,MACF;AAAA,IACF;AAEA,QAAI,KAAK,MAAM,OAAO,GAAG;AACvB,WAAK,cAAA;AAAA,IACP;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,OAAc,gBAAsB;AAClC,QAAI,cAAa,UAAU;AACzB,UAAI,cAAa,SAAS,cAAc,MAAM;AAC5C,qBAAa,cAAa,SAAS,SAAS;AAAA,MAC9C;AACA,oBAAa,WAAW;AAAA,IAC1B;AAAA,EACF;AACF;AA/FE,cAAe,WAAgC;AAD1C,IAAM,eAAN;"}
@@ -1,6 +1,7 @@
1
1
  import { EventEmitter } from '../event-emitter.js';
2
2
  import { Collection } from './index.js';
3
3
  import { CollectionStatus } from '../types.js';
4
+ import { BasicExpression } from '../query/ir.js';
4
5
  /**
5
6
  * Event emitted when the collection status changes
6
7
  */
@@ -45,15 +46,50 @@ export interface CollectionTruncateEvent {
45
46
  type: `truncate`;
46
47
  collection: Collection<any, any, any, any, any>;
47
48
  }
49
+ export type CollectionIndexSerializableValue = string | number | boolean | null | Array<CollectionIndexSerializableValue> | {
50
+ [key: string]: CollectionIndexSerializableValue;
51
+ };
52
+ export interface CollectionIndexResolverMetadata {
53
+ kind: `constructor` | `async`;
54
+ name?: string;
55
+ }
56
+ export interface CollectionIndexMetadata {
57
+ /**
58
+ * Version for the signature serialization contract.
59
+ */
60
+ signatureVersion: 1;
61
+ /**
62
+ * Stable signature derived from expression + serializable options.
63
+ * Non-serializable option fields are intentionally omitted.
64
+ */
65
+ signature: string;
66
+ indexId: number;
67
+ name?: string;
68
+ expression: BasicExpression;
69
+ resolver: CollectionIndexResolverMetadata;
70
+ options?: CollectionIndexSerializableValue;
71
+ }
72
+ export interface CollectionIndexAddedEvent {
73
+ type: `index:added`;
74
+ collection: Collection<any, any, any, any, any>;
75
+ index: CollectionIndexMetadata;
76
+ }
77
+ export interface CollectionIndexRemovedEvent {
78
+ type: `index:removed`;
79
+ collection: Collection<any, any, any, any, any>;
80
+ index: CollectionIndexMetadata;
81
+ }
48
82
  export type AllCollectionEvents = {
49
83
  'status:change': CollectionStatusChangeEvent;
50
84
  'subscribers:change': CollectionSubscribersChangeEvent;
51
85
  'loadingSubset:change': CollectionLoadingSubsetChangeEvent;
52
86
  truncate: CollectionTruncateEvent;
87
+ 'index:added': CollectionIndexAddedEvent;
88
+ 'index:removed': CollectionIndexRemovedEvent;
53
89
  } & {
54
90
  [K in CollectionStatus as `status:${K}`]: CollectionStatusEvent<K>;
55
91
  };
56
- export type CollectionEvent = AllCollectionEvents[keyof AllCollectionEvents] | CollectionStatusChangeEvent | CollectionSubscribersChangeEvent | CollectionLoadingSubsetChangeEvent | CollectionTruncateEvent;
92
+ export type CollectionEvent = AllCollectionEvents[keyof AllCollectionEvents] | CollectionStatusChangeEvent | CollectionSubscribersChangeEvent | CollectionLoadingSubsetChangeEvent | CollectionTruncateEvent | CollectionIndexAddedEvent | CollectionIndexRemovedEvent;
57
93
  export type CollectionEventHandler<T extends keyof AllCollectionEvents> = (event: AllCollectionEvents[T]) => void;
58
94
  export declare class CollectionEventsManager extends EventEmitter<AllCollectionEvents> {
59
95
  private collection;
@@ -68,5 +104,7 @@ export declare class CollectionEventsManager extends EventEmitter<AllCollectionE
68
104
  emit<T extends keyof AllCollectionEvents>(event: T, eventPayload: AllCollectionEvents[T]): void;
69
105
  emitStatusChange<T extends CollectionStatus>(status: T, previousStatus: CollectionStatus): void;
70
106
  emitSubscribersChange(subscriberCount: number, previousSubscriberCount: number): void;
107
+ emitIndexAdded(index: CollectionIndexMetadata): void;
108
+ emitIndexRemoved(index: CollectionIndexMetadata): void;
71
109
  cleanup(): void;
72
110
  }
@@ -36,6 +36,20 @@ class CollectionEventsManager extends EventEmitter {
36
36
  subscriberCount
37
37
  });
38
38
  }
39
+ emitIndexAdded(index) {
40
+ this.emit(`index:added`, {
41
+ type: `index:added`,
42
+ collection: this.collection,
43
+ index
44
+ });
45
+ }
46
+ emitIndexRemoved(index) {
47
+ this.emit(`index:removed`, {
48
+ type: `index:removed`,
49
+ collection: this.collection,
50
+ index
51
+ });
52
+ }
39
53
  cleanup() {
40
54
  this.clearListeners();
41
55
  }
@@ -1 +1 @@
1
- {"version":3,"file":"events.js","sources":["../../../src/collection/events.ts"],"sourcesContent":["import { EventEmitter } from '../event-emitter.js'\nimport 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\n/**\n * Event emitted when the collection's loading more state changes\n */\nexport interface CollectionLoadingSubsetChangeEvent {\n type: `loadingSubset:change`\n collection: Collection<any, any, any, any, any>\n isLoadingSubset: boolean\n previousIsLoadingSubset: boolean\n loadingSubsetTransition: `start` | `end`\n}\n\n/**\n * Event emitted when the collection is truncated (all data cleared)\n */\nexport interface CollectionTruncateEvent {\n type: `truncate`\n collection: Collection<any, any, any, any, any>\n}\n\nexport type AllCollectionEvents = {\n 'status:change': CollectionStatusChangeEvent\n 'subscribers:change': CollectionSubscribersChangeEvent\n 'loadingSubset:change': CollectionLoadingSubsetChangeEvent\n truncate: CollectionTruncateEvent\n} & {\n [K in CollectionStatus as `status:${K}`]: CollectionStatusEvent<K>\n}\n\nexport type CollectionEvent =\n | AllCollectionEvents[keyof AllCollectionEvents]\n | CollectionStatusChangeEvent\n | CollectionSubscribersChangeEvent\n | CollectionLoadingSubsetChangeEvent\n | CollectionTruncateEvent\n\nexport type CollectionEventHandler<T extends keyof AllCollectionEvents> = (\n event: AllCollectionEvents[T],\n) => void\n\nexport class CollectionEventsManager extends EventEmitter<AllCollectionEvents> {\n private collection!: Collection<any, any, any, any, any>\n\n constructor() {\n super()\n }\n\n setDeps(deps: { collection: Collection<any, any, any, any, any> }) {\n this.collection = deps.collection\n }\n\n /**\n * Emit an event to all listeners\n * Public API for emitting collection events\n */\n emit<T extends keyof AllCollectionEvents>(\n event: T,\n eventPayload: AllCollectionEvents[T],\n ): void {\n this.emitInner(event, eventPayload)\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.clearListeners()\n }\n}\n"],"names":[],"mappings":";AAyEO,MAAM,gCAAgC,aAAkC;AAAA,EAG7E,cAAc;AACZ,UAAA;AAAA,EACF;AAAA,EAEA,QAAQ,MAA2D;AACjE,SAAK,aAAa,KAAK;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,KACE,OACA,cACM;AACN,SAAK,UAAU,OAAO,YAAY;AAAA,EACpC;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,eAAA;AAAA,EACP;AACF;"}
1
+ {"version":3,"file":"events.js","sources":["../../../src/collection/events.ts"],"sourcesContent":["import { EventEmitter } from '../event-emitter.js'\nimport type { Collection } from './index.js'\nimport type { CollectionStatus } from '../types.js'\nimport type { BasicExpression } from '../query/ir.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\n/**\n * Event emitted when the collection's loading more state changes\n */\nexport interface CollectionLoadingSubsetChangeEvent {\n type: `loadingSubset:change`\n collection: Collection<any, any, any, any, any>\n isLoadingSubset: boolean\n previousIsLoadingSubset: boolean\n loadingSubsetTransition: `start` | `end`\n}\n\n/**\n * Event emitted when the collection is truncated (all data cleared)\n */\nexport interface CollectionTruncateEvent {\n type: `truncate`\n collection: Collection<any, any, any, any, any>\n}\n\nexport type CollectionIndexSerializableValue =\n | string\n | number\n | boolean\n | null\n | Array<CollectionIndexSerializableValue>\n | {\n [key: string]: CollectionIndexSerializableValue\n }\n\nexport interface CollectionIndexResolverMetadata {\n kind: `constructor` | `async`\n name?: string\n}\n\nexport interface CollectionIndexMetadata {\n /**\n * Version for the signature serialization contract.\n */\n signatureVersion: 1\n /**\n * Stable signature derived from expression + serializable options.\n * Non-serializable option fields are intentionally omitted.\n */\n signature: string\n indexId: number\n name?: string\n expression: BasicExpression\n resolver: CollectionIndexResolverMetadata\n options?: CollectionIndexSerializableValue\n}\n\nexport interface CollectionIndexAddedEvent {\n type: `index:added`\n collection: Collection<any, any, any, any, any>\n index: CollectionIndexMetadata\n}\n\nexport interface CollectionIndexRemovedEvent {\n type: `index:removed`\n collection: Collection<any, any, any, any, any>\n index: CollectionIndexMetadata\n}\n\nexport type AllCollectionEvents = {\n 'status:change': CollectionStatusChangeEvent\n 'subscribers:change': CollectionSubscribersChangeEvent\n 'loadingSubset:change': CollectionLoadingSubsetChangeEvent\n truncate: CollectionTruncateEvent\n 'index:added': CollectionIndexAddedEvent\n 'index:removed': CollectionIndexRemovedEvent\n} & {\n [K in CollectionStatus as `status:${K}`]: CollectionStatusEvent<K>\n}\n\nexport type CollectionEvent =\n | AllCollectionEvents[keyof AllCollectionEvents]\n | CollectionStatusChangeEvent\n | CollectionSubscribersChangeEvent\n | CollectionLoadingSubsetChangeEvent\n | CollectionTruncateEvent\n | CollectionIndexAddedEvent\n | CollectionIndexRemovedEvent\n\nexport type CollectionEventHandler<T extends keyof AllCollectionEvents> = (\n event: AllCollectionEvents[T],\n) => void\n\nexport class CollectionEventsManager extends EventEmitter<AllCollectionEvents> {\n private collection!: Collection<any, any, any, any, any>\n\n constructor() {\n super()\n }\n\n setDeps(deps: { collection: Collection<any, any, any, any, any> }) {\n this.collection = deps.collection\n }\n\n /**\n * Emit an event to all listeners\n * Public API for emitting collection events\n */\n emit<T extends keyof AllCollectionEvents>(\n event: T,\n eventPayload: AllCollectionEvents[T],\n ): void {\n this.emitInner(event, eventPayload)\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 emitIndexAdded(index: CollectionIndexMetadata) {\n this.emit(`index:added`, {\n type: `index:added`,\n collection: this.collection,\n index,\n })\n }\n\n emitIndexRemoved(index: CollectionIndexMetadata) {\n this.emit(`index:removed`, {\n type: `index:removed`,\n collection: this.collection,\n index,\n })\n }\n\n cleanup() {\n this.clearListeners()\n }\n}\n"],"names":[],"mappings":";AA0HO,MAAM,gCAAgC,aAAkC;AAAA,EAG7E,cAAc;AACZ,UAAA;AAAA,EACF;AAAA,EAEA,QAAQ,MAA2D;AACjE,SAAK,aAAa,KAAK;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,KACE,OACA,cACM;AACN,SAAK,UAAU,OAAO,YAAY;AAAA,EACpC;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,eAAe,OAAgC;AAC7C,SAAK,KAAK,eAAe;AAAA,MACvB,MAAM;AAAA,MACN,YAAY,KAAK;AAAA,MACjB;AAAA,IAAA,CACD;AAAA,EACH;AAAA,EAEA,iBAAiB,OAAgC;AAC/C,SAAK,KAAK,iBAAiB;AAAA,MACzB,MAAM;AAAA,MACN,YAAY,KAAK;AAAA,MACjB;AAAA,IAAA,CACD;AAAA,EACH;AAAA,EAEA,UAAU;AACR,SAAK,eAAA;AAAA,EACP;AACF;"}
@@ -2,14 +2,14 @@ import { CollectionStateManager } from './state.js';
2
2
  import { CollectionLifecycleManager } from './lifecycle.js';
3
3
  import { CollectionSyncManager } from './sync.js';
4
4
  import { CollectionSubscription } from './subscription.js';
5
- import { AllCollectionEvents, CollectionEventHandler } from './events.js';
6
- import { BaseIndex, IndexResolver } from '../indexes/base-index.js';
5
+ import { AllCollectionEvents, CollectionEventHandler, CollectionIndexMetadata } from './events.js';
6
+ import { BaseIndex, IndexConstructor } from '../indexes/base-index.js';
7
7
  import { IndexOptions } from '../indexes/index-options.js';
8
8
  import { ChangeMessage, CollectionConfig, CollectionStatus, CurrentStateAsChangesOptions, Fn, InferSchemaInput, InferSchemaOutput, InsertConfig, NonSingleResult, OperationConfig, SingleResult, StringCollationConfig, SubscribeChangesOptions, Transaction as TransactionType, UtilsRecord, WritableDeep } from '../types.js';
9
9
  import { SingleRowRefProxy } from '../query/builder/ref-proxy.js';
10
10
  import { StandardSchemaV1 } from '@standard-schema/spec';
11
- import { BTreeIndex } from '../indexes/btree-index.js';
12
- import { IndexProxy } from '../indexes/lazy-index.js';
11
+ import { WithVirtualProps } from '../virtual-props.js';
12
+ export type { CollectionIndexMetadata } from './events.js';
13
13
  /**
14
14
  * Enhanced Collection interface that includes both data type T and utilities TUtils
15
15
  * @template T - The type of items in the collection
@@ -130,6 +130,12 @@ export declare class CollectionImpl<TOutput extends object = Record<string, unkn
130
130
  private _indexes;
131
131
  private _mutations;
132
132
  _state: CollectionStateManager<TOutput, TKey, TSchema, TInput>;
133
+ /**
134
+ * When set, collection consumers should defer processing incoming data
135
+ * refreshes until this promise resolves. This prevents stale data from
136
+ * overwriting optimistic state while pending writes are being applied.
137
+ */
138
+ deferDataRefresh: Promise<void> | null;
133
139
  private comparisonOpts;
134
140
  /**
135
141
  * Creates a new Collection instance
@@ -188,7 +194,7 @@ export declare class CollectionImpl<TOutput extends object = Record<string, unkn
188
194
  /**
189
195
  * Get the current value for a key (virtual derived state)
190
196
  */
191
- get(key: TKey): TOutput | undefined;
197
+ get(key: TKey): WithVirtualProps<TOutput, TKey> | undefined;
192
198
  /**
193
199
  * Check if a key exists in the collection (virtual derived state)
194
200
  */
@@ -204,58 +210,65 @@ export declare class CollectionImpl<TOutput extends object = Record<string, unkn
204
210
  /**
205
211
  * Get all values (virtual derived state)
206
212
  */
207
- values(): IterableIterator<TOutput>;
213
+ values(): IterableIterator<WithVirtualProps<TOutput, TKey>>;
208
214
  /**
209
215
  * Get all entries (virtual derived state)
210
216
  */
211
- entries(): IterableIterator<[TKey, TOutput]>;
217
+ entries(): IterableIterator<[TKey, WithVirtualProps<TOutput, TKey>]>;
212
218
  /**
213
219
  * Get all entries (virtual derived state)
214
220
  */
215
- [Symbol.iterator](): IterableIterator<[TKey, TOutput]>;
221
+ [Symbol.iterator](): IterableIterator<[
222
+ TKey,
223
+ WithVirtualProps<TOutput, TKey>
224
+ ]>;
216
225
  /**
217
226
  * Execute a callback for each entry in the collection
218
227
  */
219
- forEach(callbackfn: (value: TOutput, key: TKey, index: number) => void): void;
228
+ forEach(callbackfn: (value: WithVirtualProps<TOutput, TKey>, key: TKey, index: number) => void): void;
220
229
  /**
221
230
  * Create a new array with the results of calling a function for each entry in the collection
222
231
  */
223
- map<U>(callbackfn: (value: TOutput, key: TKey, index: number) => U): Array<U>;
232
+ map<U>(callbackfn: (value: WithVirtualProps<TOutput, TKey>, key: TKey, index: number) => U): Array<U>;
224
233
  getKeyFromItem(item: TOutput): TKey;
225
234
  /**
226
235
  * Creates an index on a collection for faster queries.
227
236
  * Indexes significantly improve query performance by allowing constant time lookups
228
237
  * and logarithmic time range queries instead of full scans.
229
238
  *
230
- * @template TResolver - The type of the index resolver (constructor or async loader)
231
239
  * @param indexCallback - Function that extracts the indexed value from each item
232
240
  * @param config - Configuration including index type and type-specific options
233
- * @returns An index proxy that provides access to the index when ready
241
+ * @returns The created index
234
242
  *
235
243
  * @example
236
- * // Create a default B+ tree index
237
- * const ageIndex = collection.createIndex((row) => row.age)
244
+ * ```ts
245
+ * import { BasicIndex } from '@tanstack/db'
238
246
  *
239
- * // Create a ordered index with custom options
247
+ * // Create an index with explicit type
240
248
  * const ageIndex = collection.createIndex((row) => row.age, {
241
- * indexType: BTreeIndex,
242
- * options: {
243
- * compareFn: customComparator,
244
- * compareOptions: { direction: 'asc', nulls: 'first', stringSort: 'lexical' }
245
- * },
246
- * name: 'age_btree'
249
+ * indexType: BasicIndex
247
250
  * })
248
251
  *
249
- * // Create an async-loaded index
250
- * const textIndex = collection.createIndex((row) => row.content, {
251
- * indexType: async () => {
252
- * const { FullTextIndex } = await import('./indexes/fulltext.js')
253
- * return FullTextIndex
254
- * },
255
- * options: { language: 'en' }
256
- * })
252
+ * // Create an index with collection's default type
253
+ * const nameIndex = collection.createIndex((row) => row.name)
254
+ * ```
255
+ */
256
+ createIndex<TIndexType extends IndexConstructor<TKey>>(indexCallback: (row: SingleRowRefProxy<TOutput>) => any, config?: IndexOptions<TIndexType>): BaseIndex<TKey>;
257
+ /**
258
+ * Removes an index created with createIndex.
259
+ * Returns true when an index existed and was removed.
260
+ *
261
+ * Best-effort semantics: removing an index guarantees it is detached from
262
+ * collection query planning. Existing index proxy references should be treated
263
+ * as invalid after removal.
264
+ */
265
+ removeIndex(indexOrId: BaseIndex<TKey> | number): boolean;
266
+ /**
267
+ * Returns a snapshot of current index metadata sorted by indexId.
268
+ * Persistence wrappers can use this to bootstrap index state if indexes were
269
+ * created before event listeners were attached.
257
270
  */
258
- createIndex<TResolver extends IndexResolver<TKey> = typeof BTreeIndex>(indexCallback: (row: SingleRowRefProxy<TOutput>) => any, config?: IndexOptions<TResolver>): IndexProxy<TKey>;
271
+ getIndexMetadata(): Array<CollectionIndexMetadata>;
259
272
  /**
260
273
  * Get resolved indexes for query optimization
261
274
  */
@@ -392,27 +405,27 @@ export declare class CollectionImpl<TOutput extends object = Record<string, unkn
392
405
  * console.log("Todo 1 exists:", itemsMap.get("todo-1"))
393
406
  * }
394
407
  */
395
- get state(): Map<TKey, TOutput>;
408
+ get state(): Map<TKey, WithVirtualProps<TOutput, TKey>>;
396
409
  /**
397
410
  * Gets the current state of the collection as a Map, but only resolves when data is available
398
411
  * Waits for the first sync commit to complete before resolving
399
412
  *
400
413
  * @returns Promise that resolves to a Map containing all items in the collection
401
414
  */
402
- stateWhenReady(): Promise<Map<TKey, TOutput>>;
415
+ stateWhenReady(): Promise<Map<TKey, WithVirtualProps<TOutput, TKey>>>;
403
416
  /**
404
417
  * Gets the current state of the collection as an Array
405
418
  *
406
419
  * @returns An Array containing all items in the collection
407
420
  */
408
- get toArray(): TOutput[];
421
+ get toArray(): WithVirtualProps<TOutput, TKey>[];
409
422
  /**
410
423
  * Gets the current state of the collection as an Array, but only resolves when data is available
411
424
  * Waits for the first sync commit to complete before resolving
412
425
  *
413
426
  * @returns Promise that resolves to an Array containing all items in the collection
414
427
  */
415
- toArrayWhenReady(): Promise<Array<TOutput>>;
428
+ toArrayWhenReady(): Promise<Array<WithVirtualProps<TOutput, TKey>>>;
416
429
  /**
417
430
  * Returns the current state of the collection as an array of changes
418
431
  * @param options - Options including optional where filter
@@ -431,7 +444,7 @@ export declare class CollectionImpl<TOutput extends object = Record<string, unkn
431
444
  * whereExpression: eq(row.status, 'active')
432
445
  * })
433
446
  */
434
- currentStateAsChanges(options?: CurrentStateAsChangesOptions): Array<ChangeMessage<TOutput>> | void;
447
+ currentStateAsChanges(options?: CurrentStateAsChangesOptions): Array<ChangeMessage<WithVirtualProps<TOutput, TKey>>> | void;
435
448
  /**
436
449
  * Subscribe to changes in the collection
437
450
  * @param callback - Function called when items change
@@ -474,7 +487,7 @@ export declare class CollectionImpl<TOutput extends object = Record<string, unkn
474
487
  * where: (row) => and(eq(row.status, "active"), gt(row.priority, 5))
475
488
  * })
476
489
  */
477
- subscribeChanges(callback: (changes: Array<ChangeMessage<TOutput>>) => void, options?: SubscribeChangesOptions<TOutput>): CollectionSubscription;
490
+ subscribeChanges(callback: (changes: Array<ChangeMessage<WithVirtualProps<TOutput, TKey>>>) => void, options?: SubscribeChangesOptions<TOutput, TKey>): CollectionSubscription;
478
491
  /**
479
492
  * Subscribe to a collection event
480
493
  */
@@ -1,4 +1,4 @@
1
- import { CollectionRequiresConfigError, CollectionRequiresSyncConfigError } from "../errors.js";
1
+ import { CollectionRequiresConfigError, CollectionRequiresSyncConfigError, CollectionConfigurationError } from "../errors.js";
2
2
  import { currentStateAsChanges } from "./change-events.js";
3
3
  import { CollectionStateManager } from "./state.js";
4
4
  import { CollectionChangesManager } from "./changes.js";
@@ -27,6 +27,7 @@ class CollectionImpl {
27
27
  */
28
28
  constructor(config) {
29
29
  this.utils = {};
30
+ this.deferDataRefresh = null;
30
31
  this.insert = (data, config2) => {
31
32
  return this._mutations.insert(data, config2);
32
33
  };
@@ -46,8 +47,15 @@ class CollectionImpl {
46
47
  }
47
48
  this.config = {
48
49
  ...config,
49
- autoIndex: config.autoIndex ?? `eager`
50
+ autoIndex: config.autoIndex ?? `off`
50
51
  };
52
+ if (this.config.autoIndex === `eager` && !config.defaultIndexType) {
53
+ throw new CollectionConfigurationError(
54
+ `autoIndex: 'eager' requires defaultIndexType to be set. Import an index type and set it:
55
+ import { BasicIndex } from '@tanstack/db'
56
+ createCollection({ defaultIndexType: BasicIndex, autoIndex: 'eager', ... })`
57
+ );
58
+ }
51
59
  this._changes = new CollectionChangesManager();
52
60
  this._events = new CollectionEventsManager();
53
61
  this._indexes = new CollectionIndexesManager();
@@ -61,7 +69,9 @@ class CollectionImpl {
61
69
  // Required for passing to CollectionSubscription
62
70
  lifecycle: this._lifecycle,
63
71
  sync: this._sync,
64
- events: this._events
72
+ events: this._events,
73
+ state: this._state
74
+ // Required for enriching changes with virtual properties
65
75
  });
66
76
  this._events.setDeps({
67
77
  collection: this
@@ -69,7 +79,9 @@ class CollectionImpl {
69
79
  });
70
80
  this._indexes.setDeps({
71
81
  state: this._state,
72
- lifecycle: this._lifecycle
82
+ lifecycle: this._lifecycle,
83
+ defaultIndexType: config.defaultIndexType,
84
+ events: this._events
73
85
  });
74
86
  this._lifecycle.setDeps({
75
87
  changes: this._changes,
@@ -168,7 +180,7 @@ class CollectionImpl {
168
180
  * Get the current value for a key (virtual derived state)
169
181
  */
170
182
  get(key) {
171
- return this._state.get(key);
183
+ return this._state.getWithVirtualProps(key);
172
184
  }
173
185
  /**
174
186
  * Check if a key exists in the collection (virtual derived state)
@@ -192,31 +204,49 @@ class CollectionImpl {
192
204
  * Get all values (virtual derived state)
193
205
  */
194
206
  *values() {
195
- yield* this._state.values();
207
+ for (const key of this._state.keys()) {
208
+ const value = this.get(key);
209
+ if (value !== void 0) {
210
+ yield value;
211
+ }
212
+ }
196
213
  }
197
214
  /**
198
215
  * Get all entries (virtual derived state)
199
216
  */
200
217
  *entries() {
201
- yield* this._state.entries();
218
+ for (const key of this._state.keys()) {
219
+ const value = this.get(key);
220
+ if (value !== void 0) {
221
+ yield [key, value];
222
+ }
223
+ }
202
224
  }
203
225
  /**
204
226
  * Get all entries (virtual derived state)
205
227
  */
206
228
  *[Symbol.iterator]() {
207
- yield* this._state[Symbol.iterator]();
229
+ yield* this.entries();
208
230
  }
209
231
  /**
210
232
  * Execute a callback for each entry in the collection
211
233
  */
212
234
  forEach(callbackfn) {
213
- return this._state.forEach(callbackfn);
235
+ let index = 0;
236
+ for (const [key, value] of this.entries()) {
237
+ callbackfn(value, key, index++);
238
+ }
214
239
  }
215
240
  /**
216
241
  * Create a new array with the results of calling a function for each entry in the collection
217
242
  */
218
243
  map(callbackfn) {
219
- return this._state.map(callbackfn);
244
+ const result = [];
245
+ let index = 0;
246
+ for (const [key, value] of this.entries()) {
247
+ result.push(callbackfn(value, key, index++));
248
+ }
249
+ return result;
220
250
  }
221
251
  getKeyFromItem(item) {
222
252
  return this.config.getKey(item);
@@ -226,37 +256,45 @@ class CollectionImpl {
226
256
  * Indexes significantly improve query performance by allowing constant time lookups
227
257
  * and logarithmic time range queries instead of full scans.
228
258
  *
229
- * @template TResolver - The type of the index resolver (constructor or async loader)
230
259
  * @param indexCallback - Function that extracts the indexed value from each item
231
260
  * @param config - Configuration including index type and type-specific options
232
- * @returns An index proxy that provides access to the index when ready
261
+ * @returns The created index
233
262
  *
234
263
  * @example
235
- * // Create a default B+ tree index
236
- * const ageIndex = collection.createIndex((row) => row.age)
264
+ * ```ts
265
+ * import { BasicIndex } from '@tanstack/db'
237
266
  *
238
- * // Create a ordered index with custom options
267
+ * // Create an index with explicit type
239
268
  * const ageIndex = collection.createIndex((row) => row.age, {
240
- * indexType: BTreeIndex,
241
- * options: {
242
- * compareFn: customComparator,
243
- * compareOptions: { direction: 'asc', nulls: 'first', stringSort: 'lexical' }
244
- * },
245
- * name: 'age_btree'
269
+ * indexType: BasicIndex
246
270
  * })
247
271
  *
248
- * // Create an async-loaded index
249
- * const textIndex = collection.createIndex((row) => row.content, {
250
- * indexType: async () => {
251
- * const { FullTextIndex } = await import('./indexes/fulltext.js')
252
- * return FullTextIndex
253
- * },
254
- * options: { language: 'en' }
255
- * })
272
+ * // Create an index with collection's default type
273
+ * const nameIndex = collection.createIndex((row) => row.name)
274
+ * ```
256
275
  */
257
276
  createIndex(indexCallback, config = {}) {
258
277
  return this._indexes.createIndex(indexCallback, config);
259
278
  }
279
+ /**
280
+ * Removes an index created with createIndex.
281
+ * Returns true when an index existed and was removed.
282
+ *
283
+ * Best-effort semantics: removing an index guarantees it is detached from
284
+ * collection query planning. Existing index proxy references should be treated
285
+ * as invalid after removal.
286
+ */
287
+ removeIndex(indexOrId) {
288
+ return this._indexes.removeIndex(indexOrId);
289
+ }
290
+ /**
291
+ * Returns a snapshot of current index metadata sorted by indexId.
292
+ * Persistence wrappers can use this to bootstrap index state if indexes were
293
+ * created before event listeners were attached.
294
+ */
295
+ getIndexMetadata() {
296
+ return this._indexes.getIndexMetadataSnapshot();
297
+ }
260
298
  /**
261
299
  * Get resolved indexes for query optimization
262
300
  */