@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
@@ -1,12 +1,13 @@
1
1
  import { DEFAULT_COMPARE_OPTIONS } from "../utils.js";
2
- import { BTreeIndex } from "./btree-index.js";
2
+ import { hasVirtualPropPath } from "../virtual-props.js";
3
+ import { isDevModeEnabled, checkCollectionSizeForIndex } from "./index-registry.js";
3
4
  function shouldAutoIndex(collection) {
4
- if (collection.config.autoIndex !== `eager`) {
5
- return false;
6
- }
7
- return true;
5
+ return collection.config.autoIndex === `eager`;
8
6
  }
9
7
  function ensureIndexForField(fieldName, fieldPath, collection, compareOptions, compareFn) {
8
+ if (hasVirtualPropPath(fieldPath)) {
9
+ return;
10
+ }
10
11
  if (!shouldAutoIndex(collection)) {
11
12
  return;
12
13
  }
@@ -20,6 +21,13 @@ function ensureIndexForField(fieldName, fieldPath, collection, compareOptions, c
20
21
  if (existingIndex) {
21
22
  return;
22
23
  }
24
+ if (isDevModeEnabled()) {
25
+ checkCollectionSizeForIndex(
26
+ collection.id || `unknown`,
27
+ collection.size,
28
+ fieldPath
29
+ );
30
+ }
23
31
  try {
24
32
  collection.createIndex(
25
33
  (row) => {
@@ -31,7 +39,6 @@ function ensureIndexForField(fieldName, fieldPath, collection, compareOptions, c
31
39
  },
32
40
  {
33
41
  name: `auto:${fieldPath.join(`.`)}`,
34
- indexType: BTreeIndex,
35
42
  options: compareFn ? { compareFn, compareOptions: compareOpts } : {}
36
43
  }
37
44
  );
@@ -1 +1 @@
1
- {"version":3,"file":"auto-index.js","sources":["../../../src/indexes/auto-index.ts"],"sourcesContent":["import { DEFAULT_COMPARE_OPTIONS } from '../utils'\nimport { BTreeIndex } from './btree-index'\nimport type { CompareOptions } from '../query/builder/types'\nimport type { BasicExpression } from '../query/ir'\nimport type { CollectionImpl } from '../collection/index.js'\n\nexport interface AutoIndexConfig {\n autoIndex?: `off` | `eager`\n}\n\nfunction shouldAutoIndex(collection: CollectionImpl<any, any, any, any, any>) {\n // Only proceed if auto-indexing is enabled\n if (collection.config.autoIndex !== `eager`) {\n return false\n }\n\n return true\n}\n\nexport function ensureIndexForField<\n T extends Record<string, any>,\n TKey extends string | number,\n>(\n fieldName: string,\n fieldPath: Array<string>,\n collection: CollectionImpl<T, TKey, any, any, any>,\n compareOptions?: CompareOptions,\n compareFn?: (a: any, b: any) => number,\n) {\n if (!shouldAutoIndex(collection)) {\n return\n }\n\n const compareOpts = compareOptions ?? {\n ...DEFAULT_COMPARE_OPTIONS,\n ...collection.compareOptions,\n }\n\n // Check if we already have an index for this field\n const existingIndex = Array.from(collection.indexes.values()).find(\n (index) =>\n index.matchesField(fieldPath) && index.matchesCompareOptions(compareOpts),\n )\n\n if (existingIndex) {\n return // Index already exists\n }\n\n // Create a new index for this field using the collection's createIndex method\n try {\n // Use the proxy-based approach to create the proper accessor for nested paths\n collection.createIndex(\n (row) => {\n // Navigate through the field path\n let current: any = row\n for (const part of fieldPath) {\n current = current[part]\n }\n return current\n },\n {\n name: `auto:${fieldPath.join(`.`)}`,\n indexType: BTreeIndex,\n options: compareFn ? { compareFn, compareOptions: compareOpts } : {},\n },\n )\n } catch (error) {\n console.warn(\n `${collection.id ? `[${collection.id}] ` : ``}Failed to create auto-index for field path \"${fieldPath.join(`.`)}\":`,\n error,\n )\n }\n}\n\n/**\n * Analyzes a where expression and creates indexes for all simple operations on single fields\n */\nexport function ensureIndexForExpression<\n T extends Record<string, any>,\n TKey extends string | number,\n>(\n expression: BasicExpression,\n collection: CollectionImpl<T, TKey, any, any, any>,\n): void {\n if (!shouldAutoIndex(collection)) {\n return\n }\n\n // Extract all indexable expressions and create indexes for them\n const indexableExpressions = extractIndexableExpressions(expression)\n\n for (const { fieldName, fieldPath } of indexableExpressions) {\n ensureIndexForField(fieldName, fieldPath, collection)\n }\n}\n\n/**\n * Extracts all indexable expressions from a where expression\n */\nfunction extractIndexableExpressions(\n expression: BasicExpression,\n): Array<{ fieldName: string; fieldPath: Array<string> }> {\n const results: Array<{ fieldName: string; fieldPath: Array<string> }> = []\n\n function extractFromExpression(expr: BasicExpression): void {\n if (expr.type !== `func`) {\n return\n }\n\n const func = expr as any\n\n // Handle 'and' expressions by recursively processing all arguments\n if (func.name === `and`) {\n for (const arg of func.args) {\n extractFromExpression(arg)\n }\n return\n }\n\n // Check if this is a supported operation\n const supportedOperations = [`eq`, `gt`, `gte`, `lt`, `lte`, `in`]\n if (!supportedOperations.includes(func.name)) {\n return\n }\n\n // Check if the first argument is a property reference\n if (func.args.length < 1 || func.args[0].type !== `ref`) {\n return\n }\n\n const fieldRef = func.args[0]\n const fieldPath = fieldRef.path\n\n // Skip if the path is empty\n if (fieldPath.length === 0) {\n return\n }\n\n // For nested paths, use the full path joined with underscores as the field name\n // For simple paths, use the first (and only) element\n const fieldName = fieldPath.join(`_`)\n results.push({ fieldName, fieldPath })\n }\n\n extractFromExpression(expression)\n return results\n}\n"],"names":[],"mappings":";;AAUA,SAAS,gBAAgB,YAAqD;AAE5E,MAAI,WAAW,OAAO,cAAc,SAAS;AAC3C,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAEO,SAAS,oBAId,WACA,WACA,YACA,gBACA,WACA;AACA,MAAI,CAAC,gBAAgB,UAAU,GAAG;AAChC;AAAA,EACF;AAEA,QAAM,cAAc,kBAAkB;AAAA,IACpC,GAAG;AAAA,IACH,GAAG,WAAW;AAAA,EAAA;AAIhB,QAAM,gBAAgB,MAAM,KAAK,WAAW,QAAQ,OAAA,CAAQ,EAAE;AAAA,IAC5D,CAAC,UACC,MAAM,aAAa,SAAS,KAAK,MAAM,sBAAsB,WAAW;AAAA,EAAA;AAG5E,MAAI,eAAe;AACjB;AAAA,EACF;AAGA,MAAI;AAEF,eAAW;AAAA,MACT,CAAC,QAAQ;AAEP,YAAI,UAAe;AACnB,mBAAW,QAAQ,WAAW;AAC5B,oBAAU,QAAQ,IAAI;AAAA,QACxB;AACA,eAAO;AAAA,MACT;AAAA,MACA;AAAA,QACE,MAAM,QAAQ,UAAU,KAAK,GAAG,CAAC;AAAA,QACjC,WAAW;AAAA,QACX,SAAS,YAAY,EAAE,WAAW,gBAAgB,YAAA,IAAgB,CAAA;AAAA,MAAC;AAAA,IACrE;AAAA,EAEJ,SAAS,OAAO;AACd,YAAQ;AAAA,MACN,GAAG,WAAW,KAAK,IAAI,WAAW,EAAE,OAAO,EAAE,+CAA+C,UAAU,KAAK,GAAG,CAAC;AAAA,MAC/G;AAAA,IAAA;AAAA,EAEJ;AACF;AAKO,SAAS,yBAId,YACA,YACM;AACN,MAAI,CAAC,gBAAgB,UAAU,GAAG;AAChC;AAAA,EACF;AAGA,QAAM,uBAAuB,4BAA4B,UAAU;AAEnE,aAAW,EAAE,WAAW,UAAA,KAAe,sBAAsB;AAC3D,wBAAoB,WAAW,WAAW,UAAU;AAAA,EACtD;AACF;AAKA,SAAS,4BACP,YACwD;AACxD,QAAM,UAAkE,CAAA;AAExE,WAAS,sBAAsB,MAA6B;AAC1D,QAAI,KAAK,SAAS,QAAQ;AACxB;AAAA,IACF;AAEA,UAAM,OAAO;AAGb,QAAI,KAAK,SAAS,OAAO;AACvB,iBAAW,OAAO,KAAK,MAAM;AAC3B,8BAAsB,GAAG;AAAA,MAC3B;AACA;AAAA,IACF;AAGA,UAAM,sBAAsB,CAAC,MAAM,MAAM,OAAO,MAAM,OAAO,IAAI;AACjE,QAAI,CAAC,oBAAoB,SAAS,KAAK,IAAI,GAAG;AAC5C;AAAA,IACF;AAGA,QAAI,KAAK,KAAK,SAAS,KAAK,KAAK,KAAK,CAAC,EAAE,SAAS,OAAO;AACvD;AAAA,IACF;AAEA,UAAM,WAAW,KAAK,KAAK,CAAC;AAC5B,UAAM,YAAY,SAAS;AAG3B,QAAI,UAAU,WAAW,GAAG;AAC1B;AAAA,IACF;AAIA,UAAM,YAAY,UAAU,KAAK,GAAG;AACpC,YAAQ,KAAK,EAAE,WAAW,UAAA,CAAW;AAAA,EACvC;AAEA,wBAAsB,UAAU;AAChC,SAAO;AACT;"}
1
+ {"version":3,"file":"auto-index.js","sources":["../../../src/indexes/auto-index.ts"],"sourcesContent":["import { DEFAULT_COMPARE_OPTIONS } from '../utils'\nimport { hasVirtualPropPath } from '../virtual-props'\nimport { checkCollectionSizeForIndex, isDevModeEnabled } from './index-registry'\nimport type { CompareOptions } from '../query/builder/types'\nimport type { BasicExpression } from '../query/ir'\nimport type { CollectionImpl } from '../collection/index.js'\n\nexport interface AutoIndexConfig {\n autoIndex?: `off` | `eager`\n}\n\nfunction shouldAutoIndex(collection: CollectionImpl<any, any, any, any, any>) {\n // Only proceed if auto-indexing is enabled\n // Note: autoIndex: 'eager' without defaultIndexType is caught at construction time\n // in CollectionImpl, so we don't need to check for it here.\n return collection.config.autoIndex === `eager`\n}\n\nexport function ensureIndexForField<\n T extends Record<string, any>,\n TKey extends string | number,\n>(\n fieldName: string,\n fieldPath: Array<string>,\n collection: CollectionImpl<T, TKey, any, any, any>,\n compareOptions?: CompareOptions,\n compareFn?: (a: any, b: any) => number,\n) {\n if (hasVirtualPropPath(fieldPath)) {\n return\n }\n if (!shouldAutoIndex(collection)) {\n return\n }\n\n const compareOpts = compareOptions ?? {\n ...DEFAULT_COMPARE_OPTIONS,\n ...collection.compareOptions,\n }\n\n // Check if we already have an index for this field\n const existingIndex = Array.from(collection.indexes.values()).find(\n (index) =>\n index.matchesField(fieldPath) && index.matchesCompareOptions(compareOpts),\n )\n\n if (existingIndex) {\n return // Index already exists\n }\n\n // Dev mode: check if collection size warrants an index suggestion\n if (isDevModeEnabled()) {\n checkCollectionSizeForIndex(\n collection.id || `unknown`,\n collection.size,\n fieldPath,\n )\n }\n\n // Create a new index for this field using the collection's createIndex method\n // The collection will use its defaultIndexType\n try {\n collection.createIndex(\n (row) => {\n // Navigate through the field path\n let current: any = row\n for (const part of fieldPath) {\n current = current[part]\n }\n return current\n },\n {\n name: `auto:${fieldPath.join(`.`)}`,\n options: compareFn ? { compareFn, compareOptions: compareOpts } : {},\n },\n )\n } catch (error) {\n console.warn(\n `${collection.id ? `[${collection.id}] ` : ``}Failed to create auto-index for field path \"${fieldPath.join(`.`)}\":`,\n error,\n )\n }\n}\n\n/**\n * Analyzes a where expression and creates indexes for all simple operations on single fields\n */\nexport function ensureIndexForExpression<\n T extends Record<string, any>,\n TKey extends string | number,\n>(\n expression: BasicExpression,\n collection: CollectionImpl<T, TKey, any, any, any>,\n): void {\n if (!shouldAutoIndex(collection)) {\n return\n }\n\n // Extract all indexable expressions and create indexes for them\n const indexableExpressions = extractIndexableExpressions(expression)\n\n for (const { fieldName, fieldPath } of indexableExpressions) {\n ensureIndexForField(fieldName, fieldPath, collection)\n }\n}\n\n/**\n * Extracts all indexable expressions from a where expression\n */\nfunction extractIndexableExpressions(\n expression: BasicExpression,\n): Array<{ fieldName: string; fieldPath: Array<string> }> {\n const results: Array<{ fieldName: string; fieldPath: Array<string> }> = []\n\n function extractFromExpression(expr: BasicExpression): void {\n if (expr.type !== `func`) {\n return\n }\n\n const func = expr as any\n\n // Handle 'and' expressions by recursively processing all arguments\n if (func.name === `and`) {\n for (const arg of func.args) {\n extractFromExpression(arg)\n }\n return\n }\n\n // Check if this is a supported operation\n const supportedOperations = [`eq`, `gt`, `gte`, `lt`, `lte`, `in`]\n if (!supportedOperations.includes(func.name)) {\n return\n }\n\n // Check if the first argument is a property reference\n if (func.args.length < 1 || func.args[0].type !== `ref`) {\n return\n }\n\n const fieldRef = func.args[0]\n const fieldPath = fieldRef.path\n\n // Skip if the path is empty\n if (fieldPath.length === 0) {\n return\n }\n\n // For nested paths, use the full path joined with underscores as the field name\n // For simple paths, use the first (and only) element\n const fieldName = fieldPath.join(`_`)\n results.push({ fieldName, fieldPath })\n }\n\n extractFromExpression(expression)\n return results\n}\n"],"names":[],"mappings":";;;AAWA,SAAS,gBAAgB,YAAqD;AAI5E,SAAO,WAAW,OAAO,cAAc;AACzC;AAEO,SAAS,oBAId,WACA,WACA,YACA,gBACA,WACA;AACA,MAAI,mBAAmB,SAAS,GAAG;AACjC;AAAA,EACF;AACA,MAAI,CAAC,gBAAgB,UAAU,GAAG;AAChC;AAAA,EACF;AAEA,QAAM,cAAc,kBAAkB;AAAA,IACpC,GAAG;AAAA,IACH,GAAG,WAAW;AAAA,EAAA;AAIhB,QAAM,gBAAgB,MAAM,KAAK,WAAW,QAAQ,OAAA,CAAQ,EAAE;AAAA,IAC5D,CAAC,UACC,MAAM,aAAa,SAAS,KAAK,MAAM,sBAAsB,WAAW;AAAA,EAAA;AAG5E,MAAI,eAAe;AACjB;AAAA,EACF;AAGA,MAAI,oBAAoB;AACtB;AAAA,MACE,WAAW,MAAM;AAAA,MACjB,WAAW;AAAA,MACX;AAAA,IAAA;AAAA,EAEJ;AAIA,MAAI;AACF,eAAW;AAAA,MACT,CAAC,QAAQ;AAEP,YAAI,UAAe;AACnB,mBAAW,QAAQ,WAAW;AAC5B,oBAAU,QAAQ,IAAI;AAAA,QACxB;AACA,eAAO;AAAA,MACT;AAAA,MACA;AAAA,QACE,MAAM,QAAQ,UAAU,KAAK,GAAG,CAAC;AAAA,QACjC,SAAS,YAAY,EAAE,WAAW,gBAAgB,YAAA,IAAgB,CAAA;AAAA,MAAC;AAAA,IACrE;AAAA,EAEJ,SAAS,OAAO;AACd,YAAQ;AAAA,MACN,GAAG,WAAW,KAAK,IAAI,WAAW,EAAE,OAAO,EAAE,+CAA+C,UAAU,KAAK,GAAG,CAAC;AAAA,MAC/G;AAAA,IAAA;AAAA,EAEJ;AACF;AAKO,SAAS,yBAId,YACA,YACM;AACN,MAAI,CAAC,gBAAgB,UAAU,GAAG;AAChC;AAAA,EACF;AAGA,QAAM,uBAAuB,4BAA4B,UAAU;AAEnE,aAAW,EAAE,WAAW,UAAA,KAAe,sBAAsB;AAC3D,wBAAoB,WAAW,WAAW,UAAU;AAAA,EACtD;AACF;AAKA,SAAS,4BACP,YACwD;AACxD,QAAM,UAAkE,CAAA;AAExE,WAAS,sBAAsB,MAA6B;AAC1D,QAAI,KAAK,SAAS,QAAQ;AACxB;AAAA,IACF;AAEA,UAAM,OAAO;AAGb,QAAI,KAAK,SAAS,OAAO;AACvB,iBAAW,OAAO,KAAK,MAAM;AAC3B,8BAAsB,GAAG;AAAA,MAC3B;AACA;AAAA,IACF;AAGA,UAAM,sBAAsB,CAAC,MAAM,MAAM,OAAO,MAAM,OAAO,IAAI;AACjE,QAAI,CAAC,oBAAoB,SAAS,KAAK,IAAI,GAAG;AAC5C;AAAA,IACF;AAGA,QAAI,KAAK,KAAK,SAAS,KAAK,KAAK,KAAK,CAAC,EAAE,SAAS,OAAO;AACvD;AAAA,IACF;AAEA,UAAM,WAAW,KAAK,KAAK,CAAC;AAC5B,UAAM,YAAY,SAAS;AAG3B,QAAI,UAAU,WAAW,GAAG;AAC1B;AAAA,IACF;AAIA,UAAM,YAAY,UAAU,KAAK,GAAG;AACpC,YAAQ,KAAK,EAAE,WAAW,UAAA,CAAW;AAAA,EACvC;AAEA,wBAAsB,UAAU;AAChC,SAAO;AACT;"}
@@ -19,7 +19,7 @@ export interface IndexStats {
19
19
  readonly averageLookupTime: number;
20
20
  readonly lastUpdated: Date;
21
21
  }
22
- export interface IndexInterface<TKey extends string | number | undefined = string | number | undefined> {
22
+ export interface IndexInterface<TKey extends string | number = string | number> {
23
23
  add: (key: TKey, item: any) => void;
24
24
  remove: (key: TKey, item: any) => void;
25
25
  update: (key: TKey, oldItem: any, newItem: any) => void;
@@ -48,7 +48,7 @@ export interface IndexInterface<TKey extends string | number | undefined = strin
48
48
  /**
49
49
  * Base abstract class that all index types extend
50
50
  */
51
- export declare abstract class BaseIndex<TKey extends string | number | undefined = string | number | undefined> implements IndexInterface<TKey> {
51
+ export declare abstract class BaseIndex<TKey extends string | number = string | number> implements IndexInterface<TKey> {
52
52
  readonly id: number;
53
53
  readonly name?: string;
54
54
  readonly expression: BasicExpression;
@@ -98,7 +98,3 @@ export declare abstract class BaseIndex<TKey extends string | number | undefined
98
98
  * Type for index constructor
99
99
  */
100
100
  export type IndexConstructor<TKey extends string | number = string | number> = new (id: number, expression: BasicExpression, name?: string, options?: any) => BaseIndex<TKey>;
101
- /**
102
- * Index resolver can be either a class constructor or async loader
103
- */
104
- export type IndexResolver<TKey extends string | number = string | number> = IndexConstructor<TKey> | (() => Promise<IndexConstructor<TKey>>);
@@ -1,7 +1,5 @@
1
1
  import { compileSingleRowExpression } from "../query/compiler/evaluators.js";
2
- import { comparisonFunctions } from "../query/builder/functions.js";
3
2
  import { DEFAULT_COMPARE_OPTIONS, deepEquals } from "../utils.js";
4
- const IndexOperation = comparisonFunctions;
5
3
  class BaseIndex {
6
4
  constructor(id, expression, name, options) {
7
5
  this.lookupCount = 0;
@@ -66,7 +64,6 @@ class BaseIndex {
66
64
  }
67
65
  }
68
66
  export {
69
- BaseIndex,
70
- IndexOperation
67
+ BaseIndex
71
68
  };
72
69
  //# sourceMappingURL=base-index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"base-index.js","sources":["../../../src/indexes/base-index.ts"],"sourcesContent":["import { compileSingleRowExpression } from '../query/compiler/evaluators.js'\nimport { comparisonFunctions } from '../query/builder/functions.js'\nimport { DEFAULT_COMPARE_OPTIONS, deepEquals } from '../utils.js'\nimport type { RangeQueryOptions } from './btree-index.js'\nimport type { CompareOptions } from '../query/builder/types.js'\nimport type { BasicExpression, OrderByDirection } from '../query/ir.js'\n\n/**\n * Operations that indexes can support, imported from available comparison functions\n */\nexport const IndexOperation = comparisonFunctions\n\n/**\n * Type for index operation values\n */\nexport type IndexOperation = (typeof comparisonFunctions)[number]\n\n/**\n * Statistics about index usage and performance\n */\nexport interface IndexStats {\n readonly entryCount: number\n readonly lookupCount: number\n readonly averageLookupTime: number\n readonly lastUpdated: Date\n}\n\nexport interface IndexInterface<\n TKey extends string | number | undefined = string | number | undefined,\n> {\n add: (key: TKey, item: any) => void\n remove: (key: TKey, item: any) => void\n update: (key: TKey, oldItem: any, newItem: any) => void\n\n build: (entries: Iterable<[TKey, any]>) => void\n clear: () => void\n\n lookup: (operation: IndexOperation, value: any) => Set<TKey>\n\n equalityLookup: (value: any) => Set<TKey>\n inArrayLookup: (values: Array<any>) => Set<TKey>\n\n rangeQuery: (options: RangeQueryOptions) => Set<TKey>\n rangeQueryReversed: (options: RangeQueryOptions) => Set<TKey>\n\n take: (\n n: number,\n from: TKey,\n filterFn?: (key: TKey) => boolean,\n ) => Array<TKey>\n takeFromStart: (n: number, filterFn?: (key: TKey) => boolean) => Array<TKey>\n takeReversed: (\n n: number,\n from: TKey,\n filterFn?: (key: TKey) => boolean,\n ) => Array<TKey>\n takeReversedFromEnd: (\n n: number,\n filterFn?: (key: TKey) => boolean,\n ) => Array<TKey>\n\n get keyCount(): number\n get orderedEntriesArray(): Array<[any, Set<TKey>]>\n get orderedEntriesArrayReversed(): Array<[any, Set<TKey>]>\n\n get indexedKeysSet(): Set<TKey>\n get valueMapData(): Map<any, Set<TKey>>\n\n supports: (operation: IndexOperation) => boolean\n\n matchesField: (fieldPath: Array<string>) => boolean\n matchesCompareOptions: (compareOptions: CompareOptions) => boolean\n matchesDirection: (direction: OrderByDirection) => boolean\n\n getStats: () => IndexStats\n}\n\n/**\n * Base abstract class that all index types extend\n */\nexport abstract class BaseIndex<\n TKey extends string | number | undefined = string | number | undefined,\n> implements IndexInterface<TKey> {\n public readonly id: number\n public readonly name?: string\n public readonly expression: BasicExpression\n public abstract readonly supportedOperations: Set<IndexOperation>\n\n protected lookupCount = 0\n protected totalLookupTime = 0\n protected lastUpdated = new Date()\n protected compareOptions: CompareOptions\n\n constructor(\n id: number,\n expression: BasicExpression,\n name?: string,\n options?: any,\n ) {\n this.id = id\n this.expression = expression\n this.compareOptions = DEFAULT_COMPARE_OPTIONS\n this.name = name\n this.initialize(options)\n }\n\n // Abstract methods that each index type must implement\n abstract add(key: TKey, item: any): void\n abstract remove(key: TKey, item: any): void\n abstract update(key: TKey, oldItem: any, newItem: any): void\n abstract build(entries: Iterable<[TKey, any]>): void\n abstract clear(): void\n abstract lookup(operation: IndexOperation, value: any): Set<TKey>\n abstract take(\n n: number,\n from: TKey,\n filterFn?: (key: TKey) => boolean,\n ): Array<TKey>\n abstract takeFromStart(\n n: number,\n filterFn?: (key: TKey) => boolean,\n ): Array<TKey>\n abstract takeReversed(\n n: number,\n from: TKey,\n filterFn?: (key: TKey) => boolean,\n ): Array<TKey>\n abstract takeReversedFromEnd(\n n: number,\n filterFn?: (key: TKey) => boolean,\n ): Array<TKey>\n abstract get keyCount(): number\n abstract equalityLookup(value: any): Set<TKey>\n abstract inArrayLookup(values: Array<any>): Set<TKey>\n abstract rangeQuery(options: RangeQueryOptions): Set<TKey>\n abstract rangeQueryReversed(options: RangeQueryOptions): Set<TKey>\n abstract get orderedEntriesArray(): Array<[any, Set<TKey>]>\n abstract get orderedEntriesArrayReversed(): Array<[any, Set<TKey>]>\n abstract get indexedKeysSet(): Set<TKey>\n abstract get valueMapData(): Map<any, Set<TKey>>\n\n // Common methods\n supports(operation: IndexOperation): boolean {\n return this.supportedOperations.has(operation)\n }\n\n matchesField(fieldPath: Array<string>): boolean {\n return (\n this.expression.type === `ref` &&\n this.expression.path.length === fieldPath.length &&\n this.expression.path.every((part, i) => part === fieldPath[i])\n )\n }\n\n /**\n * Checks if the compare options match the index's compare options.\n * The direction is ignored because the index can be reversed if the direction is different.\n */\n matchesCompareOptions(compareOptions: CompareOptions): boolean {\n const thisCompareOptionsWithoutDirection = {\n ...this.compareOptions,\n direction: undefined,\n }\n const compareOptionsWithoutDirection = {\n ...compareOptions,\n direction: undefined,\n }\n\n return deepEquals(\n thisCompareOptionsWithoutDirection,\n compareOptionsWithoutDirection,\n )\n }\n\n /**\n * Checks if the index matches the provided direction.\n */\n matchesDirection(direction: OrderByDirection): boolean {\n return this.compareOptions.direction === direction\n }\n\n getStats(): IndexStats {\n return {\n entryCount: this.keyCount,\n lookupCount: this.lookupCount,\n averageLookupTime:\n this.lookupCount > 0 ? this.totalLookupTime / this.lookupCount : 0,\n lastUpdated: this.lastUpdated,\n }\n }\n\n // Protected methods for subclasses\n protected abstract initialize(options?: any): void\n\n protected evaluateIndexExpression(item: any): any {\n const evaluator = compileSingleRowExpression(this.expression)\n return evaluator(item as Record<string, unknown>)\n }\n\n protected trackLookup(startTime: number): void {\n const duration = performance.now() - startTime\n this.lookupCount++\n this.totalLookupTime += duration\n }\n\n protected updateTimestamp(): void {\n this.lastUpdated = new Date()\n }\n}\n\n/**\n * Type for index constructor\n */\nexport type IndexConstructor<TKey extends string | number = string | number> =\n new (\n id: number,\n expression: BasicExpression,\n name?: string,\n options?: any,\n ) => BaseIndex<TKey>\n\n/**\n * Index resolver can be either a class constructor or async loader\n */\nexport type IndexResolver<TKey extends string | number = string | number> =\n | IndexConstructor<TKey>\n | (() => Promise<IndexConstructor<TKey>>)\n"],"names":[],"mappings":";;;AAUO,MAAM,iBAAiB;AAsEvB,MAAe,UAEY;AAAA,EAWhC,YACE,IACA,YACA,MACA,SACA;AAVF,SAAU,cAAc;AACxB,SAAU,kBAAkB;AAC5B,SAAU,kCAAkB,KAAA;AAS1B,SAAK,KAAK;AACV,SAAK,aAAa;AAClB,SAAK,iBAAiB;AACtB,SAAK,OAAO;AACZ,SAAK,WAAW,OAAO;AAAA,EACzB;AAAA;AAAA,EAsCA,SAAS,WAAoC;AAC3C,WAAO,KAAK,oBAAoB,IAAI,SAAS;AAAA,EAC/C;AAAA,EAEA,aAAa,WAAmC;AAC9C,WACE,KAAK,WAAW,SAAS,SACzB,KAAK,WAAW,KAAK,WAAW,UAAU,UAC1C,KAAK,WAAW,KAAK,MAAM,CAAC,MAAM,MAAM,SAAS,UAAU,CAAC,CAAC;AAAA,EAEjE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,sBAAsB,gBAAyC;AAC7D,UAAM,qCAAqC;AAAA,MACzC,GAAG,KAAK;AAAA,MACR,WAAW;AAAA,IAAA;AAEb,UAAM,iCAAiC;AAAA,MACrC,GAAG;AAAA,MACH,WAAW;AAAA,IAAA;AAGb,WAAO;AAAA,MACL;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAiB,WAAsC;AACrD,WAAO,KAAK,eAAe,cAAc;AAAA,EAC3C;AAAA,EAEA,WAAuB;AACrB,WAAO;AAAA,MACL,YAAY,KAAK;AAAA,MACjB,aAAa,KAAK;AAAA,MAClB,mBACE,KAAK,cAAc,IAAI,KAAK,kBAAkB,KAAK,cAAc;AAAA,MACnE,aAAa,KAAK;AAAA,IAAA;AAAA,EAEtB;AAAA,EAKU,wBAAwB,MAAgB;AAChD,UAAM,YAAY,2BAA2B,KAAK,UAAU;AAC5D,WAAO,UAAU,IAA+B;AAAA,EAClD;AAAA,EAEU,YAAY,WAAyB;AAC7C,UAAM,WAAW,YAAY,IAAA,IAAQ;AACrC,SAAK;AACL,SAAK,mBAAmB;AAAA,EAC1B;AAAA,EAEU,kBAAwB;AAChC,SAAK,kCAAkB,KAAA;AAAA,EACzB;AACF;"}
1
+ {"version":3,"file":"base-index.js","sources":["../../../src/indexes/base-index.ts"],"sourcesContent":["import { compileSingleRowExpression } from '../query/compiler/evaluators.js'\nimport { comparisonFunctions } from '../query/builder/functions.js'\nimport { DEFAULT_COMPARE_OPTIONS, deepEquals } from '../utils.js'\nimport type { RangeQueryOptions } from './btree-index.js'\nimport type { CompareOptions } from '../query/builder/types.js'\nimport type { BasicExpression, OrderByDirection } from '../query/ir.js'\n\n/**\n * Operations that indexes can support, imported from available comparison functions\n */\nexport const IndexOperation = comparisonFunctions\n\n/**\n * Type for index operation values\n */\nexport type IndexOperation = (typeof comparisonFunctions)[number]\n\n/**\n * Statistics about index usage and performance\n */\nexport interface IndexStats {\n readonly entryCount: number\n readonly lookupCount: number\n readonly averageLookupTime: number\n readonly lastUpdated: Date\n}\n\nexport interface IndexInterface<\n TKey extends string | number = string | number,\n> {\n add: (key: TKey, item: any) => void\n remove: (key: TKey, item: any) => void\n update: (key: TKey, oldItem: any, newItem: any) => void\n\n build: (entries: Iterable<[TKey, any]>) => void\n clear: () => void\n\n lookup: (operation: IndexOperation, value: any) => Set<TKey>\n\n equalityLookup: (value: any) => Set<TKey>\n inArrayLookup: (values: Array<any>) => Set<TKey>\n\n rangeQuery: (options: RangeQueryOptions) => Set<TKey>\n rangeQueryReversed: (options: RangeQueryOptions) => Set<TKey>\n\n take: (\n n: number,\n from: TKey,\n filterFn?: (key: TKey) => boolean,\n ) => Array<TKey>\n takeFromStart: (n: number, filterFn?: (key: TKey) => boolean) => Array<TKey>\n takeReversed: (\n n: number,\n from: TKey,\n filterFn?: (key: TKey) => boolean,\n ) => Array<TKey>\n takeReversedFromEnd: (\n n: number,\n filterFn?: (key: TKey) => boolean,\n ) => Array<TKey>\n\n get keyCount(): number\n get orderedEntriesArray(): Array<[any, Set<TKey>]>\n get orderedEntriesArrayReversed(): Array<[any, Set<TKey>]>\n\n get indexedKeysSet(): Set<TKey>\n get valueMapData(): Map<any, Set<TKey>>\n\n supports: (operation: IndexOperation) => boolean\n\n matchesField: (fieldPath: Array<string>) => boolean\n matchesCompareOptions: (compareOptions: CompareOptions) => boolean\n matchesDirection: (direction: OrderByDirection) => boolean\n\n getStats: () => IndexStats\n}\n\n/**\n * Base abstract class that all index types extend\n */\nexport abstract class BaseIndex<\n TKey extends string | number = string | number,\n> implements IndexInterface<TKey> {\n public readonly id: number\n public readonly name?: string\n public readonly expression: BasicExpression\n public abstract readonly supportedOperations: Set<IndexOperation>\n\n protected lookupCount = 0\n protected totalLookupTime = 0\n protected lastUpdated = new Date()\n protected compareOptions: CompareOptions\n\n constructor(\n id: number,\n expression: BasicExpression,\n name?: string,\n options?: any,\n ) {\n this.id = id\n this.expression = expression\n this.compareOptions = DEFAULT_COMPARE_OPTIONS\n this.name = name\n this.initialize(options)\n }\n\n // Abstract methods that each index type must implement\n abstract add(key: TKey, item: any): void\n abstract remove(key: TKey, item: any): void\n abstract update(key: TKey, oldItem: any, newItem: any): void\n abstract build(entries: Iterable<[TKey, any]>): void\n abstract clear(): void\n abstract lookup(operation: IndexOperation, value: any): Set<TKey>\n abstract take(\n n: number,\n from: TKey,\n filterFn?: (key: TKey) => boolean,\n ): Array<TKey>\n abstract takeFromStart(\n n: number,\n filterFn?: (key: TKey) => boolean,\n ): Array<TKey>\n abstract takeReversed(\n n: number,\n from: TKey,\n filterFn?: (key: TKey) => boolean,\n ): Array<TKey>\n abstract takeReversedFromEnd(\n n: number,\n filterFn?: (key: TKey) => boolean,\n ): Array<TKey>\n abstract get keyCount(): number\n abstract equalityLookup(value: any): Set<TKey>\n abstract inArrayLookup(values: Array<any>): Set<TKey>\n abstract rangeQuery(options: RangeQueryOptions): Set<TKey>\n abstract rangeQueryReversed(options: RangeQueryOptions): Set<TKey>\n abstract get orderedEntriesArray(): Array<[any, Set<TKey>]>\n abstract get orderedEntriesArrayReversed(): Array<[any, Set<TKey>]>\n abstract get indexedKeysSet(): Set<TKey>\n abstract get valueMapData(): Map<any, Set<TKey>>\n\n // Common methods\n supports(operation: IndexOperation): boolean {\n return this.supportedOperations.has(operation)\n }\n\n matchesField(fieldPath: Array<string>): boolean {\n return (\n this.expression.type === `ref` &&\n this.expression.path.length === fieldPath.length &&\n this.expression.path.every((part, i) => part === fieldPath[i])\n )\n }\n\n /**\n * Checks if the compare options match the index's compare options.\n * The direction is ignored because the index can be reversed if the direction is different.\n */\n matchesCompareOptions(compareOptions: CompareOptions): boolean {\n const thisCompareOptionsWithoutDirection = {\n ...this.compareOptions,\n direction: undefined,\n }\n const compareOptionsWithoutDirection = {\n ...compareOptions,\n direction: undefined,\n }\n\n return deepEquals(\n thisCompareOptionsWithoutDirection,\n compareOptionsWithoutDirection,\n )\n }\n\n /**\n * Checks if the index matches the provided direction.\n */\n matchesDirection(direction: OrderByDirection): boolean {\n return this.compareOptions.direction === direction\n }\n\n getStats(): IndexStats {\n return {\n entryCount: this.keyCount,\n lookupCount: this.lookupCount,\n averageLookupTime:\n this.lookupCount > 0 ? this.totalLookupTime / this.lookupCount : 0,\n lastUpdated: this.lastUpdated,\n }\n }\n\n protected abstract initialize(options?: any): void\n\n protected evaluateIndexExpression(item: any): any {\n const evaluator = compileSingleRowExpression(this.expression)\n return evaluator(item as Record<string, unknown>)\n }\n\n protected trackLookup(startTime: number): void {\n const duration = performance.now() - startTime\n this.lookupCount++\n this.totalLookupTime += duration\n }\n\n protected updateTimestamp(): void {\n this.lastUpdated = new Date()\n }\n}\n\n/**\n * Type for index constructor\n */\nexport type IndexConstructor<TKey extends string | number = string | number> =\n new (\n id: number,\n expression: BasicExpression,\n name?: string,\n options?: any,\n ) => BaseIndex<TKey>\n"],"names":[],"mappings":";;AAgFO,MAAe,UAEY;AAAA,EAWhC,YACE,IACA,YACA,MACA,SACA;AAVF,SAAU,cAAc;AACxB,SAAU,kBAAkB;AAC5B,SAAU,kCAAkB,KAAA;AAS1B,SAAK,KAAK;AACV,SAAK,aAAa;AAClB,SAAK,iBAAiB;AACtB,SAAK,OAAO;AACZ,SAAK,WAAW,OAAO;AAAA,EACzB;AAAA;AAAA,EAsCA,SAAS,WAAoC;AAC3C,WAAO,KAAK,oBAAoB,IAAI,SAAS;AAAA,EAC/C;AAAA,EAEA,aAAa,WAAmC;AAC9C,WACE,KAAK,WAAW,SAAS,SACzB,KAAK,WAAW,KAAK,WAAW,UAAU,UAC1C,KAAK,WAAW,KAAK,MAAM,CAAC,MAAM,MAAM,SAAS,UAAU,CAAC,CAAC;AAAA,EAEjE;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,sBAAsB,gBAAyC;AAC7D,UAAM,qCAAqC;AAAA,MACzC,GAAG,KAAK;AAAA,MACR,WAAW;AAAA,IAAA;AAEb,UAAM,iCAAiC;AAAA,MACrC,GAAG;AAAA,MACH,WAAW;AAAA,IAAA;AAGb,WAAO;AAAA,MACL;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAiB,WAAsC;AACrD,WAAO,KAAK,eAAe,cAAc;AAAA,EAC3C;AAAA,EAEA,WAAuB;AACrB,WAAO;AAAA,MACL,YAAY,KAAK;AAAA,MACjB,aAAa,KAAK;AAAA,MAClB,mBACE,KAAK,cAAc,IAAI,KAAK,kBAAkB,KAAK,cAAc;AAAA,MACnE,aAAa,KAAK;AAAA,IAAA;AAAA,EAEtB;AAAA,EAIU,wBAAwB,MAAgB;AAChD,UAAM,YAAY,2BAA2B,KAAK,UAAU;AAC5D,WAAO,UAAU,IAA+B;AAAA,EAClD;AAAA,EAEU,YAAY,WAAyB;AAC7C,UAAM,WAAW,YAAY,IAAA,IAAQ;AACrC,SAAK;AACL,SAAK,mBAAmB;AAAA,EAC1B;AAAA,EAEU,kBAAwB;AAChC,SAAK,kCAAkB,KAAA;AAAA,EACzB;AACF;"}
@@ -0,0 +1,102 @@
1
+ import { BaseIndex, IndexOperation } from './base-index.js';
2
+ import { CompareOptions } from '../query/builder/types.js';
3
+ import { BasicExpression } from '../query/ir.js';
4
+ /**
5
+ * Options for range queries
6
+ */
7
+ export interface RangeQueryOptions {
8
+ from?: any;
9
+ to?: any;
10
+ fromInclusive?: boolean;
11
+ toInclusive?: boolean;
12
+ }
13
+ /**
14
+ * Options for Basic index
15
+ */
16
+ export interface BasicIndexOptions {
17
+ compareFn?: (a: any, b: any) => number;
18
+ compareOptions?: CompareOptions;
19
+ }
20
+ /**
21
+ * Basic index using Map + sorted Array.
22
+ *
23
+ * - Map for O(1) equality lookups
24
+ * - Sorted Array for O(log n) range queries via binary search
25
+ * - O(n) updates to maintain sort order
26
+ *
27
+ * Simpler and smaller than BTreeIndex, good for read-heavy workloads.
28
+ * Use BTreeIndex for write-heavy workloads with large collections.
29
+ */
30
+ export declare class BasicIndex<TKey extends string | number = string | number> extends BaseIndex<TKey> {
31
+ readonly supportedOperations: Set<"eq" | "gt" | "gte" | "lt" | "lte" | "in" | "like" | "ilike">;
32
+ private valueMap;
33
+ private sortedValues;
34
+ private indexedKeys;
35
+ private compareFn;
36
+ constructor(id: number, expression: BasicExpression, name?: string, options?: any);
37
+ protected initialize(_options?: BasicIndexOptions): void;
38
+ /**
39
+ * Adds a value to the index
40
+ */
41
+ add(key: TKey, item: any): void;
42
+ /**
43
+ * Removes a value from the index
44
+ */
45
+ remove(key: TKey, item: any): void;
46
+ /**
47
+ * Updates a value in the index
48
+ */
49
+ update(key: TKey, oldItem: any, newItem: any): void;
50
+ /**
51
+ * Builds the index from a collection of entries
52
+ */
53
+ build(entries: Iterable<[TKey, any]>): void;
54
+ /**
55
+ * Clears all data from the index
56
+ */
57
+ clear(): void;
58
+ /**
59
+ * Performs a lookup operation
60
+ */
61
+ lookup(operation: IndexOperation, value: any): Set<TKey>;
62
+ /**
63
+ * Gets the number of indexed keys
64
+ */
65
+ get keyCount(): number;
66
+ /**
67
+ * Performs an equality lookup - O(1)
68
+ */
69
+ equalityLookup(value: any): Set<TKey>;
70
+ /**
71
+ * Performs a range query using binary search - O(log n + m)
72
+ */
73
+ rangeQuery(options?: RangeQueryOptions): Set<TKey>;
74
+ /**
75
+ * Performs a reversed range query
76
+ */
77
+ rangeQueryReversed(options?: RangeQueryOptions): Set<TKey>;
78
+ /**
79
+ * Returns the next n items in sorted order
80
+ */
81
+ take(n: number, from?: any, filterFn?: (key: TKey) => boolean): Array<TKey>;
82
+ /**
83
+ * Returns the next n items in reverse sorted order
84
+ */
85
+ takeReversed(n: number, from?: any, filterFn?: (key: TKey) => boolean): Array<TKey>;
86
+ /**
87
+ * Returns the first n items in sorted order (from the start)
88
+ */
89
+ takeFromStart(n: number, filterFn?: (key: TKey) => boolean): Array<TKey>;
90
+ /**
91
+ * Returns the first n items in reverse sorted order (from the end)
92
+ */
93
+ takeReversedFromEnd(n: number, filterFn?: (key: TKey) => boolean): Array<TKey>;
94
+ /**
95
+ * Performs an IN array lookup - O(k) where k is values.length
96
+ */
97
+ inArrayLookup(values: Array<any>): Set<TKey>;
98
+ get indexedKeysSet(): Set<TKey>;
99
+ get orderedEntriesArray(): Array<[any, Set<TKey>]>;
100
+ get orderedEntriesArrayReversed(): Array<[any, Set<TKey>]>;
101
+ get valueMapData(): Map<any, Set<TKey>>;
102
+ }
@@ -0,0 +1,361 @@
1
+ import { defaultComparator, normalizeValue } from "../utils/comparison.js";
2
+ import { findInsertPositionInArray, deleteInSortedArray } from "../utils/array-utils.js";
3
+ import { BaseIndex } from "./base-index.js";
4
+ class BasicIndex extends BaseIndex {
5
+ constructor(id, expression, name, options) {
6
+ super(id, expression, name, options);
7
+ this.supportedOperations = /* @__PURE__ */ new Set([
8
+ `eq`,
9
+ `gt`,
10
+ `gte`,
11
+ `lt`,
12
+ `lte`,
13
+ `in`
14
+ ]);
15
+ this.valueMap = /* @__PURE__ */ new Map();
16
+ this.sortedValues = [];
17
+ this.indexedKeys = /* @__PURE__ */ new Set();
18
+ this.compareFn = defaultComparator;
19
+ this.compareFn = options?.compareFn ?? defaultComparator;
20
+ if (options?.compareOptions) {
21
+ this.compareOptions = options.compareOptions;
22
+ }
23
+ }
24
+ initialize(_options) {
25
+ }
26
+ /**
27
+ * Adds a value to the index
28
+ */
29
+ add(key, item) {
30
+ let indexedValue;
31
+ try {
32
+ indexedValue = this.evaluateIndexExpression(item);
33
+ } catch (error) {
34
+ throw new Error(
35
+ `Failed to evaluate index expression for key ${key}: ${error}`,
36
+ { cause: error }
37
+ );
38
+ }
39
+ const normalizedValue = normalizeValue(indexedValue);
40
+ if (this.valueMap.has(normalizedValue)) {
41
+ this.valueMap.get(normalizedValue).add(key);
42
+ } else {
43
+ this.valueMap.set(normalizedValue, /* @__PURE__ */ new Set([key]));
44
+ const insertIdx = findInsertPositionInArray(
45
+ this.sortedValues,
46
+ normalizedValue,
47
+ this.compareFn
48
+ );
49
+ this.sortedValues.splice(insertIdx, 0, normalizedValue);
50
+ }
51
+ this.indexedKeys.add(key);
52
+ this.updateTimestamp();
53
+ }
54
+ /**
55
+ * Removes a value from the index
56
+ */
57
+ remove(key, item) {
58
+ let indexedValue;
59
+ try {
60
+ indexedValue = this.evaluateIndexExpression(item);
61
+ } catch (error) {
62
+ console.warn(
63
+ `Failed to evaluate index expression for key ${key} during removal:`,
64
+ error
65
+ );
66
+ this.indexedKeys.delete(key);
67
+ this.updateTimestamp();
68
+ return;
69
+ }
70
+ const normalizedValue = normalizeValue(indexedValue);
71
+ if (this.valueMap.has(normalizedValue)) {
72
+ const keySet = this.valueMap.get(normalizedValue);
73
+ keySet.delete(key);
74
+ if (keySet.size === 0) {
75
+ this.valueMap.delete(normalizedValue);
76
+ deleteInSortedArray(this.sortedValues, normalizedValue, this.compareFn);
77
+ }
78
+ }
79
+ this.indexedKeys.delete(key);
80
+ this.updateTimestamp();
81
+ }
82
+ /**
83
+ * Updates a value in the index
84
+ */
85
+ update(key, oldItem, newItem) {
86
+ this.remove(key, oldItem);
87
+ this.add(key, newItem);
88
+ }
89
+ /**
90
+ * Builds the index from a collection of entries
91
+ */
92
+ build(entries) {
93
+ this.clear();
94
+ const entriesArray = [];
95
+ for (const [key, item] of entries) {
96
+ let indexedValue;
97
+ try {
98
+ indexedValue = this.evaluateIndexExpression(item);
99
+ } catch (error) {
100
+ throw new Error(
101
+ `Failed to evaluate index expression for key ${key}: ${error}`,
102
+ { cause: error }
103
+ );
104
+ }
105
+ entriesArray.push({ key, value: normalizeValue(indexedValue) });
106
+ this.indexedKeys.add(key);
107
+ }
108
+ for (const { key, value } of entriesArray) {
109
+ if (this.valueMap.has(value)) {
110
+ this.valueMap.get(value).add(key);
111
+ } else {
112
+ this.valueMap.set(value, /* @__PURE__ */ new Set([key]));
113
+ }
114
+ }
115
+ this.sortedValues = Array.from(this.valueMap.keys()).sort(this.compareFn);
116
+ this.updateTimestamp();
117
+ }
118
+ /**
119
+ * Clears all data from the index
120
+ */
121
+ clear() {
122
+ this.valueMap.clear();
123
+ this.sortedValues = [];
124
+ this.indexedKeys.clear();
125
+ this.updateTimestamp();
126
+ }
127
+ /**
128
+ * Performs a lookup operation
129
+ */
130
+ lookup(operation, value) {
131
+ const startTime = performance.now();
132
+ let result;
133
+ switch (operation) {
134
+ case `eq`:
135
+ result = this.equalityLookup(value);
136
+ break;
137
+ case `gt`:
138
+ result = this.rangeQuery({ from: value, fromInclusive: false });
139
+ break;
140
+ case `gte`:
141
+ result = this.rangeQuery({ from: value, fromInclusive: true });
142
+ break;
143
+ case `lt`:
144
+ result = this.rangeQuery({ to: value, toInclusive: false });
145
+ break;
146
+ case `lte`:
147
+ result = this.rangeQuery({ to: value, toInclusive: true });
148
+ break;
149
+ case `in`:
150
+ result = this.inArrayLookup(value);
151
+ break;
152
+ default:
153
+ throw new Error(`Operation ${operation} not supported by BasicIndex`);
154
+ }
155
+ this.trackLookup(startTime);
156
+ return result;
157
+ }
158
+ /**
159
+ * Gets the number of indexed keys
160
+ */
161
+ get keyCount() {
162
+ return this.indexedKeys.size;
163
+ }
164
+ /**
165
+ * Performs an equality lookup - O(1)
166
+ */
167
+ equalityLookup(value) {
168
+ const normalizedValue = normalizeValue(value);
169
+ return this.valueMap.get(normalizedValue) ?? /* @__PURE__ */ new Set();
170
+ }
171
+ /**
172
+ * Performs a range query using binary search - O(log n + m)
173
+ */
174
+ rangeQuery(options = {}) {
175
+ const { from, to, fromInclusive = true, toInclusive = true } = options;
176
+ const result = /* @__PURE__ */ new Set();
177
+ if (this.sortedValues.length === 0) {
178
+ return result;
179
+ }
180
+ const normalizedFrom = normalizeValue(from);
181
+ const normalizedTo = normalizeValue(to);
182
+ let startIdx = 0;
183
+ if (normalizedFrom !== void 0) {
184
+ startIdx = findInsertPositionInArray(
185
+ this.sortedValues,
186
+ normalizedFrom,
187
+ this.compareFn
188
+ );
189
+ if (!fromInclusive && startIdx < this.sortedValues.length && this.compareFn(this.sortedValues[startIdx], normalizedFrom) === 0) {
190
+ startIdx++;
191
+ }
192
+ }
193
+ let endIdx = this.sortedValues.length;
194
+ if (normalizedTo !== void 0) {
195
+ endIdx = findInsertPositionInArray(
196
+ this.sortedValues,
197
+ normalizedTo,
198
+ this.compareFn
199
+ );
200
+ if (toInclusive && endIdx < this.sortedValues.length && this.compareFn(this.sortedValues[endIdx], normalizedTo) === 0) {
201
+ endIdx++;
202
+ }
203
+ }
204
+ for (let i = startIdx; i < endIdx; i++) {
205
+ const keys = this.valueMap.get(this.sortedValues[i]);
206
+ if (keys) {
207
+ keys.forEach((key) => result.add(key));
208
+ }
209
+ }
210
+ return result;
211
+ }
212
+ /**
213
+ * Performs a reversed range query
214
+ */
215
+ rangeQueryReversed(options = {}) {
216
+ const { from, to, fromInclusive = true, toInclusive = true } = options;
217
+ const swappedFrom = to ?? (this.sortedValues.length > 0 ? this.sortedValues[this.sortedValues.length - 1] : void 0);
218
+ const swappedTo = from ?? (this.sortedValues.length > 0 ? this.sortedValues[0] : void 0);
219
+ return this.rangeQuery({
220
+ from: swappedFrom,
221
+ to: swappedTo,
222
+ fromInclusive: toInclusive,
223
+ toInclusive: fromInclusive
224
+ });
225
+ }
226
+ /**
227
+ * Returns the next n items in sorted order
228
+ */
229
+ take(n, from, filterFn) {
230
+ const result = [];
231
+ let startIdx = 0;
232
+ if (from !== void 0) {
233
+ const normalizedFrom = normalizeValue(from);
234
+ startIdx = findInsertPositionInArray(
235
+ this.sortedValues,
236
+ normalizedFrom,
237
+ this.compareFn
238
+ );
239
+ while (startIdx < this.sortedValues.length && this.compareFn(this.sortedValues[startIdx], normalizedFrom) <= 0) {
240
+ startIdx++;
241
+ }
242
+ }
243
+ for (let i = startIdx; i < this.sortedValues.length && result.length < n; i++) {
244
+ const keys = this.valueMap.get(this.sortedValues[i]);
245
+ if (keys) {
246
+ for (const key of keys) {
247
+ if (result.length >= n) break;
248
+ if (!filterFn || filterFn(key)) {
249
+ result.push(key);
250
+ }
251
+ }
252
+ }
253
+ }
254
+ return result;
255
+ }
256
+ /**
257
+ * Returns the next n items in reverse sorted order
258
+ */
259
+ takeReversed(n, from, filterFn) {
260
+ const result = [];
261
+ let startIdx = this.sortedValues.length - 1;
262
+ if (from !== void 0) {
263
+ const normalizedFrom = normalizeValue(from);
264
+ startIdx = findInsertPositionInArray(
265
+ this.sortedValues,
266
+ normalizedFrom,
267
+ this.compareFn
268
+ ) - 1;
269
+ while (startIdx >= 0 && this.compareFn(this.sortedValues[startIdx], normalizedFrom) >= 0) {
270
+ startIdx--;
271
+ }
272
+ }
273
+ for (let i = startIdx; i >= 0 && result.length < n; i--) {
274
+ const keys = this.valueMap.get(this.sortedValues[i]);
275
+ if (keys) {
276
+ for (const key of keys) {
277
+ if (result.length >= n) break;
278
+ if (!filterFn || filterFn(key)) {
279
+ result.push(key);
280
+ }
281
+ }
282
+ }
283
+ }
284
+ return result;
285
+ }
286
+ /**
287
+ * Returns the first n items in sorted order (from the start)
288
+ */
289
+ takeFromStart(n, filterFn) {
290
+ const result = [];
291
+ for (let i = 0; i < this.sortedValues.length && result.length < n; i++) {
292
+ const keys = this.valueMap.get(this.sortedValues[i]);
293
+ if (keys) {
294
+ for (const key of keys) {
295
+ if (result.length >= n) break;
296
+ if (!filterFn || filterFn(key)) {
297
+ result.push(key);
298
+ }
299
+ }
300
+ }
301
+ }
302
+ return result;
303
+ }
304
+ /**
305
+ * Returns the first n items in reverse sorted order (from the end)
306
+ */
307
+ takeReversedFromEnd(n, filterFn) {
308
+ const result = [];
309
+ for (let i = this.sortedValues.length - 1; i >= 0 && result.length < n; i--) {
310
+ const keys = this.valueMap.get(this.sortedValues[i]);
311
+ if (keys) {
312
+ for (const key of keys) {
313
+ if (result.length >= n) break;
314
+ if (!filterFn || filterFn(key)) {
315
+ result.push(key);
316
+ }
317
+ }
318
+ }
319
+ }
320
+ return result;
321
+ }
322
+ /**
323
+ * Performs an IN array lookup - O(k) where k is values.length
324
+ */
325
+ inArrayLookup(values) {
326
+ const result = /* @__PURE__ */ new Set();
327
+ for (const value of values) {
328
+ const normalizedValue = normalizeValue(value);
329
+ const keys = this.valueMap.get(normalizedValue);
330
+ if (keys) {
331
+ keys.forEach((key) => result.add(key));
332
+ }
333
+ }
334
+ return result;
335
+ }
336
+ // Getter methods for testing/compatibility
337
+ get indexedKeysSet() {
338
+ return this.indexedKeys;
339
+ }
340
+ get orderedEntriesArray() {
341
+ return this.sortedValues.map((value) => [
342
+ value,
343
+ this.valueMap.get(value) ?? /* @__PURE__ */ new Set()
344
+ ]);
345
+ }
346
+ get orderedEntriesArrayReversed() {
347
+ const result = [];
348
+ for (let i = this.sortedValues.length - 1; i >= 0; i--) {
349
+ const value = this.sortedValues[i];
350
+ result.push([value, this.valueMap.get(value) ?? /* @__PURE__ */ new Set()]);
351
+ }
352
+ return result;
353
+ }
354
+ get valueMapData() {
355
+ return this.valueMap;
356
+ }
357
+ }
358
+ export {
359
+ BasicIndex
360
+ };
361
+ //# sourceMappingURL=basic-index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"basic-index.js","sources":["../../../src/indexes/basic-index.ts"],"sourcesContent":["import { defaultComparator, normalizeValue } from '../utils/comparison.js'\nimport {\n deleteInSortedArray,\n findInsertPositionInArray,\n} from '../utils/array-utils.js'\nimport { BaseIndex } from './base-index.js'\nimport type { CompareOptions } from '../query/builder/types.js'\nimport type { BasicExpression } from '../query/ir.js'\nimport type { IndexOperation } from './base-index.js'\n\n/**\n * Options for range queries\n */\nexport interface RangeQueryOptions {\n from?: any\n to?: any\n fromInclusive?: boolean\n toInclusive?: boolean\n}\n\n/**\n * Options for Basic index\n */\nexport interface BasicIndexOptions {\n compareFn?: (a: any, b: any) => number\n compareOptions?: CompareOptions\n}\n\n/**\n * Basic index using Map + sorted Array.\n *\n * - Map for O(1) equality lookups\n * - Sorted Array for O(log n) range queries via binary search\n * - O(n) updates to maintain sort order\n *\n * Simpler and smaller than BTreeIndex, good for read-heavy workloads.\n * Use BTreeIndex for write-heavy workloads with large collections.\n */\nexport class BasicIndex<\n TKey extends string | number = string | number,\n> extends BaseIndex<TKey> {\n public readonly supportedOperations = new Set<IndexOperation>([\n `eq`,\n `gt`,\n `gte`,\n `lt`,\n `lte`,\n `in`,\n ])\n\n // Map for O(1) equality lookups: indexedValue -> Set of PKs\n private valueMap = new Map<any, Set<TKey>>()\n // Sorted array of unique indexed values for range queries\n private sortedValues: Array<any> = []\n // Set of all indexed PKs\n private indexedKeys = new Set<TKey>()\n // Comparator function\n private compareFn: (a: any, b: any) => number = defaultComparator\n\n constructor(\n id: number,\n expression: BasicExpression,\n name?: string,\n options?: any,\n ) {\n super(id, expression, name, options)\n this.compareFn = options?.compareFn ?? defaultComparator\n if (options?.compareOptions) {\n this.compareOptions = options!.compareOptions\n }\n }\n\n protected initialize(_options?: BasicIndexOptions): void {}\n\n /**\n * Adds a value to the index\n */\n add(key: TKey, item: any): void {\n let indexedValue: any\n try {\n indexedValue = this.evaluateIndexExpression(item)\n } catch (error) {\n throw new Error(\n `Failed to evaluate index expression for key ${key}: ${error}`,\n { cause: error },\n )\n }\n\n const normalizedValue = normalizeValue(indexedValue)\n\n if (this.valueMap.has(normalizedValue)) {\n // Value already exists, just add the key to the set\n this.valueMap.get(normalizedValue)!.add(key)\n } else {\n // New value - add to map and insert into sorted array\n this.valueMap.set(normalizedValue, new Set([key]))\n\n // Insert into sorted position\n const insertIdx = findInsertPositionInArray(\n this.sortedValues,\n normalizedValue,\n this.compareFn,\n )\n this.sortedValues.splice(insertIdx, 0, normalizedValue)\n }\n\n this.indexedKeys.add(key)\n this.updateTimestamp()\n }\n\n /**\n * Removes a value from the index\n */\n remove(key: TKey, item: any): void {\n let indexedValue: any\n try {\n indexedValue = this.evaluateIndexExpression(item)\n } catch (error) {\n console.warn(\n `Failed to evaluate index expression for key ${key} during removal:`,\n error,\n )\n this.indexedKeys.delete(key)\n this.updateTimestamp()\n return\n }\n\n const normalizedValue = normalizeValue(indexedValue)\n\n if (this.valueMap.has(normalizedValue)) {\n const keySet = this.valueMap.get(normalizedValue)!\n keySet.delete(key)\n\n if (keySet.size === 0) {\n // No more keys for this value, remove from map and sorted array\n this.valueMap.delete(normalizedValue)\n deleteInSortedArray(this.sortedValues, normalizedValue, this.compareFn)\n }\n }\n\n this.indexedKeys.delete(key)\n this.updateTimestamp()\n }\n\n /**\n * Updates a value in the index\n */\n update(key: TKey, oldItem: any, newItem: any): void {\n this.remove(key, oldItem)\n this.add(key, newItem)\n }\n\n /**\n * Builds the index from a collection of entries\n */\n build(entries: Iterable<[TKey, any]>): void {\n this.clear()\n\n // Collect all entries first\n const entriesArray: Array<{ key: TKey; value: any }> = []\n for (const [key, item] of entries) {\n let indexedValue: any\n try {\n indexedValue = this.evaluateIndexExpression(item)\n } catch (error) {\n throw new Error(\n `Failed to evaluate index expression for key ${key}: ${error}`,\n { cause: error },\n )\n }\n entriesArray.push({ key, value: normalizeValue(indexedValue) })\n this.indexedKeys.add(key)\n }\n\n // Group by value\n for (const { key, value } of entriesArray) {\n if (this.valueMap.has(value)) {\n this.valueMap.get(value)!.add(key)\n } else {\n this.valueMap.set(value, new Set([key]))\n }\n }\n\n // Build sorted array from unique values\n this.sortedValues = Array.from(this.valueMap.keys()).sort(this.compareFn)\n\n this.updateTimestamp()\n }\n\n /**\n * Clears all data from the index\n */\n clear(): void {\n this.valueMap.clear()\n this.sortedValues = []\n this.indexedKeys.clear()\n this.updateTimestamp()\n }\n\n /**\n * Performs a lookup operation\n */\n lookup(operation: IndexOperation, value: any): Set<TKey> {\n const startTime = performance.now()\n\n let result: Set<TKey>\n\n switch (operation) {\n case `eq`:\n result = this.equalityLookup(value)\n break\n case `gt`:\n result = this.rangeQuery({ from: value, fromInclusive: false })\n break\n case `gte`:\n result = this.rangeQuery({ from: value, fromInclusive: true })\n break\n case `lt`:\n result = this.rangeQuery({ to: value, toInclusive: false })\n break\n case `lte`:\n result = this.rangeQuery({ to: value, toInclusive: true })\n break\n case `in`:\n result = this.inArrayLookup(value)\n break\n default:\n throw new Error(`Operation ${operation} not supported by BasicIndex`)\n }\n\n this.trackLookup(startTime)\n return result\n }\n\n /**\n * Gets the number of indexed keys\n */\n get keyCount(): number {\n return this.indexedKeys.size\n }\n\n /**\n * Performs an equality lookup - O(1)\n */\n equalityLookup(value: any): Set<TKey> {\n const normalizedValue = normalizeValue(value)\n return this.valueMap.get(normalizedValue) ?? new Set()\n }\n\n /**\n * Performs a range query using binary search - O(log n + m)\n */\n rangeQuery(options: RangeQueryOptions = {}): Set<TKey> {\n const { from, to, fromInclusive = true, toInclusive = true } = options\n const result = new Set<TKey>()\n\n if (this.sortedValues.length === 0) {\n return result\n }\n\n const normalizedFrom = normalizeValue(from)\n const normalizedTo = normalizeValue(to)\n\n // Find start index\n let startIdx = 0\n if (normalizedFrom !== undefined) {\n startIdx = findInsertPositionInArray(\n this.sortedValues,\n normalizedFrom,\n this.compareFn,\n )\n // If not inclusive and we found exact match, skip it\n if (\n !fromInclusive &&\n startIdx < this.sortedValues.length &&\n this.compareFn(this.sortedValues[startIdx], normalizedFrom) === 0\n ) {\n startIdx++\n }\n }\n\n // Find end index\n let endIdx = this.sortedValues.length\n if (normalizedTo !== undefined) {\n endIdx = findInsertPositionInArray(\n this.sortedValues,\n normalizedTo,\n this.compareFn,\n )\n // If inclusive and we found the value, include it\n if (\n toInclusive &&\n endIdx < this.sortedValues.length &&\n this.compareFn(this.sortedValues[endIdx], normalizedTo) === 0\n ) {\n endIdx++\n }\n }\n\n // Collect all keys in range\n for (let i = startIdx; i < endIdx; i++) {\n const keys = this.valueMap.get(this.sortedValues[i])\n if (keys) {\n keys.forEach((key) => result.add(key))\n }\n }\n\n return result\n }\n\n /**\n * Performs a reversed range query\n */\n rangeQueryReversed(options: RangeQueryOptions = {}): Set<TKey> {\n const { from, to, fromInclusive = true, toInclusive = true } = options\n\n // Swap from/to and fromInclusive/toInclusive to handle reversed ranges\n // If to is undefined, we want to start from the end (max value)\n // If from is undefined, we want to end at the beginning (min value)\n const swappedFrom =\n to ??\n (this.sortedValues.length > 0\n ? this.sortedValues[this.sortedValues.length - 1]\n : undefined)\n const swappedTo =\n from ?? (this.sortedValues.length > 0 ? this.sortedValues[0] : undefined)\n\n return this.rangeQuery({\n from: swappedFrom,\n to: swappedTo,\n fromInclusive: toInclusive,\n toInclusive: fromInclusive,\n })\n }\n\n /**\n * Returns the next n items in sorted order\n */\n take(n: number, from?: any, filterFn?: (key: TKey) => boolean): Array<TKey> {\n const result: Array<TKey> = []\n\n let startIdx = 0\n if (from !== undefined) {\n const normalizedFrom = normalizeValue(from)\n startIdx = findInsertPositionInArray(\n this.sortedValues,\n normalizedFrom,\n this.compareFn,\n )\n // Skip past the 'from' value (exclusive)\n while (\n startIdx < this.sortedValues.length &&\n this.compareFn(this.sortedValues[startIdx], normalizedFrom) <= 0\n ) {\n startIdx++\n }\n }\n\n for (\n let i = startIdx;\n i < this.sortedValues.length && result.length < n;\n i++\n ) {\n const keys = this.valueMap.get(this.sortedValues[i])\n if (keys) {\n for (const key of keys) {\n if (result.length >= n) break\n if (!filterFn || filterFn(key)) {\n result.push(key)\n }\n }\n }\n }\n\n return result\n }\n\n /**\n * Returns the next n items in reverse sorted order\n */\n takeReversed(\n n: number,\n from?: any,\n filterFn?: (key: TKey) => boolean,\n ): Array<TKey> {\n const result: Array<TKey> = []\n\n let startIdx = this.sortedValues.length - 1\n if (from !== undefined) {\n const normalizedFrom = normalizeValue(from)\n startIdx =\n findInsertPositionInArray(\n this.sortedValues,\n normalizedFrom,\n this.compareFn,\n ) - 1\n // Skip past the 'from' value (exclusive)\n while (\n startIdx >= 0 &&\n this.compareFn(this.sortedValues[startIdx], normalizedFrom) >= 0\n ) {\n startIdx--\n }\n }\n\n for (let i = startIdx; i >= 0 && result.length < n; i--) {\n const keys = this.valueMap.get(this.sortedValues[i])\n if (keys) {\n for (const key of keys) {\n if (result.length >= n) break\n if (!filterFn || filterFn(key)) {\n result.push(key)\n }\n }\n }\n }\n\n return result\n }\n\n /**\n * Returns the first n items in sorted order (from the start)\n */\n takeFromStart(n: number, filterFn?: (key: TKey) => boolean): Array<TKey> {\n const result: Array<TKey> = []\n for (let i = 0; i < this.sortedValues.length && result.length < n; i++) {\n const keys = this.valueMap.get(this.sortedValues[i])\n if (keys) {\n for (const key of keys) {\n if (result.length >= n) break\n if (!filterFn || filterFn(key)) {\n result.push(key)\n }\n }\n }\n }\n return result\n }\n\n /**\n * Returns the first n items in reverse sorted order (from the end)\n */\n takeReversedFromEnd(\n n: number,\n filterFn?: (key: TKey) => boolean,\n ): Array<TKey> {\n const result: Array<TKey> = []\n for (\n let i = this.sortedValues.length - 1;\n i >= 0 && result.length < n;\n i--\n ) {\n const keys = this.valueMap.get(this.sortedValues[i])\n if (keys) {\n for (const key of keys) {\n if (result.length >= n) break\n if (!filterFn || filterFn(key)) {\n result.push(key)\n }\n }\n }\n }\n return result\n }\n\n /**\n * Performs an IN array lookup - O(k) where k is values.length\n */\n inArrayLookup(values: Array<any>): Set<TKey> {\n const result = new Set<TKey>()\n\n for (const value of values) {\n const normalizedValue = normalizeValue(value)\n const keys = this.valueMap.get(normalizedValue)\n if (keys) {\n keys.forEach((key) => result.add(key))\n }\n }\n\n return result\n }\n\n // Getter methods for testing/compatibility\n get indexedKeysSet(): Set<TKey> {\n return this.indexedKeys\n }\n\n get orderedEntriesArray(): Array<[any, Set<TKey>]> {\n return this.sortedValues.map((value) => [\n value,\n this.valueMap.get(value) ?? new Set(),\n ])\n }\n\n get orderedEntriesArrayReversed(): Array<[any, Set<TKey>]> {\n const result: Array<[any, Set<TKey>]> = []\n for (let i = this.sortedValues.length - 1; i >= 0; i--) {\n const value = this.sortedValues[i]\n result.push([value, this.valueMap.get(value) ?? new Set()])\n }\n return result\n }\n\n get valueMapData(): Map<any, Set<TKey>> {\n return this.valueMap\n }\n}\n"],"names":[],"mappings":"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"}