@substrate-ai/core 0.19.54

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 (486) hide show
  1. package/README.md +55 -0
  2. package/dist/__tests__/adapter.test.d.ts +12 -0
  3. package/dist/__tests__/adapter.test.d.ts.map +1 -0
  4. package/dist/__tests__/adapter.test.js +259 -0
  5. package/dist/__tests__/adapter.test.js.map +1 -0
  6. package/dist/__tests__/event-bus.test.d.ts +14 -0
  7. package/dist/__tests__/event-bus.test.d.ts.map +1 -0
  8. package/dist/__tests__/event-bus.test.js +199 -0
  9. package/dist/__tests__/event-bus.test.js.map +1 -0
  10. package/dist/__tests__/output-quality.test.d.ts +8 -0
  11. package/dist/__tests__/output-quality.test.d.ts.map +1 -0
  12. package/dist/__tests__/output-quality.test.js +166 -0
  13. package/dist/__tests__/output-quality.test.js.map +1 -0
  14. package/dist/__tests__/schema-suffix.test.d.ts +9 -0
  15. package/dist/__tests__/schema-suffix.test.d.ts.map +1 -0
  16. package/dist/__tests__/schema-suffix.test.js +126 -0
  17. package/dist/__tests__/schema-suffix.test.js.map +1 -0
  18. package/dist/__tests__/yaml-parser.test.d.ts +18 -0
  19. package/dist/__tests__/yaml-parser.test.d.ts.map +1 -0
  20. package/dist/__tests__/yaml-parser.test.js +475 -0
  21. package/dist/__tests__/yaml-parser.test.js.map +1 -0
  22. package/dist/__type-checks__.d.ts +11 -0
  23. package/dist/__type-checks__.d.ts.map +1 -0
  24. package/dist/__type-checks__.js +19 -0
  25. package/dist/__type-checks__.js.map +1 -0
  26. package/dist/adapters/__tests__/adapter-output-normalizer.test.d.ts +12 -0
  27. package/dist/adapters/__tests__/adapter-output-normalizer.test.d.ts.map +1 -0
  28. package/dist/adapters/__tests__/adapter-output-normalizer.test.js +193 -0
  29. package/dist/adapters/__tests__/adapter-output-normalizer.test.js.map +1 -0
  30. package/dist/adapters/adapter-format-error.d.ts +35 -0
  31. package/dist/adapters/adapter-format-error.d.ts.map +1 -0
  32. package/dist/adapters/adapter-format-error.js +38 -0
  33. package/dist/adapters/adapter-format-error.js.map +1 -0
  34. package/dist/adapters/adapter-output-normalizer.d.ts +50 -0
  35. package/dist/adapters/adapter-output-normalizer.d.ts.map +1 -0
  36. package/dist/adapters/adapter-output-normalizer.js +233 -0
  37. package/dist/adapters/adapter-output-normalizer.js.map +1 -0
  38. package/dist/adapters/adapter-registry.d.ts +50 -0
  39. package/dist/adapters/adapter-registry.d.ts.map +1 -0
  40. package/dist/adapters/adapter-registry.js +101 -0
  41. package/dist/adapters/adapter-registry.js.map +1 -0
  42. package/dist/adapters/claude-adapter.d.ts +59 -0
  43. package/dist/adapters/claude-adapter.d.ts.map +1 -0
  44. package/dist/adapters/claude-adapter.js +367 -0
  45. package/dist/adapters/claude-adapter.js.map +1 -0
  46. package/dist/adapters/codex-adapter.d.ts +64 -0
  47. package/dist/adapters/codex-adapter.d.ts.map +1 -0
  48. package/dist/adapters/codex-adapter.js +263 -0
  49. package/dist/adapters/codex-adapter.js.map +1 -0
  50. package/dist/adapters/gemini-adapter.d.ts +57 -0
  51. package/dist/adapters/gemini-adapter.d.ts.map +1 -0
  52. package/dist/adapters/gemini-adapter.js +311 -0
  53. package/dist/adapters/gemini-adapter.js.map +1 -0
  54. package/dist/adapters/index.d.ts +10 -0
  55. package/dist/adapters/index.d.ts.map +1 -0
  56. package/dist/adapters/index.js +14 -0
  57. package/dist/adapters/index.js.map +1 -0
  58. package/dist/adapters/schemas.d.ts +137 -0
  59. package/dist/adapters/schemas.d.ts.map +1 -0
  60. package/dist/adapters/schemas.js +140 -0
  61. package/dist/adapters/schemas.js.map +1 -0
  62. package/dist/adapters/types.d.ts +245 -0
  63. package/dist/adapters/types.d.ts.map +1 -0
  64. package/dist/adapters/types.js +6 -0
  65. package/dist/adapters/types.js.map +1 -0
  66. package/dist/adapters/worker-adapter.d.ts +188 -0
  67. package/dist/adapters/worker-adapter.d.ts.map +1 -0
  68. package/dist/adapters/worker-adapter.js +19 -0
  69. package/dist/adapters/worker-adapter.js.map +1 -0
  70. package/dist/budget/budget-tracker.d.ts +22 -0
  71. package/dist/budget/budget-tracker.d.ts.map +1 -0
  72. package/dist/budget/budget-tracker.js +39 -0
  73. package/dist/budget/budget-tracker.js.map +1 -0
  74. package/dist/budget/index.d.ts +6 -0
  75. package/dist/budget/index.d.ts.map +1 -0
  76. package/dist/budget/index.js +5 -0
  77. package/dist/budget/index.js.map +1 -0
  78. package/dist/config/config-migrator.d.ts +58 -0
  79. package/dist/config/config-migrator.d.ts.map +1 -0
  80. package/dist/config/config-migrator.js +158 -0
  81. package/dist/config/config-migrator.js.map +1 -0
  82. package/dist/config/config-system-impl.d.ts +63 -0
  83. package/dist/config/config-system-impl.d.ts.map +1 -0
  84. package/dist/config/config-system-impl.js +364 -0
  85. package/dist/config/config-system-impl.js.map +1 -0
  86. package/dist/config/config-watcher.d.ts +59 -0
  87. package/dist/config/config-watcher.d.ts.map +1 -0
  88. package/dist/config/config-watcher.js +137 -0
  89. package/dist/config/config-watcher.js.map +1 -0
  90. package/dist/config/defaults.d.ts +13 -0
  91. package/dist/config/defaults.d.ts.map +1 -0
  92. package/dist/config/defaults.js +62 -0
  93. package/dist/config/defaults.js.map +1 -0
  94. package/dist/config/errors.d.ts +25 -0
  95. package/dist/config/errors.d.ts.map +1 -0
  96. package/dist/config/errors.js +49 -0
  97. package/dist/config/errors.js.map +1 -0
  98. package/dist/config/index.d.ts +19 -0
  99. package/dist/config/index.d.ts.map +1 -0
  100. package/dist/config/index.js +39 -0
  101. package/dist/config/index.js.map +1 -0
  102. package/dist/config/types.d.ts +456 -0
  103. package/dist/config/types.d.ts.map +1 -0
  104. package/dist/config/types.js +174 -0
  105. package/dist/config/types.js.map +1 -0
  106. package/dist/config/version-utils.d.ts +39 -0
  107. package/dist/config/version-utils.d.ts.map +1 -0
  108. package/dist/config/version-utils.js +66 -0
  109. package/dist/config/version-utils.js.map +1 -0
  110. package/dist/context/index.d.ts +2 -0
  111. package/dist/context/index.d.ts.map +1 -0
  112. package/dist/context/index.js +2 -0
  113. package/dist/context/index.js.map +1 -0
  114. package/dist/context/types.d.ts +113 -0
  115. package/dist/context/types.d.ts.map +1 -0
  116. package/dist/context/types.js +59 -0
  117. package/dist/context/types.js.map +1 -0
  118. package/dist/cost-tracker/cost-tracker-impl.d.ts +51 -0
  119. package/dist/cost-tracker/cost-tracker-impl.d.ts.map +1 -0
  120. package/dist/cost-tracker/cost-tracker-impl.js +85 -0
  121. package/dist/cost-tracker/cost-tracker-impl.js.map +1 -0
  122. package/dist/cost-tracker/cost-tracker-subscriber.d.ts +31 -0
  123. package/dist/cost-tracker/cost-tracker-subscriber.d.ts.map +1 -0
  124. package/dist/cost-tracker/cost-tracker-subscriber.js +116 -0
  125. package/dist/cost-tracker/cost-tracker-subscriber.js.map +1 -0
  126. package/dist/cost-tracker/index.d.ts +11 -0
  127. package/dist/cost-tracker/index.d.ts.map +1 -0
  128. package/dist/cost-tracker/index.js +7 -0
  129. package/dist/cost-tracker/index.js.map +1 -0
  130. package/dist/cost-tracker/token-rates.d.ts +25 -0
  131. package/dist/cost-tracker/token-rates.d.ts.map +1 -0
  132. package/dist/cost-tracker/token-rates.js +99 -0
  133. package/dist/cost-tracker/token-rates.js.map +1 -0
  134. package/dist/cost-tracker/types.d.ts +6 -0
  135. package/dist/cost-tracker/types.d.ts.map +1 -0
  136. package/dist/cost-tracker/types.js +2 -0
  137. package/dist/cost-tracker/types.js.map +1 -0
  138. package/dist/dispatch/dispatcher-impl.d.ts +92 -0
  139. package/dist/dispatch/dispatcher-impl.d.ts.map +1 -0
  140. package/dist/dispatch/dispatcher-impl.js +847 -0
  141. package/dist/dispatch/dispatcher-impl.js.map +1 -0
  142. package/dist/dispatch/index.d.ts +15 -0
  143. package/dist/dispatch/index.d.ts.map +1 -0
  144. package/dist/dispatch/index.js +14 -0
  145. package/dist/dispatch/index.js.map +1 -0
  146. package/dist/dispatch/interface-change-detector.d.ts +46 -0
  147. package/dist/dispatch/interface-change-detector.d.ts.map +1 -0
  148. package/dist/dispatch/interface-change-detector.js +135 -0
  149. package/dist/dispatch/interface-change-detector.js.map +1 -0
  150. package/dist/dispatch/output-quality.d.ts +43 -0
  151. package/dist/dispatch/output-quality.d.ts.map +1 -0
  152. package/dist/dispatch/output-quality.js +148 -0
  153. package/dist/dispatch/output-quality.js.map +1 -0
  154. package/dist/dispatch/types.d.ts +271 -0
  155. package/dist/dispatch/types.d.ts.map +1 -0
  156. package/dist/dispatch/types.js +76 -0
  157. package/dist/dispatch/types.js.map +1 -0
  158. package/dist/dispatch/yaml-parser.d.ts +40 -0
  159. package/dist/dispatch/yaml-parser.d.ts.map +1 -0
  160. package/dist/dispatch/yaml-parser.js +323 -0
  161. package/dist/dispatch/yaml-parser.js.map +1 -0
  162. package/dist/events/core-events.d.ts +288 -0
  163. package/dist/events/core-events.d.ts.map +1 -0
  164. package/dist/events/core-events.js +10 -0
  165. package/dist/events/core-events.js.map +1 -0
  166. package/dist/events/event-bus.d.ts +55 -0
  167. package/dist/events/event-bus.d.ts.map +1 -0
  168. package/dist/events/event-bus.js +52 -0
  169. package/dist/events/event-bus.js.map +1 -0
  170. package/dist/events/index.d.ts +9 -0
  171. package/dist/events/index.d.ts.map +1 -0
  172. package/dist/events/index.js +6 -0
  173. package/dist/events/index.js.map +1 -0
  174. package/dist/events/types.d.ts +21 -0
  175. package/dist/events/types.d.ts.map +1 -0
  176. package/dist/events/types.js +8 -0
  177. package/dist/events/types.js.map +1 -0
  178. package/dist/git/git-manager.d.ts +31 -0
  179. package/dist/git/git-manager.d.ts.map +1 -0
  180. package/dist/git/git-manager.js +46 -0
  181. package/dist/git/git-manager.js.map +1 -0
  182. package/dist/git/git-utils.d.ts +166 -0
  183. package/dist/git/git-utils.d.ts.map +1 -0
  184. package/dist/git/git-utils.js +347 -0
  185. package/dist/git/git-utils.js.map +1 -0
  186. package/dist/git/git-worktree-manager-impl.d.ts +58 -0
  187. package/dist/git/git-worktree-manager-impl.d.ts.map +1 -0
  188. package/dist/git/git-worktree-manager-impl.js +336 -0
  189. package/dist/git/git-worktree-manager-impl.js.map +1 -0
  190. package/dist/git/git-worktree-manager.d.ts +122 -0
  191. package/dist/git/git-worktree-manager.d.ts.map +1 -0
  192. package/dist/git/git-worktree-manager.js +14 -0
  193. package/dist/git/git-worktree-manager.js.map +1 -0
  194. package/dist/git/index.d.ts +11 -0
  195. package/dist/git/index.d.ts.map +1 -0
  196. package/dist/git/index.js +8 -0
  197. package/dist/git/index.js.map +1 -0
  198. package/dist/index.d.ts +33 -0
  199. package/dist/index.d.ts.map +1 -0
  200. package/dist/index.js +47 -0
  201. package/dist/index.js.map +1 -0
  202. package/dist/llm/client.d.ts +42 -0
  203. package/dist/llm/client.d.ts.map +1 -0
  204. package/dist/llm/client.js +27 -0
  205. package/dist/llm/client.js.map +1 -0
  206. package/dist/monitor/index.d.ts +15 -0
  207. package/dist/monitor/index.d.ts.map +1 -0
  208. package/dist/monitor/index.js +9 -0
  209. package/dist/monitor/index.js.map +1 -0
  210. package/dist/monitor/monitor-agent-impl.d.ts +56 -0
  211. package/dist/monitor/monitor-agent-impl.d.ts.map +1 -0
  212. package/dist/monitor/monitor-agent-impl.js +178 -0
  213. package/dist/monitor/monitor-agent-impl.js.map +1 -0
  214. package/dist/monitor/monitor-agent.d.ts +36 -0
  215. package/dist/monitor/monitor-agent.d.ts.map +1 -0
  216. package/dist/monitor/monitor-agent.js +6 -0
  217. package/dist/monitor/monitor-agent.js.map +1 -0
  218. package/dist/monitor/performance-aggregates.d.ts +41 -0
  219. package/dist/monitor/performance-aggregates.d.ts.map +1 -0
  220. package/dist/monitor/performance-aggregates.js +6 -0
  221. package/dist/monitor/performance-aggregates.js.map +1 -0
  222. package/dist/monitor/recommendation-engine.d.ts +27 -0
  223. package/dist/monitor/recommendation-engine.d.ts.map +1 -0
  224. package/dist/monitor/recommendation-engine.js +140 -0
  225. package/dist/monitor/recommendation-engine.js.map +1 -0
  226. package/dist/monitor/recommendation-types.d.ts +30 -0
  227. package/dist/monitor/recommendation-types.d.ts.map +1 -0
  228. package/dist/monitor/recommendation-types.js +43 -0
  229. package/dist/monitor/recommendation-types.js.map +1 -0
  230. package/dist/monitor/report-generator.d.ts +48 -0
  231. package/dist/monitor/report-generator.d.ts.map +1 -0
  232. package/dist/monitor/report-generator.js +123 -0
  233. package/dist/monitor/report-generator.js.map +1 -0
  234. package/dist/monitor/task-type-classifier.d.ts +19 -0
  235. package/dist/monitor/task-type-classifier.d.ts.map +1 -0
  236. package/dist/monitor/task-type-classifier.js +68 -0
  237. package/dist/monitor/task-type-classifier.js.map +1 -0
  238. package/dist/persistence/adapter.d.ts +4 -0
  239. package/dist/persistence/adapter.d.ts.map +1 -0
  240. package/dist/persistence/adapter.js +56 -0
  241. package/dist/persistence/adapter.js.map +1 -0
  242. package/dist/persistence/cost-types.d.ts +87 -0
  243. package/dist/persistence/cost-types.d.ts.map +1 -0
  244. package/dist/persistence/cost-types.js +14 -0
  245. package/dist/persistence/cost-types.js.map +1 -0
  246. package/dist/persistence/dolt-adapter-transaction.test.d.ts +10 -0
  247. package/dist/persistence/dolt-adapter-transaction.test.d.ts.map +1 -0
  248. package/dist/persistence/dolt-adapter-transaction.test.js +359 -0
  249. package/dist/persistence/dolt-adapter-transaction.test.js.map +1 -0
  250. package/dist/persistence/dolt-adapter.d.ts +77 -0
  251. package/dist/persistence/dolt-adapter.d.ts.map +1 -0
  252. package/dist/persistence/dolt-adapter.js +98 -0
  253. package/dist/persistence/dolt-adapter.js.map +1 -0
  254. package/dist/persistence/dolt-client.d.ts +69 -0
  255. package/dist/persistence/dolt-client.d.ts.map +1 -0
  256. package/dist/persistence/dolt-client.js +278 -0
  257. package/dist/persistence/dolt-client.js.map +1 -0
  258. package/dist/persistence/dolt-errors.d.ts +10 -0
  259. package/dist/persistence/dolt-errors.d.ts.map +1 -0
  260. package/dist/persistence/dolt-errors.js +15 -0
  261. package/dist/persistence/dolt-errors.js.map +1 -0
  262. package/dist/persistence/dolt-init.d.ts +64 -0
  263. package/dist/persistence/dolt-init.d.ts.map +1 -0
  264. package/dist/persistence/dolt-init.js +233 -0
  265. package/dist/persistence/dolt-init.js.map +1 -0
  266. package/dist/persistence/index.d.ts +23 -0
  267. package/dist/persistence/index.d.ts.map +1 -0
  268. package/dist/persistence/index.js +23 -0
  269. package/dist/persistence/index.js.map +1 -0
  270. package/dist/persistence/memory-adapter.d.ts +156 -0
  271. package/dist/persistence/memory-adapter.d.ts.map +1 -0
  272. package/dist/persistence/memory-adapter.js +1167 -0
  273. package/dist/persistence/memory-adapter.js.map +1 -0
  274. package/dist/persistence/monitor-database.d.ts +113 -0
  275. package/dist/persistence/monitor-database.d.ts.map +1 -0
  276. package/dist/persistence/monitor-database.js +345 -0
  277. package/dist/persistence/monitor-database.js.map +1 -0
  278. package/dist/persistence/queries/amendments.d.ts +91 -0
  279. package/dist/persistence/queries/amendments.d.ts.map +1 -0
  280. package/dist/persistence/queries/amendments.js +185 -0
  281. package/dist/persistence/queries/amendments.js.map +1 -0
  282. package/dist/persistence/queries/cost.d.ts +130 -0
  283. package/dist/persistence/queries/cost.d.ts.map +1 -0
  284. package/dist/persistence/queries/cost.js +384 -0
  285. package/dist/persistence/queries/cost.js.map +1 -0
  286. package/dist/persistence/queries/decisions.d.ts +131 -0
  287. package/dist/persistence/queries/decisions.d.ts.map +1 -0
  288. package/dist/persistence/queries/decisions.js +339 -0
  289. package/dist/persistence/queries/decisions.js.map +1 -0
  290. package/dist/persistence/queries/metrics.d.ts +155 -0
  291. package/dist/persistence/queries/metrics.d.ts.map +1 -0
  292. package/dist/persistence/queries/metrics.js +237 -0
  293. package/dist/persistence/queries/metrics.js.map +1 -0
  294. package/dist/persistence/queries/retry-escalated.d.ts +35 -0
  295. package/dist/persistence/queries/retry-escalated.d.ts.map +1 -0
  296. package/dist/persistence/queries/retry-escalated.js +83 -0
  297. package/dist/persistence/queries/retry-escalated.js.map +1 -0
  298. package/dist/persistence/schema-version.d.ts +89 -0
  299. package/dist/persistence/schema-version.d.ts.map +1 -0
  300. package/dist/persistence/schema-version.js +67 -0
  301. package/dist/persistence/schema-version.js.map +1 -0
  302. package/dist/persistence/schema.d.ts +26 -0
  303. package/dist/persistence/schema.d.ts.map +1 -0
  304. package/dist/persistence/schema.js +509 -0
  305. package/dist/persistence/schema.js.map +1 -0
  306. package/dist/persistence/schemas/decisions.d.ts +176 -0
  307. package/dist/persistence/schemas/decisions.d.ts.map +1 -0
  308. package/dist/persistence/schemas/decisions.js +139 -0
  309. package/dist/persistence/schemas/decisions.js.map +1 -0
  310. package/dist/persistence/schemas/operational.d.ts +194 -0
  311. package/dist/persistence/schemas/operational.d.ts.map +1 -0
  312. package/dist/persistence/schemas/operational.js +197 -0
  313. package/dist/persistence/schemas/operational.js.map +1 -0
  314. package/dist/persistence/types.d.ts +98 -0
  315. package/dist/persistence/types.d.ts.map +1 -0
  316. package/dist/persistence/types.js +22 -0
  317. package/dist/persistence/types.js.map +1 -0
  318. package/dist/quality-gates/index.d.ts +2 -0
  319. package/dist/quality-gates/index.d.ts.map +1 -0
  320. package/dist/quality-gates/index.js +2 -0
  321. package/dist/quality-gates/index.js.map +1 -0
  322. package/dist/quality-gates/types.d.ts +106 -0
  323. package/dist/quality-gates/types.d.ts.map +1 -0
  324. package/dist/quality-gates/types.js +5 -0
  325. package/dist/quality-gates/types.js.map +1 -0
  326. package/dist/routing/index.d.ts +19 -0
  327. package/dist/routing/index.d.ts.map +1 -0
  328. package/dist/routing/index.js +32 -0
  329. package/dist/routing/index.js.map +1 -0
  330. package/dist/routing/model-routing-config.d.ts +75 -0
  331. package/dist/routing/model-routing-config.d.ts.map +1 -0
  332. package/dist/routing/model-routing-config.js +110 -0
  333. package/dist/routing/model-routing-config.js.map +1 -0
  334. package/dist/routing/model-routing-resolver.d.ts +48 -0
  335. package/dist/routing/model-routing-resolver.d.ts.map +1 -0
  336. package/dist/routing/model-routing-resolver.js +105 -0
  337. package/dist/routing/model-routing-resolver.js.map +1 -0
  338. package/dist/routing/model-tier.d.ts +21 -0
  339. package/dist/routing/model-tier.d.ts.map +1 -0
  340. package/dist/routing/model-tier.js +34 -0
  341. package/dist/routing/model-tier.js.map +1 -0
  342. package/dist/routing/provider-status.d.ts +99 -0
  343. package/dist/routing/provider-status.d.ts.map +1 -0
  344. package/dist/routing/provider-status.js +163 -0
  345. package/dist/routing/provider-status.js.map +1 -0
  346. package/dist/routing/routing-decision.d.ts +127 -0
  347. package/dist/routing/routing-decision.d.ts.map +1 -0
  348. package/dist/routing/routing-decision.js +111 -0
  349. package/dist/routing/routing-decision.js.map +1 -0
  350. package/dist/routing/routing-engine-impl.d.ts +132 -0
  351. package/dist/routing/routing-engine-impl.d.ts.map +1 -0
  352. package/dist/routing/routing-engine-impl.js +450 -0
  353. package/dist/routing/routing-engine-impl.js.map +1 -0
  354. package/dist/routing/routing-engine.d.ts +83 -0
  355. package/dist/routing/routing-engine.d.ts.map +1 -0
  356. package/dist/routing/routing-engine.js +24 -0
  357. package/dist/routing/routing-engine.js.map +1 -0
  358. package/dist/routing/routing-policy.d.ts +138 -0
  359. package/dist/routing/routing-policy.d.ts.map +1 -0
  360. package/dist/routing/routing-policy.js +159 -0
  361. package/dist/routing/routing-policy.js.map +1 -0
  362. package/dist/routing/routing-recommender.d.ts +60 -0
  363. package/dist/routing/routing-recommender.d.ts.map +1 -0
  364. package/dist/routing/routing-recommender.js +209 -0
  365. package/dist/routing/routing-recommender.js.map +1 -0
  366. package/dist/routing/routing-telemetry.d.ts +36 -0
  367. package/dist/routing/routing-telemetry.d.ts.map +1 -0
  368. package/dist/routing/routing-telemetry.js +39 -0
  369. package/dist/routing/routing-telemetry.js.map +1 -0
  370. package/dist/routing/routing-token-accumulator.d.ts +68 -0
  371. package/dist/routing/routing-token-accumulator.d.ts.map +1 -0
  372. package/dist/routing/routing-token-accumulator.js +111 -0
  373. package/dist/routing/routing-token-accumulator.js.map +1 -0
  374. package/dist/routing/routing-tuner.d.ts +69 -0
  375. package/dist/routing/routing-tuner.d.ts.map +1 -0
  376. package/dist/routing/routing-tuner.js +217 -0
  377. package/dist/routing/routing-tuner.js.map +1 -0
  378. package/dist/routing/types.d.ts +152 -0
  379. package/dist/routing/types.d.ts.map +1 -0
  380. package/dist/routing/types.js +13 -0
  381. package/dist/routing/types.js.map +1 -0
  382. package/dist/supervisor/analysis.d.ts +178 -0
  383. package/dist/supervisor/analysis.d.ts.map +1 -0
  384. package/dist/supervisor/analysis.js +420 -0
  385. package/dist/supervisor/analysis.js.map +1 -0
  386. package/dist/supervisor/experimenter.d.ts +118 -0
  387. package/dist/supervisor/experimenter.d.ts.map +1 -0
  388. package/dist/supervisor/experimenter.js +493 -0
  389. package/dist/supervisor/experimenter.js.map +1 -0
  390. package/dist/supervisor/index.d.ts +13 -0
  391. package/dist/supervisor/index.d.ts.map +1 -0
  392. package/dist/supervisor/index.js +11 -0
  393. package/dist/supervisor/index.js.map +1 -0
  394. package/dist/telemetry/batch-buffer.d.ts +53 -0
  395. package/dist/telemetry/batch-buffer.d.ts.map +1 -0
  396. package/dist/telemetry/batch-buffer.js +83 -0
  397. package/dist/telemetry/batch-buffer.js.map +1 -0
  398. package/dist/telemetry/categorizer.d.ts +65 -0
  399. package/dist/telemetry/categorizer.d.ts.map +1 -0
  400. package/dist/telemetry/categorizer.js +338 -0
  401. package/dist/telemetry/categorizer.js.map +1 -0
  402. package/dist/telemetry/consumer-analyzer.d.ts +53 -0
  403. package/dist/telemetry/consumer-analyzer.d.ts.map +1 -0
  404. package/dist/telemetry/consumer-analyzer.js +182 -0
  405. package/dist/telemetry/consumer-analyzer.js.map +1 -0
  406. package/dist/telemetry/cost-table.d.ts +36 -0
  407. package/dist/telemetry/cost-table.d.ts.map +1 -0
  408. package/dist/telemetry/cost-table.js +127 -0
  409. package/dist/telemetry/cost-table.js.map +1 -0
  410. package/dist/telemetry/efficiency-scorer.d.ts +103 -0
  411. package/dist/telemetry/efficiency-scorer.d.ts.map +1 -0
  412. package/dist/telemetry/efficiency-scorer.js +311 -0
  413. package/dist/telemetry/efficiency-scorer.js.map +1 -0
  414. package/dist/telemetry/index.d.ts +28 -0
  415. package/dist/telemetry/index.d.ts.map +1 -0
  416. package/dist/telemetry/index.js +37 -0
  417. package/dist/telemetry/index.js.map +1 -0
  418. package/dist/telemetry/ingestion-server.d.ts +99 -0
  419. package/dist/telemetry/ingestion-server.d.ts.map +1 -0
  420. package/dist/telemetry/ingestion-server.js +315 -0
  421. package/dist/telemetry/ingestion-server.js.map +1 -0
  422. package/dist/telemetry/log-turn-analyzer.d.ts +35 -0
  423. package/dist/telemetry/log-turn-analyzer.d.ts.map +1 -0
  424. package/dist/telemetry/log-turn-analyzer.js +132 -0
  425. package/dist/telemetry/log-turn-analyzer.js.map +1 -0
  426. package/dist/telemetry/normalizer.d.ts +43 -0
  427. package/dist/telemetry/normalizer.d.ts.map +1 -0
  428. package/dist/telemetry/normalizer.js +320 -0
  429. package/dist/telemetry/normalizer.js.map +1 -0
  430. package/dist/telemetry/recommender.d.ts +116 -0
  431. package/dist/telemetry/recommender.d.ts.map +1 -0
  432. package/dist/telemetry/recommender.js +532 -0
  433. package/dist/telemetry/recommender.js.map +1 -0
  434. package/dist/telemetry/source-detector.d.ts +19 -0
  435. package/dist/telemetry/source-detector.d.ts.map +1 -0
  436. package/dist/telemetry/source-detector.js +73 -0
  437. package/dist/telemetry/source-detector.js.map +1 -0
  438. package/dist/telemetry/task-baselines.d.ts +30 -0
  439. package/dist/telemetry/task-baselines.d.ts.map +1 -0
  440. package/dist/telemetry/task-baselines.js +44 -0
  441. package/dist/telemetry/task-baselines.js.map +1 -0
  442. package/dist/telemetry/telemetry-pipeline.d.ts +122 -0
  443. package/dist/telemetry/telemetry-pipeline.d.ts.map +1 -0
  444. package/dist/telemetry/telemetry-pipeline.js +349 -0
  445. package/dist/telemetry/telemetry-pipeline.js.map +1 -0
  446. package/dist/telemetry/timestamp-normalizer.d.ts +32 -0
  447. package/dist/telemetry/timestamp-normalizer.d.ts.map +1 -0
  448. package/dist/telemetry/timestamp-normalizer.js +133 -0
  449. package/dist/telemetry/timestamp-normalizer.js.map +1 -0
  450. package/dist/telemetry/token-extractor.d.ts +57 -0
  451. package/dist/telemetry/token-extractor.d.ts.map +1 -0
  452. package/dist/telemetry/token-extractor.js +200 -0
  453. package/dist/telemetry/token-extractor.js.map +1 -0
  454. package/dist/telemetry/turn-analyzer.d.ts +34 -0
  455. package/dist/telemetry/turn-analyzer.d.ts.map +1 -0
  456. package/dist/telemetry/turn-analyzer.js +101 -0
  457. package/dist/telemetry/turn-analyzer.js.map +1 -0
  458. package/dist/telemetry/types.d.ts +456 -0
  459. package/dist/telemetry/types.d.ts.map +1 -0
  460. package/dist/telemetry/types.js +186 -0
  461. package/dist/telemetry/types.js.map +1 -0
  462. package/dist/types.d.ts +80 -0
  463. package/dist/types.d.ts.map +1 -0
  464. package/dist/types.js +6 -0
  465. package/dist/types.js.map +1 -0
  466. package/dist/version-manager/index.d.ts +11 -0
  467. package/dist/version-manager/index.d.ts.map +1 -0
  468. package/dist/version-manager/index.js +8 -0
  469. package/dist/version-manager/index.js.map +1 -0
  470. package/dist/version-manager/update-checker.d.ts +44 -0
  471. package/dist/version-manager/update-checker.d.ts.map +1 -0
  472. package/dist/version-manager/update-checker.js +171 -0
  473. package/dist/version-manager/update-checker.js.map +1 -0
  474. package/dist/version-manager/version-cache.d.ts +42 -0
  475. package/dist/version-manager/version-cache.d.ts.map +1 -0
  476. package/dist/version-manager/version-cache.js +69 -0
  477. package/dist/version-manager/version-cache.js.map +1 -0
  478. package/dist/version-manager/version-manager-impl.d.ts +81 -0
  479. package/dist/version-manager/version-manager-impl.d.ts.map +1 -0
  480. package/dist/version-manager/version-manager-impl.js +223 -0
  481. package/dist/version-manager/version-manager-impl.js.map +1 -0
  482. package/dist/version-manager/version-manager.d.ts +70 -0
  483. package/dist/version-manager/version-manager.d.ts.map +1 -0
  484. package/dist/version-manager/version-manager.js +8 -0
  485. package/dist/version-manager/version-manager.js.map +1 -0
  486. package/package.json +27 -0
@@ -0,0 +1,1167 @@
1
+ /**
2
+ * InMemoryDatabaseAdapter — satisfies the DatabaseAdapter interface using
3
+ * plain in-memory Maps and arrays. Designed for CI environments and unit
4
+ * tests where no external database is available.
5
+ *
6
+ * SQL support is intentionally limited to the patterns used by
7
+ * `src/persistence/queries/`:
8
+ * - CREATE TABLE [IF NOT EXISTS] name (col type, ...)
9
+ * - DROP TABLE [IF EXISTS] name
10
+ * - INSERT INTO name (cols) VALUES (vals)
11
+ * - SELECT * / cols FROM name [WHERE simple-conditions]
12
+ * - SELECT literal-expressions (no FROM clause)
13
+ * - UPDATE name SET col = val [WHERE simple-conditions]
14
+ * - DELETE FROM name [WHERE simple-conditions]
15
+ *
16
+ * WHERE clauses support simple equality conditions joined by AND.
17
+ * Transactions use a snapshot-and-restore pattern.
18
+ */
19
+ // ---------------------------------------------------------------------------
20
+ // InMemoryDatabaseAdapter
21
+ // ---------------------------------------------------------------------------
22
+ export class InMemoryDatabaseAdapter {
23
+ backendType = 'memory';
24
+ _tables = new Map();
25
+ _indexes = [];
26
+ /** Maps table name → auto-increment column name */
27
+ _autoIncrementCols = new Map();
28
+ /** Maps table name → last assigned auto-increment value */
29
+ _autoIncrementCounters = new Map();
30
+ /** Maps "tableName.colName" → default value expression ('CURRENT_TIMESTAMP' → ISO string, else literal) */
31
+ _columnDefaults = new Map();
32
+ /** Maps table name → array of primary key column names */
33
+ _primaryKeys = new Map();
34
+ /** Maps table name → ordered list of column names (from CREATE TABLE) */
35
+ _tableColumns = new Map();
36
+ // -------------------------------------------------------------------------
37
+ // DatabaseAdapter implementation
38
+ // -------------------------------------------------------------------------
39
+ async query(sql, params) {
40
+ const rows = this._execute(sql.trim(), params);
41
+ return rows;
42
+ }
43
+ async exec(sql) {
44
+ this._execute(sql.trim(), undefined);
45
+ }
46
+ // -------------------------------------------------------------------------
47
+ // SyncAdapter implementation
48
+ // -------------------------------------------------------------------------
49
+ querySync(sql, params) {
50
+ const rows = this._execute(sql.trim(), params);
51
+ return rows;
52
+ }
53
+ execSync(sql) {
54
+ this._execute(sql.trim(), undefined);
55
+ }
56
+ async transaction(fn) {
57
+ // Snapshot: deep-clone each table's rows and auto-increment counters
58
+ const snapshot = new Map();
59
+ for (const [name, rows] of this._tables) {
60
+ snapshot.set(name, rows.map((r) => ({ ...r })));
61
+ }
62
+ const counterSnapshot = new Map(this._autoIncrementCounters);
63
+ try {
64
+ const result = await fn(this);
65
+ return result;
66
+ }
67
+ catch (err) {
68
+ // Restore snapshot on failure (data + counters; schema/defaults are immutable per-table)
69
+ this._tables = snapshot;
70
+ this._autoIncrementCounters = counterSnapshot;
71
+ throw err;
72
+ }
73
+ }
74
+ async close() {
75
+ this._tables.clear();
76
+ }
77
+ /**
78
+ * Work graph not supported in InMemoryDatabaseAdapter.
79
+ * Returns `[]` to signal the caller to use the legacy discovery path.
80
+ */
81
+ async queryReadyStories() {
82
+ return [];
83
+ }
84
+ // -------------------------------------------------------------------------
85
+ // SQL execution dispatcher
86
+ // -------------------------------------------------------------------------
87
+ _execute(sql, params) {
88
+ const resolved = this._substituteParams(sql, params);
89
+ const upper = resolved.trimStart().toUpperCase();
90
+ if (/^CREATE\s+TABLE/i.test(upper)) {
91
+ return this._createTable(resolved);
92
+ }
93
+ if (/^DROP\s+TABLE/i.test(upper)) {
94
+ return this._dropTable(resolved);
95
+ }
96
+ if (/^CREATE\s+(?:UNIQUE\s+)?INDEX/i.test(upper)) {
97
+ return this._createIndex(resolved);
98
+ }
99
+ if (/^DROP\s+INDEX/i.test(upper)) {
100
+ return this._dropIndex(resolved);
101
+ }
102
+ if (/^CREATE\s+(?:OR\s+REPLACE\s+)?VIEW/i.test(upper)) {
103
+ // VIEWs are not supported in InMemoryDatabaseAdapter — treat as no-op.
104
+ return [];
105
+ }
106
+ if (/^INSERT\s+(?:IGNORE\s+)?INTO/i.test(upper)) {
107
+ return this._insert(resolved, /^INSERT\s+IGNORE\s+INTO/i.test(upper));
108
+ }
109
+ if (/^SELECT/i.test(upper)) {
110
+ return this._select(resolved);
111
+ }
112
+ if (/^UPDATE/i.test(upper)) {
113
+ return this._update(resolved);
114
+ }
115
+ if (/^DELETE\s+FROM/i.test(upper)) {
116
+ return this._delete(resolved);
117
+ }
118
+ // PRAGMA table_info(tableName) — return column metadata
119
+ if (/^PRAGMA\s+table_info\s*\(/i.test(upper)) {
120
+ return this._pragmaTableInfo(resolved);
121
+ }
122
+ // Unknown statement — silently ignore (BEGIN, COMMIT, ROLLBACK, other pragmas, etc.)
123
+ return [];
124
+ }
125
+ // -------------------------------------------------------------------------
126
+ // Parameter substitution
127
+ // -------------------------------------------------------------------------
128
+ /**
129
+ * Replace each `?` placeholder with an escaped literal value.
130
+ */
131
+ _substituteParams(sql, params) {
132
+ if (!params || params.length === 0)
133
+ return sql;
134
+ let idx = 0;
135
+ return sql.replace(/\?/g, () => {
136
+ const val = params[idx++];
137
+ if (val === null || val === undefined)
138
+ return 'NULL';
139
+ if (typeof val === 'number')
140
+ return String(val);
141
+ if (typeof val === 'boolean')
142
+ return val ? '1' : '0';
143
+ return `'${String(val).replace(/'/g, "''")}'`;
144
+ });
145
+ }
146
+ // -------------------------------------------------------------------------
147
+ // CREATE TABLE
148
+ // -------------------------------------------------------------------------
149
+ _createTable(sql) {
150
+ // CREATE TABLE [IF NOT EXISTS] tableName (...)
151
+ const m = /CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?(\w+)/i.exec(sql);
152
+ if (m) {
153
+ const name = m[1];
154
+ if (!this._tables.has(name)) {
155
+ this._tables.set(name, []);
156
+ }
157
+ // Detect AUTO_INCREMENT / AUTOINCREMENT column and DEFAULT values
158
+ const colDefsM = /\((.+)\)\s*$/is.exec(sql);
159
+ if (colDefsM) {
160
+ const colDefs = colDefsM[1];
161
+ // Find column with AUTO_INCREMENT
162
+ const aiMatch = /^\s*(\w+)\s+\w+.*?(?:AUTO_INCREMENT|AUTOINCREMENT)/im.exec(colDefs);
163
+ if (aiMatch) {
164
+ this._autoIncrementCols.set(name, aiMatch[1]);
165
+ if (!this._autoIncrementCounters.has(name)) {
166
+ this._autoIncrementCounters.set(name, 0);
167
+ }
168
+ }
169
+ // Find columns with DEFAULT values and PRIMARY KEY constraints
170
+ const colLines = this._splitTopLevelCommas(colDefs);
171
+ const pkCols = [];
172
+ for (const colLine of colLines) {
173
+ const trimmedLine = colLine.trim();
174
+ // Table-level PRIMARY KEY constraint: PRIMARY KEY (col1, col2, ...)
175
+ const tablePkM = /^PRIMARY\s+KEY\s*\(([^)]+)\)/i.exec(trimmedLine);
176
+ if (tablePkM) {
177
+ const cols = tablePkM[1].split(',').map((c) => c.trim().replace(/^[`"](.+)[`"]$/, '$1'));
178
+ pkCols.push(...cols);
179
+ continue;
180
+ }
181
+ // Column-level DEFAULT value
182
+ const defaultM = /^\s*(\w+)\s+\S+.*?\bDEFAULT\s+(.+?)(?:\s*,?\s*$)/i.exec(trimmedLine);
183
+ if (defaultM) {
184
+ const colName = defaultM[1];
185
+ const defaultVal = defaultM[2].trim();
186
+ this._columnDefaults.set(`${name}.${colName}`, defaultVal);
187
+ }
188
+ // Column-level PRIMARY KEY: colName TYPE ... PRIMARY KEY
189
+ const colPkM = /^(\w+)\s+\w+.*?\bPRIMARY\s+KEY\b/i.exec(trimmedLine);
190
+ if (colPkM && !/^PRIMARY\s+KEY\b/i.test(trimmedLine)) {
191
+ pkCols.push(colPkM[1]);
192
+ }
193
+ }
194
+ if (pkCols.length > 0 && !this._primaryKeys.has(name)) {
195
+ this._primaryKeys.set(name, pkCols);
196
+ }
197
+ // Track column names in definition order (skip table-level constraints)
198
+ if (!this._tableColumns.has(name)) {
199
+ const colNames = [];
200
+ for (const colLine of colLines) {
201
+ const trimmedLine = colLine.trim();
202
+ // Skip table-level constraints (PRIMARY KEY, UNIQUE, FOREIGN KEY, CHECK)
203
+ if (/^(?:PRIMARY\s+KEY|UNIQUE|FOREIGN\s+KEY|CHECK)\s*[\s(]/i.test(trimmedLine))
204
+ continue;
205
+ // Extract column name: first identifier (bare word or backtick/double-quote quoted)
206
+ const quotedM = /^[`"](\w+)[`"]\s+\S/i.exec(trimmedLine);
207
+ if (quotedM) {
208
+ colNames.push(quotedM[1]);
209
+ continue;
210
+ }
211
+ const bareM = /^(\w+)\s+\S/i.exec(trimmedLine);
212
+ if (bareM)
213
+ colNames.push(bareM[1]);
214
+ }
215
+ if (colNames.length > 0)
216
+ this._tableColumns.set(name, colNames);
217
+ }
218
+ }
219
+ }
220
+ return [];
221
+ }
222
+ // -------------------------------------------------------------------------
223
+ // DROP TABLE
224
+ // -------------------------------------------------------------------------
225
+ _dropTable(sql) {
226
+ const m = /DROP\s+TABLE\s+(?:IF\s+EXISTS\s+)?(\w+)/i.exec(sql);
227
+ if (m) {
228
+ this._tables.delete(m[1]);
229
+ }
230
+ return [];
231
+ }
232
+ // -------------------------------------------------------------------------
233
+ // CREATE INDEX
234
+ // -------------------------------------------------------------------------
235
+ _createIndex(sql) {
236
+ // CREATE [UNIQUE] INDEX [IF NOT EXISTS] indexName ON tableName (...)
237
+ const m = /CREATE\s+(?:UNIQUE\s+)?INDEX\s+(?:IF\s+NOT\s+EXISTS\s+)?(\w+)\s+ON\s+(\w+)/i.exec(sql);
238
+ if (m) {
239
+ const name = m[1];
240
+ const tbl_name = m[2];
241
+ // Don't add duplicates
242
+ if (!this._indexes.some((idx) => idx.name === name)) {
243
+ this._indexes.push({ name, tbl_name, type: 'index' });
244
+ }
245
+ }
246
+ return [];
247
+ }
248
+ // -------------------------------------------------------------------------
249
+ // DROP INDEX
250
+ // -------------------------------------------------------------------------
251
+ _dropIndex(sql) {
252
+ const m = /DROP\s+INDEX\s+(?:IF\s+EXISTS\s+)?(\w+)/i.exec(sql);
253
+ if (m) {
254
+ const name = m[1];
255
+ this._indexes = this._indexes.filter((idx) => idx.name !== name);
256
+ }
257
+ return [];
258
+ }
259
+ // -------------------------------------------------------------------------
260
+ // PRAGMA table_info
261
+ // -------------------------------------------------------------------------
262
+ /**
263
+ * Emulate PRAGMA table_info(tableName) — returns one row per column with
264
+ * {cid, name, type, notnull, dflt_value, pk} shape.
265
+ * Column names are derived from the CREATE TABLE definition order.
266
+ * This is sufficient for schema compatibility checks.
267
+ */
268
+ _pragmaTableInfo(sql) {
269
+ const m = /PRAGMA\s+table_info\s*\(\s*[`'"]?(\w+)[`'"]?\s*\)/i.exec(sql);
270
+ if (!m)
271
+ return [];
272
+ const tableName = m[1];
273
+ const colNames = this._tableColumns.get(tableName);
274
+ if (!colNames || colNames.length === 0)
275
+ return [];
276
+ const pkCols = this._primaryKeys.get(tableName) ?? [];
277
+ return colNames.map((name, cid) => ({
278
+ cid,
279
+ name,
280
+ type: 'TEXT',
281
+ notnull: 0,
282
+ dflt_value: null,
283
+ pk: pkCols.includes(name) ? 1 : 0,
284
+ }));
285
+ }
286
+ // -------------------------------------------------------------------------
287
+ // sqlite_master virtual table
288
+ // -------------------------------------------------------------------------
289
+ _selectFromSqliteMaster(sql) {
290
+ // Build virtual rows: tables + indexes
291
+ const masterRows = [];
292
+ for (const name of this._tables.keys()) {
293
+ masterRows.push({ type: 'table', name, tbl_name: name, rootpage: 0, sql: null });
294
+ }
295
+ for (const idx of this._indexes) {
296
+ masterRows.push({ type: 'index', name: idx.name, tbl_name: idx.tbl_name, rootpage: 0, sql: null });
297
+ }
298
+ // Extract WHERE clause conditions manually
299
+ const whereM = /WHERE\s+(.+?)(?:\s+ORDER\s+BY|\s+LIMIT|\s*$)/is.exec(sql);
300
+ let rows = masterRows;
301
+ if (whereM) {
302
+ rows = rows.filter((row) => this._matchWhere(whereM[1].trim(), row));
303
+ }
304
+ // Extract SELECT columns
305
+ const colsM = /SELECT\s+(.+?)\s+FROM\s+sqlite_master/is.exec(sql);
306
+ if (!colsM)
307
+ return rows;
308
+ const colsStr = colsM[1].trim();
309
+ if (colsStr === '*')
310
+ return rows;
311
+ return rows.map((row) => this._projectCols(colsStr, row));
312
+ }
313
+ // -------------------------------------------------------------------------
314
+ // INSERT
315
+ // -------------------------------------------------------------------------
316
+ _insert(sql, _ignoreConflicts = false) {
317
+ // INSERT INTO tableName (col1, col2, ...) VALUES (val1, val2, ...)
318
+ const m = /INSERT\s+(?:IGNORE\s+)?INTO\s+(\w+)\s*\(([^)]+)\)\s*VALUES\s*\((.+)\)\s*$/is.exec(sql);
319
+ if (!m)
320
+ return [];
321
+ const tableName = m[1];
322
+ // Strip backtick and double-quote quoting from column names
323
+ const cols = m[2].split(',').map((c) => c.trim().replace(/^[`"](.+)[`"]$/, '$1'));
324
+ const valStr = m[3];
325
+ const vals = this._parseValueList(valStr);
326
+ const row = {};
327
+ for (let i = 0; i < cols.length; i++) {
328
+ row[cols[i]] = vals[i] ?? null;
329
+ }
330
+ // Auto-assign auto-increment ID if the column is missing from INSERT
331
+ const aiCol = this._autoIncrementCols.get(tableName);
332
+ if (aiCol && !(aiCol in row)) {
333
+ const next = (this._autoIncrementCounters.get(tableName) ?? 0) + 1;
334
+ this._autoIncrementCounters.set(tableName, next);
335
+ row[aiCol] = next;
336
+ }
337
+ // Apply DEFAULT values for columns not present in INSERT
338
+ for (const [key, defaultExpr] of this._columnDefaults) {
339
+ const [tbl, col] = key.split('.');
340
+ if (tbl === tableName && !(col in row)) {
341
+ if (/^CURRENT_TIMESTAMP$/i.test(defaultExpr)) {
342
+ row[col] = new Date().toISOString();
343
+ }
344
+ else {
345
+ row[col] = this._evalLiteral(defaultExpr);
346
+ }
347
+ }
348
+ }
349
+ if (!this._tables.has(tableName)) {
350
+ this._tables.set(tableName, []);
351
+ }
352
+ // Enforce PRIMARY KEY uniqueness (throw to support INSERT OR IGNORE via try/catch)
353
+ const pkCols = this._primaryKeys.get(tableName);
354
+ if (pkCols && pkCols.length > 0 && !_ignoreConflicts) {
355
+ const table = this._tables.get(tableName);
356
+ const isDuplicate = table.some((existingRow) => pkCols.every((col) => existingRow[col] !== undefined && String(existingRow[col]) === String(row[col])));
357
+ if (isDuplicate) {
358
+ throw new Error(`UNIQUE constraint failed: ${tableName} (${pkCols.join(', ')})`);
359
+ }
360
+ }
361
+ this._tables.get(tableName).push(row);
362
+ return [];
363
+ }
364
+ // -------------------------------------------------------------------------
365
+ // SELECT
366
+ // -------------------------------------------------------------------------
367
+ _select(sql) {
368
+ // SELECT without FROM: evaluate literal expressions
369
+ if (!/FROM/i.test(sql)) {
370
+ const m = /SELECT\s+(.+)$/is.exec(sql);
371
+ if (!m)
372
+ return [];
373
+ return [this._evalSelectExprs(m[1].trim())];
374
+ }
375
+ // Handle sqlite_master virtual table queries (for index/schema inspection)
376
+ if (/FROM\s+sqlite_master/i.test(sql)) {
377
+ return this._selectFromSqliteMaster(sql);
378
+ }
379
+ // Extract LIMIT before stripping
380
+ let limitValue;
381
+ const limitMatch = /\s+LIMIT\s+(\d+)\s*$/is.exec(sql);
382
+ if (limitMatch) {
383
+ limitValue = parseInt(limitMatch[1], 10);
384
+ }
385
+ // Extract ORDER BY clause before stripping
386
+ let orderByExprs;
387
+ const orderByMatch = /\s+ORDER\s+BY\s+(.+?)(?:\s+LIMIT\s+\d+\s*)?$/is.exec(sql);
388
+ if (orderByMatch) {
389
+ orderByExprs = this._parseOrderBy(orderByMatch[1].trim());
390
+ }
391
+ // Strip ORDER BY and LIMIT clauses
392
+ let stripped = sql.replace(/\s+ORDER\s+BY\s+.+?(?=\s+LIMIT\s|\s*$)/is, '')
393
+ .replace(/\s+LIMIT\s+\d+\s*$/is, '');
394
+ // Extract GROUP BY clause (present before ORDER BY, after WHERE)
395
+ let groupByCols = null;
396
+ const groupByMatch = /\s+GROUP\s+BY\s+(.+?)(?:\s+HAVING\s+.+?)?$/is.exec(stripped);
397
+ if (groupByMatch) {
398
+ groupByCols = groupByMatch[1].split(',').map((c) => c.trim());
399
+ stripped = stripped.replace(/\s+GROUP\s+BY\s+.+$/is, '');
400
+ }
401
+ // SELECT cols FROM tableName [WHERE ...]
402
+ const m = /SELECT\s+(.+?)\s+FROM\s+(\w+)(?:\s+WHERE\s+(.+))?$/is.exec(stripped);
403
+ if (!m)
404
+ return [];
405
+ const colsStr = m[1].trim();
406
+ const tableName = m[2];
407
+ const whereStr = m[3];
408
+ const table = this._tables.get(tableName) ?? [];
409
+ let rows = table.map((r) => ({ ...r }));
410
+ if (whereStr) {
411
+ rows = rows.filter((row) => this._matchWhere(whereStr.trim(), row));
412
+ }
413
+ // Apply GROUP BY when present
414
+ if (groupByCols !== null) {
415
+ const grouped = this._applyGroupBy(colsStr, rows, groupByCols);
416
+ const sorted = orderByExprs ? this._applyOrderBy(grouped, orderByExprs) : grouped;
417
+ return limitValue !== undefined ? sorted.slice(0, limitValue) : sorted;
418
+ }
419
+ // Apply ORDER BY
420
+ if (orderByExprs) {
421
+ rows = this._applyOrderBy(rows, orderByExprs);
422
+ }
423
+ // Apply LIMIT
424
+ if (limitValue !== undefined) {
425
+ rows = rows.slice(0, limitValue);
426
+ }
427
+ if (colsStr === '*') {
428
+ return rows;
429
+ }
430
+ // Detect aggregate functions (SUM, COALESCE, COUNT, etc.)
431
+ if (/\b(?:SUM|COALESCE|COUNT|AVG|MIN|MAX)\s*\(/i.test(colsStr)) {
432
+ return [this._evalAggregate(colsStr, rows)];
433
+ }
434
+ // Project specific columns
435
+ return rows.map((row) => this._projectCols(colsStr, row));
436
+ }
437
+ // -------------------------------------------------------------------------
438
+ // GROUP BY helpers
439
+ // -------------------------------------------------------------------------
440
+ /**
441
+ * Apply GROUP BY: bucket rows by the group-by columns, then evaluate
442
+ * aggregate expressions for each bucket. Plain column references in the
443
+ * SELECT list that are also GROUP BY columns return the group value from
444
+ * the first row in the bucket (all rows in a group share the same value).
445
+ */
446
+ _applyGroupBy(colsStr, rows, groupByCols) {
447
+ // Bucket rows by the GROUP BY key
448
+ const groups = new Map();
449
+ for (const row of rows) {
450
+ const key = groupByCols.map((col) => String(row[col] ?? '')).join('\0');
451
+ if (!groups.has(key))
452
+ groups.set(key, []);
453
+ groups.get(key).push(row);
454
+ }
455
+ const result = [];
456
+ for (const [, groupRows] of groups) {
457
+ result.push(this._evalAggregateGroup(colsStr, groupRows));
458
+ }
459
+ return result;
460
+ }
461
+ // -------------------------------------------------------------------------
462
+ // ORDER BY helpers
463
+ // -------------------------------------------------------------------------
464
+ /**
465
+ * Parse ORDER BY expression list into sort keys.
466
+ * Handles: simple columns, COALESCE(col, default) DESC, CASE...END expressions.
467
+ */
468
+ _parseOrderBy(orderByStr) {
469
+ // Split by top-level commas
470
+ const parts = this._splitTopLevelCommas(orderByStr);
471
+ return parts.map((part) => {
472
+ const trimmed = part.trim();
473
+ // Check for trailing ASC/DESC
474
+ const dirMatch = /^(.*?)\s+(ASC|DESC)\s*$/i.exec(trimmed);
475
+ if (dirMatch) {
476
+ return { expr: dirMatch[1].trim(), dir: dirMatch[2].toUpperCase() };
477
+ }
478
+ return { expr: trimmed, dir: 'ASC' };
479
+ });
480
+ }
481
+ /**
482
+ * Sort rows according to ORDER BY expression list.
483
+ */
484
+ _applyOrderBy(rows, orderBy) {
485
+ return [...rows].sort((a, b) => {
486
+ for (const { expr, dir } of orderBy) {
487
+ const aVal = this._evalOrderByExpr(expr, a);
488
+ const bVal = this._evalOrderByExpr(expr, b);
489
+ const cmp = this._compareValues(aVal, bVal);
490
+ if (cmp !== 0)
491
+ return dir === 'DESC' ? -cmp : cmp;
492
+ }
493
+ return 0;
494
+ });
495
+ }
496
+ /**
497
+ * Evaluate an ORDER BY expression for a single row.
498
+ */
499
+ _evalOrderByExpr(expr, row) {
500
+ const trimmed = expr.trim();
501
+ // Simple CASE: CASE col WHEN val1 THEN r1 [WHEN val2 THEN r2]... [ELSE default] END
502
+ const simpleCaseM = /^CASE\s+(\w+)\s+(.+?)\s+END$/is.exec(trimmed);
503
+ if (simpleCaseM) {
504
+ const col = simpleCaseM[1];
505
+ const colVal = String(row[col] ?? '');
506
+ const body = simpleCaseM[2].trim();
507
+ // Match WHEN 'val' THEN result pairs
508
+ const whenMatches = [...body.matchAll(/WHEN\s+'([^']*)'\s+THEN\s+(\S+)/gi)];
509
+ for (const wm of whenMatches) {
510
+ if (colVal === wm[1])
511
+ return this._evalLiteral(wm[2]);
512
+ }
513
+ // ELSE clause
514
+ const elseM = /ELSE\s+(\S+)\s*$/i.exec(body);
515
+ if (elseM)
516
+ return this._evalLiteral(elseM[1]);
517
+ return null;
518
+ }
519
+ // Fall through to row expression evaluator
520
+ return this._evalRowExpr(trimmed, row);
521
+ }
522
+ /**
523
+ * Compare two values for sorting (handles null, numbers, strings).
524
+ * Returns negative if a < b, 0 if equal, positive if a > b.
525
+ */
526
+ _compareValues(a, b) {
527
+ if (a === null || a === undefined)
528
+ return b === null || b === undefined ? 0 : 1;
529
+ if (b === null || b === undefined)
530
+ return -1;
531
+ if (typeof a === 'number' && typeof b === 'number')
532
+ return a - b;
533
+ return String(a).localeCompare(String(b));
534
+ }
535
+ /**
536
+ * Evaluate SELECT expressions against a single GROUP BY bucket.
537
+ * Handles both aggregate expressions (SUM, COUNT, COALESCE) and plain
538
+ * column references (for GROUP BY projected columns).
539
+ */
540
+ _evalAggregateGroup(colsStr, rows) {
541
+ const result = {};
542
+ const cols = this._splitTopLevelCommas(colsStr);
543
+ for (const col of cols) {
544
+ const aliasM = /^(.+?)\s+AS\s+(\w+)$/i.exec(col);
545
+ const expr = aliasM ? aliasM[1].trim() : col.trim();
546
+ const alias = aliasM ? aliasM[2] : col.trim();
547
+ result[alias] = this._evalAggregateExprGrouped(expr, rows);
548
+ }
549
+ return result;
550
+ }
551
+ /**
552
+ * Evaluate a single aggregate expression against a GROUP BY bucket.
553
+ * Extends _evalAggregateExpr with:
554
+ * - SUM(expr) where expr may be CASE WHEN ... END
555
+ * - Plain column references (returns first-row value, for GROUP BY cols)
556
+ */
557
+ _evalAggregateExprGrouped(expr, rows) {
558
+ const trimmed = expr.trim();
559
+ // COALESCE(expr, default)
560
+ const coalesceM = /^COALESCE\((.+)\)$/i.exec(trimmed);
561
+ if (coalesceM) {
562
+ const args = this._splitTopLevelCommas(coalesceM[1]);
563
+ for (const arg of args) {
564
+ const val = this._evalAggregateExprGrouped(arg.trim(), rows);
565
+ if (val !== null && val !== undefined)
566
+ return val;
567
+ }
568
+ return null;
569
+ }
570
+ // SUM(expr) — expr may be CASE WHEN ... or a plain column name
571
+ const sumM = /^SUM\((.+)\)$/i.exec(trimmed);
572
+ if (sumM) {
573
+ if (rows.length === 0)
574
+ return null;
575
+ let total = 0;
576
+ for (const row of rows) {
577
+ total += Number(this._evalRowExpr(sumM[1].trim(), row) ?? 0);
578
+ }
579
+ return total;
580
+ }
581
+ // COUNT(*)
582
+ if (/^COUNT\(\*\)$/i.test(trimmed)) {
583
+ return rows.length;
584
+ }
585
+ // COUNT(col)
586
+ const countColM = /^COUNT\((\w+)\)$/i.exec(trimmed);
587
+ if (countColM) {
588
+ const col = countColM[1];
589
+ return rows.filter((r) => r[col] !== null && r[col] !== undefined).length;
590
+ }
591
+ // MAX(col)
592
+ const maxColM = /^MAX\((\w+)\)$/i.exec(trimmed);
593
+ if (maxColM) {
594
+ const col = maxColM[1];
595
+ const values = rows.map((r) => r[col]).filter((v) => v !== null && v !== undefined);
596
+ if (values.length === 0)
597
+ return null;
598
+ return values.reduce((a, b) => (String(a) >= String(b) ? a : b));
599
+ }
600
+ // MIN(col)
601
+ const minColM = /^MIN\((\w+)\)$/i.exec(trimmed);
602
+ if (minColM) {
603
+ const col = minColM[1];
604
+ const values = rows.map((r) => r[col]).filter((v) => v !== null && v !== undefined);
605
+ if (values.length === 0)
606
+ return null;
607
+ return values.reduce((a, b) => (String(a) <= String(b) ? a : b));
608
+ }
609
+ // Plain column reference — GROUP BY projected columns share the same value
610
+ if (/^\w+$/.test(trimmed) && rows.length > 0 && trimmed in rows[0]) {
611
+ return rows[0][trimmed];
612
+ }
613
+ // Literal value (e.g. the 0 in COALESCE(SUM(x), 0))
614
+ return this._evalLiteral(trimmed);
615
+ }
616
+ /**
617
+ * Evaluate an expression for a single row.
618
+ * Supports CASE WHEN conditions, COALESCE, column references, and literals.
619
+ * Used by _evalAggregateExprGrouped to evaluate the argument of SUM().
620
+ */
621
+ _evalRowExpr(expr, row) {
622
+ const trimmed = expr.trim();
623
+ // COALESCE(expr1, expr2, ...)
624
+ const coalesceM = /^COALESCE\((.+)\)$/i.exec(trimmed);
625
+ if (coalesceM) {
626
+ const args = this._splitTopLevelCommas(coalesceM[1]);
627
+ for (const arg of args) {
628
+ const val = this._evalRowExpr(arg.trim(), row);
629
+ if (val !== null && val !== undefined)
630
+ return val;
631
+ }
632
+ return null;
633
+ }
634
+ // CASE WHEN condition THEN thenExpr ELSE elseExpr END
635
+ const caseM = /^CASE\s+WHEN\s+(.+?)\s+THEN\s+(.+?)\s+ELSE\s+(.+?)\s+END$/is.exec(trimmed);
636
+ if (caseM) {
637
+ const matches = this._evalRowCondition(caseM[1].trim(), row);
638
+ return this._evalRowExpr(matches ? caseM[2].trim() : caseM[3].trim(), row);
639
+ }
640
+ // Plain column reference (identifier starting with a letter or underscore)
641
+ if (/^\w+$/.test(trimmed) && /^[a-zA-Z_]/.test(trimmed)) {
642
+ if (trimmed in row)
643
+ return row[trimmed];
644
+ // Column not in row — SQL NULL semantics (avoid treating as string literal)
645
+ return null;
646
+ }
647
+ // Literal (number, quoted string, etc.)
648
+ return this._evalLiteral(trimmed);
649
+ }
650
+ /**
651
+ * Evaluate a simple condition (col = 'str', col = num) for a single row.
652
+ * Returns true if the condition holds.
653
+ */
654
+ _evalRowCondition(condition, row) {
655
+ const trimmed = condition.trim();
656
+ // col = 'string'
657
+ const strM = /^(\w+)\s*=\s*'(.*)'$/is.exec(trimmed);
658
+ if (strM) {
659
+ const colVal = String(row[strM[1]] ?? '');
660
+ const literal = strM[2].replace(/''/g, "'");
661
+ return colVal === literal;
662
+ }
663
+ // col = numeric
664
+ const numM = /^(\w+)\s*=\s*(-?\d+(?:\.\d+)?)$/.exec(trimmed);
665
+ if (numM) {
666
+ return Number(row[numM[1]]) === parseFloat(numM[2]);
667
+ }
668
+ return false;
669
+ }
670
+ // -------------------------------------------------------------------------
671
+ // UPDATE
672
+ // -------------------------------------------------------------------------
673
+ _update(sql) {
674
+ // UPDATE tableName SET col1 = val1, col2 = val2 [WHERE ...]
675
+ const m = /UPDATE\s+(\w+)\s+SET\s+(.+?)(?:\s+WHERE\s+(.+))?$/is.exec(sql);
676
+ if (!m)
677
+ return [];
678
+ const tableName = m[1];
679
+ const setStr = m[2];
680
+ const whereStr = m[3];
681
+ const table = this._tables.get(tableName);
682
+ if (!table)
683
+ return [];
684
+ for (const row of table) {
685
+ if (!whereStr || this._matchWhere(whereStr.trim(), row)) {
686
+ const assignments = this._parseAssignmentsForRow(setStr, row);
687
+ for (const [col, val] of assignments) {
688
+ row[col] = val;
689
+ }
690
+ }
691
+ }
692
+ return [];
693
+ }
694
+ // -------------------------------------------------------------------------
695
+ // DELETE
696
+ // -------------------------------------------------------------------------
697
+ _delete(sql) {
698
+ // DELETE FROM tableName [WHERE ...]
699
+ const m = /DELETE\s+FROM\s+(\w+)(?:\s+WHERE\s+(.+))?$/is.exec(sql);
700
+ if (!m)
701
+ return [];
702
+ const tableName = m[1];
703
+ const whereStr = m[2];
704
+ const table = this._tables.get(tableName);
705
+ if (!table)
706
+ return [];
707
+ if (!whereStr) {
708
+ this._tables.set(tableName, []);
709
+ return [];
710
+ }
711
+ const kept = table.filter((row) => !this._matchWhere(whereStr.trim(), row));
712
+ this._tables.set(tableName, kept);
713
+ return [];
714
+ }
715
+ // -------------------------------------------------------------------------
716
+ // Helpers: WHERE evaluation
717
+ // -------------------------------------------------------------------------
718
+ /**
719
+ * Evaluate a simple WHERE clause against a row.
720
+ * Supports: `col = val` conditions joined by AND.
721
+ */
722
+ _matchWhere(whereClause, row) {
723
+ // Split by AND (simple case — no OR, no nested parens)
724
+ const conditions = whereClause.split(/\s+AND\s+/i);
725
+ for (const condition of conditions) {
726
+ const trimmed = condition.trim();
727
+ // col = 'string literal' (col may be backtick or double-quote quoted)
728
+ const strM = /^[`"]?(\w+)[`"]?\s*=\s*'(.*)'$/is.exec(trimmed);
729
+ if (strM) {
730
+ const colVal = String(row[strM[1]] ?? '');
731
+ const literal = strM[2].replace(/''/g, "'");
732
+ if (colVal !== literal)
733
+ return false;
734
+ continue;
735
+ }
736
+ // col != 'string literal' (inequality)
737
+ const strNeqM = /^[`"]?(\w+)[`"]?\s*!=\s*'(.*)'$/is.exec(trimmed);
738
+ if (strNeqM) {
739
+ const colVal = String(row[strNeqM[1]] ?? '');
740
+ const literal = strNeqM[2].replace(/''/g, "'");
741
+ if (colVal === literal)
742
+ return false;
743
+ continue;
744
+ }
745
+ // col = numeric literal
746
+ const numM = /^[`"]?(\w+)[`"]?\s*=\s*(-?\d+(?:\.\d+)?)$/.exec(trimmed);
747
+ if (numM) {
748
+ if (Number(row[numM[1]]) !== parseFloat(numM[2]))
749
+ return false;
750
+ continue;
751
+ }
752
+ // col != numeric literal (inequality)
753
+ const numNeqM = /^[`"]?(\w+)[`"]?\s*!=\s*(-?\d+(?:\.\d+)?)$/.exec(trimmed);
754
+ if (numNeqM) {
755
+ if (Number(row[numNeqM[1]]) === parseFloat(numNeqM[2]))
756
+ return false;
757
+ continue;
758
+ }
759
+ // col < / > / <= / >= 'string literal' (handles ISO date comparisons)
760
+ const strCmpM = /^[`"]?(\w+)[`"]?\s*(>=|<=|>|<)\s*'(.*)'$/s.exec(trimmed);
761
+ if (strCmpM) {
762
+ const colVal = row[strCmpM[1]];
763
+ if (colVal === null || colVal === undefined)
764
+ return false;
765
+ const lhs = String(colVal);
766
+ const rhs = strCmpM[3].replace(/''/g, "'");
767
+ const op = strCmpM[2];
768
+ if (op === '<' && !(lhs < rhs))
769
+ return false;
770
+ if (op === '<=' && !(lhs <= rhs))
771
+ return false;
772
+ if (op === '>' && !(lhs > rhs))
773
+ return false;
774
+ if (op === '>=' && !(lhs >= rhs))
775
+ return false;
776
+ continue;
777
+ }
778
+ // col < / > / <= / >= numeric literal
779
+ const numCmpM = /^[`"]?(\w+)[`"]?\s*(>=|<=|>|<)\s*(-?\d+(?:\.\d+)?)$/.exec(trimmed);
780
+ if (numCmpM) {
781
+ const colVal = Number(row[numCmpM[1]] ?? 0);
782
+ const rhs = parseFloat(numCmpM[3]);
783
+ const op = numCmpM[2];
784
+ if (op === '<' && !(colVal < rhs))
785
+ return false;
786
+ if (op === '<=' && !(colVal <= rhs))
787
+ return false;
788
+ if (op === '>' && !(colVal > rhs))
789
+ return false;
790
+ if (op === '>=' && !(colVal >= rhs))
791
+ return false;
792
+ continue;
793
+ }
794
+ // col IS NULL
795
+ const nullM = /^[`"]?(\w+)[`"]?\s+IS\s+NULL$/i.exec(trimmed);
796
+ if (nullM) {
797
+ if (row[nullM[1]] !== null && row[nullM[1]] !== undefined)
798
+ return false;
799
+ continue;
800
+ }
801
+ // col IS NOT NULL
802
+ const notNullM = /^[`"]?(\w+)[`"]?\s+IS\s+NOT\s+NULL$/i.exec(trimmed);
803
+ if (notNullM) {
804
+ if (row[notNullM[1]] === null || row[notNullM[1]] === undefined)
805
+ return false;
806
+ continue;
807
+ }
808
+ // col LIKE 'pattern' (supports % wildcard)
809
+ const likeM = /^[`"]?(\w+)[`"]?\s+LIKE\s+'(.*)'$/is.exec(trimmed);
810
+ if (likeM) {
811
+ const colVal = row[likeM[1]];
812
+ if (colVal === null || colVal === undefined)
813
+ return false;
814
+ const pattern = likeM[2].replace(/''/g, "'");
815
+ // Convert SQL LIKE pattern to regex: % → .*, _ → .
816
+ const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, (ch) => ch === '%' || ch === '_' ? ch : '\\' + ch);
817
+ const regex = new RegExp('^' + escaped.replace(/%/g, '.*').replace(/_/g, '.') + '$', 's');
818
+ if (!regex.test(String(colVal)))
819
+ return false;
820
+ continue;
821
+ }
822
+ // col IN ('val1', 'val2', ...)
823
+ const inM = /^[`"]?(\w+)[`"]?\s+IN\s*\((.+)\)$/is.exec(trimmed);
824
+ if (inM) {
825
+ const colVal = row[inM[1]];
826
+ const inValues = this._parseValueList(inM[2]);
827
+ const colStr = colVal === null || colVal === undefined ? null : typeof colVal === 'number' ? colVal : String(colVal);
828
+ if (!inValues.some((v) => v === colStr || String(v) === String(colStr)))
829
+ return false;
830
+ continue;
831
+ }
832
+ // col NOT IN ('val1', 'val2', ...)
833
+ const notInM = /^[`"]?(\w+)[`"]?\s+NOT\s+IN\s*\((.+)\)$/is.exec(trimmed);
834
+ if (notInM) {
835
+ const colVal = row[notInM[1]];
836
+ const notInValues = this._parseValueList(notInM[2]);
837
+ const colStr = colVal === null || colVal === undefined ? null : typeof colVal === 'number' ? colVal : String(colVal);
838
+ if (notInValues.some((v) => v === colStr || String(v) === String(colStr)))
839
+ return false;
840
+ continue;
841
+ }
842
+ // Unrecognised condition: skip (treat as matching)
843
+ }
844
+ return true;
845
+ }
846
+ // -------------------------------------------------------------------------
847
+ // Helpers: column projection
848
+ // -------------------------------------------------------------------------
849
+ _projectCols(colsStr, row) {
850
+ const result = {};
851
+ const cols = this._splitTopLevelCommas(colsStr);
852
+ for (const col of cols) {
853
+ // Handle "expr AS alias" or plain "col"
854
+ const aliasM = /^(.+?)\s+AS\s+(\w+)$/i.exec(col);
855
+ if (aliasM) {
856
+ result[aliasM[2]] = this._evalExprAgainstRow(aliasM[1].trim(), row);
857
+ }
858
+ else {
859
+ // Strip backtick/double-quote quoting from column names (e.g. `key` → key)
860
+ const unquoted = col.replace(/^[`"](.+)[`"]$/, '$1');
861
+ // Return null for columns not present in the row (matches SQLite NULL behavior)
862
+ result[unquoted] = unquoted in row ? row[unquoted] : null;
863
+ }
864
+ }
865
+ return result;
866
+ }
867
+ // -------------------------------------------------------------------------
868
+ // Helpers: literal expression evaluation (for SELECT without FROM)
869
+ // -------------------------------------------------------------------------
870
+ _evalSelectExprs(exprs) {
871
+ const result = {};
872
+ const parts = this._splitTopLevelCommas(exprs);
873
+ for (const part of parts) {
874
+ const aliasM = /^(.+?)\s+AS\s+(\w+)$/i.exec(part);
875
+ if (aliasM) {
876
+ result[aliasM[2]] = this._evalLiteral(aliasM[1].trim());
877
+ }
878
+ else {
879
+ result[part] = this._evalLiteral(part);
880
+ }
881
+ }
882
+ return result;
883
+ }
884
+ _evalLiteral(expr) {
885
+ const trimmed = expr.trim();
886
+ if (trimmed.toUpperCase() === 'NULL')
887
+ return null;
888
+ if (/^'.*'$/.test(trimmed))
889
+ return trimmed.slice(1, -1).replace(/''/g, "'");
890
+ if (/^-?\d+$/.test(trimmed))
891
+ return parseInt(trimmed, 10);
892
+ if (/^-?\d+\.\d+$/.test(trimmed))
893
+ return parseFloat(trimmed);
894
+ return trimmed;
895
+ }
896
+ _evalExprAgainstRow(expr, row) {
897
+ // Try literal first, then column name lookup
898
+ const literal = this._evalLiteral(expr);
899
+ if (typeof literal !== 'string')
900
+ return literal;
901
+ // If it looks like an identifier (word chars only) and exists in row, use it
902
+ if (/^\w+$/.test(expr) && expr in row)
903
+ return row[expr];
904
+ return literal;
905
+ }
906
+ // -------------------------------------------------------------------------
907
+ // Helpers: parenthesis-aware comma splitting
908
+ // -------------------------------------------------------------------------
909
+ /**
910
+ * Split a string by commas that are NOT inside parentheses.
911
+ * E.g. "COALESCE(SUM(x), 0) as a, y" → ["COALESCE(SUM(x), 0) as a", "y"]
912
+ */
913
+ _splitTopLevelCommas(str) {
914
+ const parts = [];
915
+ let current = '';
916
+ let depth = 0;
917
+ let inStr = false;
918
+ for (let i = 0; i < str.length; i++) {
919
+ const ch = str[i];
920
+ if (ch === "'" && !inStr) {
921
+ inStr = true;
922
+ current += ch;
923
+ }
924
+ else if (ch === "'" && inStr) {
925
+ if (str[i + 1] === "'") {
926
+ current += "''";
927
+ i++;
928
+ }
929
+ else {
930
+ inStr = false;
931
+ current += ch;
932
+ }
933
+ }
934
+ else if (!inStr && ch === '(') {
935
+ depth++;
936
+ current += ch;
937
+ }
938
+ else if (!inStr && ch === ')') {
939
+ depth--;
940
+ current += ch;
941
+ }
942
+ else if (!inStr && ch === ',' && depth === 0) {
943
+ parts.push(current.trim());
944
+ current = '';
945
+ }
946
+ else {
947
+ current += ch;
948
+ }
949
+ }
950
+ if (current.trim() !== '')
951
+ parts.push(current.trim());
952
+ return parts;
953
+ }
954
+ // -------------------------------------------------------------------------
955
+ // Helpers: aggregate evaluation
956
+ // -------------------------------------------------------------------------
957
+ /**
958
+ * Evaluate aggregate SELECT expressions (SUM, COALESCE, COUNT) across
959
+ * a set of filtered rows, returning a single result row.
960
+ */
961
+ _evalAggregate(colsStr, rows) {
962
+ const result = {};
963
+ const cols = this._splitTopLevelCommas(colsStr);
964
+ for (const col of cols) {
965
+ const aliasM = /^(.+?)\s+AS\s+(\w+)$/i.exec(col);
966
+ const expr = aliasM ? aliasM[1].trim() : col.trim();
967
+ const alias = aliasM ? aliasM[2] : col.trim();
968
+ result[alias] = this._evalAggregateExpr(expr, rows);
969
+ }
970
+ return result;
971
+ }
972
+ /**
973
+ * Evaluate a single aggregate expression against a set of rows.
974
+ * Supports: SUM(expr), COALESCE(expr, default), COUNT(*), MAX(col), MIN(col).
975
+ */
976
+ _evalAggregateExpr(expr, rows) {
977
+ const trimmed = expr.trim();
978
+ // COALESCE(expr, default)
979
+ const coalesceM = /^COALESCE\((.+)\)$/i.exec(trimmed);
980
+ if (coalesceM) {
981
+ const args = this._splitTopLevelCommas(coalesceM[1]);
982
+ for (const arg of args) {
983
+ const val = this._evalAggregateExpr(arg.trim(), rows);
984
+ if (val !== null && val !== undefined)
985
+ return val;
986
+ }
987
+ return null;
988
+ }
989
+ // SUM(expr) — expr may be a plain column name or CASE WHEN ... expression
990
+ const sumM = /^SUM\((.+)\)$/i.exec(trimmed);
991
+ if (sumM) {
992
+ if (rows.length === 0)
993
+ return null;
994
+ let total = 0;
995
+ for (const row of rows) {
996
+ total += Number(this._evalRowExpr(sumM[1].trim(), row) ?? 0);
997
+ }
998
+ return total;
999
+ }
1000
+ // COUNT(*)
1001
+ if (/^COUNT\(\*\)$/i.test(trimmed)) {
1002
+ return rows.length;
1003
+ }
1004
+ // COUNT(DISTINCT col)
1005
+ const countDistinctM = /^COUNT\(\s*DISTINCT\s+(\w+)\s*\)$/i.exec(trimmed);
1006
+ if (countDistinctM) {
1007
+ const col = countDistinctM[1];
1008
+ const distinct = new Set(rows.map((r) => r[col]).filter((v) => v !== null && v !== undefined));
1009
+ return distinct.size;
1010
+ }
1011
+ // COUNT(col)
1012
+ const countM = /^COUNT\((\w+)\)$/i.exec(trimmed);
1013
+ if (countM) {
1014
+ const col = countM[1];
1015
+ return rows.filter((r) => r[col] !== null && r[col] !== undefined).length;
1016
+ }
1017
+ // MAX(col)
1018
+ const maxM = /^MAX\((\w+)\)$/i.exec(trimmed);
1019
+ if (maxM) {
1020
+ const col = maxM[1];
1021
+ const values = rows.map((r) => r[col]).filter((v) => v !== null && v !== undefined);
1022
+ if (values.length === 0)
1023
+ return null;
1024
+ return values.reduce((a, b) => (String(a) >= String(b) ? a : b));
1025
+ }
1026
+ // MIN(col)
1027
+ const minM = /^MIN\((\w+)\)$/i.exec(trimmed);
1028
+ if (minM) {
1029
+ const col = minM[1];
1030
+ const values = rows.map((r) => r[col]).filter((v) => v !== null && v !== undefined);
1031
+ if (values.length === 0)
1032
+ return null;
1033
+ return values.reduce((a, b) => (String(a) <= String(b) ? a : b));
1034
+ }
1035
+ // Literal value (e.g. the 0 in COALESCE(SUM(x), 0))
1036
+ return this._evalLiteral(trimmed);
1037
+ }
1038
+ // -------------------------------------------------------------------------
1039
+ // Helpers: value list parsing
1040
+ // -------------------------------------------------------------------------
1041
+ /**
1042
+ * Parse a comma-separated list of SQL literal values.
1043
+ * Handles: NULL, numbers, single-quoted strings.
1044
+ * Simple split by comma (assumes no commas inside string values).
1045
+ */
1046
+ _parseValueList(valStr) {
1047
+ // Tokenise respecting single-quoted strings
1048
+ const tokens = [];
1049
+ let current = '';
1050
+ let inStr = false;
1051
+ for (let i = 0; i < valStr.length; i++) {
1052
+ const ch = valStr[i];
1053
+ if (ch === "'" && !inStr) {
1054
+ inStr = true;
1055
+ current += ch;
1056
+ }
1057
+ else if (ch === "'" && inStr) {
1058
+ // Peek ahead for escaped quote ('')
1059
+ if (valStr[i + 1] === "'") {
1060
+ current += "''";
1061
+ i++;
1062
+ }
1063
+ else {
1064
+ inStr = false;
1065
+ current += ch;
1066
+ }
1067
+ }
1068
+ else if (ch === ',' && !inStr) {
1069
+ tokens.push(current.trim());
1070
+ current = '';
1071
+ }
1072
+ else {
1073
+ current += ch;
1074
+ }
1075
+ }
1076
+ if (current.trim() !== '')
1077
+ tokens.push(current.trim());
1078
+ return tokens.map((t) => this._evalLiteral(t));
1079
+ }
1080
+ // -------------------------------------------------------------------------
1081
+ // Helpers: SET clause parsing
1082
+ // -------------------------------------------------------------------------
1083
+ /**
1084
+ * Parse `col1 = val1, col2 = val2` assignments into an array of [col, val] pairs.
1085
+ * Evaluates each RHS expression against the provided row for arithmetic support.
1086
+ */
1087
+ _parseAssignmentsForRow(setStr, row) {
1088
+ const assignments = [];
1089
+ const parts = [];
1090
+ let current = '';
1091
+ let inStr = false;
1092
+ for (let i = 0; i < setStr.length; i++) {
1093
+ const ch = setStr[i];
1094
+ if (ch === "'" && !inStr) {
1095
+ inStr = true;
1096
+ current += ch;
1097
+ }
1098
+ else if (ch === "'" && inStr) {
1099
+ if (setStr[i + 1] === "'") {
1100
+ current += "''";
1101
+ i++;
1102
+ }
1103
+ else {
1104
+ inStr = false;
1105
+ current += ch;
1106
+ }
1107
+ }
1108
+ else if (ch === ',' && !inStr) {
1109
+ parts.push(current.trim());
1110
+ current = '';
1111
+ }
1112
+ else {
1113
+ current += ch;
1114
+ }
1115
+ }
1116
+ if (current.trim() !== '')
1117
+ parts.push(current.trim());
1118
+ for (const part of parts) {
1119
+ const eqIdx = part.indexOf('=');
1120
+ if (eqIdx === -1)
1121
+ continue;
1122
+ const col = part.slice(0, eqIdx).trim();
1123
+ const valStr = part.slice(eqIdx + 1).trim();
1124
+ assignments.push([col, this._evalAssignmentExpr(valStr, row)]);
1125
+ }
1126
+ return assignments;
1127
+ }
1128
+ /**
1129
+ * Evaluate a SET assignment RHS expression against the current row.
1130
+ * Handles: simple literals, column arithmetic (col +/- val), COALESCE, CURRENT_TIMESTAMP.
1131
+ */
1132
+ _evalAssignmentExpr(expr, row) {
1133
+ const trimmed = expr.trim();
1134
+ // CURRENT_TIMESTAMP
1135
+ if (/^CURRENT_TIMESTAMP$/i.test(trimmed)) {
1136
+ return new Date().toISOString();
1137
+ }
1138
+ // COALESCE(expr, default)
1139
+ const coalesceM = /^COALESCE\((.+)\)$/i.exec(trimmed);
1140
+ if (coalesceM) {
1141
+ const args = this._splitTopLevelCommas(coalesceM[1]);
1142
+ for (const arg of args) {
1143
+ const val = this._evalAssignmentExpr(arg.trim(), row);
1144
+ if (val !== null && val !== undefined)
1145
+ return val;
1146
+ }
1147
+ return null;
1148
+ }
1149
+ // Arithmetic: col +/- numeric (e.g. cost_usd + 0.0105)
1150
+ const arithM = /^(\w+)\s*([+\-*\/])\s*(-?\d+(?:\.\d+)?)$/.exec(trimmed);
1151
+ if (arithM) {
1152
+ const colName = arithM[1];
1153
+ const op = arithM[2];
1154
+ const num = parseFloat(arithM[3]);
1155
+ const colVal = Number(row[colName] ?? 0);
1156
+ switch (op) {
1157
+ case '+': return colVal + num;
1158
+ case '-': return colVal - num;
1159
+ case '*': return colVal * num;
1160
+ case '/': return num !== 0 ? colVal / num : null;
1161
+ }
1162
+ }
1163
+ // Plain literal
1164
+ return this._evalLiteral(trimmed);
1165
+ }
1166
+ }
1167
+ //# sourceMappingURL=memory-adapter.js.map