@tanstack/db 0.5.32 → 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 (287) 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 +24 -4
  40. package/dist/cjs/index.cjs.map +1 -1
  41. package/dist/cjs/index.d.cts +12 -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/effect.cjs +602 -0
  81. package/dist/cjs/query/effect.cjs.map +1 -0
  82. package/dist/cjs/query/effect.d.cts +94 -0
  83. package/dist/cjs/query/index.d.cts +2 -1
  84. package/dist/cjs/query/ir.cjs +18 -1
  85. package/dist/cjs/query/ir.cjs.map +1 -1
  86. package/dist/cjs/query/ir.d.cts +21 -1
  87. package/dist/cjs/query/live/collection-config-builder.cjs +493 -66
  88. package/dist/cjs/query/live/collection-config-builder.cjs.map +1 -1
  89. package/dist/cjs/query/live/collection-config-builder.d.cts +7 -0
  90. package/dist/cjs/query/live/collection-subscriber.cjs +33 -100
  91. package/dist/cjs/query/live/collection-subscriber.cjs.map +1 -1
  92. package/dist/cjs/query/live/collection-subscriber.d.cts +0 -1
  93. package/dist/cjs/query/live/types.d.cts +3 -3
  94. package/dist/cjs/query/live/utils.cjs +219 -0
  95. package/dist/cjs/query/live/utils.cjs.map +1 -0
  96. package/dist/cjs/query/live/utils.d.cts +110 -0
  97. package/dist/cjs/query/live-query-collection.cjs.map +1 -1
  98. package/dist/cjs/query/live-query-collection.d.cts +9 -6
  99. package/dist/cjs/query/query-once.cjs.map +1 -1
  100. package/dist/cjs/query/query-once.d.cts +7 -5
  101. package/dist/cjs/query/subset-dedupe.cjs +9 -3
  102. package/dist/cjs/query/subset-dedupe.cjs.map +1 -1
  103. package/dist/cjs/types.d.cts +42 -8
  104. package/dist/cjs/utils/array-utils.cjs +27 -0
  105. package/dist/cjs/utils/array-utils.cjs.map +1 -0
  106. package/dist/cjs/utils/array-utils.d.cts +16 -0
  107. package/dist/cjs/utils/comparison.cjs +11 -0
  108. package/dist/cjs/utils/comparison.cjs.map +1 -1
  109. package/dist/cjs/utils/index-optimization.cjs +4 -0
  110. package/dist/cjs/utils/index-optimization.cjs.map +1 -1
  111. package/dist/cjs/utils.cjs +7 -9
  112. package/dist/cjs/utils.cjs.map +1 -1
  113. package/dist/cjs/utils.d.cts +6 -1
  114. package/dist/cjs/virtual-props.cjs +33 -0
  115. package/dist/cjs/virtual-props.cjs.map +1 -0
  116. package/dist/cjs/virtual-props.d.cts +196 -0
  117. package/dist/esm/collection/change-events.d.ts +3 -2
  118. package/dist/esm/collection/change-events.js.map +1 -1
  119. package/dist/esm/collection/changes.d.ts +10 -1
  120. package/dist/esm/collection/changes.js +13 -4
  121. package/dist/esm/collection/changes.js.map +1 -1
  122. package/dist/esm/collection/cleanup-queue.d.ts +30 -0
  123. package/dist/esm/collection/cleanup-queue.js +89 -0
  124. package/dist/esm/collection/cleanup-queue.js.map +1 -0
  125. package/dist/esm/collection/events.d.ts +39 -1
  126. package/dist/esm/collection/events.js +14 -0
  127. package/dist/esm/collection/events.js.map +1 -1
  128. package/dist/esm/collection/index.d.ts +49 -36
  129. package/dist/esm/collection/index.js +67 -29
  130. package/dist/esm/collection/index.js.map +1 -1
  131. package/dist/esm/collection/indexes.d.ts +27 -17
  132. package/dist/esm/collection/indexes.js +211 -62
  133. package/dist/esm/collection/indexes.js.map +1 -1
  134. package/dist/esm/collection/lifecycle.d.ts +0 -1
  135. package/dist/esm/collection/lifecycle.js +5 -22
  136. package/dist/esm/collection/lifecycle.js.map +1 -1
  137. package/dist/esm/collection/mutations.d.ts +1 -0
  138. package/dist/esm/collection/mutations.js +18 -0
  139. package/dist/esm/collection/mutations.js.map +1 -1
  140. package/dist/esm/collection/state.d.ts +65 -1
  141. package/dist/esm/collection/state.js +381 -53
  142. package/dist/esm/collection/state.js.map +1 -1
  143. package/dist/esm/collection/subscription.d.ts +4 -0
  144. package/dist/esm/collection/subscription.js +6 -0
  145. package/dist/esm/collection/subscription.js.map +1 -1
  146. package/dist/esm/collection/sync.d.ts +2 -0
  147. package/dist/esm/collection/sync.js +108 -1
  148. package/dist/esm/collection/sync.js.map +1 -1
  149. package/dist/esm/collection/transaction-metadata.d.ts +1 -0
  150. package/dist/esm/collection/transaction-metadata.js +5 -0
  151. package/dist/esm/collection/transaction-metadata.js.map +1 -0
  152. package/dist/esm/errors.d.ts +3 -0
  153. package/dist/esm/errors.js +8 -0
  154. package/dist/esm/errors.js.map +1 -1
  155. package/dist/esm/index.d.ts +12 -3
  156. package/dist/esm/index.js +27 -7
  157. package/dist/esm/index.js.map +1 -1
  158. package/dist/esm/indexes/auto-index.js +13 -6
  159. package/dist/esm/indexes/auto-index.js.map +1 -1
  160. package/dist/esm/indexes/base-index.d.ts +2 -6
  161. package/dist/esm/indexes/base-index.js +1 -4
  162. package/dist/esm/indexes/base-index.js.map +1 -1
  163. package/dist/esm/indexes/basic-index.d.ts +102 -0
  164. package/dist/esm/indexes/basic-index.js +361 -0
  165. package/dist/esm/indexes/basic-index.js.map +1 -0
  166. package/dist/esm/indexes/btree-index.d.ts +1 -1
  167. package/dist/esm/indexes/btree-index.js.map +1 -1
  168. package/dist/esm/indexes/index-options.d.ts +8 -9
  169. package/dist/esm/indexes/index-registry.d.ts +61 -0
  170. package/dist/esm/indexes/index-registry.js +89 -0
  171. package/dist/esm/indexes/index-registry.js.map +1 -0
  172. package/dist/esm/local-only.js +5 -0
  173. package/dist/esm/local-only.js.map +1 -1
  174. package/dist/esm/query/builder/functions.d.ts +25 -3
  175. package/dist/esm/query/builder/functions.js +27 -11
  176. package/dist/esm/query/builder/functions.js.map +1 -1
  177. package/dist/esm/query/builder/index.d.ts +4 -3
  178. package/dist/esm/query/builder/index.js +201 -40
  179. package/dist/esm/query/builder/index.js.map +1 -1
  180. package/dist/esm/query/builder/ref-proxy.d.ts +14 -3
  181. package/dist/esm/query/builder/ref-proxy.js.map +1 -1
  182. package/dist/esm/query/builder/types.d.ts +84 -19
  183. package/dist/esm/query/compiler/evaluators.js +51 -0
  184. package/dist/esm/query/compiler/evaluators.js.map +1 -1
  185. package/dist/esm/query/compiler/group-by.d.ts +4 -2
  186. package/dist/esm/query/compiler/group-by.js +101 -29
  187. package/dist/esm/query/compiler/group-by.js.map +1 -1
  188. package/dist/esm/query/compiler/index.d.ts +30 -2
  189. package/dist/esm/query/compiler/index.js +285 -13
  190. package/dist/esm/query/compiler/index.js.map +1 -1
  191. package/dist/esm/query/compiler/order-by.d.ts +1 -1
  192. package/dist/esm/query/compiler/order-by.js +30 -11
  193. package/dist/esm/query/compiler/order-by.js.map +1 -1
  194. package/dist/esm/query/compiler/select.js +8 -0
  195. package/dist/esm/query/compiler/select.js.map +1 -1
  196. package/dist/esm/query/effect.d.ts +94 -0
  197. package/dist/esm/query/effect.js +602 -0
  198. package/dist/esm/query/effect.js.map +1 -0
  199. package/dist/esm/query/index.d.ts +2 -1
  200. package/dist/esm/query/ir.d.ts +21 -1
  201. package/dist/esm/query/ir.js +18 -1
  202. package/dist/esm/query/ir.js.map +1 -1
  203. package/dist/esm/query/live/collection-config-builder.d.ts +7 -0
  204. package/dist/esm/query/live/collection-config-builder.js +492 -65
  205. package/dist/esm/query/live/collection-config-builder.js.map +1 -1
  206. package/dist/esm/query/live/collection-subscriber.d.ts +0 -1
  207. package/dist/esm/query/live/collection-subscriber.js +31 -98
  208. package/dist/esm/query/live/collection-subscriber.js.map +1 -1
  209. package/dist/esm/query/live/types.d.ts +3 -3
  210. package/dist/esm/query/live/utils.d.ts +110 -0
  211. package/dist/esm/query/live/utils.js +219 -0
  212. package/dist/esm/query/live/utils.js.map +1 -0
  213. package/dist/esm/query/live-query-collection.d.ts +9 -6
  214. package/dist/esm/query/live-query-collection.js.map +1 -1
  215. package/dist/esm/query/query-once.d.ts +7 -5
  216. package/dist/esm/query/query-once.js.map +1 -1
  217. package/dist/esm/query/subset-dedupe.js +9 -3
  218. package/dist/esm/query/subset-dedupe.js.map +1 -1
  219. package/dist/esm/types.d.ts +42 -8
  220. package/dist/esm/utils/array-utils.d.ts +16 -0
  221. package/dist/esm/utils/array-utils.js +27 -0
  222. package/dist/esm/utils/array-utils.js.map +1 -0
  223. package/dist/esm/utils/comparison.js +11 -0
  224. package/dist/esm/utils/comparison.js.map +1 -1
  225. package/dist/esm/utils/index-optimization.js +4 -0
  226. package/dist/esm/utils/index-optimization.js.map +1 -1
  227. package/dist/esm/utils.d.ts +6 -1
  228. package/dist/esm/utils.js +7 -9
  229. package/dist/esm/utils.js.map +1 -1
  230. package/dist/esm/virtual-props.d.ts +196 -0
  231. package/dist/esm/virtual-props.js +33 -0
  232. package/dist/esm/virtual-props.js.map +1 -0
  233. package/package.json +2 -2
  234. package/skills/db-core/collection-setup/references/electric-adapter.md +1 -1
  235. package/src/collection/change-events.ts +13 -9
  236. package/src/collection/changes.ts +30 -7
  237. package/src/collection/cleanup-queue.ts +105 -0
  238. package/src/collection/events.ts +65 -0
  239. package/src/collection/index.ts +110 -45
  240. package/src/collection/indexes.ts +283 -76
  241. package/src/collection/lifecycle.ts +5 -26
  242. package/src/collection/mutations.ts +21 -0
  243. package/src/collection/state.ts +545 -71
  244. package/src/collection/subscription.ts +7 -0
  245. package/src/collection/sync.ts +137 -0
  246. package/src/collection/transaction-metadata.ts +1 -0
  247. package/src/errors.ts +9 -0
  248. package/src/index.ts +57 -3
  249. package/src/indexes/auto-index.ts +18 -8
  250. package/src/indexes/base-index.ts +2 -10
  251. package/src/indexes/basic-index.ts +507 -0
  252. package/src/indexes/btree-index.ts +1 -1
  253. package/src/indexes/index-options.ts +17 -37
  254. package/src/indexes/index-registry.ts +174 -0
  255. package/src/local-only.ts +7 -0
  256. package/src/query/builder/functions.ts +84 -7
  257. package/src/query/builder/index.ts +329 -9
  258. package/src/query/builder/ref-proxy.ts +22 -4
  259. package/src/query/builder/types.ts +257 -62
  260. package/src/query/compiler/evaluators.ts +57 -0
  261. package/src/query/compiler/group-by.ts +156 -35
  262. package/src/query/compiler/index.ts +445 -15
  263. package/src/query/compiler/order-by.ts +51 -12
  264. package/src/query/compiler/select.ts +9 -0
  265. package/src/query/effect.ts +1119 -0
  266. package/src/query/index.ts +7 -0
  267. package/src/query/ir.ts +23 -2
  268. package/src/query/live/collection-config-builder.ts +778 -104
  269. package/src/query/live/collection-subscriber.ts +40 -156
  270. package/src/query/live/types.ts +10 -4
  271. package/src/query/live/utils.ts +417 -0
  272. package/src/query/live-query-collection.ts +43 -18
  273. package/src/query/query-once.ts +31 -12
  274. package/src/query/subset-dedupe.ts +11 -7
  275. package/src/types.ts +49 -9
  276. package/src/utils/array-utils.ts +49 -0
  277. package/src/utils/comparison.ts +14 -0
  278. package/src/utils/index-optimization.ts +4 -0
  279. package/src/utils.ts +12 -9
  280. package/src/virtual-props.ts +282 -0
  281. package/dist/cjs/indexes/lazy-index.cjs +0 -190
  282. package/dist/cjs/indexes/lazy-index.cjs.map +0 -1
  283. package/dist/cjs/indexes/lazy-index.d.cts +0 -96
  284. package/dist/esm/indexes/lazy-index.d.ts +0 -96
  285. package/dist/esm/indexes/lazy-index.js +0 -190
  286. package/dist/esm/indexes/lazy-index.js.map +0 -1
  287. package/src/indexes/lazy-index.ts +0 -251
@@ -0,0 +1,507 @@
1
+ import { defaultComparator, normalizeValue } from '../utils/comparison.js'
2
+ import {
3
+ deleteInSortedArray,
4
+ findInsertPositionInArray,
5
+ } from '../utils/array-utils.js'
6
+ import { BaseIndex } from './base-index.js'
7
+ import type { CompareOptions } from '../query/builder/types.js'
8
+ import type { BasicExpression } from '../query/ir.js'
9
+ import type { IndexOperation } from './base-index.js'
10
+
11
+ /**
12
+ * Options for range queries
13
+ */
14
+ export interface RangeQueryOptions {
15
+ from?: any
16
+ to?: any
17
+ fromInclusive?: boolean
18
+ toInclusive?: boolean
19
+ }
20
+
21
+ /**
22
+ * Options for Basic index
23
+ */
24
+ export interface BasicIndexOptions {
25
+ compareFn?: (a: any, b: any) => number
26
+ compareOptions?: CompareOptions
27
+ }
28
+
29
+ /**
30
+ * Basic index using Map + sorted Array.
31
+ *
32
+ * - Map for O(1) equality lookups
33
+ * - Sorted Array for O(log n) range queries via binary search
34
+ * - O(n) updates to maintain sort order
35
+ *
36
+ * Simpler and smaller than BTreeIndex, good for read-heavy workloads.
37
+ * Use BTreeIndex for write-heavy workloads with large collections.
38
+ */
39
+ export class BasicIndex<
40
+ TKey extends string | number = string | number,
41
+ > extends BaseIndex<TKey> {
42
+ public readonly supportedOperations = new Set<IndexOperation>([
43
+ `eq`,
44
+ `gt`,
45
+ `gte`,
46
+ `lt`,
47
+ `lte`,
48
+ `in`,
49
+ ])
50
+
51
+ // Map for O(1) equality lookups: indexedValue -> Set of PKs
52
+ private valueMap = new Map<any, Set<TKey>>()
53
+ // Sorted array of unique indexed values for range queries
54
+ private sortedValues: Array<any> = []
55
+ // Set of all indexed PKs
56
+ private indexedKeys = new Set<TKey>()
57
+ // Comparator function
58
+ private compareFn: (a: any, b: any) => number = defaultComparator
59
+
60
+ constructor(
61
+ id: number,
62
+ expression: BasicExpression,
63
+ name?: string,
64
+ options?: any,
65
+ ) {
66
+ super(id, expression, name, options)
67
+ this.compareFn = options?.compareFn ?? defaultComparator
68
+ if (options?.compareOptions) {
69
+ this.compareOptions = options!.compareOptions
70
+ }
71
+ }
72
+
73
+ protected initialize(_options?: BasicIndexOptions): void {}
74
+
75
+ /**
76
+ * Adds a value to the index
77
+ */
78
+ add(key: TKey, item: any): void {
79
+ let indexedValue: any
80
+ try {
81
+ indexedValue = this.evaluateIndexExpression(item)
82
+ } catch (error) {
83
+ throw new Error(
84
+ `Failed to evaluate index expression for key ${key}: ${error}`,
85
+ { cause: error },
86
+ )
87
+ }
88
+
89
+ const normalizedValue = normalizeValue(indexedValue)
90
+
91
+ if (this.valueMap.has(normalizedValue)) {
92
+ // Value already exists, just add the key to the set
93
+ this.valueMap.get(normalizedValue)!.add(key)
94
+ } else {
95
+ // New value - add to map and insert into sorted array
96
+ this.valueMap.set(normalizedValue, new Set([key]))
97
+
98
+ // Insert into sorted position
99
+ const insertIdx = findInsertPositionInArray(
100
+ this.sortedValues,
101
+ normalizedValue,
102
+ this.compareFn,
103
+ )
104
+ this.sortedValues.splice(insertIdx, 0, normalizedValue)
105
+ }
106
+
107
+ this.indexedKeys.add(key)
108
+ this.updateTimestamp()
109
+ }
110
+
111
+ /**
112
+ * Removes a value from the index
113
+ */
114
+ remove(key: TKey, item: any): void {
115
+ let indexedValue: any
116
+ try {
117
+ indexedValue = this.evaluateIndexExpression(item)
118
+ } catch (error) {
119
+ console.warn(
120
+ `Failed to evaluate index expression for key ${key} during removal:`,
121
+ error,
122
+ )
123
+ this.indexedKeys.delete(key)
124
+ this.updateTimestamp()
125
+ return
126
+ }
127
+
128
+ const normalizedValue = normalizeValue(indexedValue)
129
+
130
+ if (this.valueMap.has(normalizedValue)) {
131
+ const keySet = this.valueMap.get(normalizedValue)!
132
+ keySet.delete(key)
133
+
134
+ if (keySet.size === 0) {
135
+ // No more keys for this value, remove from map and sorted array
136
+ this.valueMap.delete(normalizedValue)
137
+ deleteInSortedArray(this.sortedValues, normalizedValue, this.compareFn)
138
+ }
139
+ }
140
+
141
+ this.indexedKeys.delete(key)
142
+ this.updateTimestamp()
143
+ }
144
+
145
+ /**
146
+ * Updates a value in the index
147
+ */
148
+ update(key: TKey, oldItem: any, newItem: any): void {
149
+ this.remove(key, oldItem)
150
+ this.add(key, newItem)
151
+ }
152
+
153
+ /**
154
+ * Builds the index from a collection of entries
155
+ */
156
+ build(entries: Iterable<[TKey, any]>): void {
157
+ this.clear()
158
+
159
+ // Collect all entries first
160
+ const entriesArray: Array<{ key: TKey; value: any }> = []
161
+ for (const [key, item] of entries) {
162
+ let indexedValue: any
163
+ try {
164
+ indexedValue = this.evaluateIndexExpression(item)
165
+ } catch (error) {
166
+ throw new Error(
167
+ `Failed to evaluate index expression for key ${key}: ${error}`,
168
+ { cause: error },
169
+ )
170
+ }
171
+ entriesArray.push({ key, value: normalizeValue(indexedValue) })
172
+ this.indexedKeys.add(key)
173
+ }
174
+
175
+ // Group by value
176
+ for (const { key, value } of entriesArray) {
177
+ if (this.valueMap.has(value)) {
178
+ this.valueMap.get(value)!.add(key)
179
+ } else {
180
+ this.valueMap.set(value, new Set([key]))
181
+ }
182
+ }
183
+
184
+ // Build sorted array from unique values
185
+ this.sortedValues = Array.from(this.valueMap.keys()).sort(this.compareFn)
186
+
187
+ this.updateTimestamp()
188
+ }
189
+
190
+ /**
191
+ * Clears all data from the index
192
+ */
193
+ clear(): void {
194
+ this.valueMap.clear()
195
+ this.sortedValues = []
196
+ this.indexedKeys.clear()
197
+ this.updateTimestamp()
198
+ }
199
+
200
+ /**
201
+ * Performs a lookup operation
202
+ */
203
+ lookup(operation: IndexOperation, value: any): Set<TKey> {
204
+ const startTime = performance.now()
205
+
206
+ let result: Set<TKey>
207
+
208
+ switch (operation) {
209
+ case `eq`:
210
+ result = this.equalityLookup(value)
211
+ break
212
+ case `gt`:
213
+ result = this.rangeQuery({ from: value, fromInclusive: false })
214
+ break
215
+ case `gte`:
216
+ result = this.rangeQuery({ from: value, fromInclusive: true })
217
+ break
218
+ case `lt`:
219
+ result = this.rangeQuery({ to: value, toInclusive: false })
220
+ break
221
+ case `lte`:
222
+ result = this.rangeQuery({ to: value, toInclusive: true })
223
+ break
224
+ case `in`:
225
+ result = this.inArrayLookup(value)
226
+ break
227
+ default:
228
+ throw new Error(`Operation ${operation} not supported by BasicIndex`)
229
+ }
230
+
231
+ this.trackLookup(startTime)
232
+ return result
233
+ }
234
+
235
+ /**
236
+ * Gets the number of indexed keys
237
+ */
238
+ get keyCount(): number {
239
+ return this.indexedKeys.size
240
+ }
241
+
242
+ /**
243
+ * Performs an equality lookup - O(1)
244
+ */
245
+ equalityLookup(value: any): Set<TKey> {
246
+ const normalizedValue = normalizeValue(value)
247
+ return this.valueMap.get(normalizedValue) ?? new Set()
248
+ }
249
+
250
+ /**
251
+ * Performs a range query using binary search - O(log n + m)
252
+ */
253
+ rangeQuery(options: RangeQueryOptions = {}): Set<TKey> {
254
+ const { from, to, fromInclusive = true, toInclusive = true } = options
255
+ const result = new Set<TKey>()
256
+
257
+ if (this.sortedValues.length === 0) {
258
+ return result
259
+ }
260
+
261
+ const normalizedFrom = normalizeValue(from)
262
+ const normalizedTo = normalizeValue(to)
263
+
264
+ // Find start index
265
+ let startIdx = 0
266
+ if (normalizedFrom !== undefined) {
267
+ startIdx = findInsertPositionInArray(
268
+ this.sortedValues,
269
+ normalizedFrom,
270
+ this.compareFn,
271
+ )
272
+ // If not inclusive and we found exact match, skip it
273
+ if (
274
+ !fromInclusive &&
275
+ startIdx < this.sortedValues.length &&
276
+ this.compareFn(this.sortedValues[startIdx], normalizedFrom) === 0
277
+ ) {
278
+ startIdx++
279
+ }
280
+ }
281
+
282
+ // Find end index
283
+ let endIdx = this.sortedValues.length
284
+ if (normalizedTo !== undefined) {
285
+ endIdx = findInsertPositionInArray(
286
+ this.sortedValues,
287
+ normalizedTo,
288
+ this.compareFn,
289
+ )
290
+ // If inclusive and we found the value, include it
291
+ if (
292
+ toInclusive &&
293
+ endIdx < this.sortedValues.length &&
294
+ this.compareFn(this.sortedValues[endIdx], normalizedTo) === 0
295
+ ) {
296
+ endIdx++
297
+ }
298
+ }
299
+
300
+ // Collect all keys in range
301
+ for (let i = startIdx; i < endIdx; i++) {
302
+ const keys = this.valueMap.get(this.sortedValues[i])
303
+ if (keys) {
304
+ keys.forEach((key) => result.add(key))
305
+ }
306
+ }
307
+
308
+ return result
309
+ }
310
+
311
+ /**
312
+ * Performs a reversed range query
313
+ */
314
+ rangeQueryReversed(options: RangeQueryOptions = {}): Set<TKey> {
315
+ const { from, to, fromInclusive = true, toInclusive = true } = options
316
+
317
+ // Swap from/to and fromInclusive/toInclusive to handle reversed ranges
318
+ // If to is undefined, we want to start from the end (max value)
319
+ // If from is undefined, we want to end at the beginning (min value)
320
+ const swappedFrom =
321
+ to ??
322
+ (this.sortedValues.length > 0
323
+ ? this.sortedValues[this.sortedValues.length - 1]
324
+ : undefined)
325
+ const swappedTo =
326
+ from ?? (this.sortedValues.length > 0 ? this.sortedValues[0] : undefined)
327
+
328
+ return this.rangeQuery({
329
+ from: swappedFrom,
330
+ to: swappedTo,
331
+ fromInclusive: toInclusive,
332
+ toInclusive: fromInclusive,
333
+ })
334
+ }
335
+
336
+ /**
337
+ * Returns the next n items in sorted order
338
+ */
339
+ take(n: number, from?: any, filterFn?: (key: TKey) => boolean): Array<TKey> {
340
+ const result: Array<TKey> = []
341
+
342
+ let startIdx = 0
343
+ if (from !== undefined) {
344
+ const normalizedFrom = normalizeValue(from)
345
+ startIdx = findInsertPositionInArray(
346
+ this.sortedValues,
347
+ normalizedFrom,
348
+ this.compareFn,
349
+ )
350
+ // Skip past the 'from' value (exclusive)
351
+ while (
352
+ startIdx < this.sortedValues.length &&
353
+ this.compareFn(this.sortedValues[startIdx], normalizedFrom) <= 0
354
+ ) {
355
+ startIdx++
356
+ }
357
+ }
358
+
359
+ for (
360
+ let i = startIdx;
361
+ i < this.sortedValues.length && result.length < n;
362
+ i++
363
+ ) {
364
+ const keys = this.valueMap.get(this.sortedValues[i])
365
+ if (keys) {
366
+ for (const key of keys) {
367
+ if (result.length >= n) break
368
+ if (!filterFn || filterFn(key)) {
369
+ result.push(key)
370
+ }
371
+ }
372
+ }
373
+ }
374
+
375
+ return result
376
+ }
377
+
378
+ /**
379
+ * Returns the next n items in reverse sorted order
380
+ */
381
+ takeReversed(
382
+ n: number,
383
+ from?: any,
384
+ filterFn?: (key: TKey) => boolean,
385
+ ): Array<TKey> {
386
+ const result: Array<TKey> = []
387
+
388
+ let startIdx = this.sortedValues.length - 1
389
+ if (from !== undefined) {
390
+ const normalizedFrom = normalizeValue(from)
391
+ startIdx =
392
+ findInsertPositionInArray(
393
+ this.sortedValues,
394
+ normalizedFrom,
395
+ this.compareFn,
396
+ ) - 1
397
+ // Skip past the 'from' value (exclusive)
398
+ while (
399
+ startIdx >= 0 &&
400
+ this.compareFn(this.sortedValues[startIdx], normalizedFrom) >= 0
401
+ ) {
402
+ startIdx--
403
+ }
404
+ }
405
+
406
+ for (let i = startIdx; i >= 0 && result.length < n; i--) {
407
+ const keys = this.valueMap.get(this.sortedValues[i])
408
+ if (keys) {
409
+ for (const key of keys) {
410
+ if (result.length >= n) break
411
+ if (!filterFn || filterFn(key)) {
412
+ result.push(key)
413
+ }
414
+ }
415
+ }
416
+ }
417
+
418
+ return result
419
+ }
420
+
421
+ /**
422
+ * Returns the first n items in sorted order (from the start)
423
+ */
424
+ takeFromStart(n: number, filterFn?: (key: TKey) => boolean): Array<TKey> {
425
+ const result: Array<TKey> = []
426
+ for (let i = 0; i < this.sortedValues.length && result.length < n; i++) {
427
+ const keys = this.valueMap.get(this.sortedValues[i])
428
+ if (keys) {
429
+ for (const key of keys) {
430
+ if (result.length >= n) break
431
+ if (!filterFn || filterFn(key)) {
432
+ result.push(key)
433
+ }
434
+ }
435
+ }
436
+ }
437
+ return result
438
+ }
439
+
440
+ /**
441
+ * Returns the first n items in reverse sorted order (from the end)
442
+ */
443
+ takeReversedFromEnd(
444
+ n: number,
445
+ filterFn?: (key: TKey) => boolean,
446
+ ): Array<TKey> {
447
+ const result: Array<TKey> = []
448
+ for (
449
+ let i = this.sortedValues.length - 1;
450
+ i >= 0 && result.length < n;
451
+ i--
452
+ ) {
453
+ const keys = this.valueMap.get(this.sortedValues[i])
454
+ if (keys) {
455
+ for (const key of keys) {
456
+ if (result.length >= n) break
457
+ if (!filterFn || filterFn(key)) {
458
+ result.push(key)
459
+ }
460
+ }
461
+ }
462
+ }
463
+ return result
464
+ }
465
+
466
+ /**
467
+ * Performs an IN array lookup - O(k) where k is values.length
468
+ */
469
+ inArrayLookup(values: Array<any>): Set<TKey> {
470
+ const result = new Set<TKey>()
471
+
472
+ for (const value of values) {
473
+ const normalizedValue = normalizeValue(value)
474
+ const keys = this.valueMap.get(normalizedValue)
475
+ if (keys) {
476
+ keys.forEach((key) => result.add(key))
477
+ }
478
+ }
479
+
480
+ return result
481
+ }
482
+
483
+ // Getter methods for testing/compatibility
484
+ get indexedKeysSet(): Set<TKey> {
485
+ return this.indexedKeys
486
+ }
487
+
488
+ get orderedEntriesArray(): Array<[any, Set<TKey>]> {
489
+ return this.sortedValues.map((value) => [
490
+ value,
491
+ this.valueMap.get(value) ?? new Set(),
492
+ ])
493
+ }
494
+
495
+ get orderedEntriesArrayReversed(): Array<[any, Set<TKey>]> {
496
+ const result: Array<[any, Set<TKey>]> = []
497
+ for (let i = this.sortedValues.length - 1; i >= 0; i--) {
498
+ const value = this.sortedValues[i]
499
+ result.push([value, this.valueMap.get(value) ?? new Set()])
500
+ }
501
+ return result
502
+ }
503
+
504
+ get valueMapData(): Map<any, Set<TKey>> {
505
+ return this.valueMap
506
+ }
507
+ }
@@ -33,7 +33,7 @@ export interface RangeQueryOptions {
33
33
  * This maintains items in sorted order and provides efficient range operations
34
34
  */
35
35
  export class BTreeIndex<
36
- TKey extends string | number | undefined = string | number | undefined,
36
+ TKey extends string | number = string | number,
37
37
  > extends BaseIndex<TKey> {
38
38
  public readonly supportedOperations = new Set<IndexOperation>([
39
39
  `eq`,
@@ -1,42 +1,22 @@
1
- import type { IndexConstructor, IndexResolver } from './base-index.js'
1
+ import type { IndexConstructor } from './base-index.js'
2
2
 
3
3
  /**
4
- * Enhanced index options that support both sync and async resolvers
4
+ * Options for creating an index
5
5
  */
6
- export interface IndexOptions<TResolver extends IndexResolver = IndexResolver> {
6
+ export interface IndexOptions<
7
+ TIndexType extends IndexConstructor = IndexConstructor,
8
+ > {
9
+ /** Optional name for the index */
7
10
  name?: string
8
- indexType?: TResolver
9
- options?: TResolver extends IndexConstructor<any>
10
- ? TResolver extends new (
11
- id: number,
12
- expr: any,
13
- name?: string,
14
- options?: infer O,
15
- ) => any
16
- ? O
17
- : never
18
- : TResolver extends () => Promise<infer TCtor>
19
- ? TCtor extends new (
20
- id: number,
21
- expr: any,
22
- name?: string,
23
- options?: infer O,
24
- ) => any
25
- ? O
26
- : never
27
- : never
11
+ /** Index type to use (e.g., BasicIndex, BTreeIndex) */
12
+ indexType?: TIndexType
13
+ /** Options passed to the index constructor */
14
+ options?: TIndexType extends new (
15
+ id: number,
16
+ expr: any,
17
+ name?: string,
18
+ options?: infer O,
19
+ ) => any
20
+ ? O
21
+ : never
28
22
  }
29
-
30
- /**
31
- * Utility type to extract the constructed index type from a resolver
32
- */
33
- export type ResolvedIndexType<TResolver extends IndexResolver> =
34
- TResolver extends IndexConstructor<any>
35
- ? InstanceType<TResolver>
36
- : TResolver extends () => Promise<IndexConstructor<any>>
37
- ? TResolver extends () => Promise<infer TCtor>
38
- ? TCtor extends IndexConstructor<any>
39
- ? InstanceType<TCtor>
40
- : never
41
- : never
42
- : never