@kaelio/ktx 0.5.0 → 0.7.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 (875) hide show
  1. package/assets/python/{kaelio_ktx-0.5.0-py3-none-any.whl → kaelio_ktx-0.7.0-py3-none-any.whl} +0 -0
  2. package/assets/python/manifest.json +4 -4
  3. package/dist/.tsbuildinfo +1 -1
  4. package/dist/clack.d.ts +8 -0
  5. package/dist/clack.js +14 -0
  6. package/dist/connection.js +2 -9
  7. package/dist/connectors/bigquery/connector.d.ts +6 -1
  8. package/dist/connectors/bigquery/connector.js +38 -9
  9. package/dist/connectors/bigquery/dialect.d.ts +11 -9
  10. package/dist/connectors/bigquery/dialect.js +25 -45
  11. package/dist/connectors/clickhouse/connector.d.ts +5 -0
  12. package/dist/connectors/clickhouse/connector.js +36 -3
  13. package/dist/connectors/clickhouse/dialect.d.ts +11 -12
  14. package/dist/connectors/clickhouse/dialect.js +25 -100
  15. package/dist/connectors/mysql/connector.d.ts +7 -1
  16. package/dist/connectors/mysql/connector.js +67 -9
  17. package/dist/connectors/mysql/dialect.d.ts +11 -9
  18. package/dist/connectors/mysql/dialect.js +25 -43
  19. package/dist/connectors/postgres/connector.d.ts +6 -0
  20. package/dist/connectors/postgres/connector.js +67 -12
  21. package/dist/connectors/postgres/dialect.d.ts +11 -9
  22. package/dist/connectors/postgres/dialect.js +26 -35
  23. package/dist/connectors/snowflake/connector.d.ts +8 -3
  24. package/dist/connectors/snowflake/connector.js +39 -20
  25. package/dist/connectors/snowflake/dialect.d.ts +11 -9
  26. package/dist/connectors/snowflake/dialect.js +25 -24
  27. package/dist/connectors/sqlite/connector.d.ts +3 -1
  28. package/dist/connectors/sqlite/connector.js +23 -3
  29. package/dist/connectors/sqlite/dialect.d.ts +11 -9
  30. package/dist/connectors/sqlite/dialect.js +25 -29
  31. package/dist/connectors/sqlserver/connector.d.ts +6 -0
  32. package/dist/connectors/sqlserver/connector.js +56 -9
  33. package/dist/connectors/sqlserver/dialect.d.ts +11 -10
  34. package/dist/connectors/sqlserver/dialect.js +24 -40
  35. package/dist/context/connections/connection-type.d.ts +1 -1
  36. package/dist/context/connections/dialect-helpers.d.ts +9 -0
  37. package/dist/context/connections/dialect-helpers.js +67 -0
  38. package/dist/context/connections/dialects.d.ts +23 -5
  39. package/dist/context/connections/dialects.js +18 -56
  40. package/dist/context/connections/drivers.d.ts +23 -0
  41. package/dist/context/connections/drivers.js +171 -0
  42. package/dist/context/connections/local-query-executor.js +25 -7
  43. package/dist/context/connections/local-warehouse-descriptor.js +0 -2
  44. package/dist/context/connections/postgres-query-executor.js +1 -1
  45. package/dist/context/connections/sqlite-query-executor.js +1 -1
  46. package/dist/context/ingest/adapters/historic-sql/chunk-unified.js +1 -1
  47. package/dist/context/ingest/adapters/historic-sql/connection-dialect.js +11 -7
  48. package/dist/context/ingest/adapters/historic-sql/evidence-tool.d.ts +1 -1
  49. package/dist/context/ingest/adapters/historic-sql/evidence-tool.js +8 -5
  50. package/dist/context/ingest/adapters/historic-sql/evidence.d.ts +4 -4
  51. package/dist/context/ingest/adapters/historic-sql/evidence.js +2 -2
  52. package/dist/context/ingest/adapters/historic-sql/projection.js +5 -2
  53. package/dist/context/ingest/adapters/live-database/daemon-introspection.js +1 -1
  54. package/dist/context/ingest/adapters/live-database/stage.d.ts +2 -0
  55. package/dist/context/ingest/adapters/live-database/stage.js +9 -0
  56. package/dist/context/ingest/adapters/looker/mapping.d.ts +0 -3
  57. package/dist/context/ingest/adapters/looker/mapping.js +0 -3
  58. package/dist/context/ingest/adapters/looker/types.d.ts +1 -1
  59. package/dist/context/ingest/historic-sql-probes/bigquery-runner.d.ts +34 -0
  60. package/dist/context/ingest/historic-sql-probes/bigquery-runner.js +99 -0
  61. package/dist/context/ingest/historic-sql-probes/postgres-runner.d.ts +26 -0
  62. package/dist/context/ingest/historic-sql-probes/postgres-runner.js +76 -0
  63. package/dist/context/ingest/historic-sql-probes/snowflake-runner.d.ts +29 -0
  64. package/dist/context/ingest/historic-sql-probes/snowflake-runner.js +62 -0
  65. package/dist/context/ingest/historic-sql-probes.d.ts +46 -0
  66. package/dist/context/ingest/historic-sql-probes.js +62 -0
  67. package/dist/context/ingest/local-adapters.js +0 -1
  68. package/dist/context/ingest/local-ingest.js +1 -1
  69. package/dist/context/llm/claude-code-runtime.js +16 -1
  70. package/dist/context/mcp/context-tools.js +11 -48
  71. package/dist/context/mcp/local-project-ports.js +0 -3
  72. package/dist/context/project/config.d.ts +0 -8
  73. package/dist/context/project/driver-schemas.d.ts +0 -4
  74. package/dist/context/project/driver-schemas.js +0 -2
  75. package/dist/context/scan/constraint-discovery.d.ts +19 -0
  76. package/dist/context/scan/constraint-discovery.js +23 -0
  77. package/dist/context/scan/enabled-tables.d.ts +4 -5
  78. package/dist/context/scan/enabled-tables.js +4 -18
  79. package/dist/context/scan/entity-details.js +14 -44
  80. package/dist/context/scan/local-enrichment.js +13 -1
  81. package/dist/context/scan/local-scan.js +5 -4
  82. package/dist/context/scan/local-structural-artifacts.js +51 -0
  83. package/dist/context/scan/relationship-benchmarks.js +9 -6
  84. package/dist/context/scan/relationship-composite-candidates.d.ts +3 -2
  85. package/dist/context/scan/relationship-composite-candidates.js +21 -33
  86. package/dist/context/scan/relationship-discovery.d.ts +3 -2
  87. package/dist/context/scan/relationship-discovery.js +4 -4
  88. package/dist/context/scan/relationship-profiling.d.ts +2 -3
  89. package/dist/context/scan/relationship-profiling.js +25 -94
  90. package/dist/context/scan/relationship-validation.d.ts +3 -2
  91. package/dist/context/scan/relationship-validation.js +12 -22
  92. package/dist/context/scan/table-ref.d.ts +1 -2
  93. package/dist/context/scan/table-ref.js +3 -4
  94. package/dist/context/scan/types.d.ts +6 -2
  95. package/dist/context/scan/warehouse-catalog.js +31 -48
  96. package/dist/context/sl/local-query.js +0 -3
  97. package/dist/context/sl/local-sl.js +0 -13
  98. package/dist/context/sl/semantic-layer.service.js +1 -4
  99. package/dist/context/tools/context-candidate-write.tool.d.ts +2 -2
  100. package/dist/context-build-view.js +1 -1
  101. package/dist/database-tree-picker.js +14 -7
  102. package/dist/error-message.d.ts +1 -0
  103. package/dist/error-message.js +29 -0
  104. package/dist/ingest-depth.js +0 -1
  105. package/dist/ingest.js +2 -2
  106. package/dist/llm/embedding-health.js +2 -2
  107. package/dist/local-scan-connectors.js +13 -56
  108. package/dist/managed-local-embeddings.js +2 -1
  109. package/dist/managed-python-daemon.d.ts +5 -0
  110. package/dist/managed-python-daemon.js +29 -9
  111. package/dist/managed-python-http.js +2 -1
  112. package/dist/public-ingest.js +1 -6
  113. package/dist/runtime-requirements.js +2 -2
  114. package/dist/setup-agents.d.ts +1 -1
  115. package/dist/setup-agents.js +16 -74
  116. package/dist/setup-context.js +2 -1
  117. package/dist/setup-databases.d.ts +3 -13
  118. package/dist/setup-databases.js +141 -313
  119. package/dist/setup-embeddings.js +10 -2
  120. package/dist/setup-project.d.ts +0 -8
  121. package/dist/setup-project.js +3 -27
  122. package/dist/setup-runtime.js +2 -1
  123. package/dist/setup-sources.js +2 -1
  124. package/dist/setup.js +11 -18
  125. package/dist/skills/historic_sql_patterns/SKILL.md +1 -3
  126. package/dist/skills/historic_sql_table_digest/SKILL.md +0 -1
  127. package/dist/skills/sl/SKILL.md +2 -2
  128. package/dist/sql.js +0 -4
  129. package/dist/status-project.d.ts +3 -18
  130. package/dist/status-project.js +42 -216
  131. package/dist/telemetry/events.d.ts +1 -1
  132. package/dist/telemetry/index.js +8 -3
  133. package/dist/tree-picker-state.d.ts +2 -2
  134. package/dist/tree-picker-state.js +29 -13
  135. package/dist/tree-picker-tui.d.ts +3 -1
  136. package/dist/tree-picker-tui.js +20 -32
  137. package/package.json +6 -6
  138. package/dist/admin-reindex.test.d.ts +0 -1
  139. package/dist/admin-reindex.test.js +0 -119
  140. package/dist/admin.test.d.ts +0 -1
  141. package/dist/admin.test.js +0 -201
  142. package/dist/cli-program-telemetry.test.d.ts +0 -1
  143. package/dist/cli-program-telemetry.test.js +0 -89
  144. package/dist/cli-program.test.d.ts +0 -1
  145. package/dist/cli-program.test.js +0 -71
  146. package/dist/command-tree.test.d.ts +0 -1
  147. package/dist/command-tree.test.js +0 -126
  148. package/dist/commands/mcp-commands.test.d.ts +0 -1
  149. package/dist/commands/mcp-commands.test.js +0 -111
  150. package/dist/commands/sql-commands.test.d.ts +0 -1
  151. package/dist/commands/sql-commands.test.js +0 -68
  152. package/dist/connection.test.d.ts +0 -1
  153. package/dist/connection.test.js +0 -426
  154. package/dist/connectors/bigquery/connector.test.d.ts +0 -1
  155. package/dist/connectors/bigquery/connector.test.js +0 -363
  156. package/dist/connectors/bigquery/dialect.test.d.ts +0 -1
  157. package/dist/connectors/bigquery/dialect.test.js +0 -36
  158. package/dist/connectors/clickhouse/connector.test.d.ts +0 -1
  159. package/dist/connectors/clickhouse/connector.test.js +0 -342
  160. package/dist/connectors/clickhouse/dialect.test.d.ts +0 -1
  161. package/dist/connectors/clickhouse/dialect.test.js +0 -36
  162. package/dist/connectors/mysql/connector.test.d.ts +0 -1
  163. package/dist/connectors/mysql/connector.test.js +0 -365
  164. package/dist/connectors/mysql/dialect.test.d.ts +0 -1
  165. package/dist/connectors/mysql/dialect.test.js +0 -36
  166. package/dist/connectors/postgres/connector.test.d.ts +0 -1
  167. package/dist/connectors/postgres/connector.test.js +0 -391
  168. package/dist/connectors/postgres/dialect.test.d.ts +0 -1
  169. package/dist/connectors/postgres/dialect.test.js +0 -37
  170. package/dist/connectors/postgres/historic-sql-query-client.test.d.ts +0 -1
  171. package/dist/connectors/postgres/historic-sql-query-client.test.js +0 -45
  172. package/dist/connectors/snowflake/connector.test.d.ts +0 -1
  173. package/dist/connectors/snowflake/connector.test.js +0 -462
  174. package/dist/connectors/snowflake/dialect.test.d.ts +0 -1
  175. package/dist/connectors/snowflake/dialect.test.js +0 -34
  176. package/dist/connectors/snowflake/identifiers.test.d.ts +0 -1
  177. package/dist/connectors/snowflake/identifiers.test.js +0 -12
  178. package/dist/connectors/snowflake/sdk-logger.test.d.ts +0 -1
  179. package/dist/connectors/snowflake/sdk-logger.test.js +0 -47
  180. package/dist/connectors/sqlite/connector.test.d.ts +0 -1
  181. package/dist/connectors/sqlite/connector.test.js +0 -207
  182. package/dist/connectors/sqlite/dialect.test.d.ts +0 -1
  183. package/dist/connectors/sqlite/dialect.test.js +0 -23
  184. package/dist/connectors/sqlserver/connector.test.d.ts +0 -1
  185. package/dist/connectors/sqlserver/connector.test.js +0 -313
  186. package/dist/connectors/sqlserver/dialect.test.d.ts +0 -1
  187. package/dist/connectors/sqlserver/dialect.test.js +0 -36
  188. package/dist/context/connections/bigquery-identifiers.test.d.ts +0 -1
  189. package/dist/context/connections/bigquery-identifiers.test.js +0 -13
  190. package/dist/context/connections/dialects.test.d.ts +0 -1
  191. package/dist/context/connections/dialects.test.js +0 -24
  192. package/dist/context/connections/local-query-executor.test.d.ts +0 -1
  193. package/dist/context/connections/local-query-executor.test.js +0 -48
  194. package/dist/context/connections/local-warehouse-descriptor.test.d.ts +0 -1
  195. package/dist/context/connections/local-warehouse-descriptor.test.js +0 -53
  196. package/dist/context/connections/notion-config.test.d.ts +0 -1
  197. package/dist/context/connections/notion-config.test.js +0 -121
  198. package/dist/context/connections/postgres-query-executor.test.d.ts +0 -1
  199. package/dist/context/connections/postgres-query-executor.test.js +0 -91
  200. package/dist/context/connections/read-only-sql.test.d.ts +0 -1
  201. package/dist/context/connections/read-only-sql.test.js +0 -20
  202. package/dist/context/connections/sqlite-query-executor.test.d.ts +0 -1
  203. package/dist/context/connections/sqlite-query-executor.test.js +0 -113
  204. package/dist/context/core/config-reference.test.d.ts +0 -1
  205. package/dist/context/core/config-reference.test.js +0 -27
  206. package/dist/context/core/git.service.assert-worktree-clean.test.d.ts +0 -1
  207. package/dist/context/core/git.service.assert-worktree-clean.test.js +0 -62
  208. package/dist/context/core/git.service.delete-directories.test.d.ts +0 -1
  209. package/dist/context/core/git.service.delete-directories.test.js +0 -61
  210. package/dist/context/core/git.service.patch.test.d.ts +0 -1
  211. package/dist/context/core/git.service.patch.test.js +0 -40
  212. package/dist/context/core/git.service.reset-hard.test.d.ts +0 -1
  213. package/dist/context/core/git.service.reset-hard.test.js +0 -47
  214. package/dist/context/core/git.service.test.d.ts +0 -1
  215. package/dist/context/core/git.service.test.js +0 -357
  216. package/dist/context/core/session-worktree.service.test.d.ts +0 -1
  217. package/dist/context/core/session-worktree.service.test.js +0 -97
  218. package/dist/context/daemon/semantic-layer-compute.test.d.ts +0 -1
  219. package/dist/context/daemon/semantic-layer-compute.test.js +0 -305
  220. package/dist/context/index-sync/reindex.test.d.ts +0 -1
  221. package/dist/context/index-sync/reindex.test.js +0 -139
  222. package/dist/context/ingest/action-identity.test.d.ts +0 -1
  223. package/dist/context/ingest/action-identity.test.js +0 -19
  224. package/dist/context/ingest/adapters/dbt/chunk.test.d.ts +0 -1
  225. package/dist/context/ingest/adapters/dbt/chunk.test.js +0 -30
  226. package/dist/context/ingest/adapters/dbt/dbt.adapter.test.d.ts +0 -1
  227. package/dist/context/ingest/adapters/dbt/dbt.adapter.test.js +0 -43
  228. package/dist/context/ingest/adapters/dbt/fetch.test.d.ts +0 -1
  229. package/dist/context/ingest/adapters/dbt/fetch.test.js +0 -30
  230. package/dist/context/ingest/adapters/dbt/parse.test.d.ts +0 -1
  231. package/dist/context/ingest/adapters/dbt/parse.test.js +0 -7
  232. package/dist/context/ingest/adapters/dbt-descriptions/parse-schema.test.d.ts +0 -1
  233. package/dist/context/ingest/adapters/dbt-descriptions/parse-schema.test.js +0 -195
  234. package/dist/context/ingest/adapters/historic-sql/bigquery-query-history-reader.test.d.ts +0 -1
  235. package/dist/context/ingest/adapters/historic-sql/bigquery-query-history-reader.test.js +0 -121
  236. package/dist/context/ingest/adapters/historic-sql/buckets.test.d.ts +0 -1
  237. package/dist/context/ingest/adapters/historic-sql/buckets.test.js +0 -49
  238. package/dist/context/ingest/adapters/historic-sql/chunk-unified.test.d.ts +0 -1
  239. package/dist/context/ingest/adapters/historic-sql/chunk-unified.test.js +0 -160
  240. package/dist/context/ingest/adapters/historic-sql/detect.test.d.ts +0 -1
  241. package/dist/context/ingest/adapters/historic-sql/detect.test.js +0 -48
  242. package/dist/context/ingest/adapters/historic-sql/evidence-tool.test.d.ts +0 -1
  243. package/dist/context/ingest/adapters/historic-sql/evidence-tool.test.js +0 -67
  244. package/dist/context/ingest/adapters/historic-sql/evidence.test.d.ts +0 -1
  245. package/dist/context/ingest/adapters/historic-sql/evidence.test.js +0 -43
  246. package/dist/context/ingest/adapters/historic-sql/historic-sql.adapter.test.d.ts +0 -1
  247. package/dist/context/ingest/adapters/historic-sql/historic-sql.adapter.test.js +0 -98
  248. package/dist/context/ingest/adapters/historic-sql/local-ingest-acceptance.test.d.ts +0 -1
  249. package/dist/context/ingest/adapters/historic-sql/local-ingest-acceptance.test.js +0 -235
  250. package/dist/context/ingest/adapters/historic-sql/pattern-inputs.test.d.ts +0 -1
  251. package/dist/context/ingest/adapters/historic-sql/pattern-inputs.test.js +0 -68
  252. package/dist/context/ingest/adapters/historic-sql/postgres-pgss-reader.test.d.ts +0 -1
  253. package/dist/context/ingest/adapters/historic-sql/postgres-pgss-reader.test.js +0 -205
  254. package/dist/context/ingest/adapters/historic-sql/projection.test.d.ts +0 -1
  255. package/dist/context/ingest/adapters/historic-sql/projection.test.js +0 -392
  256. package/dist/context/ingest/adapters/historic-sql/redaction.test.d.ts +0 -1
  257. package/dist/context/ingest/adapters/historic-sql/redaction.test.js +0 -22
  258. package/dist/context/ingest/adapters/historic-sql/skill-schemas.test.d.ts +0 -1
  259. package/dist/context/ingest/adapters/historic-sql/skill-schemas.test.js +0 -62
  260. package/dist/context/ingest/adapters/historic-sql/snowflake-query-history-reader.test.d.ts +0 -1
  261. package/dist/context/ingest/adapters/historic-sql/snowflake-query-history-reader.test.js +0 -117
  262. package/dist/context/ingest/adapters/historic-sql/stage-unified.test.d.ts +0 -1
  263. package/dist/context/ingest/adapters/historic-sql/stage-unified.test.js +0 -405
  264. package/dist/context/ingest/adapters/historic-sql/types.test.d.ts +0 -1
  265. package/dist/context/ingest/adapters/historic-sql/types.test.js +0 -87
  266. package/dist/context/ingest/adapters/live-database/chunk.test.d.ts +0 -1
  267. package/dist/context/ingest/adapters/live-database/chunk.test.js +0 -95
  268. package/dist/context/ingest/adapters/live-database/daemon-introspection.test.d.ts +0 -1
  269. package/dist/context/ingest/adapters/live-database/daemon-introspection.test.js +0 -241
  270. package/dist/context/ingest/adapters/live-database/live-database.adapter.test.d.ts +0 -1
  271. package/dist/context/ingest/adapters/live-database/live-database.adapter.test.js +0 -105
  272. package/dist/context/ingest/adapters/live-database/manifest.test.d.ts +0 -1
  273. package/dist/context/ingest/adapters/live-database/manifest.test.js +0 -291
  274. package/dist/context/ingest/adapters/live-database/stage.test.d.ts +0 -1
  275. package/dist/context/ingest/adapters/live-database/stage.test.js +0 -133
  276. package/dist/context/ingest/adapters/looker/chunk.test.d.ts +0 -1
  277. package/dist/context/ingest/adapters/looker/chunk.test.js +0 -142
  278. package/dist/context/ingest/adapters/looker/client-boundary.test.d.ts +0 -1
  279. package/dist/context/ingest/adapters/looker/client-boundary.test.js +0 -12
  280. package/dist/context/ingest/adapters/looker/client.test.d.ts +0 -1
  281. package/dist/context/ingest/adapters/looker/client.test.js +0 -407
  282. package/dist/context/ingest/adapters/looker/daemon-table-identifier-parser.test.d.ts +0 -1
  283. package/dist/context/ingest/adapters/looker/daemon-table-identifier-parser.test.js +0 -40
  284. package/dist/context/ingest/adapters/looker/detect.test.d.ts +0 -1
  285. package/dist/context/ingest/adapters/looker/detect.test.js +0 -39
  286. package/dist/context/ingest/adapters/looker/evidence-documents.test.d.ts +0 -1
  287. package/dist/context/ingest/adapters/looker/evidence-documents.test.js +0 -178
  288. package/dist/context/ingest/adapters/looker/factory.test.d.ts +0 -1
  289. package/dist/context/ingest/adapters/looker/factory.test.js +0 -55
  290. package/dist/context/ingest/adapters/looker/fetch-report.test.d.ts +0 -1
  291. package/dist/context/ingest/adapters/looker/fetch-report.test.js +0 -71
  292. package/dist/context/ingest/adapters/looker/fetch.test.d.ts +0 -1
  293. package/dist/context/ingest/adapters/looker/fetch.test.js +0 -592
  294. package/dist/context/ingest/adapters/looker/local-runtime-store.test.d.ts +0 -1
  295. package/dist/context/ingest/adapters/looker/local-runtime-store.test.js +0 -106
  296. package/dist/context/ingest/adapters/looker/looker.adapter.test.d.ts +0 -1
  297. package/dist/context/ingest/adapters/looker/looker.adapter.test.js +0 -99
  298. package/dist/context/ingest/adapters/looker/mapping.test.d.ts +0 -1
  299. package/dist/context/ingest/adapters/looker/mapping.test.js +0 -334
  300. package/dist/context/ingest/adapters/looker/reconcile.test.d.ts +0 -1
  301. package/dist/context/ingest/adapters/looker/reconcile.test.js +0 -12
  302. package/dist/context/ingest/adapters/looker/scope.test.d.ts +0 -1
  303. package/dist/context/ingest/adapters/looker/scope.test.js +0 -84
  304. package/dist/context/ingest/adapters/looker/target-connections.test.d.ts +0 -1
  305. package/dist/context/ingest/adapters/looker/target-connections.test.js +0 -71
  306. package/dist/context/ingest/adapters/looker/tools/looker-query-to-sl.tool.test.d.ts +0 -1
  307. package/dist/context/ingest/adapters/looker/tools/looker-query-to-sl.tool.test.js +0 -211
  308. package/dist/context/ingest/adapters/looker/types.test.d.ts +0 -1
  309. package/dist/context/ingest/adapters/looker/types.test.js +0 -261
  310. package/dist/context/ingest/adapters/lookml/chunk.test.d.ts +0 -1
  311. package/dist/context/ingest/adapters/lookml/chunk.test.js +0 -213
  312. package/dist/context/ingest/adapters/lookml/detect.test.d.ts +0 -1
  313. package/dist/context/ingest/adapters/lookml/detect.test.js +0 -37
  314. package/dist/context/ingest/adapters/lookml/fetch-report.test.d.ts +0 -1
  315. package/dist/context/ingest/adapters/lookml/fetch-report.test.js +0 -82
  316. package/dist/context/ingest/adapters/lookml/fetch.test.d.ts +0 -1
  317. package/dist/context/ingest/adapters/lookml/fetch.test.js +0 -121
  318. package/dist/context/ingest/adapters/lookml/graph.test.d.ts +0 -1
  319. package/dist/context/ingest/adapters/lookml/graph.test.js +0 -105
  320. package/dist/context/ingest/adapters/lookml/lookml.adapter.test.d.ts +0 -1
  321. package/dist/context/ingest/adapters/lookml/lookml.adapter.test.js +0 -49
  322. package/dist/context/ingest/adapters/lookml/parse.test.d.ts +0 -1
  323. package/dist/context/ingest/adapters/lookml/parse.test.js +0 -118
  324. package/dist/context/ingest/adapters/lookml/pull-config.test.d.ts +0 -1
  325. package/dist/context/ingest/adapters/lookml/pull-config.test.js +0 -128
  326. package/dist/context/ingest/adapters/metabase/card-references.test.d.ts +0 -1
  327. package/dist/context/ingest/adapters/metabase/card-references.test.js +0 -36
  328. package/dist/context/ingest/adapters/metabase/chunk.test.d.ts +0 -1
  329. package/dist/context/ingest/adapters/metabase/chunk.test.js +0 -299
  330. package/dist/context/ingest/adapters/metabase/client-boundary.test.d.ts +0 -1
  331. package/dist/context/ingest/adapters/metabase/client-boundary.test.js +0 -38
  332. package/dist/context/ingest/adapters/metabase/client-port.test.d.ts +0 -1
  333. package/dist/context/ingest/adapters/metabase/client-port.test.js +0 -86
  334. package/dist/context/ingest/adapters/metabase/client.test.d.ts +0 -1
  335. package/dist/context/ingest/adapters/metabase/client.test.js +0 -377
  336. package/dist/context/ingest/adapters/metabase/detect.test.d.ts +0 -1
  337. package/dist/context/ingest/adapters/metabase/detect.test.js +0 -42
  338. package/dist/context/ingest/adapters/metabase/fanout-planner.test.d.ts +0 -1
  339. package/dist/context/ingest/adapters/metabase/fanout-planner.test.js +0 -44
  340. package/dist/context/ingest/adapters/metabase/fetch-scope.test.d.ts +0 -1
  341. package/dist/context/ingest/adapters/metabase/fetch-scope.test.js +0 -124
  342. package/dist/context/ingest/adapters/metabase/fetch.test.d.ts +0 -1
  343. package/dist/context/ingest/adapters/metabase/fetch.test.js +0 -557
  344. package/dist/context/ingest/adapters/metabase/local-metabase.adapter.test.d.ts +0 -1
  345. package/dist/context/ingest/adapters/metabase/local-metabase.adapter.test.js +0 -56
  346. package/dist/context/ingest/adapters/metabase/local-source-state-store.test.d.ts +0 -1
  347. package/dist/context/ingest/adapters/metabase/local-source-state-store.test.js +0 -99
  348. package/dist/context/ingest/adapters/metabase/mapping.test.d.ts +0 -1
  349. package/dist/context/ingest/adapters/metabase/mapping.test.js +0 -215
  350. package/dist/context/ingest/adapters/metabase/metabase.adapter.test.d.ts +0 -1
  351. package/dist/context/ingest/adapters/metabase/metabase.adapter.test.js +0 -129
  352. package/dist/context/ingest/adapters/metabase/serialize-card.test.d.ts +0 -1
  353. package/dist/context/ingest/adapters/metabase/serialize-card.test.js +0 -205
  354. package/dist/context/ingest/adapters/metabase/types.test.d.ts +0 -1
  355. package/dist/context/ingest/adapters/metabase/types.test.js +0 -75
  356. package/dist/context/ingest/adapters/metricflow/chunk.test.d.ts +0 -1
  357. package/dist/context/ingest/adapters/metricflow/chunk.test.js +0 -114
  358. package/dist/context/ingest/adapters/metricflow/deep-parse.test.d.ts +0 -1
  359. package/dist/context/ingest/adapters/metricflow/deep-parse.test.js +0 -1139
  360. package/dist/context/ingest/adapters/metricflow/detect.test.d.ts +0 -1
  361. package/dist/context/ingest/adapters/metricflow/detect.test.js +0 -43
  362. package/dist/context/ingest/adapters/metricflow/fetch.test.d.ts +0 -1
  363. package/dist/context/ingest/adapters/metricflow/fetch.test.js +0 -97
  364. package/dist/context/ingest/adapters/metricflow/graph.test.d.ts +0 -1
  365. package/dist/context/ingest/adapters/metricflow/graph.test.js +0 -245
  366. package/dist/context/ingest/adapters/metricflow/import-semantic-models.test.d.ts +0 -1
  367. package/dist/context/ingest/adapters/metricflow/import-semantic-models.test.js +0 -318
  368. package/dist/context/ingest/adapters/metricflow/metricflow.adapter.test.d.ts +0 -1
  369. package/dist/context/ingest/adapters/metricflow/metricflow.adapter.test.js +0 -212
  370. package/dist/context/ingest/adapters/metricflow/parse.test.d.ts +0 -1
  371. package/dist/context/ingest/adapters/metricflow/parse.test.js +0 -171
  372. package/dist/context/ingest/adapters/metricflow/pull-config.test.d.ts +0 -1
  373. package/dist/context/ingest/adapters/metricflow/pull-config.test.js +0 -57
  374. package/dist/context/ingest/adapters/metricflow/semantic-models.test.d.ts +0 -1
  375. package/dist/context/ingest/adapters/metricflow/semantic-models.test.js +0 -204
  376. package/dist/context/ingest/adapters/notion/cluster.test.d.ts +0 -1
  377. package/dist/context/ingest/adapters/notion/cluster.test.js +0 -123
  378. package/dist/context/ingest/adapters/notion/fetch.test.d.ts +0 -1
  379. package/dist/context/ingest/adapters/notion/fetch.test.js +0 -358
  380. package/dist/context/ingest/adapters/notion/local-state-store.test.d.ts +0 -1
  381. package/dist/context/ingest/adapters/notion/local-state-store.test.js +0 -29
  382. package/dist/context/ingest/adapters/notion/normalize.test.d.ts +0 -1
  383. package/dist/context/ingest/adapters/notion/normalize.test.js +0 -64
  384. package/dist/context/ingest/adapters/notion/notion-client.test.d.ts +0 -1
  385. package/dist/context/ingest/adapters/notion/notion-client.test.js +0 -49
  386. package/dist/context/ingest/adapters/notion/notion.adapter.test.d.ts +0 -1
  387. package/dist/context/ingest/adapters/notion/notion.adapter.test.js +0 -315
  388. package/dist/context/ingest/artifact-gates.test.d.ts +0 -1
  389. package/dist/context/ingest/artifact-gates.test.js +0 -167
  390. package/dist/context/ingest/canonical-pins.test.d.ts +0 -1
  391. package/dist/context/ingest/canonical-pins.test.js +0 -66
  392. package/dist/context/ingest/clustering/kmeans.test.d.ts +0 -1
  393. package/dist/context/ingest/clustering/kmeans.test.js +0 -61
  394. package/dist/context/ingest/context-candidates/candidate-dedup.service.test.d.ts +0 -1
  395. package/dist/context/ingest/context-candidates/candidate-dedup.service.test.js +0 -216
  396. package/dist/context/ingest/context-candidates/context-candidate-carryforward.service.test.d.ts +0 -1
  397. package/dist/context/ingest/context-candidates/context-candidate-carryforward.service.test.js +0 -161
  398. package/dist/context/ingest/context-candidates/curator-pagination.service.test.d.ts +0 -1
  399. package/dist/context/ingest/context-candidates/curator-pagination.service.test.js +0 -168
  400. package/dist/context/ingest/context-candidates/embedding-text.test.d.ts +0 -1
  401. package/dist/context/ingest/context-candidates/embedding-text.test.js +0 -10
  402. package/dist/context/ingest/context-candidates/store.test.d.ts +0 -1
  403. package/dist/context/ingest/context-candidates/store.test.js +0 -67
  404. package/dist/context/ingest/context-evidence/context-evidence-index.service.test.d.ts +0 -1
  405. package/dist/context/ingest/context-evidence/context-evidence-index.service.test.js +0 -374
  406. package/dist/context/ingest/context-evidence/sqlite-context-evidence-store.test.d.ts +0 -1
  407. package/dist/context/ingest/context-evidence/sqlite-context-evidence-store.test.js +0 -416
  408. package/dist/context/ingest/context-evidence/store.test.d.ts +0 -1
  409. package/dist/context/ingest/context-evidence/store.test.js +0 -55
  410. package/dist/context/ingest/dbt-shared/project-vars.test.d.ts +0 -1
  411. package/dist/context/ingest/dbt-shared/project-vars.test.js +0 -90
  412. package/dist/context/ingest/dbt-shared/schema-files.test.d.ts +0 -1
  413. package/dist/context/ingest/dbt-shared/schema-files.test.js +0 -35
  414. package/dist/context/ingest/diff-set.service.test.d.ts +0 -1
  415. package/dist/context/ingest/diff-set.service.test.js +0 -132
  416. package/dist/context/ingest/final-gate-repair.test.d.ts +0 -1
  417. package/dist/context/ingest/final-gate-repair.test.js +0 -109
  418. package/dist/context/ingest/finalization-scope.test.d.ts +0 -1
  419. package/dist/context/ingest/finalization-scope.test.js +0 -114
  420. package/dist/context/ingest/ingest-bundle.runner.isolated-diff.test.d.ts +0 -1
  421. package/dist/context/ingest/ingest-bundle.runner.isolated-diff.test.js +0 -1928
  422. package/dist/context/ingest/ingest-bundle.runner.test.d.ts +0 -1
  423. package/dist/context/ingest/ingest-bundle.runner.test.js +0 -1899
  424. package/dist/context/ingest/ingest-prompts.test.d.ts +0 -1
  425. package/dist/context/ingest/ingest-prompts.test.js +0 -32
  426. package/dist/context/ingest/ingest-runtime-assets.test.d.ts +0 -1
  427. package/dist/context/ingest/ingest-runtime-assets.test.js +0 -89
  428. package/dist/context/ingest/ingest-trace.test.d.ts +0 -1
  429. package/dist/context/ingest/ingest-trace.test.js +0 -76
  430. package/dist/context/ingest/isolated-diff/git-patch.test.d.ts +0 -1
  431. package/dist/context/ingest/isolated-diff/git-patch.test.js +0 -76
  432. package/dist/context/ingest/isolated-diff/patch-integrator.test.d.ts +0 -1
  433. package/dist/context/ingest/isolated-diff/patch-integrator.test.js +0 -369
  434. package/dist/context/ingest/isolated-diff/textual-conflict-resolver.test.d.ts +0 -1
  435. package/dist/context/ingest/isolated-diff/textual-conflict-resolver.test.js +0 -101
  436. package/dist/context/ingest/isolated-diff/work-unit-executor.test.d.ts +0 -1
  437. package/dist/context/ingest/isolated-diff/work-unit-executor.test.js +0 -137
  438. package/dist/context/ingest/local-adapters.test.d.ts +0 -1
  439. package/dist/context/ingest/local-adapters.test.js +0 -612
  440. package/dist/context/ingest/local-bundle-ingest.test.d.ts +0 -1
  441. package/dist/context/ingest/local-bundle-ingest.test.js +0 -794
  442. package/dist/context/ingest/local-bundle-runtime.test.d.ts +0 -1
  443. package/dist/context/ingest/local-bundle-runtime.test.js +0 -240
  444. package/dist/context/ingest/local-embedding-provider.integration.test.d.ts +0 -1
  445. package/dist/context/ingest/local-embedding-provider.integration.test.js +0 -139
  446. package/dist/context/ingest/local-mapping-reconcile.test.d.ts +0 -1
  447. package/dist/context/ingest/local-mapping-reconcile.test.js +0 -61
  448. package/dist/context/ingest/local-metabase-ingest.test.d.ts +0 -1
  449. package/dist/context/ingest/local-metabase-ingest.test.js +0 -227
  450. package/dist/context/ingest/local-stage-ingest.test.d.ts +0 -1
  451. package/dist/context/ingest/local-stage-ingest.test.js +0 -581
  452. package/dist/context/ingest/memory-flow/acceptance-fixtures.d.ts +0 -6
  453. package/dist/context/ingest/memory-flow/acceptance-fixtures.js +0 -155
  454. package/dist/context/ingest/memory-flow/acceptance.test.d.ts +0 -1
  455. package/dist/context/ingest/memory-flow/acceptance.test.js +0 -43
  456. package/dist/context/ingest/memory-flow/events.test.d.ts +0 -1
  457. package/dist/context/ingest/memory-flow/events.test.js +0 -319
  458. package/dist/context/ingest/memory-flow/interaction.test.d.ts +0 -1
  459. package/dist/context/ingest/memory-flow/interaction.test.js +0 -264
  460. package/dist/context/ingest/memory-flow/interactive-render.test.d.ts +0 -1
  461. package/dist/context/ingest/memory-flow/interactive-render.test.js +0 -160
  462. package/dist/context/ingest/memory-flow/live-buffer.test.d.ts +0 -1
  463. package/dist/context/ingest/memory-flow/live-buffer.test.js +0 -77
  464. package/dist/context/ingest/memory-flow/render.test.d.ts +0 -1
  465. package/dist/context/ingest/memory-flow/render.test.js +0 -105
  466. package/dist/context/ingest/memory-flow/schema.test.d.ts +0 -1
  467. package/dist/context/ingest/memory-flow/schema.test.js +0 -147
  468. package/dist/context/ingest/memory-flow/summary.test.d.ts +0 -1
  469. package/dist/context/ingest/memory-flow/summary.test.js +0 -130
  470. package/dist/context/ingest/memory-flow/view-model.test.d.ts +0 -1
  471. package/dist/context/ingest/memory-flow/view-model.test.js +0 -397
  472. package/dist/context/ingest/memory-flow/visuals.test.d.ts +0 -1
  473. package/dist/context/ingest/memory-flow/visuals.test.js +0 -49
  474. package/dist/context/ingest/page-triage/page-triage.service.test.d.ts +0 -1
  475. package/dist/context/ingest/page-triage/page-triage.service.test.js +0 -311
  476. package/dist/context/ingest/raw-sources-paths.test.d.ts +0 -1
  477. package/dist/context/ingest/raw-sources-paths.test.js +0 -18
  478. package/dist/context/ingest/repo-fetch.test.d.ts +0 -1
  479. package/dist/context/ingest/repo-fetch.test.js +0 -168
  480. package/dist/context/ingest/report-snapshot.test.d.ts +0 -1
  481. package/dist/context/ingest/report-snapshot.test.js +0 -329
  482. package/dist/context/ingest/semantic-layer-target-policy.test.d.ts +0 -1
  483. package/dist/context/ingest/semantic-layer-target-policy.test.js +0 -25
  484. package/dist/context/ingest/source-adapter-registry.test.d.ts +0 -1
  485. package/dist/context/ingest/source-adapter-registry.test.js +0 -35
  486. package/dist/context/ingest/sqlite-bundle-ingest-store.test.d.ts +0 -1
  487. package/dist/context/ingest/sqlite-bundle-ingest-store.test.js +0 -517
  488. package/dist/context/ingest/sqlite-local-ingest-store.test.d.ts +0 -1
  489. package/dist/context/ingest/sqlite-local-ingest-store.test.js +0 -143
  490. package/dist/context/ingest/stages/build-reconcile-context.context-candidates.test.d.ts +0 -1
  491. package/dist/context/ingest/stages/build-reconcile-context.context-candidates.test.js +0 -102
  492. package/dist/context/ingest/stages/build-reconcile-context.test.d.ts +0 -1
  493. package/dist/context/ingest/stages/build-reconcile-context.test.js +0 -141
  494. package/dist/context/ingest/stages/build-wu-context.test.d.ts +0 -1
  495. package/dist/context/ingest/stages/build-wu-context.test.js +0 -196
  496. package/dist/context/ingest/stages/stage-1-stage-raw-files.test.d.ts +0 -1
  497. package/dist/context/ingest/stages/stage-1-stage-raw-files.test.js +0 -54
  498. package/dist/context/ingest/stages/stage-3-work-units.test.d.ts +0 -1
  499. package/dist/context/ingest/stages/stage-3-work-units.test.js +0 -175
  500. package/dist/context/ingest/stages/stage-4-reconciliation.test.d.ts +0 -1
  501. package/dist/context/ingest/stages/stage-4-reconciliation.test.js +0 -144
  502. package/dist/context/ingest/stages/validate-wu-sources.test.d.ts +0 -1
  503. package/dist/context/ingest/stages/validate-wu-sources.test.js +0 -27
  504. package/dist/context/ingest/tools/emit-reconciliation-records.tool.test.d.ts +0 -1
  505. package/dist/context/ingest/tools/emit-reconciliation-records.tool.test.js +0 -237
  506. package/dist/context/ingest/tools/eviction-list.tool.test.d.ts +0 -1
  507. package/dist/context/ingest/tools/eviction-list.tool.test.js +0 -44
  508. package/dist/context/ingest/tools/read-raw-file.tool.test.d.ts +0 -1
  509. package/dist/context/ingest/tools/read-raw-file.tool.test.js +0 -45
  510. package/dist/context/ingest/tools/read-raw-span.tool.test.d.ts +0 -1
  511. package/dist/context/ingest/tools/read-raw-span.tool.test.js +0 -34
  512. package/dist/context/ingest/tools/stage-diff.tool.test.d.ts +0 -1
  513. package/dist/context/ingest/tools/stage-diff.tool.test.js +0 -112
  514. package/dist/context/ingest/tools/stage-list.tool.test.d.ts +0 -1
  515. package/dist/context/ingest/tools/stage-list.tool.test.js +0 -58
  516. package/dist/context/ingest/tools/tool-transcript-summary.test.d.ts +0 -1
  517. package/dist/context/ingest/tools/tool-transcript-summary.test.js +0 -141
  518. package/dist/context/ingest/tools/warehouse-verification/discover-data.tool.test.d.ts +0 -1
  519. package/dist/context/ingest/tools/warehouse-verification/discover-data.tool.test.js +0 -107
  520. package/dist/context/ingest/tools/warehouse-verification/entity-details.tool.test.d.ts +0 -1
  521. package/dist/context/ingest/tools/warehouse-verification/entity-details.tool.test.js +0 -146
  522. package/dist/context/ingest/tools/warehouse-verification/sql-execution.tool.test.d.ts +0 -1
  523. package/dist/context/ingest/tools/warehouse-verification/sql-execution.tool.test.js +0 -50
  524. package/dist/context/ingest/wiki-body-refs.test.d.ts +0 -1
  525. package/dist/context/ingest/wiki-body-refs.test.js +0 -138
  526. package/dist/context/ingest/wiki-sl-ref-repair.test.d.ts +0 -1
  527. package/dist/context/ingest/wiki-sl-ref-repair.test.js +0 -81
  528. package/dist/context/llm/ai-sdk-runtime.test.d.ts +0 -1
  529. package/dist/context/llm/ai-sdk-runtime.test.js +0 -308
  530. package/dist/context/llm/claude-code-env.test.d.ts +0 -1
  531. package/dist/context/llm/claude-code-env.test.js +0 -17
  532. package/dist/context/llm/claude-code-models.test.d.ts +0 -1
  533. package/dist/context/llm/claude-code-models.test.js +0 -15
  534. package/dist/context/llm/claude-code-runtime.test.d.ts +0 -1
  535. package/dist/context/llm/claude-code-runtime.test.js +0 -434
  536. package/dist/context/llm/debug-request-recorder.test.d.ts +0 -1
  537. package/dist/context/llm/debug-request-recorder.test.js +0 -112
  538. package/dist/context/llm/embedding-port.test.d.ts +0 -1
  539. package/dist/context/llm/embedding-port.test.js +0 -34
  540. package/dist/context/llm/local-config.test.d.ts +0 -1
  541. package/dist/context/llm/local-config.test.js +0 -164
  542. package/dist/context/llm/runtime-local-config.test.d.ts +0 -1
  543. package/dist/context/llm/runtime-local-config.test.js +0 -17
  544. package/dist/context/llm/runtime-tools.test.d.ts +0 -1
  545. package/dist/context/llm/runtime-tools.test.js +0 -36
  546. package/dist/context/mcp/local-project-ports.test.d.ts +0 -1
  547. package/dist/context/mcp/local-project-ports.test.js +0 -689
  548. package/dist/context/mcp/server.test.d.ts +0 -1
  549. package/dist/context/mcp/server.test.js +0 -902
  550. package/dist/context/memory/local-memory.test.d.ts +0 -1
  551. package/dist/context/memory/local-memory.test.js +0 -173
  552. package/dist/context/memory/memory-agent.service.ingest.test.d.ts +0 -1
  553. package/dist/context/memory/memory-agent.service.ingest.test.js +0 -355
  554. package/dist/context/memory/memory-agent.service.test.d.ts +0 -1
  555. package/dist/context/memory/memory-agent.service.test.js +0 -413
  556. package/dist/context/memory/memory-runs.test.d.ts +0 -1
  557. package/dist/context/memory/memory-runs.test.js +0 -158
  558. package/dist/context/memory/memory-runtime-assets.test.d.ts +0 -1
  559. package/dist/context/memory/memory-runtime-assets.test.js +0 -162
  560. package/dist/context/project/config.test.d.ts +0 -1
  561. package/dist/context/project/config.test.js +0 -467
  562. package/dist/context/project/driver-schemas.test.d.ts +0 -1
  563. package/dist/context/project/driver-schemas.test.js +0 -125
  564. package/dist/context/project/local-git-file-store.test.d.ts +0 -1
  565. package/dist/context/project/local-git-file-store.test.js +0 -71
  566. package/dist/context/project/mappings-yaml-schema.test.d.ts +0 -1
  567. package/dist/context/project/mappings-yaml-schema.test.js +0 -79
  568. package/dist/context/project/project.test.d.ts +0 -1
  569. package/dist/context/project/project.test.js +0 -55
  570. package/dist/context/project/setup-config.test.d.ts +0 -1
  571. package/dist/context/project/setup-config.test.js +0 -38
  572. package/dist/context/prompts/prompt.service.test.d.ts +0 -1
  573. package/dist/context/prompts/prompt.service.test.js +0 -43
  574. package/dist/context/scan/credentials.test.d.ts +0 -1
  575. package/dist/context/scan/credentials.test.js +0 -162
  576. package/dist/context/scan/data-dictionary.test.d.ts +0 -1
  577. package/dist/context/scan/data-dictionary.test.js +0 -92
  578. package/dist/context/scan/description-generation.test.d.ts +0 -1
  579. package/dist/context/scan/description-generation.test.js +0 -693
  580. package/dist/context/scan/embedding-text.test.d.ts +0 -1
  581. package/dist/context/scan/embedding-text.test.js +0 -36
  582. package/dist/context/scan/enrichment-state.test.d.ts +0 -1
  583. package/dist/context/scan/enrichment-state.test.js +0 -147
  584. package/dist/context/scan/enrichment-summary.test.d.ts +0 -1
  585. package/dist/context/scan/enrichment-summary.test.js +0 -34
  586. package/dist/context/scan/enrichment-types.test.d.ts +0 -1
  587. package/dist/context/scan/enrichment-types.test.js +0 -141
  588. package/dist/context/scan/entity-details.test.d.ts +0 -1
  589. package/dist/context/scan/entity-details.test.js +0 -234
  590. package/dist/context/scan/local-enrichment-artifacts.test.d.ts +0 -1
  591. package/dist/context/scan/local-enrichment-artifacts.test.js +0 -771
  592. package/dist/context/scan/local-enrichment.test.d.ts +0 -1
  593. package/dist/context/scan/local-enrichment.test.js +0 -765
  594. package/dist/context/scan/local-scan.test.d.ts +0 -1
  595. package/dist/context/scan/local-scan.test.js +0 -1663
  596. package/dist/context/scan/local-structural-artifacts.test.d.ts +0 -1
  597. package/dist/context/scan/local-structural-artifacts.test.js +0 -144
  598. package/dist/context/scan/relationship-benchmark-report.test.d.ts +0 -1
  599. package/dist/context/scan/relationship-benchmark-report.test.js +0 -389
  600. package/dist/context/scan/relationship-benchmarks.test.d.ts +0 -1
  601. package/dist/context/scan/relationship-benchmarks.test.js +0 -1072
  602. package/dist/context/scan/relationship-budget.test.d.ts +0 -1
  603. package/dist/context/scan/relationship-budget.test.js +0 -71
  604. package/dist/context/scan/relationship-candidates.test.d.ts +0 -1
  605. package/dist/context/scan/relationship-candidates.test.js +0 -747
  606. package/dist/context/scan/relationship-composite-candidates.test.d.ts +0 -1
  607. package/dist/context/scan/relationship-composite-candidates.test.js +0 -69
  608. package/dist/context/scan/relationship-diagnostics.test.d.ts +0 -1
  609. package/dist/context/scan/relationship-diagnostics.test.js +0 -333
  610. package/dist/context/scan/relationship-discovery.test.d.ts +0 -1
  611. package/dist/context/scan/relationship-discovery.test.js +0 -618
  612. package/dist/context/scan/relationship-formal-metadata.test.d.ts +0 -1
  613. package/dist/context/scan/relationship-formal-metadata.test.js +0 -125
  614. package/dist/context/scan/relationship-graph-resolver.test.d.ts +0 -1
  615. package/dist/context/scan/relationship-graph-resolver.test.js +0 -604
  616. package/dist/context/scan/relationship-llm-proposal.test.d.ts +0 -1
  617. package/dist/context/scan/relationship-llm-proposal.test.js +0 -197
  618. package/dist/context/scan/relationship-locality.test.d.ts +0 -1
  619. package/dist/context/scan/relationship-locality.test.js +0 -128
  620. package/dist/context/scan/relationship-name-similarity.test.d.ts +0 -1
  621. package/dist/context/scan/relationship-name-similarity.test.js +0 -68
  622. package/dist/context/scan/relationship-profiling.test.d.ts +0 -1
  623. package/dist/context/scan/relationship-profiling.test.js +0 -392
  624. package/dist/context/scan/relationship-scoring.test.d.ts +0 -1
  625. package/dist/context/scan/relationship-scoring.test.js +0 -86
  626. package/dist/context/scan/relationship-validation.test.d.ts +0 -1
  627. package/dist/context/scan/relationship-validation.test.js +0 -455
  628. package/dist/context/scan/table-ref.test.d.ts +0 -1
  629. package/dist/context/scan/table-ref.test.js +0 -53
  630. package/dist/context/scan/type-normalization.test.d.ts +0 -1
  631. package/dist/context/scan/type-normalization.test.js +0 -21
  632. package/dist/context/scan/types.test.d.ts +0 -1
  633. package/dist/context/scan/types.test.js +0 -206
  634. package/dist/context/scan/warehouse-catalog.test.d.ts +0 -1
  635. package/dist/context/scan/warehouse-catalog.test.js +0 -158
  636. package/dist/context/search/backend-conformance.test-utils.d.ts +0 -39
  637. package/dist/context/search/backend-conformance.test-utils.js +0 -88
  638. package/dist/context/search/backend-conformance.test-utils.test.d.ts +0 -1
  639. package/dist/context/search/backend-conformance.test-utils.test.js +0 -408
  640. package/dist/context/search/discover.test.d.ts +0 -1
  641. package/dist/context/search/discover.test.js +0 -197
  642. package/dist/context/search/hybrid-search-core.test.d.ts +0 -1
  643. package/dist/context/search/hybrid-search-core.test.js +0 -113
  644. package/dist/context/search/pglite-owner-process.test.d.ts +0 -1
  645. package/dist/context/search/pglite-owner-process.test.js +0 -273
  646. package/dist/context/search/pglite-runtime-boundary.test.d.ts +0 -1
  647. package/dist/context/search/pglite-runtime-boundary.test.js +0 -40
  648. package/dist/context/search/pglite-spike.test.d.ts +0 -1
  649. package/dist/context/search/pglite-spike.test.js +0 -249
  650. package/dist/context/search/query.test.d.ts +0 -1
  651. package/dist/context/search/query.test.js +0 -23
  652. package/dist/context/search/rrf.test.d.ts +0 -1
  653. package/dist/context/search/rrf.test.js +0 -47
  654. package/dist/context/skills/skills-registry.service.test.d.ts +0 -1
  655. package/dist/context/skills/skills-registry.service.test.js +0 -161
  656. package/dist/context/sl/dictionary-search.test.d.ts +0 -1
  657. package/dist/context/sl/dictionary-search.test.js +0 -204
  658. package/dist/context/sl/local-query.test.d.ts +0 -1
  659. package/dist/context/sl/local-query.test.js +0 -283
  660. package/dist/context/sl/local-sl.test.d.ts +0 -1
  661. package/dist/context/sl/local-sl.test.js +0 -334
  662. package/dist/context/sl/pglite-sl-search-prototype.test.d.ts +0 -1
  663. package/dist/context/sl/pglite-sl-search-prototype.test.js +0 -240
  664. package/dist/context/sl/schemas.contract.test.d.ts +0 -1
  665. package/dist/context/sl/schemas.contract.test.js +0 -62
  666. package/dist/context/sl/semantic-layer.service.test.d.ts +0 -1
  667. package/dist/context/sl/semantic-layer.service.test.js +0 -1107
  668. package/dist/context/sl/sl-dictionary-profile.test.d.ts +0 -1
  669. package/dist/context/sl/sl-dictionary-profile.test.js +0 -88
  670. package/dist/context/sl/sl-search.service.test.d.ts +0 -1
  671. package/dist/context/sl/sl-search.service.test.js +0 -256
  672. package/dist/context/sl/sqlite-sl-sources-index.test.d.ts +0 -1
  673. package/dist/context/sl/sqlite-sl-sources-index.test.js +0 -175
  674. package/dist/context/sl/tools/connection-id-schema.test.d.ts +0 -1
  675. package/dist/context/sl/tools/connection-id-schema.test.js +0 -14
  676. package/dist/context/sl/tools/sl-discover.tool.test.d.ts +0 -1
  677. package/dist/context/sl/tools/sl-discover.tool.test.js +0 -72
  678. package/dist/context/sl/tools/sl-edit-source.tool.test.d.ts +0 -1
  679. package/dist/context/sl/tools/sl-edit-source.tool.test.js +0 -184
  680. package/dist/context/sl/tools/sl-read-source.tool.session.test.d.ts +0 -1
  681. package/dist/context/sl/tools/sl-read-source.tool.session.test.js +0 -55
  682. package/dist/context/sl/tools/sl-rollback.tool.test.d.ts +0 -1
  683. package/dist/context/sl/tools/sl-rollback.tool.test.js +0 -57
  684. package/dist/context/sl/tools/sl-validate.tool.test.d.ts +0 -1
  685. package/dist/context/sl/tools/sl-validate.tool.test.js +0 -54
  686. package/dist/context/sl/tools/sl-warehouse-validation.test.d.ts +0 -1
  687. package/dist/context/sl/tools/sl-warehouse-validation.test.js +0 -136
  688. package/dist/context/sl/tools/sl-write-source.tool.test.d.ts +0 -1
  689. package/dist/context/sl/tools/sl-write-source.tool.test.js +0 -307
  690. package/dist/context/sql-analysis/http-sql-analysis-port.test.d.ts +0 -1
  691. package/dist/context/sql-analysis/http-sql-analysis-port.test.js +0 -147
  692. package/dist/context/test/make-local-git-repo.d.ts +0 -10
  693. package/dist/context/test/make-local-git-repo.js +0 -34
  694. package/dist/context/tools/context-evidence-tools.test.d.ts +0 -1
  695. package/dist/context/tools/context-evidence-tools.test.js +0 -486
  696. package/dist/context/tools/touched-sl-sources.test.d.ts +0 -1
  697. package/dist/context/tools/touched-sl-sources.test.js +0 -31
  698. package/dist/context/wiki/knowledge-wiki.service.test.d.ts +0 -1
  699. package/dist/context/wiki/knowledge-wiki.service.test.js +0 -205
  700. package/dist/context/wiki/local-knowledge.test.d.ts +0 -1
  701. package/dist/context/wiki/local-knowledge.test.js +0 -270
  702. package/dist/context/wiki/sqlite-knowledge-index.test.d.ts +0 -1
  703. package/dist/context/wiki/sqlite-knowledge-index.test.js +0 -129
  704. package/dist/context/wiki/tools/wiki-list-tags.tool.test.d.ts +0 -1
  705. package/dist/context/wiki/tools/wiki-list-tags.tool.test.js +0 -35
  706. package/dist/context/wiki/tools/wiki-read.tool.test.d.ts +0 -1
  707. package/dist/context/wiki/tools/wiki-read.tool.test.js +0 -66
  708. package/dist/context/wiki/tools/wiki-remove.tool.test.d.ts +0 -1
  709. package/dist/context/wiki/tools/wiki-remove.tool.test.js +0 -95
  710. package/dist/context/wiki/tools/wiki-search.tool.test.d.ts +0 -1
  711. package/dist/context/wiki/tools/wiki-search.tool.test.js +0 -35
  712. package/dist/context/wiki/tools/wiki-write.tool.test.d.ts +0 -1
  713. package/dist/context/wiki/tools/wiki-write.tool.test.js +0 -264
  714. package/dist/context/wiki/wiki-ref-validation.test.d.ts +0 -1
  715. package/dist/context/wiki/wiki-ref-validation.test.js +0 -64
  716. package/dist/context-build-view.test.d.ts +0 -1
  717. package/dist/context-build-view.test.js +0 -942
  718. package/dist/database-tree-picker.test.d.ts +0 -1
  719. package/dist/database-tree-picker.test.js +0 -188
  720. package/dist/demo-assets.test.d.ts +0 -1
  721. package/dist/demo-assets.test.js +0 -121
  722. package/dist/demo-metrics.test.d.ts +0 -1
  723. package/dist/demo-metrics.test.js +0 -108
  724. package/dist/doctor.test.d.ts +0 -1
  725. package/dist/doctor.test.js +0 -596
  726. package/dist/embedding-resolution.test.d.ts +0 -1
  727. package/dist/embedding-resolution.test.js +0 -132
  728. package/dist/example-smoke.test.d.ts +0 -1
  729. package/dist/example-smoke.test.js +0 -83
  730. package/dist/index.test.d.ts +0 -1
  731. package/dist/index.test.js +0 -1300
  732. package/dist/ingest-query-executor.test.d.ts +0 -1
  733. package/dist/ingest-query-executor.test.js +0 -71
  734. package/dist/ingest-report-file.test.d.ts +0 -1
  735. package/dist/ingest-report-file.test.js +0 -63
  736. package/dist/ingest-viz.test.d.ts +0 -1
  737. package/dist/ingest-viz.test.js +0 -691
  738. package/dist/ingest.test-utils.d.ts +0 -126
  739. package/dist/ingest.test-utils.js +0 -629
  740. package/dist/ingest.test.d.ts +0 -1
  741. package/dist/ingest.test.js +0 -1568
  742. package/dist/io/logger.test.d.ts +0 -1
  743. package/dist/io/logger.test.js +0 -55
  744. package/dist/io/mode.test.d.ts +0 -1
  745. package/dist/io/mode.test.js +0 -48
  746. package/dist/io/print-list.test.d.ts +0 -1
  747. package/dist/io/print-list.test.js +0 -277
  748. package/dist/knowledge.test.d.ts +0 -1
  749. package/dist/knowledge.test.js +0 -198
  750. package/dist/llm/embedding-health.test.d.ts +0 -1
  751. package/dist/llm/embedding-health.test.js +0 -72
  752. package/dist/llm/embedding-provider.test.d.ts +0 -1
  753. package/dist/llm/embedding-provider.test.js +0 -84
  754. package/dist/llm/message-builder.test.d.ts +0 -1
  755. package/dist/llm/message-builder.test.js +0 -127
  756. package/dist/llm/model-health.test.d.ts +0 -1
  757. package/dist/llm/model-health.test.js +0 -55
  758. package/dist/llm/model-provider.test.d.ts +0 -1
  759. package/dist/llm/model-provider.test.js +0 -246
  760. package/dist/llm/repair.test.d.ts +0 -1
  761. package/dist/llm/repair.test.js +0 -78
  762. package/dist/local-adapters.test.d.ts +0 -1
  763. package/dist/local-adapters.test.js +0 -166
  764. package/dist/local-scan-connectors.test.d.ts +0 -1
  765. package/dist/local-scan-connectors.test.js +0 -92
  766. package/dist/managed-local-embeddings.test.d.ts +0 -1
  767. package/dist/managed-local-embeddings.test.js +0 -229
  768. package/dist/managed-mcp-daemon.test.d.ts +0 -1
  769. package/dist/managed-mcp-daemon.test.js +0 -187
  770. package/dist/managed-python-command.test.d.ts +0 -1
  771. package/dist/managed-python-command.test.js +0 -262
  772. package/dist/managed-python-daemon.test.d.ts +0 -1
  773. package/dist/managed-python-daemon.test.js +0 -360
  774. package/dist/managed-python-http.test.d.ts +0 -1
  775. package/dist/managed-python-http.test.js +0 -177
  776. package/dist/managed-python-runtime.test.d.ts +0 -1
  777. package/dist/managed-python-runtime.test.js +0 -426
  778. package/dist/mcp-http-server.test.d.ts +0 -1
  779. package/dist/mcp-http-server.test.js +0 -209
  780. package/dist/mcp-server-factory.test.d.ts +0 -1
  781. package/dist/mcp-server-factory.test.js +0 -142
  782. package/dist/memory-flow-interactive.test.d.ts +0 -1
  783. package/dist/memory-flow-interactive.test.js +0 -109
  784. package/dist/memory-flow-tui.test.d.ts +0 -1
  785. package/dist/memory-flow-tui.test.js +0 -247
  786. package/dist/next-steps.test.d.ts +0 -1
  787. package/dist/next-steps.test.js +0 -77
  788. package/dist/notion-page-picker.test.d.ts +0 -1
  789. package/dist/notion-page-picker.test.js +0 -244
  790. package/dist/print-command-tree.test.d.ts +0 -1
  791. package/dist/print-command-tree.test.js +0 -37
  792. package/dist/project-dir.test.d.ts +0 -1
  793. package/dist/project-dir.test.js +0 -124
  794. package/dist/project-resolver.test.d.ts +0 -1
  795. package/dist/project-resolver.test.js +0 -49
  796. package/dist/prompt-navigation.test.d.ts +0 -1
  797. package/dist/prompt-navigation.test.js +0 -33
  798. package/dist/proxy-env.test.d.ts +0 -1
  799. package/dist/proxy-env.test.js +0 -17
  800. package/dist/public-ingest-copy.test.d.ts +0 -1
  801. package/dist/public-ingest-copy.test.js +0 -24
  802. package/dist/public-ingest.test.d.ts +0 -1
  803. package/dist/public-ingest.test.js +0 -891
  804. package/dist/runtime-requirements.test.d.ts +0 -1
  805. package/dist/runtime-requirements.test.js +0 -73
  806. package/dist/runtime.test.d.ts +0 -1
  807. package/dist/runtime.test.js +0 -381
  808. package/dist/scan.test.d.ts +0 -1
  809. package/dist/scan.test.js +0 -1123
  810. package/dist/setup-agents.test.d.ts +0 -1
  811. package/dist/setup-agents.test.js +0 -1028
  812. package/dist/setup-context.test.d.ts +0 -1
  813. package/dist/setup-context.test.js +0 -491
  814. package/dist/setup-databases.test.d.ts +0 -1
  815. package/dist/setup-databases.test.js +0 -2101
  816. package/dist/setup-demo-tour.test.d.ts +0 -1
  817. package/dist/setup-demo-tour.test.js +0 -221
  818. package/dist/setup-embeddings.test.d.ts +0 -1
  819. package/dist/setup-embeddings.test.js +0 -436
  820. package/dist/setup-interrupt.test.d.ts +0 -1
  821. package/dist/setup-interrupt.test.js +0 -77
  822. package/dist/setup-models.test.d.ts +0 -1
  823. package/dist/setup-models.test.js +0 -885
  824. package/dist/setup-project.test.d.ts +0 -1
  825. package/dist/setup-project.test.js +0 -209
  826. package/dist/setup-prompts.test.d.ts +0 -1
  827. package/dist/setup-prompts.test.js +0 -208
  828. package/dist/setup-ready-menu.test.d.ts +0 -1
  829. package/dist/setup-ready-menu.test.js +0 -44
  830. package/dist/setup-runtime.test.d.ts +0 -1
  831. package/dist/setup-runtime.test.js +0 -111
  832. package/dist/setup-secrets.test.d.ts +0 -1
  833. package/dist/setup-secrets.test.js +0 -30
  834. package/dist/setup-sources-notion.test.d.ts +0 -1
  835. package/dist/setup-sources-notion.test.js +0 -109
  836. package/dist/setup-sources.test.d.ts +0 -1
  837. package/dist/setup-sources.test.js +0 -1303
  838. package/dist/setup.test.d.ts +0 -1
  839. package/dist/setup.test.js +0 -1825
  840. package/dist/sl.test.d.ts +0 -1
  841. package/dist/sl.test.js +0 -567
  842. package/dist/source-mapping.test.d.ts +0 -1
  843. package/dist/source-mapping.test.js +0 -65
  844. package/dist/sql.test.d.ts +0 -1
  845. package/dist/sql.test.js +0 -253
  846. package/dist/standalone-smoke.test.d.ts +0 -1
  847. package/dist/standalone-smoke.test.js +0 -250
  848. package/dist/status-project.test.d.ts +0 -1
  849. package/dist/status-project.test.js +0 -502
  850. package/dist/telemetry/command-hook.test.d.ts +0 -1
  851. package/dist/telemetry/command-hook.test.js +0 -31
  852. package/dist/telemetry/demo-detect.test.d.ts +0 -1
  853. package/dist/telemetry/demo-detect.test.js +0 -22
  854. package/dist/telemetry/emitter.test.d.ts +0 -1
  855. package/dist/telemetry/emitter.test.js +0 -103
  856. package/dist/telemetry/events.snapshot.test.d.ts +0 -1
  857. package/dist/telemetry/events.snapshot.test.js +0 -135
  858. package/dist/telemetry/events.test.d.ts +0 -1
  859. package/dist/telemetry/events.test.js +0 -136
  860. package/dist/telemetry/identity.test.d.ts +0 -1
  861. package/dist/telemetry/identity.test.js +0 -148
  862. package/dist/telemetry/project-snapshot.test.d.ts +0 -1
  863. package/dist/telemetry/project-snapshot.test.js +0 -71
  864. package/dist/telemetry/schema-writer.test.d.ts +0 -1
  865. package/dist/telemetry/schema-writer.test.js +0 -23
  866. package/dist/telemetry/scrubber.test.d.ts +0 -1
  867. package/dist/telemetry/scrubber.test.js +0 -21
  868. package/dist/text-ingest.test.d.ts +0 -1
  869. package/dist/text-ingest.test.js +0 -247
  870. package/dist/tree-picker-state.test.d.ts +0 -1
  871. package/dist/tree-picker-state.test.js +0 -303
  872. package/dist/tree-picker-tui.test.d.ts +0 -1
  873. package/dist/tree-picker-tui.test.js +0 -248
  874. package/dist/viz-fallback.test.d.ts +0 -1
  875. package/dist/viz-fallback.test.js +0 -77
@@ -1,2101 +0,0 @@
1
- import { mkdtemp, readFile, rm, stat, writeFile } from 'node:fs/promises';
2
- import { tmpdir } from 'node:os';
3
- import { join, resolve } from 'node:path';
4
- import { initKtxProject, loadKtxProject } from './context/project/project.js';
5
- import { parseKtxProjectConfig } from './context/project/config.js';
6
- import { readKtxSetupState, writeKtxSetupState } from './context/project/setup-config.js';
7
- import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
8
- import { runKtxSetupDatabasesStep, } from './setup-databases.js';
9
- function makeIo() {
10
- let stdout = '';
11
- let stderr = '';
12
- return {
13
- io: {
14
- stdout: {
15
- isTTY: true,
16
- write: (chunk) => {
17
- stdout += chunk;
18
- },
19
- },
20
- stderr: {
21
- write: (chunk) => {
22
- stderr += chunk;
23
- },
24
- },
25
- },
26
- stdout: () => stdout,
27
- stderr: () => stderr,
28
- };
29
- }
30
- function makePickerStubs(options = {}) {
31
- const queue = [...(options.scopes ?? [])];
32
- const scopeCalls = [];
33
- return {
34
- scopeCalls,
35
- pickDatabaseScope: vi.fn(async (args) => {
36
- scopeCalls.push(args);
37
- const next = queue.shift();
38
- if (next === undefined || next === 'enable-all') {
39
- const schemas = args.initialSchemas && args.initialSchemas.length > 0 ? [...args.initialSchemas] : [...args.schemas];
40
- const discovered = await args.listTablesForSchemas(schemas);
41
- const enabledTables = discovered.map((t) => `${t.schema}.${t.name}`);
42
- const activeSchemas = args.supportsSchemaScope
43
- ? Array.from(new Set(discovered.map((t) => t.schema)))
44
- : [];
45
- return { kind: 'selected', activeSchemas, enabledTables };
46
- }
47
- if (next === 'back') {
48
- return { kind: 'back' };
49
- }
50
- await args.listTablesForSchemas(next.schemas);
51
- if (next.tables === 'back') {
52
- return { kind: 'back' };
53
- }
54
- return {
55
- kind: 'selected',
56
- activeSchemas: args.supportsSchemaScope ? next.schemas : [],
57
- enabledTables: next.tables,
58
- };
59
- }),
60
- };
61
- }
62
- function makePromptAdapter(options) {
63
- const multiselectValues = [...(options.multiselectValues ?? [])];
64
- const selectValues = [...(options.selectValues ?? [])];
65
- const textValues = [...(options.textValues ?? [])];
66
- const passwordValues = [...(options.passwordValues ?? [])];
67
- return {
68
- multiselect: vi.fn(async () => multiselectValues.shift() ?? ['postgres']),
69
- autocompleteMultiselect: vi.fn(async (options) => {
70
- if (multiselectValues.length > 0) {
71
- return multiselectValues.shift() ?? [];
72
- }
73
- if (options.initialValues && options.initialValues.length > 0) {
74
- return options.initialValues;
75
- }
76
- return options.options.length > 0
77
- ? options.options.map((option) => option.value)
78
- : ['back'];
79
- }),
80
- select: vi.fn(async ({ message }) => {
81
- if (message.startsWith('Enable all tables in ') && message.includes(', or refine tables?')) {
82
- return 'save';
83
- }
84
- if (message.includes('How much database context should KTX build?')) {
85
- const nextValue = selectValues[0];
86
- return nextValue === 'fast' || nextValue === 'deep' || nextValue === 'back'
87
- ? (selectValues.shift() ?? 'fast')
88
- : 'fast';
89
- }
90
- return selectValues.shift() ?? 'finish';
91
- }),
92
- text: vi.fn(async () => (textValues.length > 0 ? textValues.shift() : '')),
93
- password: vi.fn(async () => (passwordValues.length > 0 ? passwordValues.shift() : '')),
94
- cancel: vi.fn(),
95
- };
96
- }
97
- function connectionNamePrompt(label) {
98
- return `Name this ${label} connection\nKTX will use this short name in commands and config. You can rename it now.`;
99
- }
100
- function textInputPrompt(message) {
101
- const normalized = message.replace(/\n+$/, '');
102
- if (!normalized.includes('\n')) {
103
- return `${normalized}\n│ Press Escape to go back.\n│`;
104
- }
105
- const [title, ...bodyLines] = normalized.split('\n');
106
- return `${title}\n│\n│ ${bodyLines.join('\n│ ')}\n│ Press Escape to go back.\n│`;
107
- }
108
- describe('setup databases step', () => {
109
- let tempDir;
110
- beforeEach(async () => {
111
- tempDir = await mkdtemp(join(tmpdir(), 'ktx-setup-databases-'));
112
- await initKtxProject({ projectDir: tempDir });
113
- });
114
- afterEach(async () => {
115
- vi.unstubAllEnvs();
116
- await rm(tempDir, { recursive: true, force: true });
117
- });
118
- it('shows every supported database in the interactive checklist', async () => {
119
- const prompts = makePromptAdapter({ multiselectValues: [['back']] });
120
- const result = await runKtxSetupDatabasesStep({ projectDir: tempDir, inputMode: 'auto', skipDatabases: false, databaseSchemas: [] }, makeIo().io, { prompts });
121
- expect(result.status).toBe('back');
122
- expect(prompts.multiselect).toHaveBeenCalledWith({
123
- message: 'Which databases should KTX connect to?\n' +
124
- 'Use Up/Down to move, Space to select or unselect, Enter to confirm, Escape to go back, or Ctrl+C to exit.',
125
- options: [
126
- { value: 'sqlite', label: 'SQLite' },
127
- { value: 'postgres', label: 'PostgreSQL' },
128
- { value: 'mysql', label: 'MySQL' },
129
- { value: 'clickhouse', label: 'ClickHouse' },
130
- { value: 'sqlserver', label: 'SQL Server' },
131
- { value: 'bigquery', label: 'BigQuery' },
132
- { value: 'snowflake', label: 'Snowflake' },
133
- ],
134
- required: true,
135
- });
136
- });
137
- it('lets Back from connection method selection return to database selection when adding a new driver', async () => {
138
- const prompts = makePromptAdapter({
139
- multiselectValues: [['postgres'], ['back']],
140
- selectValues: ['back'],
141
- });
142
- const result = await runKtxSetupDatabasesStep({ projectDir: tempDir, inputMode: 'auto', skipDatabases: false, databaseSchemas: [] }, makeIo().io, { prompts });
143
- expect(result.status).toBe('back');
144
- expect(prompts.select).toHaveBeenCalledWith({
145
- message: 'How do you want to connect to PostgreSQL?',
146
- options: [
147
- { value: 'url', label: 'Paste a connection URL' },
148
- { value: 'fields', label: 'Enter connection details (host, port, database, user)' },
149
- { value: 'back', label: 'Back' },
150
- ],
151
- });
152
- expect(prompts.multiselect).toHaveBeenCalledTimes(2);
153
- expect(vi.mocked(prompts.multiselect).mock.calls[1]?.[0].message).toBe('Which databases should KTX connect to?\n' +
154
- 'Use Up/Down to move, Space to select or unselect, Enter to confirm, Escape to go back, or Ctrl+C to exit.');
155
- });
156
- it('offers connection URL paste first for URL-capable databases', async () => {
157
- const cases = [
158
- { driver: 'postgres', label: 'PostgreSQL' },
159
- { driver: 'mysql', label: 'MySQL' },
160
- { driver: 'clickhouse', label: 'ClickHouse' },
161
- { driver: 'sqlserver', label: 'SQL Server' },
162
- ];
163
- for (const testCase of cases) {
164
- const prompts = makePromptAdapter({
165
- selectValues: ['back'],
166
- });
167
- const result = await runKtxSetupDatabasesStep({
168
- projectDir: tempDir,
169
- inputMode: 'auto',
170
- databaseDrivers: [testCase.driver],
171
- skipDatabases: false,
172
- databaseSchemas: [],
173
- }, makeIo().io, { prompts });
174
- expect(result.status).toBe('back');
175
- expect(prompts.select).toHaveBeenCalledWith({
176
- message: `How do you want to connect to ${testCase.label}?`,
177
- options: [
178
- { value: 'url', label: 'Paste a connection URL' },
179
- { value: 'fields', label: 'Enter connection details (host, port, database, user)' },
180
- { value: 'back', label: 'Back' },
181
- ],
182
- });
183
- }
184
- });
185
- it('lets Back leave database setup when the driver came from flags', async () => {
186
- const prompts = makePromptAdapter({ selectValues: ['back'] });
187
- const result = await runKtxSetupDatabasesStep({
188
- projectDir: tempDir,
189
- inputMode: 'auto',
190
- databaseDrivers: ['postgres'],
191
- skipDatabases: false,
192
- databaseSchemas: [],
193
- }, makeIo().io, { prompts });
194
- expect(result.status).toBe('back');
195
- expect(prompts.multiselect).not.toHaveBeenCalled();
196
- expect(prompts.select).toHaveBeenCalledTimes(1);
197
- });
198
- it('preserves context.depth when editing an existing database connection', async () => {
199
- await writeFile(join(tempDir, 'ktx.yaml'), [
200
- 'connections:',
201
- ' warehouse:',
202
- ' driver: sqlite',
203
- ' path: ./warehouse.sqlite',
204
- ' context:',
205
- ' depth: deep',
206
- '',
207
- ].join('\n'), 'utf-8');
208
- const prompts = makePromptAdapter({
209
- selectValues: ['edit', 'warehouse', 'continue'],
210
- textValues: ['./warehouse.sqlite'],
211
- });
212
- const testConnection = vi.fn(async () => 0);
213
- const scanConnection = vi.fn(async () => 0);
214
- const io = makeIo();
215
- const result = await runKtxSetupDatabasesStep({
216
- projectDir: tempDir,
217
- inputMode: 'auto',
218
- skipDatabases: false,
219
- databaseSchemas: [],
220
- disableQueryHistory: true,
221
- }, io.io, { prompts, testConnection, scanConnection });
222
- expect(result.status, io.stderr()).toBe('ready');
223
- const config = parseKtxProjectConfig(await readFile(join(tempDir, 'ktx.yaml'), 'utf-8'));
224
- expect(config.connections.warehouse).toMatchObject({
225
- driver: 'sqlite',
226
- path: './warehouse.sqlite',
227
- context: { depth: 'deep' },
228
- });
229
- });
230
- it('labels existing database connections with the database type', async () => {
231
- await writeFile(join(tempDir, 'ktx.yaml'), [
232
- 'connections:',
233
- ' warehouse:',
234
- ' driver: postgres',
235
- ' url: env:DATABASE_URL',
236
- '',
237
- ].join('\n'), 'utf-8');
238
- const prompts = makePromptAdapter({ selectValues: ['back'] });
239
- const result = await runKtxSetupDatabasesStep({
240
- projectDir: tempDir,
241
- inputMode: 'auto',
242
- databaseDrivers: ['postgres'],
243
- skipDatabases: false,
244
- databaseSchemas: [],
245
- }, makeIo().io, { prompts });
246
- expect(result.status).toBe('back');
247
- expect(prompts.select).toHaveBeenCalledWith({
248
- message: 'Configure PostgreSQL',
249
- options: [
250
- { value: 'existing:warehouse', label: 'Keep existing PostgreSQL connection: warehouse' },
251
- { value: 'edit:warehouse', label: 'Edit PostgreSQL connection: warehouse' },
252
- { value: 'new', label: 'Add another PostgreSQL connection' },
253
- { value: 'back', label: 'Back' },
254
- ],
255
- });
256
- });
257
- it('uses a database-specific editable connection name for new interactive connections', async () => {
258
- const io = makeIo();
259
- const prompts = makePromptAdapter({
260
- selectValues: ['url'],
261
- textValues: ['', 'env:DATABASE_URL'],
262
- });
263
- const testConnection = vi.fn(async () => 0);
264
- const scanConnection = vi.fn(async () => 0);
265
- const result = await runKtxSetupDatabasesStep({
266
- projectDir: tempDir,
267
- inputMode: 'auto',
268
- databaseDrivers: ['postgres'],
269
- databaseSchemas: [],
270
- skipDatabases: false,
271
- }, io.io, { prompts, testConnection, scanConnection });
272
- expect(result.status).toBe('ready');
273
- expect(prompts.text).toHaveBeenNthCalledWith(1, {
274
- message: textInputPrompt(connectionNamePrompt('PostgreSQL')),
275
- placeholder: 'postgres-warehouse',
276
- initialValue: 'postgres-warehouse',
277
- });
278
- expect(testConnection).toHaveBeenCalledWith(tempDir, 'postgres-warehouse', expect.anything());
279
- expect(scanConnection).toHaveBeenCalledWith(tempDir, 'postgres-warehouse', expect.anything());
280
- const configText = await readFile(join(tempDir, 'ktx.yaml'), 'utf-8');
281
- const config = parseKtxProjectConfig(configText);
282
- expect(config.connections['postgres-warehouse']).toEqual({
283
- driver: 'postgres',
284
- url: 'env:DATABASE_URL',
285
- context: { depth: 'fast' },
286
- });
287
- });
288
- it('emits debug telemetry when setup writes a database connection', async () => {
289
- vi.stubEnv('KTX_TELEMETRY_DEBUG', '1');
290
- vi.stubEnv('CI', '');
291
- const io = makeIo();
292
- const prompts = makePromptAdapter({
293
- selectValues: ['url'],
294
- textValues: ['', 'env:DATABASE_URL'],
295
- });
296
- const result = await runKtxSetupDatabasesStep({
297
- projectDir: tempDir,
298
- inputMode: 'auto',
299
- databaseDrivers: ['postgres'],
300
- databaseSchemas: [],
301
- skipDatabases: false,
302
- }, io.io, { prompts, testConnection: vi.fn(async () => 0), scanConnection: vi.fn(async () => 0) });
303
- expect(result.status).toBe('ready');
304
- expect(io.stderr()).toContain('"event":"connection_added"');
305
- expect(io.stderr()).toContain('"driver":"postgres"');
306
- expect(io.stderr()).toContain('"isDemoConnection":false');
307
- expect(io.stderr()).not.toContain(tempDir);
308
- });
309
- it('tells users Escape goes back in free-text connection prompts', async () => {
310
- const prompts = makePromptAdapter({
311
- selectValues: ['url'],
312
- textValues: ['', 'env:DATABASE_URL'],
313
- });
314
- const result = await runKtxSetupDatabasesStep({
315
- projectDir: tempDir,
316
- inputMode: 'auto',
317
- databaseDrivers: ['postgres'],
318
- databaseSchemas: [],
319
- skipDatabases: false,
320
- }, makeIo().io, {
321
- prompts,
322
- testConnection: vi.fn(async () => 0),
323
- scanConnection: vi.fn(async () => 0),
324
- });
325
- expect(result.status).toBe('ready');
326
- expect(prompts.text).toHaveBeenNthCalledWith(1, {
327
- message: textInputPrompt(connectionNamePrompt('PostgreSQL')),
328
- placeholder: 'postgres-warehouse',
329
- initialValue: 'postgres-warehouse',
330
- });
331
- expect(prompts.text).toHaveBeenNthCalledWith(2, {
332
- message: textInputPrompt('PostgreSQL connection URL'),
333
- });
334
- });
335
- it('uses clear setup prompts for every new database connection type', async () => {
336
- const cases = [
337
- {
338
- driver: 'sqlite',
339
- textValues: ['', './warehouse.sqlite'],
340
- expectedTextPrompts: [
341
- {
342
- message: connectionNamePrompt('SQLite'),
343
- placeholder: 'sqlite-local',
344
- initialValue: 'sqlite-local',
345
- },
346
- {
347
- message: 'SQLite database file\nEnter a relative or absolute path, for example ./warehouse.sqlite.',
348
- },
349
- ],
350
- },
351
- {
352
- driver: 'postgres',
353
- selectValues: ['url'],
354
- textValues: ['', 'env:DATABASE_URL'],
355
- expectedTextPrompts: [
356
- {
357
- message: connectionNamePrompt('PostgreSQL'),
358
- placeholder: 'postgres-warehouse',
359
- initialValue: 'postgres-warehouse',
360
- },
361
- {
362
- message: 'PostgreSQL connection URL',
363
- },
364
- ],
365
- },
366
- {
367
- driver: 'mysql',
368
- selectValues: ['url'],
369
- textValues: ['', 'env:MYSQL_DATABASE_URL'],
370
- expectedTextPrompts: [
371
- {
372
- message: connectionNamePrompt('MySQL'),
373
- placeholder: 'mysql-warehouse',
374
- initialValue: 'mysql-warehouse',
375
- },
376
- {
377
- message: 'MySQL connection URL',
378
- },
379
- ],
380
- },
381
- {
382
- driver: 'clickhouse',
383
- selectValues: ['url'],
384
- textValues: ['', 'env:CLICKHOUSE_URL'],
385
- expectedTextPrompts: [
386
- {
387
- message: connectionNamePrompt('ClickHouse'),
388
- placeholder: 'clickhouse-warehouse',
389
- initialValue: 'clickhouse-warehouse',
390
- },
391
- {
392
- message: 'ClickHouse connection URL',
393
- },
394
- ],
395
- },
396
- {
397
- driver: 'sqlserver',
398
- selectValues: ['url'],
399
- textValues: ['', 'env:SQLSERVER_DATABASE_URL'],
400
- expectedTextPrompts: [
401
- {
402
- message: connectionNamePrompt('SQL Server'),
403
- placeholder: 'sqlserver-warehouse',
404
- initialValue: 'sqlserver-warehouse',
405
- },
406
- {
407
- message: 'SQL Server connection URL',
408
- },
409
- ],
410
- },
411
- {
412
- driver: 'bigquery',
413
- selectValues: ['no'],
414
- textValues: ['', '/path/to/service-account.json', ''],
415
- expectedTextPrompts: [
416
- {
417
- message: connectionNamePrompt('BigQuery'),
418
- placeholder: 'bigquery-warehouse',
419
- initialValue: 'bigquery-warehouse',
420
- },
421
- {
422
- message: 'Path to service account JSON file',
423
- },
424
- {
425
- message: 'BigQuery location\nPress Enter for US, or enter a location like EU.',
426
- placeholder: 'US',
427
- initialValue: 'US',
428
- },
429
- ],
430
- },
431
- {
432
- driver: 'snowflake',
433
- selectValues: ['password', 'no'],
434
- textValues: ['', 'env:SNOWFLAKE_ACCOUNT', 'ANALYTICS_WH', 'ANALYTICS', 'env:SNOWFLAKE_USER', ''],
435
- passwordValues: ['env:SNOWFLAKE_PASSWORD'],
436
- expectedTextPrompts: [
437
- {
438
- message: connectionNamePrompt('Snowflake'),
439
- placeholder: 'snowflake-warehouse',
440
- initialValue: 'snowflake-warehouse',
441
- },
442
- {
443
- message: 'Snowflake account identifier',
444
- },
445
- {
446
- message: 'Snowflake warehouse\nFor example ANALYTICS_WH.',
447
- },
448
- {
449
- message: 'Snowflake database name',
450
- },
451
- {
452
- message: 'Snowflake username',
453
- },
454
- {
455
- message: 'Snowflake role (optional)\nPress Enter to skip.',
456
- },
457
- ],
458
- expectedPasswordPrompts: [
459
- {
460
- message: 'Snowflake password',
461
- },
462
- ],
463
- },
464
- ];
465
- for (const testCase of cases) {
466
- const prompts = makePromptAdapter({
467
- selectValues: testCase.selectValues ?? ['new'],
468
- textValues: testCase.textValues,
469
- passwordValues: testCase.passwordValues,
470
- });
471
- const result = await runKtxSetupDatabasesStep({
472
- projectDir: tempDir,
473
- inputMode: 'auto',
474
- databaseDrivers: [testCase.driver],
475
- databaseSchemas: [],
476
- skipDatabases: false,
477
- }, makeIo().io, {
478
- prompts,
479
- testConnection: vi.fn(async () => 0),
480
- scanConnection: vi.fn(async () => 0),
481
- listSchemas: vi.fn(async () => []),
482
- listTables: vi.fn(async () => []),
483
- });
484
- expect(result.status).toBe('ready');
485
- expect(vi.mocked(prompts.text).mock.calls.map(([options]) => options)).toEqual(testCase.expectedTextPrompts.map((expectedPrompt) => ({
486
- ...expectedPrompt,
487
- message: textInputPrompt(expectedPrompt.message),
488
- })));
489
- if (testCase.expectedPasswordPrompts) {
490
- expect(vi.mocked(prompts.password).mock.calls.map(([options]) => options)).toEqual(testCase.expectedPasswordPrompts.map((expectedPrompt) => ({
491
- ...expectedPrompt,
492
- message: textInputPrompt(expectedPrompt.message),
493
- })));
494
- }
495
- }
496
- });
497
- it('lets Back from connection method selection return to database selection', async () => {
498
- const prompts = makePromptAdapter({
499
- multiselectValues: [['postgres'], ['back']],
500
- selectValues: ['back'],
501
- textValues: [''],
502
- });
503
- const testConnection = vi.fn(async () => 0);
504
- const scanConnection = vi.fn(async () => 0);
505
- const listSchemas = vi.fn(async () => []);
506
- const listTables = vi.fn(async () => []);
507
- const result = await runKtxSetupDatabasesStep({
508
- projectDir: tempDir,
509
- inputMode: 'auto',
510
- skipDatabases: false,
511
- databaseSchemas: [],
512
- disableQueryHistory: true,
513
- }, makeIo().io, { prompts, testConnection, scanConnection, listSchemas, listTables });
514
- expect(result.status).toBe('back');
515
- expect(prompts.select).toHaveBeenNthCalledWith(1, {
516
- message: 'How do you want to connect to PostgreSQL?',
517
- options: [
518
- { value: 'url', label: 'Paste a connection URL' },
519
- { value: 'fields', label: 'Enter connection details (host, port, database, user)' },
520
- { value: 'back', label: 'Back' },
521
- ],
522
- });
523
- expect(prompts.multiselect).toHaveBeenCalledTimes(2);
524
- expect(testConnection).not.toHaveBeenCalled();
525
- expect(scanConnection).not.toHaveBeenCalled();
526
- });
527
- it('shows a configured database menu instead of the type checklist when a database exists', async () => {
528
- await writeFile(join(tempDir, 'ktx.yaml'), [
529
- 'connections:',
530
- ' warehouse:',
531
- ' driver: postgres',
532
- ' url: env:DATABASE_URL',
533
- 'setup:',
534
- ' database_connection_ids:',
535
- ' - warehouse',
536
- '',
537
- ].join('\n'), 'utf-8');
538
- await writeKtxSetupState(tempDir, { completed_steps: ['databases'] });
539
- const prompts = makePromptAdapter({ multiselectValues: [['back']], selectValues: ['continue'] });
540
- const testConnection = vi.fn(async () => 0);
541
- const scanConnection = vi.fn(async () => 0);
542
- const result = await runKtxSetupDatabasesStep({
543
- projectDir: tempDir,
544
- inputMode: 'auto',
545
- skipDatabases: false,
546
- databaseSchemas: [],
547
- disableQueryHistory: true,
548
- }, makeIo().io, { prompts, testConnection, scanConnection });
549
- expect(result).toEqual({ status: 'ready', projectDir: tempDir, connectionIds: ['warehouse'] });
550
- expect(prompts.multiselect).not.toHaveBeenCalled();
551
- expect(prompts.select).toHaveBeenCalledWith({
552
- message: 'Databases configured: warehouse\nWhat would you like to do?',
553
- options: [
554
- { value: 'continue', label: 'Continue to context sources' },
555
- { value: 'edit', label: 'Edit an existing database' },
556
- { value: 'add', label: 'Add another database' },
557
- ],
558
- });
559
- expect(testConnection).not.toHaveBeenCalled();
560
- expect(scanConnection).not.toHaveBeenCalled();
561
- });
562
- it('preserves existing database ids when adding another database from the configured menu', async () => {
563
- await writeFile(join(tempDir, 'ktx.yaml'), [
564
- 'connections:',
565
- ' warehouse:',
566
- ' driver: postgres',
567
- ' url: env:DATABASE_URL',
568
- 'setup:',
569
- ' database_connection_ids:',
570
- ' - warehouse',
571
- '',
572
- ].join('\n'), 'utf-8');
573
- await writeKtxSetupState(tempDir, { completed_steps: ['databases'] });
574
- const prompts = makePromptAdapter({
575
- selectValues: ['add', 'url', 'continue'],
576
- multiselectValues: [['mysql']],
577
- textValues: ['', 'env:MYSQL_DATABASE_URL'],
578
- });
579
- const testConnection = vi.fn(async () => 0);
580
- const scanConnection = vi.fn(async () => 0);
581
- const result = await runKtxSetupDatabasesStep({
582
- projectDir: tempDir,
583
- inputMode: 'auto',
584
- skipDatabases: false,
585
- databaseSchemas: [],
586
- disableQueryHistory: true,
587
- }, makeIo().io, { prompts, testConnection, scanConnection });
588
- expect(result).toEqual({
589
- status: 'ready',
590
- projectDir: tempDir,
591
- connectionIds: ['warehouse', 'mysql-warehouse'],
592
- });
593
- expect(prompts.multiselect).toHaveBeenCalledTimes(1);
594
- expect(prompts.multiselect).toHaveBeenCalledWith(expect.objectContaining({
595
- initialValues: ['postgres'],
596
- required: true,
597
- }));
598
- expect(prompts.select).toHaveBeenCalledWith({
599
- message: 'Databases configured: warehouse\nWhat would you like to do?',
600
- options: [
601
- { value: 'continue', label: 'Continue to context sources' },
602
- { value: 'edit', label: 'Edit an existing database' },
603
- { value: 'add', label: 'Add another database' },
604
- ],
605
- });
606
- expect(testConnection).toHaveBeenCalledTimes(1);
607
- expect(testConnection).toHaveBeenCalledWith(tempDir, 'mysql-warehouse', expect.anything());
608
- const configText = await readFile(join(tempDir, 'ktx.yaml'), 'utf-8');
609
- const config = parseKtxProjectConfig(configText);
610
- expect(config.setup?.database_connection_ids).toEqual(['warehouse', 'mysql-warehouse']);
611
- });
612
- it('lets users add another database after completing the first one', async () => {
613
- const prompts = makePromptAdapter({
614
- multiselectValues: [['postgres'], ['mysql']],
615
- selectValues: ['url', 'add', 'url', 'continue'],
616
- textValues: ['', 'env:DATABASE_URL', '', 'env:MYSQL_DATABASE_URL'],
617
- });
618
- const testConnection = vi.fn(async () => 0);
619
- const scanConnection = vi.fn(async () => 0);
620
- const listSchemas = vi.fn(async () => []);
621
- const listTables = vi.fn(async () => []);
622
- const result = await runKtxSetupDatabasesStep({
623
- projectDir: tempDir,
624
- inputMode: 'auto',
625
- skipDatabases: false,
626
- databaseSchemas: [],
627
- disableQueryHistory: true,
628
- }, makeIo().io, { prompts, testConnection, scanConnection, listSchemas, listTables });
629
- expect(result).toEqual({
630
- status: 'ready',
631
- projectDir: tempDir,
632
- connectionIds: ['postgres-warehouse', 'mysql-warehouse'],
633
- });
634
- expect(prompts.multiselect).toHaveBeenCalledTimes(2);
635
- expect(prompts.multiselect).toHaveBeenNthCalledWith(2, expect.objectContaining({
636
- initialValues: ['postgres'],
637
- required: true,
638
- }));
639
- expect(prompts.select).toHaveBeenCalledWith({
640
- message: 'Databases configured: postgres-warehouse\nWhat would you like to do?',
641
- options: [
642
- { value: 'continue', label: 'Continue to context sources' },
643
- { value: 'edit', label: 'Edit an existing database' },
644
- { value: 'add', label: 'Add another database' },
645
- ],
646
- });
647
- const config = parseKtxProjectConfig(await readFile(join(tempDir, 'ktx.yaml'), 'utf-8'));
648
- expect(config.setup?.database_connection_ids).toEqual(['postgres-warehouse', 'mysql-warehouse']);
649
- });
650
- it('returns to configured primary menu when pressing back on driver selection after adding a source', async () => {
651
- const io = makeIo();
652
- const prompts = makePromptAdapter({
653
- multiselectValues: [['postgres'], ['back']],
654
- selectValues: ['url', 'add', 'continue'],
655
- textValues: ['', 'env:DATABASE_URL'],
656
- });
657
- const testConnection = vi.fn(async () => 0);
658
- const scanConnection = vi.fn(async () => 0);
659
- const result = await runKtxSetupDatabasesStep({
660
- projectDir: tempDir,
661
- inputMode: 'auto',
662
- skipDatabases: false,
663
- databaseSchemas: [],
664
- disableQueryHistory: true,
665
- }, io.io, { prompts, testConnection, scanConnection });
666
- expect(result).toEqual({
667
- status: 'ready',
668
- projectDir: tempDir,
669
- connectionIds: ['postgres-warehouse'],
670
- });
671
- expect(prompts.multiselect).toHaveBeenCalledTimes(2);
672
- expect(prompts.multiselect).toHaveBeenNthCalledWith(2, expect.objectContaining({
673
- initialValues: ['postgres'],
674
- required: true,
675
- }));
676
- expect(io.stdout()).not.toContain('KTX cannot work without at least one database');
677
- expect(prompts.select).toHaveBeenNthCalledWith(3, {
678
- message: 'Databases configured: postgres-warehouse\nWhat would you like to do?',
679
- options: [
680
- { value: 'continue', label: 'Continue to context sources' },
681
- { value: 'edit', label: 'Edit an existing database' },
682
- { value: 'add', label: 'Add another database' },
683
- ],
684
- });
685
- });
686
- it('returns to configured primary menu when pressing back on driver selection with pre-existing source', async () => {
687
- await writeFile(join(tempDir, 'ktx.yaml'), [
688
- 'connections:',
689
- ' warehouse:',
690
- ' driver: postgres',
691
- ' url: env:DATABASE_URL',
692
- 'setup:',
693
- ' database_connection_ids:',
694
- ' - warehouse',
695
- '',
696
- ].join('\n'), 'utf-8');
697
- await writeKtxSetupState(tempDir, { completed_steps: ['databases'] });
698
- const io = makeIo();
699
- const prompts = makePromptAdapter({
700
- multiselectValues: [['back']],
701
- selectValues: ['add', 'continue'],
702
- });
703
- const result = await runKtxSetupDatabasesStep({ projectDir: tempDir, inputMode: 'auto', skipDatabases: false, databaseSchemas: [] }, io.io, { prompts });
704
- expect(result).toEqual({ status: 'ready', projectDir: tempDir, connectionIds: ['warehouse'] });
705
- expect(prompts.multiselect).toHaveBeenCalledWith(expect.objectContaining({
706
- initialValues: ['postgres'],
707
- required: true,
708
- }));
709
- expect(io.stdout()).not.toContain('KTX cannot work without at least one database');
710
- expect(prompts.select).toHaveBeenNthCalledWith(2, {
711
- message: 'Databases configured: warehouse\nWhat would you like to do?',
712
- options: [
713
- { value: 'continue', label: 'Continue to context sources' },
714
- { value: 'edit', label: 'Edit an existing database' },
715
- { value: 'add', label: 'Add another database' },
716
- ],
717
- });
718
- });
719
- it('returns from database edit selection back to the configured source menu', async () => {
720
- await writeFile(join(tempDir, 'ktx.yaml'), [
721
- 'connections:',
722
- ' warehouse:',
723
- ' driver: postgres',
724
- ' url: env:DATABASE_URL',
725
- 'setup:',
726
- ' database_connection_ids:',
727
- ' - warehouse',
728
- '',
729
- ].join('\n'), 'utf-8');
730
- await writeKtxSetupState(tempDir, { completed_steps: ['databases'] });
731
- const prompts = makePromptAdapter({
732
- selectValues: ['edit', 'back', 'continue'],
733
- });
734
- const testConnection = vi.fn(async () => 0);
735
- const scanConnection = vi.fn(async () => 0);
736
- const result = await runKtxSetupDatabasesStep({ projectDir: tempDir, inputMode: 'auto', skipDatabases: false, databaseSchemas: [] }, makeIo().io, { prompts, testConnection, scanConnection });
737
- expect(result).toEqual({ status: 'ready', projectDir: tempDir, connectionIds: ['warehouse'] });
738
- expect(prompts.select).toHaveBeenNthCalledWith(2, {
739
- message: 'Database to edit',
740
- options: [
741
- { value: 'warehouse', label: 'warehouse (PostgreSQL)' },
742
- { value: 'back', label: 'Back' },
743
- ],
744
- });
745
- expect(prompts.select).toHaveBeenNthCalledWith(3, {
746
- message: 'Databases configured: warehouse\nWhat would you like to do?',
747
- options: [
748
- { value: 'continue', label: 'Continue to context sources' },
749
- { value: 'edit', label: 'Edit an existing database' },
750
- { value: 'add', label: 'Add another database' },
751
- ],
752
- });
753
- expect(testConnection).not.toHaveBeenCalled();
754
- expect(scanConnection).not.toHaveBeenCalled();
755
- });
756
- it('reruns table selection after editing schema scope so stale enabled tables are removed', async () => {
757
- await writeFile(join(tempDir, 'ktx.yaml'), [
758
- 'connections:',
759
- ' warehouse:',
760
- ' driver: postgres',
761
- ' url: env:DATABASE_URL',
762
- ' schemas:',
763
- ' - public',
764
- ' enabled_tables:',
765
- ' - public.orders',
766
- 'setup:',
767
- ' database_connection_ids:',
768
- ' - warehouse',
769
- '',
770
- ].join('\n'), 'utf-8');
771
- await writeKtxSetupState(tempDir, { completed_steps: ['databases'] });
772
- const prompts = makePromptAdapter({
773
- textValues: ['env:DATABASE_URL'],
774
- });
775
- let primaryMenuCount = 0;
776
- vi.mocked(prompts.select).mockImplementation(async (options) => {
777
- if (options.message === 'Databases configured: warehouse\nWhat would you like to do?') {
778
- primaryMenuCount += 1;
779
- return primaryMenuCount === 1 ? 'edit' : 'continue';
780
- }
781
- if (options.message === 'Database to edit')
782
- return 'warehouse';
783
- if (options.message === 'How do you want to connect to PostgreSQL?')
784
- return 'url';
785
- if (options.message.startsWith('Enable query-history ingest'))
786
- return 'no';
787
- return 'back';
788
- });
789
- const testConnection = vi.fn(async () => 0);
790
- const scanConnection = vi.fn(async () => 0);
791
- const listSchemas = vi.fn(async () => ['analytics', 'public']);
792
- const listTables = vi.fn(async () => [{ schema: 'analytics', name: 'customers', kind: 'table' }]);
793
- const pickers = makePickerStubs({
794
- scopes: [{ schemas: ['analytics'], tables: ['analytics.customers'] }],
795
- });
796
- const result = await runKtxSetupDatabasesStep({ projectDir: tempDir, inputMode: 'auto', skipDatabases: false, databaseSchemas: [] }, makeIo().io, {
797
- prompts,
798
- testConnection,
799
- scanConnection,
800
- listSchemas,
801
- listTables,
802
- pickDatabaseScope: pickers.pickDatabaseScope,
803
- });
804
- expect(result).toEqual({ status: 'ready', projectDir: tempDir, connectionIds: ['warehouse'] });
805
- expect(prompts.text).toHaveBeenCalledWith({
806
- message: textInputPrompt('PostgreSQL connection URL'),
807
- placeholder: 'env:DATABASE_URL',
808
- initialValue: 'env:DATABASE_URL',
809
- });
810
- expect(listTables).toHaveBeenCalledWith(tempDir, 'warehouse', ['analytics']);
811
- expect(testConnection).toHaveBeenCalledWith(tempDir, 'warehouse', expect.anything());
812
- expect(scanConnection).toHaveBeenCalledWith(tempDir, 'warehouse', expect.anything());
813
- const config = parseKtxProjectConfig(await readFile(join(tempDir, 'ktx.yaml'), 'utf-8'));
814
- expect(config.connections.warehouse).toMatchObject({
815
- schemas: ['analytics'],
816
- enabled_tables: ['analytics.customers'],
817
- });
818
- });
819
- it('preselects existing schema and table choices when editing a database', async () => {
820
- await writeFile(join(tempDir, 'ktx.yaml'), [
821
- 'connections:',
822
- ' warehouse:',
823
- ' driver: postgres',
824
- ' url: env:DATABASE_URL',
825
- ' schemas:',
826
- ' - public',
827
- ' enabled_tables:',
828
- ' - public.customers',
829
- ' - public.orders',
830
- 'setup:',
831
- ' database_connection_ids:',
832
- ' - warehouse',
833
- '',
834
- ].join('\n'), 'utf-8');
835
- await writeKtxSetupState(tempDir, { completed_steps: ['databases'] });
836
- const prompts = makePromptAdapter({
837
- textValues: ['env:DATABASE_URL'],
838
- });
839
- let primaryMenuCount = 0;
840
- vi.mocked(prompts.select).mockImplementation(async (options) => {
841
- if (options.message === 'Databases configured: warehouse\nWhat would you like to do?') {
842
- primaryMenuCount += 1;
843
- return primaryMenuCount === 1 ? 'edit' : 'continue';
844
- }
845
- if (options.message === 'Database to edit')
846
- return 'warehouse';
847
- if (options.message === 'How do you want to connect to PostgreSQL?')
848
- return 'url';
849
- if (options.message.startsWith('Enable query-history ingest'))
850
- return 'no';
851
- return 'back';
852
- });
853
- const listSchemas = vi.fn(async () => ['orbit_analytics', 'orbit_raw', 'public']);
854
- const listTables = vi.fn(async () => [
855
- { schema: 'public', name: 'customers', kind: 'table' },
856
- { schema: 'public', name: 'orders', kind: 'table' },
857
- { schema: 'public', name: 'products', kind: 'table' },
858
- ]);
859
- const pickers = makePickerStubs({
860
- scopes: [{ schemas: ['public'], tables: ['public.customers', 'public.orders'] }],
861
- });
862
- const result = await runKtxSetupDatabasesStep({ projectDir: tempDir, inputMode: 'auto', skipDatabases: false, databaseSchemas: [] }, makeIo().io, {
863
- prompts,
864
- testConnection: vi.fn(async () => 0),
865
- scanConnection: vi.fn(async () => 0),
866
- listSchemas,
867
- listTables,
868
- pickDatabaseScope: pickers.pickDatabaseScope,
869
- });
870
- expect(result).toEqual({ status: 'ready', projectDir: tempDir, connectionIds: ['warehouse'] });
871
- expect(pickers.scopeCalls).toHaveLength(1);
872
- expect(pickers.scopeCalls[0]).toMatchObject({
873
- connectionId: 'warehouse',
874
- schemaNoun: 'schema',
875
- supportsSchemaScope: true,
876
- existing: { enabledTables: ['public.customers', 'public.orders'] },
877
- });
878
- const config = parseKtxProjectConfig(await readFile(join(tempDir, 'ktx.yaml'), 'utf-8'));
879
- expect(config.connections.warehouse).toMatchObject({
880
- schemas: ['public'],
881
- enabled_tables: ['public.customers', 'public.orders'],
882
- });
883
- });
884
- it('returns to the configured primary menu when backing out of schema review during edit', async () => {
885
- await writeFile(join(tempDir, 'ktx.yaml'), [
886
- 'connections:',
887
- ' warehouse:',
888
- ' driver: postgres',
889
- ' url: env:DATABASE_URL',
890
- ' schemas:',
891
- ' - public',
892
- ' enabled_tables:',
893
- ' - public.orders',
894
- 'setup:',
895
- ' database_connection_ids:',
896
- ' - warehouse',
897
- '',
898
- ].join('\n'), 'utf-8');
899
- await writeKtxSetupState(tempDir, { completed_steps: ['databases'] });
900
- const prompts = makePromptAdapter({
901
- textValues: ['env:DATABASE_URL'],
902
- });
903
- let primaryMenuCount = 0;
904
- vi.mocked(prompts.select).mockImplementation(async (options) => {
905
- if (options.message === 'Databases configured: warehouse\nWhat would you like to do?') {
906
- primaryMenuCount += 1;
907
- return primaryMenuCount === 1 ? 'edit' : 'continue';
908
- }
909
- if (options.message === 'Database to edit')
910
- return 'warehouse';
911
- if (options.message === 'How do you want to connect to PostgreSQL?')
912
- return 'url';
913
- if (options.message.startsWith('Enable query-history ingest'))
914
- return 'no';
915
- return 'back';
916
- });
917
- const testConnection = vi.fn(async () => 0);
918
- const scanConnection = vi.fn(async () => 0);
919
- const listSchemas = vi.fn(async () => ['analytics', 'public']);
920
- const listTables = vi.fn(async () => [
921
- { schema: 'analytics', name: 'customers', kind: 'table' },
922
- { schema: 'public', name: 'orders', kind: 'table' },
923
- ]);
924
- const pickers = makePickerStubs({ scopes: ['back'] });
925
- const result = await runKtxSetupDatabasesStep({ projectDir: tempDir, inputMode: 'auto', skipDatabases: false, databaseSchemas: [] }, makeIo().io, {
926
- prompts,
927
- testConnection,
928
- scanConnection,
929
- listSchemas,
930
- listTables,
931
- pickDatabaseScope: pickers.pickDatabaseScope,
932
- });
933
- expect(result).toEqual({ status: 'ready', projectDir: tempDir, connectionIds: ['warehouse'] });
934
- expect(primaryMenuCount).toBe(2);
935
- expect(testConnection).toHaveBeenCalledWith(tempDir, 'warehouse', expect.anything());
936
- expect(scanConnection).not.toHaveBeenCalled();
937
- const config = parseKtxProjectConfig(await readFile(join(tempDir, 'ktx.yaml'), 'utf-8'));
938
- expect(config.connections.warehouse).toMatchObject({
939
- url: 'env:DATABASE_URL',
940
- schemas: ['public'],
941
- enabled_tables: ['public.orders'],
942
- });
943
- });
944
- it('returns to the configured primary menu when backing out of table review during edit', async () => {
945
- await writeFile(join(tempDir, 'ktx.yaml'), [
946
- 'connections:',
947
- ' warehouse:',
948
- ' driver: postgres',
949
- ' url: env:DATABASE_URL',
950
- ' schemas:',
951
- ' - public',
952
- ' enabled_tables:',
953
- ' - public.orders',
954
- 'setup:',
955
- ' database_connection_ids:',
956
- ' - warehouse',
957
- '',
958
- ].join('\n'), 'utf-8');
959
- await writeKtxSetupState(tempDir, { completed_steps: ['databases'] });
960
- const prompts = makePromptAdapter({ textValues: ['env:DATABASE_URL'] });
961
- let primaryMenuCount = 0;
962
- vi.mocked(prompts.select).mockImplementation(async (options) => {
963
- if (options.message === 'Databases configured: warehouse\nWhat would you like to do?') {
964
- primaryMenuCount += 1;
965
- return primaryMenuCount === 1 ? 'edit' : 'continue';
966
- }
967
- if (options.message === 'Database to edit')
968
- return 'warehouse';
969
- if (options.message === 'How do you want to connect to PostgreSQL?')
970
- return 'url';
971
- if (options.message.startsWith('Enable query-history ingest'))
972
- return 'no';
973
- return 'back';
974
- });
975
- const testConnection = vi.fn(async () => 0);
976
- const scanConnection = vi.fn(async () => 0);
977
- const listSchemas = vi.fn(async () => ['public']);
978
- const listTables = vi.fn(async () => [
979
- { schema: 'public', name: 'customers', kind: 'table' },
980
- { schema: 'public', name: 'orders', kind: 'table' },
981
- ]);
982
- const pickers = makePickerStubs({ scopes: [{ schemas: ['public'], tables: 'back' }] });
983
- const result = await runKtxSetupDatabasesStep({ projectDir: tempDir, inputMode: 'auto', skipDatabases: false, databaseSchemas: [] }, makeIo().io, {
984
- prompts,
985
- testConnection,
986
- scanConnection,
987
- listSchemas,
988
- listTables,
989
- pickDatabaseScope: pickers.pickDatabaseScope,
990
- });
991
- expect(result).toEqual({ status: 'ready', projectDir: tempDir, connectionIds: ['warehouse'] });
992
- expect(primaryMenuCount).toBe(2);
993
- expect(listTables).toHaveBeenCalledWith(tempDir, 'warehouse', ['public']);
994
- expect(scanConnection).not.toHaveBeenCalled();
995
- const config = parseKtxProjectConfig(await readFile(join(tempDir, 'ktx.yaml'), 'utf-8'));
996
- expect(config.connections.warehouse).toMatchObject({
997
- url: 'env:DATABASE_URL',
998
- schemas: ['public'],
999
- enabled_tables: ['public.orders'],
1000
- });
1001
- });
1002
- it('restores an existing database edit when the follow-up scan fails', async () => {
1003
- await writeFile(join(tempDir, 'ktx.yaml'), [
1004
- 'connections:',
1005
- ' warehouse:',
1006
- ' driver: postgres',
1007
- ' url: env:DATABASE_URL',
1008
- ' schemas:',
1009
- ' - public',
1010
- ' enabled_tables:',
1011
- ' - public.orders',
1012
- 'setup:',
1013
- ' database_connection_ids:',
1014
- ' - warehouse',
1015
- '',
1016
- ].join('\n'), 'utf-8');
1017
- await writeKtxSetupState(tempDir, { completed_steps: ['databases'] });
1018
- const prompts = makePromptAdapter({
1019
- textValues: ['env:DATABASE_URL'],
1020
- });
1021
- vi.mocked(prompts.select).mockImplementation(async (options) => {
1022
- if (options.message === 'Databases configured: warehouse\nWhat would you like to do?')
1023
- return 'edit';
1024
- if (options.message === 'Database to edit')
1025
- return 'warehouse';
1026
- if (options.message === 'How do you want to connect to PostgreSQL?')
1027
- return 'url';
1028
- if (options.message.startsWith('Enable query-history ingest'))
1029
- return 'no';
1030
- return 'back';
1031
- });
1032
- const listTables = vi.fn(async () => [
1033
- { schema: 'public', name: 'customers', kind: 'table' },
1034
- { schema: 'public', name: 'orders', kind: 'table' },
1035
- ]);
1036
- const pickers = makePickerStubs({ scopes: ['enable-all'] });
1037
- const result = await runKtxSetupDatabasesStep({ projectDir: tempDir, inputMode: 'auto', skipDatabases: false, databaseSchemas: [] }, makeIo().io, {
1038
- prompts,
1039
- testConnection: vi.fn(async () => 0),
1040
- scanConnection: vi.fn(async () => 1),
1041
- listTables,
1042
- pickDatabaseScope: pickers.pickDatabaseScope,
1043
- });
1044
- expect(result).toEqual({ status: 'failed', projectDir: tempDir });
1045
- const config = parseKtxProjectConfig(await readFile(join(tempDir, 'ktx.yaml'), 'utf-8'));
1046
- expect(config.connections.warehouse).toMatchObject({
1047
- enabled_tables: ['public.orders'],
1048
- });
1049
- });
1050
- it('lets Escape from connection fields return to connection method selection', async () => {
1051
- const prompts = makePromptAdapter({
1052
- selectValues: ['fields', 'url'],
1053
- textValues: ['', undefined, 'env:DATABASE_URL'],
1054
- });
1055
- const testConnection = vi.fn(async () => 0);
1056
- const scanConnection = vi.fn(async () => 0);
1057
- const result = await runKtxSetupDatabasesStep({
1058
- projectDir: tempDir,
1059
- inputMode: 'auto',
1060
- databaseDrivers: ['postgres'],
1061
- databaseSchemas: [],
1062
- skipDatabases: false,
1063
- disableQueryHistory: true,
1064
- }, makeIo().io, { prompts, testConnection, scanConnection });
1065
- expect(result.status).toBe('ready');
1066
- const selectMessages = vi.mocked(prompts.select).mock.calls.map(([options]) => options.message);
1067
- expect(selectMessages.filter((message) => message === 'How do you want to connect to PostgreSQL?')).toHaveLength(2);
1068
- expect(testConnection).toHaveBeenCalledWith(tempDir, 'postgres-warehouse', expect.anything());
1069
- });
1070
- it('explains where Back goes after missing PostgreSQL field input', async () => {
1071
- const prompts = makePromptAdapter({
1072
- multiselectValues: [['postgres'], ['back']],
1073
- selectValues: ['fields', 'back'],
1074
- textValues: ['', 'db.example.com', '5432', ''],
1075
- });
1076
- const result = await runKtxSetupDatabasesStep({ projectDir: tempDir, inputMode: 'auto', skipDatabases: false, databaseSchemas: [] }, makeIo().io, {
1077
- prompts,
1078
- testConnection: vi.fn(async () => 0),
1079
- scanConnection: vi.fn(async () => 0),
1080
- });
1081
- expect(result.status).toBe('back');
1082
- expect(prompts.select).toHaveBeenNthCalledWith(2, {
1083
- message: 'Some PostgreSQL connection details are missing.\n' +
1084
- 'Continue entering details, or go back to database selection.',
1085
- options: [
1086
- { value: 'retry', label: 'Continue entering PostgreSQL details' },
1087
- { value: 'back', label: 'Back to database selection' },
1088
- ],
1089
- });
1090
- });
1091
- it('lets Escape from connection name return to database selection', async () => {
1092
- const prompts = makePromptAdapter({
1093
- multiselectValues: [['postgres'], ['back']],
1094
- textValues: [undefined],
1095
- });
1096
- const testConnection = vi.fn(async () => 0);
1097
- const scanConnection = vi.fn(async () => 0);
1098
- const result = await runKtxSetupDatabasesStep({
1099
- projectDir: tempDir,
1100
- inputMode: 'auto',
1101
- databaseSchemas: [],
1102
- skipDatabases: false,
1103
- }, makeIo().io, { prompts, testConnection, scanConnection });
1104
- expect(result.status).toBe('back');
1105
- expect(prompts.multiselect).toHaveBeenCalledTimes(2);
1106
- expect(prompts.select).not.toHaveBeenCalled();
1107
- expect(testConnection).not.toHaveBeenCalled();
1108
- expect(scanConnection).not.toHaveBeenCalled();
1109
- });
1110
- it('builds a Postgres connection from individual fields and stores password in .ktx/secrets', async () => {
1111
- const io = makeIo();
1112
- const prompts = makePromptAdapter({
1113
- selectValues: ['fields'],
1114
- textValues: ['', 'db.example.com', '', 'analytics', 'readonly'],
1115
- passwordValues: ['s3cret'],
1116
- });
1117
- const testConnection = vi.fn(async () => 0);
1118
- const scanConnection = vi.fn(async () => 0);
1119
- const result = await runKtxSetupDatabasesStep({
1120
- projectDir: tempDir,
1121
- inputMode: 'auto',
1122
- databaseDrivers: ['postgres'],
1123
- databaseSchemas: [],
1124
- skipDatabases: false,
1125
- }, io.io, { prompts, testConnection, scanConnection });
1126
- expect(result.status).toBe('ready');
1127
- const configText = await readFile(join(tempDir, 'ktx.yaml'), 'utf-8');
1128
- const config = parseKtxProjectConfig(configText);
1129
- const connection = config.connections['postgres-warehouse'];
1130
- expect(connection).toMatchObject({
1131
- driver: 'postgres',
1132
- host: 'db.example.com',
1133
- port: 5432,
1134
- database: 'analytics',
1135
- username: 'readonly',
1136
- });
1137
- expect(connection.password).toMatch(/^file:/);
1138
- const secretPath = join(tempDir, '.ktx/secrets/postgres-warehouse-password');
1139
- await expect(readFile(secretPath, 'utf-8')).resolves.toBe('s3cret\n');
1140
- if (process.platform !== 'win32') {
1141
- expect((await stat(secretPath)).mode & 0o777).toBe(0o600);
1142
- }
1143
- });
1144
- it('stores credential-bearing pasted URLs in .ktx/secrets automatically', async () => {
1145
- const io = makeIo();
1146
- const prompts = makePromptAdapter({
1147
- selectValues: ['url'],
1148
- textValues: ['', 'postgresql://myuser:s3cret@db.example.com:5432/analytics'], // pragma: allowlist secret
1149
- });
1150
- const testConnection = vi.fn(async () => 0);
1151
- const scanConnection = vi.fn(async () => 0);
1152
- const result = await runKtxSetupDatabasesStep({
1153
- projectDir: tempDir,
1154
- inputMode: 'auto',
1155
- databaseDrivers: ['postgres'],
1156
- databaseSchemas: [],
1157
- skipDatabases: false,
1158
- }, io.io, { prompts, testConnection, scanConnection });
1159
- expect(result.status).toBe('ready');
1160
- const configText = await readFile(join(tempDir, 'ktx.yaml'), 'utf-8');
1161
- const config = parseKtxProjectConfig(configText);
1162
- const connection = config.connections['postgres-warehouse'];
1163
- expect(connection.url).toBe(`file:${resolve(tempDir, '.ktx/secrets/postgres-warehouse-url')}`);
1164
- expect(connection.driver).toBe('postgres');
1165
- const secretContent = await readFile(join(tempDir, '.ktx/secrets/postgres-warehouse-url'), 'utf-8');
1166
- expect(secretContent).toBe('postgresql://myuser:s3cret@db.example.com:5432/analytics\n'); // pragma: allowlist secret
1167
- });
1168
- it('summarizes connection test and structural scan output during setup', async () => {
1169
- const io = makeIo();
1170
- const prompts = makePromptAdapter({
1171
- selectValues: ['url'],
1172
- textValues: ['', 'env:DATABASE_URL'],
1173
- });
1174
- const testConnection = vi.fn(async (_projectDir, _connectionId, commandIo) => {
1175
- commandIo.stdout.write('Connection test passed: postgres-warehouse\n');
1176
- commandIo.stdout.write('Driver: postgres\n');
1177
- commandIo.stdout.write('Status: ok\n');
1178
- return 0;
1179
- });
1180
- const scanConnection = vi.fn(async (_projectDir, _connectionId, commandIo) => {
1181
- commandIo.stdout.write('Scanning postgres-warehouse for context. Large databases can take a while.\n');
1182
- commandIo.stdout.write('[5%] Preparing scan\n');
1183
- commandIo.stdout.write('[15%] Inspecting database schema\n');
1184
- commandIo.stdout.write('[55%] Semantic layer comparison found 2 changes across 2 tables\n');
1185
- commandIo.stdout.write('[70%] Writing schema artifacts\n');
1186
- commandIo.stdout.write('[100%] Scan completed\n');
1187
- commandIo.stdout.write('✓ KTX scan completed\n');
1188
- commandIo.stdout.write('Status: done\n');
1189
- commandIo.stdout.write('Run: local-moywh3ky\n');
1190
- commandIo.stdout.write('Connection: postgres-warehouse\n');
1191
- commandIo.stdout.write('Mode: structural\n');
1192
- commandIo.stdout.write('Sync: 2026-05-09-221301-local-moywh3ky\n');
1193
- commandIo.stdout.write('Dry run: no\n\n');
1194
- commandIo.stdout.write('What changed\n');
1195
- commandIo.stdout.write(' Semantic layer comparison found 2 changes across 2 tables\n');
1196
- commandIo.stdout.write(' New tables: 2\n');
1197
- commandIo.stdout.write(' Changed tables: 0\n');
1198
- commandIo.stdout.write(' Removed tables: 0\n');
1199
- commandIo.stdout.write(' Unchanged tables: 0\n\n');
1200
- commandIo.stdout.write('Needs attention\n');
1201
- commandIo.stdout.write(' None\n\n');
1202
- commandIo.stdout.write('Artifacts\n');
1203
- commandIo.stdout.write(' Report: raw-sources/postgres-warehouse/live-database/2026-05-09-221301-local-moywh3ky/scan-report.json\n');
1204
- commandIo.stdout.write(' Raw sources: raw-sources/postgres-warehouse/live-database/2026-05-09-221301-local-moywh3ky\n');
1205
- commandIo.stdout.write(' Schema shards: 1\n\n');
1206
- commandIo.stdout.write('Next:\n');
1207
- commandIo.stdout.write(` ktx status --project-dir ${tempDir} local-moywh3ky\n`);
1208
- return 0;
1209
- });
1210
- const result = await runKtxSetupDatabasesStep({
1211
- projectDir: tempDir,
1212
- inputMode: 'auto',
1213
- databaseDrivers: ['postgres'],
1214
- databaseSchemas: [],
1215
- skipDatabases: false,
1216
- }, io.io, { prompts, testConnection, scanConnection });
1217
- expect(result.status).toBe('ready');
1218
- expect(io.stdout()).toContain([
1219
- '◇ Testing postgres-warehouse',
1220
- '│ ✓ Connection test passed',
1221
- '│ Driver: PostgreSQL',
1222
- '│',
1223
- ].join('\n'));
1224
- expect(io.stdout()).not.toContain('Tables: 2');
1225
- expect(io.stdout()).toContain('◇ Building schema context for postgres-warehouse');
1226
- expect(io.stdout()).toContain('│ Running fast database ingest…');
1227
- expect(io.stdout()).toContain('◇ Schema context complete for postgres-warehouse');
1228
- expect(io.stdout()).toContain('│ Changes: 2 new tables');
1229
- expect(io.stdout()).toContain('◇ Database ready');
1230
- expect(io.stdout()).not.toContain(['Primary source', 'ready'].join(' '));
1231
- expect(io.stdout()).toContain('│ postgres-warehouse · PostgreSQL · schema context complete');
1232
- expect(io.stdout()).not.toContain('Scanning postgres-warehouse');
1233
- expect(io.stdout()).not.toContain('Scan complete for postgres-warehouse');
1234
- expect(io.stdout()).not.toContain('structural scan complete');
1235
- expect(io.stdout()).not.toContain('Report: raw-sources');
1236
- expect(io.stdout()).not.toContain('live-database');
1237
- expect(io.stdout()).not.toContain('[5%] Preparing scan');
1238
- expect(io.stdout()).not.toContain('What changed');
1239
- expect(io.stdout()).not.toContain('Next:');
1240
- });
1241
- it('normalizes $ENV_VAR syntax to env: references in pasted URLs', async () => {
1242
- const io = makeIo();
1243
- const prompts = makePromptAdapter({
1244
- selectValues: ['url'],
1245
- textValues: ['', '$DATABASE_URL'],
1246
- });
1247
- const testConnection = vi.fn(async () => 0);
1248
- const scanConnection = vi.fn(async () => 0);
1249
- const result = await runKtxSetupDatabasesStep({
1250
- projectDir: tempDir,
1251
- inputMode: 'auto',
1252
- databaseDrivers: ['postgres'],
1253
- databaseSchemas: [],
1254
- skipDatabases: false,
1255
- }, io.io, { prompts, testConnection, scanConnection });
1256
- expect(result.status).toBe('ready');
1257
- const config = parseKtxProjectConfig(await readFile(join(tempDir, 'ktx.yaml'), 'utf-8'));
1258
- expect(config.connections['postgres-warehouse']).toMatchObject({
1259
- driver: 'postgres',
1260
- url: 'env:DATABASE_URL',
1261
- });
1262
- });
1263
- it('offers schema scope discovery for MySQL and writes selected schemas', async () => {
1264
- const prompts = makePromptAdapter({
1265
- multiselectValues: [['mysql']],
1266
- selectValues: ['url', 'continue'],
1267
- textValues: ['mysql-warehouse', 'mysql://reader@localhost/analytics'],
1268
- });
1269
- const listSchemas = vi.fn(async () => ['analytics', 'mart']);
1270
- const listTables = vi.fn(async (_projectDir, _connectionId, schemas) => (schemas ?? []).map((schema) => ({ schema, name: 'orders', kind: 'table' })));
1271
- const pickDatabaseScope = vi.fn(async (args) => {
1272
- const scopedArgs = args;
1273
- expect(args.schemaNoun).toBe('database');
1274
- expect(args.schemas).toEqual(['analytics', 'mart']);
1275
- expect(scopedArgs.schemaSuggestion.suggested).toEqual(new Set(['analytics', 'mart']));
1276
- return { kind: 'selected', activeSchemas: ['mart'], enabledTables: ['mart.orders'] };
1277
- });
1278
- await runKtxSetupDatabasesStep({ projectDir: tempDir, inputMode: 'auto', skipDatabases: false, databaseSchemas: [] }, makeIo().io, { prompts, testConnection: vi.fn(async () => 0), scanConnection: vi.fn(async () => 0), listSchemas, listTables, pickDatabaseScope });
1279
- const project = await loadKtxProject({ projectDir: tempDir });
1280
- expect(project.config.connections['mysql-warehouse']).toMatchObject({
1281
- driver: 'mysql',
1282
- schemas: ['mart'],
1283
- enabled_tables: ['mart.orders'],
1284
- });
1285
- });
1286
- it('maps ClickHouse scripted database schema input to databases and preserves database', async () => {
1287
- await runKtxSetupDatabasesStep({
1288
- projectDir: tempDir,
1289
- inputMode: 'disabled',
1290
- skipDatabases: false,
1291
- databaseDrivers: ['clickhouse'],
1292
- databaseConnectionId: 'clickhouse-warehouse',
1293
- databaseUrl: 'clickhouse://reader@localhost/analytics',
1294
- databaseSchemas: ['analytics', 'mart'],
1295
- }, makeIo().io, { testConnection: vi.fn(async () => 0), scanConnection: vi.fn(async () => 0) });
1296
- const project = await loadKtxProject({ projectDir: tempDir });
1297
- expect(project.config.connections['clickhouse-warehouse']).toMatchObject({
1298
- driver: 'clickhouse',
1299
- database: 'analytics',
1300
- databases: ['analytics', 'mart'],
1301
- });
1302
- expect(project.config.connections['clickhouse-warehouse']).not.toHaveProperty('schemas');
1303
- });
1304
- it('does not prompt for a bootstrap BigQuery dataset before scope discovery', async () => {
1305
- const prompts = makePromptAdapter({
1306
- multiselectValues: [['bigquery']],
1307
- selectValues: ['no', 'continue'],
1308
- textValues: ['bigquery-warehouse', '/tmp/service-account.json', 'US'],
1309
- });
1310
- const listSchemas = vi.fn(async () => ['analytics']);
1311
- const listTables = vi.fn(async () => [{ schema: 'analytics', name: 'orders', kind: 'table' }]);
1312
- const pickDatabaseScope = vi.fn(async () => ({
1313
- kind: 'selected',
1314
- activeSchemas: ['analytics'],
1315
- enabledTables: ['analytics.orders'],
1316
- }));
1317
- await runKtxSetupDatabasesStep({ projectDir: tempDir, inputMode: 'auto', skipDatabases: false, databaseSchemas: [] }, makeIo().io, { prompts, testConnection: vi.fn(async () => 0), scanConnection: vi.fn(async () => 0), listSchemas, listTables, pickDatabaseScope });
1318
- const textMessages = vi.mocked(prompts.text).mock.calls.map(([options]) => options.message);
1319
- expect(textMessages).not.toContain(textInputPrompt('BigQuery dataset\nFor example analytics.'));
1320
- });
1321
- it('prompts for discovered Postgres schemas before the first scan', async () => {
1322
- const io = makeIo();
1323
- const prompts = makePromptAdapter({
1324
- selectValues: ['url'],
1325
- textValues: ['', 'env:DATABASE_URL'],
1326
- });
1327
- const testConnection = vi.fn(async () => 0);
1328
- const scanConnection = vi.fn(async (asyncScanProjectDir) => {
1329
- const config = parseKtxProjectConfig(await readFile(join(asyncScanProjectDir, 'ktx.yaml'), 'utf-8'));
1330
- expect(config.connections['postgres-warehouse']).toMatchObject({
1331
- schemas: ['orbit_analytics', 'orbit_raw'],
1332
- });
1333
- return 0;
1334
- });
1335
- const listSchemas = vi.fn(async () => ['orbit_analytics', 'orbit_raw', 'public']);
1336
- const listTables = vi.fn(async () => [
1337
- { schema: 'orbit_analytics', name: 'events', kind: 'table' },
1338
- { schema: 'orbit_raw', name: 'inputs', kind: 'table' },
1339
- { schema: 'public', name: 'misc', kind: 'table' },
1340
- ]);
1341
- const pickers = makePickerStubs({
1342
- scopes: [
1343
- {
1344
- schemas: ['orbit_analytics', 'orbit_raw'],
1345
- tables: ['orbit_analytics.events', 'orbit_raw.inputs'],
1346
- },
1347
- ],
1348
- });
1349
- const result = await runKtxSetupDatabasesStep({
1350
- projectDir: tempDir,
1351
- inputMode: 'auto',
1352
- databaseDrivers: ['postgres'],
1353
- databaseSchemas: [],
1354
- skipDatabases: false,
1355
- }, io.io, {
1356
- prompts,
1357
- testConnection,
1358
- scanConnection,
1359
- listSchemas,
1360
- listTables,
1361
- pickDatabaseScope: pickers.pickDatabaseScope,
1362
- });
1363
- expect(result.status).toBe('ready');
1364
- expect(listSchemas).toHaveBeenCalledWith(tempDir, 'postgres-warehouse');
1365
- expect(pickers.scopeCalls).toHaveLength(1);
1366
- expect(pickers.scopeCalls[0]).toMatchObject({
1367
- connectionId: 'postgres-warehouse',
1368
- schemaNoun: 'schema',
1369
- schemaNounPlural: 'schemas',
1370
- schemas: ['orbit_analytics', 'orbit_raw', 'public'],
1371
- schemaSuggestion: { excluded: new Set(), suggested: new Set() },
1372
- });
1373
- const config = parseKtxProjectConfig(await readFile(join(tempDir, 'ktx.yaml'), 'utf-8'));
1374
- expect(config.connections['postgres-warehouse']).toMatchObject({
1375
- schemas: ['orbit_analytics', 'orbit_raw'],
1376
- });
1377
- expect(io.stdout()).toContain('✓ orbit_analytics, orbit_raw');
1378
- });
1379
- it('falls back to comma-separated free-text when listSchemas fails interactively', async () => {
1380
- const io = makeIo();
1381
- const prompts = makePromptAdapter({
1382
- selectValues: ['url'],
1383
- textValues: ['', 'env:DATABASE_URL', 'orbit_analytics, orbit_raw'],
1384
- });
1385
- const testConnection = vi.fn(async () => 0);
1386
- const scanConnection = vi.fn(async () => 0);
1387
- const listSchemas = vi.fn(async () => {
1388
- throw new Error('permission denied to list schemas');
1389
- });
1390
- const listTables = vi.fn(async (_projectDir, _connectionId, schemas) => (schemas ?? []).map((schema) => ({ schema, name: 'events', kind: 'table' })));
1391
- const pickers = makePickerStubs({
1392
- scopes: [
1393
- {
1394
- schemas: ['orbit_analytics', 'orbit_raw'],
1395
- tables: ['orbit_analytics.events', 'orbit_raw.events'],
1396
- },
1397
- ],
1398
- });
1399
- const result = await runKtxSetupDatabasesStep({
1400
- projectDir: tempDir,
1401
- inputMode: 'auto',
1402
- databaseDrivers: ['postgres'],
1403
- databaseSchemas: [],
1404
- skipDatabases: false,
1405
- }, io.io, {
1406
- prompts,
1407
- testConnection,
1408
- scanConnection,
1409
- listSchemas,
1410
- listTables,
1411
- pickDatabaseScope: pickers.pickDatabaseScope,
1412
- });
1413
- expect(result.status).toBe('ready');
1414
- expect(io.stderr()).toContain('Could not discover postgresql schemas');
1415
- expect(vi.mocked(prompts.text).mock.calls.map(([options]) => options.message)).toContain(textInputPrompt('Enter schemas for postgres-warehouse as a comma-separated list (e.g. SALES, MARKETING).'));
1416
- expect(pickers.scopeCalls[0]).toMatchObject({
1417
- schemas: ['orbit_analytics', 'orbit_raw'],
1418
- initialSchemas: ['orbit_analytics', 'orbit_raw'],
1419
- schemaSuggestion: { suggested: new Set(['orbit_analytics', 'orbit_raw']) },
1420
- });
1421
- });
1422
- it('passes schemas and a lazy table callback to the scope picker instead of eager table discovery', async () => {
1423
- const listSchemas = vi.fn(async () => ['analytics', 'raw']);
1424
- const listTables = vi.fn(async (_projectDir, _connectionId, schemas) => (schemas ?? []).map((schema) => ({ schema, name: 'orders', kind: 'table' })));
1425
- const pickDatabaseScope = vi.fn(async (args) => {
1426
- const lazyArgs = args;
1427
- expect(lazyArgs.schemas).toEqual(['analytics', 'raw']);
1428
- expect(args).not.toHaveProperty('discovered');
1429
- expect(listTables).not.toHaveBeenCalled();
1430
- const tables = await lazyArgs.listTablesForSchemas(['analytics']);
1431
- expect(tables).toEqual([{ schema: 'analytics', name: 'orders', kind: 'table' }]);
1432
- return { kind: 'selected', activeSchemas: ['analytics'], enabledTables: ['analytics.orders'] };
1433
- });
1434
- await runKtxSetupDatabasesStep({ projectDir: tempDir, inputMode: 'auto', databaseDrivers: ['postgres'], skipDatabases: false, databaseSchemas: [] }, makeIo().io, {
1435
- prompts: makePromptAdapter({ selectValues: ['url'], textValues: ['', 'env:DATABASE_URL'] }),
1436
- testConnection: vi.fn(async () => 0),
1437
- scanConnection: vi.fn(async () => 0),
1438
- listSchemas,
1439
- listTables,
1440
- pickDatabaseScope,
1441
- });
1442
- expect(listTables).toHaveBeenCalledTimes(1);
1443
- expect(listTables).toHaveBeenCalledWith(tempDir, 'postgres-warehouse', ['analytics']);
1444
- });
1445
- it('auto-selects all discovered Postgres schemas in non-interactive setup', async () => {
1446
- const io = makeIo();
1447
- const prompts = makePromptAdapter({});
1448
- const testConnection = vi.fn(async () => 0);
1449
- const scanConnection = vi.fn(async (asyncScanProjectDir) => {
1450
- const config = parseKtxProjectConfig(await readFile(join(asyncScanProjectDir, 'ktx.yaml'), 'utf-8'));
1451
- expect(config.connections.warehouse).toMatchObject({
1452
- schemas: ['orbit_analytics', 'orbit_raw', 'public'],
1453
- });
1454
- return 0;
1455
- });
1456
- const listSchemas = vi.fn(async () => ['orbit_analytics', 'orbit_raw', 'public']);
1457
- const result = await runKtxSetupDatabasesStep({
1458
- projectDir: tempDir,
1459
- inputMode: 'disabled',
1460
- databaseDrivers: ['postgres'],
1461
- databaseConnectionId: 'warehouse',
1462
- databaseUrl: 'env:DATABASE_URL',
1463
- databaseSchemas: [],
1464
- skipDatabases: false,
1465
- }, io.io, { prompts, testConnection, scanConnection, listSchemas });
1466
- expect(result.status).toBe('ready');
1467
- expect(prompts.multiselect).not.toHaveBeenCalled();
1468
- const config = parseKtxProjectConfig(await readFile(join(tempDir, 'ktx.yaml'), 'utf-8'));
1469
- expect(config.connections.warehouse).toMatchObject({
1470
- schemas: ['orbit_analytics', 'orbit_raw', 'public'],
1471
- });
1472
- expect(io.stdout()).toContain('✓ orbit_analytics, orbit_raw, public');
1473
- });
1474
- it('adds one non-interactive Postgres URL connection, tests it, scans it, and marks databases complete', async () => {
1475
- const io = makeIo();
1476
- const testConnection = vi.fn(async () => 0);
1477
- const scanConnection = vi.fn(async () => 0);
1478
- const listSchemas = vi.fn(async () => ['orbit_analytics', 'orbit_raw', 'public']);
1479
- const result = await runKtxSetupDatabasesStep({
1480
- projectDir: tempDir,
1481
- inputMode: 'auto',
1482
- databaseDrivers: ['postgres'],
1483
- databaseConnectionId: 'warehouse',
1484
- databaseUrl: 'env:DATABASE_URL',
1485
- databaseSchemas: ['public'],
1486
- skipDatabases: false,
1487
- disableQueryHistory: true,
1488
- }, io.io, { testConnection, scanConnection, listSchemas });
1489
- expect(result.status).toBe('ready');
1490
- expect(listSchemas).not.toHaveBeenCalled();
1491
- expect(testConnection).toHaveBeenCalledWith(tempDir, 'warehouse', expect.anything());
1492
- expect(scanConnection).toHaveBeenCalledWith(tempDir, 'warehouse', expect.anything());
1493
- const config = parseKtxProjectConfig(await readFile(join(tempDir, 'ktx.yaml'), 'utf-8'));
1494
- expect(config.connections.warehouse).toEqual({
1495
- driver: 'postgres',
1496
- url: 'env:DATABASE_URL',
1497
- schemas: ['public'],
1498
- context: { queryHistory: { enabled: false }, depth: 'fast' },
1499
- });
1500
- expect(config.setup).toEqual({
1501
- database_connection_ids: ['warehouse'],
1502
- });
1503
- expect((await readKtxSetupState(tempDir)).completed_steps).toContain('databases');
1504
- expect(io.stdout()).toContain('Database ready');
1505
- expect(io.stdout()).not.toContain(['Primary source', 'ready'].join(' '));
1506
- expect(io.stdout()).not.toContain('DATABASE_URL=');
1507
- });
1508
- it('adds one non-interactive SQLite connection from --database-url without prompting', async () => {
1509
- const io = makeIo();
1510
- const prompts = makePromptAdapter({});
1511
- const testConnection = vi.fn(async () => 0);
1512
- const scanConnection = vi.fn(async () => 0);
1513
- const result = await runKtxSetupDatabasesStep({
1514
- projectDir: tempDir,
1515
- inputMode: 'disabled',
1516
- databaseDrivers: ['sqlite'],
1517
- databaseConnectionId: 'warehouse',
1518
- databaseUrl: './warehouse.sqlite',
1519
- databaseSchemas: [],
1520
- skipDatabases: false,
1521
- }, io.io, { prompts, testConnection, scanConnection });
1522
- expect(result.status).toBe('ready');
1523
- expect(prompts.text).not.toHaveBeenCalled();
1524
- expect(testConnection).toHaveBeenCalledWith(tempDir, 'warehouse', expect.anything());
1525
- expect(scanConnection).toHaveBeenCalledWith(tempDir, 'warehouse', expect.anything());
1526
- const config = parseKtxProjectConfig(await readFile(join(tempDir, 'ktx.yaml'), 'utf-8'));
1527
- expect(config.connections.warehouse).toEqual({
1528
- driver: 'sqlite',
1529
- path: './warehouse.sqlite',
1530
- context: { depth: 'fast' },
1531
- });
1532
- expect(config.setup).toEqual({
1533
- database_connection_ids: ['warehouse'],
1534
- });
1535
- expect((await readKtxSetupState(tempDir)).completed_steps).toContain('databases');
1536
- });
1537
- it('selects multiple existing connections and validates each before recording setup ids', async () => {
1538
- await writeFile(join(tempDir, 'ktx.yaml'), [
1539
- 'connections:',
1540
- ' warehouse:',
1541
- ' driver: postgres',
1542
- ' url: env:DATABASE_URL',
1543
- ' analytics:',
1544
- ' driver: snowflake',
1545
- ' authMethod: password',
1546
- ' account: env:SNOWFLAKE_ACCOUNT',
1547
- ' warehouse: WH',
1548
- ' database: ANALYTICS',
1549
- ' schema_name: PUBLIC',
1550
- ' username: reader',
1551
- ' password: env:SNOWFLAKE_PASSWORD',
1552
- '',
1553
- ].join('\n'), 'utf-8');
1554
- const io = makeIo();
1555
- const testConnection = vi.fn(async () => 0);
1556
- const scanConnection = vi.fn(async () => 0);
1557
- const result = await runKtxSetupDatabasesStep({
1558
- projectDir: tempDir,
1559
- inputMode: 'disabled',
1560
- databaseConnectionIds: ['warehouse', 'analytics'],
1561
- databaseSchemas: [],
1562
- skipDatabases: false,
1563
- }, io.io, { testConnection, scanConnection });
1564
- expect(result.status).toBe('ready');
1565
- expect(testConnection).toHaveBeenCalledTimes(2);
1566
- expect(scanConnection).toHaveBeenCalledTimes(2);
1567
- const config = parseKtxProjectConfig(await readFile(join(tempDir, 'ktx.yaml'), 'utf-8'));
1568
- expect(config.setup?.database_connection_ids).toEqual(['warehouse', 'analytics']);
1569
- expect(await readFile(join(tempDir, 'ktx.yaml'), 'utf-8')).not.toContain('completed_steps:');
1570
- expect((await readKtxSetupState(tempDir)).completed_steps).toContain('databases');
1571
- });
1572
- it('keeps the connection config but does not mark databases complete when scanning fails', async () => {
1573
- const io = makeIo();
1574
- const result = await runKtxSetupDatabasesStep({
1575
- projectDir: tempDir,
1576
- inputMode: 'disabled',
1577
- databaseDrivers: ['postgres'],
1578
- databaseConnectionId: 'warehouse',
1579
- databaseUrl: 'env:DATABASE_URL',
1580
- databaseSchemas: [],
1581
- skipDatabases: false,
1582
- }, io.io, {
1583
- testConnection: vi.fn(async () => 0),
1584
- scanConnection: vi.fn(async () => 1),
1585
- });
1586
- expect(result.status).toBe('failed');
1587
- const config = parseKtxProjectConfig(await readFile(join(tempDir, 'ktx.yaml'), 'utf-8'));
1588
- expect(config.connections.warehouse).toMatchObject({ driver: 'postgres', url: 'env:DATABASE_URL' });
1589
- expect(await readFile(join(tempDir, 'ktx.yaml'), 'utf-8')).not.toContain('completed_steps:');
1590
- expect(io.stderr()).toContain('Fast database ingest failed for warehouse.');
1591
- expect(io.stderr()).toContain('│ Fast database ingest failed for warehouse.');
1592
- expect(io.stderr()).toContain(`Debug command: ktx ingest warehouse --project-dir ${tempDir} --fast --debug`);
1593
- expect(io.stderr()).not.toContain('Structural scan failed for warehouse.');
1594
- expect(io.stderr()).not.toMatch(/^Fast database ingest failed for warehouse\./m);
1595
- });
1596
- it('prints the native SQLite rebuild command when scanning hits a Node ABI mismatch', async () => {
1597
- const io = makeIo();
1598
- const result = await runKtxSetupDatabasesStep({
1599
- projectDir: tempDir,
1600
- inputMode: 'disabled',
1601
- databaseDrivers: ['postgres'],
1602
- databaseConnectionId: 'warehouse',
1603
- databaseUrl: 'env:DATABASE_URL',
1604
- databaseSchemas: [],
1605
- skipDatabases: false,
1606
- }, io.io, {
1607
- testConnection: vi.fn(async () => 0),
1608
- rebuildNativeSqlite: vi.fn(async () => 1),
1609
- scanConnection: vi.fn(async (_projectDir, _connectionId, commandIo) => {
1610
- commandIo.stderr.write([
1611
- "The module '/workspace/node_modules/better-sqlite3/build/Release/better_sqlite3.node'",
1612
- 'was compiled against a different Node.js version using',
1613
- 'NODE_MODULE_VERSION 147. This version of Node.js requires',
1614
- 'NODE_MODULE_VERSION 137. Please try re-compiling or re-installing',
1615
- 'the module (for instance, using `npm rebuild` or `npm install`).',
1616
- '',
1617
- ].join('\n'));
1618
- return 1;
1619
- }),
1620
- });
1621
- expect(result.status).toBe('failed');
1622
- expect(io.stderr()).toContain('Native SQLite is built for a different Node.js ABI.');
1623
- expect(io.stderr()).toContain('│ Native SQLite is built for a different Node.js ABI.');
1624
- expect(io.stderr()).toContain('Fix: pnpm run native:rebuild');
1625
- expect(io.stderr()).toContain(`Retry: ktx ingest warehouse --project-dir ${tempDir} --fast`);
1626
- expect(io.stderr()).not.toContain('ktx scan');
1627
- expect(io.stderr()).not.toContain('npm rebuild');
1628
- expect(io.stderr()).not.toMatch(/^Native SQLite is built for a different Node.js ABI\./m);
1629
- });
1630
- it('rebuilds native SQLite once and retries setup scanning after a Node ABI mismatch', async () => {
1631
- const io = makeIo();
1632
- const scanConnection = vi.fn(async (_projectDir, _connectionId, commandIo) => {
1633
- if (scanConnection.mock.calls.length === 1) {
1634
- commandIo.stderr.write([
1635
- "The module '/workspace/node_modules/better-sqlite3/build/Release/better_sqlite3.node'",
1636
- 'was compiled against a different Node.js version using',
1637
- 'NODE_MODULE_VERSION 147. This version of Node.js requires',
1638
- 'NODE_MODULE_VERSION 137. Please try re-compiling or re-installing',
1639
- 'the module (for instance, using `npm rebuild` or `npm install`).',
1640
- '',
1641
- ].join('\n'));
1642
- return 1;
1643
- }
1644
- commandIo.stdout.write('What changed\n');
1645
- commandIo.stdout.write(' Semantic layer comparison found 0 changes across 56 tables\n');
1646
- commandIo.stdout.write(' New tables: 0\n');
1647
- commandIo.stdout.write(' Changed tables: 0\n');
1648
- commandIo.stdout.write(' Removed tables: 0\n');
1649
- commandIo.stdout.write(' Unchanged tables: 56\n');
1650
- return 0;
1651
- });
1652
- const rebuildNativeSqlite = vi.fn(async () => 0);
1653
- const result = await runKtxSetupDatabasesStep({
1654
- projectDir: tempDir,
1655
- inputMode: 'disabled',
1656
- databaseDrivers: ['postgres'],
1657
- databaseConnectionId: 'warehouse',
1658
- databaseUrl: 'env:DATABASE_URL',
1659
- databaseSchemas: [],
1660
- skipDatabases: false,
1661
- }, io.io, {
1662
- testConnection: vi.fn(async () => 0),
1663
- scanConnection,
1664
- rebuildNativeSqlite,
1665
- });
1666
- expect(result.status).toBe('ready');
1667
- expect(rebuildNativeSqlite).toHaveBeenCalledOnce();
1668
- expect(rebuildNativeSqlite).toHaveBeenCalledWith(expect.anything());
1669
- expect(scanConnection).toHaveBeenCalledTimes(2);
1670
- expect(io.stderr()).toContain('Native SQLite is built for a different Node.js ABI.');
1671
- expect(io.stderr()).toContain('Rebuilding Native SQLite with pnpm run native:rebuild…');
1672
- expect(io.stdout()).toContain('◇ Schema context complete for warehouse');
1673
- expect(io.stdout()).toContain('│ Changes: 0 changes across 56 tables');
1674
- });
1675
- it('writes query history config for supported Snowflake databases after validation succeeds', async () => {
1676
- const io = makeIo();
1677
- const historicSqlProbe = vi.fn(async () => ({ ok: true, lines: [] }));
1678
- const result = await runKtxSetupDatabasesStep({
1679
- projectDir: tempDir,
1680
- inputMode: 'disabled',
1681
- databaseDrivers: ['snowflake'],
1682
- databaseConnectionId: 'snowflake',
1683
- databaseSchemas: [],
1684
- enableQueryHistory: true,
1685
- queryHistoryWindowDays: 30,
1686
- queryHistoryServiceAccountPatterns: ['^svc_'],
1687
- queryHistoryRedactionPatterns: ['(?i)secret'],
1688
- skipDatabases: false,
1689
- }, io.io, {
1690
- testConnection: vi.fn(async () => 0),
1691
- scanConnection: vi.fn(async () => 0),
1692
- historicSqlProbe,
1693
- prompts: makePromptAdapter({
1694
- selectValues: ['password'],
1695
- textValues: ['env:SNOWFLAKE_ACCOUNT', 'WH', 'ANALYTICS', 'reader', ''],
1696
- passwordValues: ['env:SNOWFLAKE_PASSWORD'],
1697
- }),
1698
- });
1699
- expect(historicSqlProbe).toHaveBeenCalledWith(expect.objectContaining({
1700
- projectDir: tempDir,
1701
- connectionId: 'snowflake',
1702
- dialect: 'snowflake',
1703
- }));
1704
- expect(result.status).toBe('ready');
1705
- const configText = await readFile(join(tempDir, 'ktx.yaml'), 'utf-8');
1706
- const config = parseKtxProjectConfig(configText);
1707
- expect(config.connections.snowflake).toMatchObject({
1708
- driver: 'snowflake',
1709
- authMethod: 'password',
1710
- context: {
1711
- queryHistory: {
1712
- enabled: true,
1713
- windowDays: 30,
1714
- filters: {
1715
- dropTrivialProbes: true,
1716
- serviceAccounts: {
1717
- patterns: ['^svc_'],
1718
- mode: 'exclude',
1719
- },
1720
- },
1721
- redactionPatterns: ['(?i)secret'],
1722
- },
1723
- },
1724
- });
1725
- expect(configText).not.toContain('live-database');
1726
- expect(configText).not.toContain('historic-sql');
1727
- expect(configText).not.toMatch(/^\s+adapters:/m);
1728
- expect(config.ingest.adapters).toEqual([]);
1729
- });
1730
- it('configures Snowflake with RSA key-pair auth via setup wizard', async () => {
1731
- const io = makeIo();
1732
- const result = await runKtxSetupDatabasesStep({
1733
- projectDir: tempDir,
1734
- inputMode: 'disabled',
1735
- databaseDrivers: ['snowflake'],
1736
- databaseConnectionId: 'snowflake',
1737
- databaseSchemas: [],
1738
- skipDatabases: false,
1739
- }, io.io, {
1740
- testConnection: vi.fn(async () => 0),
1741
- scanConnection: vi.fn(async () => 0),
1742
- prompts: makePromptAdapter({
1743
- selectValues: ['rsa'],
1744
- textValues: [
1745
- 'env:SNOWFLAKE_ACCOUNT',
1746
- 'WH',
1747
- 'ANALYTICS',
1748
- 'reader',
1749
- '~/.ssh/snowflake_rsa_key.p8',
1750
- '',
1751
- ],
1752
- passwordValues: ['env:SNOWFLAKE_KEY_PASS'],
1753
- }),
1754
- });
1755
- expect(result.status).toBe('ready');
1756
- const config = parseKtxProjectConfig(await readFile(join(tempDir, 'ktx.yaml'), 'utf-8'));
1757
- expect(config.connections.snowflake).toMatchObject({
1758
- driver: 'snowflake',
1759
- authMethod: 'rsa',
1760
- account: 'env:SNOWFLAKE_ACCOUNT',
1761
- warehouse: 'WH',
1762
- database: 'ANALYTICS',
1763
- username: 'reader',
1764
- privateKey: 'file:~/.ssh/snowflake_rsa_key.p8', // pragma: allowlist secret
1765
- passphrase: 'env:SNOWFLAKE_KEY_PASS', // pragma: allowlist secret
1766
- });
1767
- expect(config.connections.snowflake.password).toBeUndefined();
1768
- });
1769
- it('writes Postgres query history config with minExecutions and ignores window/redaction output', async () => {
1770
- const io = makeIo();
1771
- const result = await runKtxSetupDatabasesStep({
1772
- projectDir: tempDir,
1773
- inputMode: 'disabled',
1774
- databaseDrivers: ['postgres'],
1775
- databaseConnectionId: 'warehouse',
1776
- databaseUrl: 'env:DATABASE_URL',
1777
- databaseSchemas: ['public'],
1778
- enableQueryHistory: true,
1779
- queryHistoryWindowDays: 30,
1780
- queryHistoryMinExecutions: 12,
1781
- queryHistoryServiceAccountPatterns: ['^svc_'],
1782
- queryHistoryRedactionPatterns: ['(?i)secret'],
1783
- skipDatabases: false,
1784
- }, io.io, {
1785
- testConnection: vi.fn(async () => 0),
1786
- scanConnection: vi.fn(async () => 0),
1787
- historicSqlProbe: vi.fn(async () => ({ ok: true, lines: [' OK pg_stat_statements ready (PostgreSQL 16.4)'] })),
1788
- });
1789
- expect(result.status).toBe('ready');
1790
- const configText = await readFile(join(tempDir, 'ktx.yaml'), 'utf-8');
1791
- const config = parseKtxProjectConfig(configText);
1792
- expect(config.connections.warehouse).toMatchObject({
1793
- driver: 'postgres',
1794
- url: 'env:DATABASE_URL',
1795
- schemas: ['public'],
1796
- context: {
1797
- queryHistory: {
1798
- enabled: true,
1799
- minExecutions: 12,
1800
- filters: {
1801
- dropTrivialProbes: true,
1802
- serviceAccounts: {
1803
- patterns: ['^svc_'],
1804
- mode: 'exclude',
1805
- },
1806
- },
1807
- },
1808
- },
1809
- });
1810
- const warehouseContext = config.connections.warehouse.context &&
1811
- typeof config.connections.warehouse.context === 'object' &&
1812
- !Array.isArray(config.connections.warehouse.context)
1813
- ? config.connections.warehouse.context
1814
- : {};
1815
- expect(warehouseContext.queryHistory).not.toHaveProperty('windowDays');
1816
- expect(warehouseContext.queryHistory).not.toHaveProperty('redactionPatterns');
1817
- expect(configText).not.toContain('live-database');
1818
- expect(configText).not.toContain('historic-sql');
1819
- expect(configText).not.toMatch(/^\s+adapters:/m);
1820
- expect(config.ingest.adapters).toEqual([]);
1821
- expect(config.ingest.workUnits.maxConcurrency).toBe(6);
1822
- expect(io.stdout()).toContain('Query history probe...');
1823
- expect(io.stdout()).not.toContain('Historic SQL probe...');
1824
- expect(io.stdout()).toContain('pg_stat_statements ready');
1825
- });
1826
- it('asks interactive Postgres setup whether to enable query history', async () => {
1827
- await writeFile(join(tempDir, 'ktx.yaml'), [
1828
- 'connections:',
1829
- ' warehouse:',
1830
- ' driver: postgres',
1831
- ' url: env:DATABASE_URL',
1832
- ' readonly: true',
1833
- 'llm:',
1834
- ' provider:',
1835
- ' backend: anthropic',
1836
- ' models:',
1837
- ' default: claude-sonnet-4-6',
1838
- 'scan:',
1839
- ' enrichment:',
1840
- ' mode: llm',
1841
- ' embeddings:',
1842
- ' backend: openai',
1843
- ' model: text-embedding-3-small',
1844
- ' dimensions: 1536',
1845
- '',
1846
- ].join('\n'), 'utf-8');
1847
- const io = makeIo();
1848
- const prompts = makePromptAdapter({ selectValues: ['yes', 'deep'] });
1849
- const historicSqlProbe = vi.fn(async () => ({ ok: true, lines: [] }));
1850
- const result = await runKtxSetupDatabasesStep({
1851
- projectDir: tempDir,
1852
- inputMode: 'auto',
1853
- databaseConnectionIds: ['warehouse'],
1854
- databaseSchemas: [],
1855
- skipDatabases: false,
1856
- }, io.io, {
1857
- prompts,
1858
- testConnection: vi.fn(async () => 0),
1859
- scanConnection: vi.fn(async () => 0),
1860
- historicSqlProbe,
1861
- });
1862
- expect(result.status).toBe('ready');
1863
- expect(prompts.select).toHaveBeenCalledWith({
1864
- message: 'Enable query-history ingest for this PostgreSQL connection?',
1865
- options: [
1866
- { value: 'yes', label: 'Enable query history (recommended)' },
1867
- { value: 'no', label: 'Do not enable query history' },
1868
- { value: 'back', label: 'Back' },
1869
- ],
1870
- });
1871
- expect(prompts.select).toHaveBeenNthCalledWith(2, expect.objectContaining({
1872
- message: expect.stringContaining('How much database context should KTX build?'),
1873
- }));
1874
- expect(historicSqlProbe).toHaveBeenCalledWith({
1875
- projectDir: tempDir,
1876
- connectionId: 'warehouse',
1877
- dialect: 'postgres',
1878
- });
1879
- const config = parseKtxProjectConfig(await readFile(join(tempDir, 'ktx.yaml'), 'utf-8'));
1880
- expect(config.connections.warehouse).toMatchObject({
1881
- context: {
1882
- queryHistory: {
1883
- enabled: true,
1884
- minExecutions: 5,
1885
- filters: { dropTrivialProbes: true },
1886
- },
1887
- depth: 'deep',
1888
- },
1889
- });
1890
- });
1891
- it('writes query history config for supported existing database connections', async () => {
1892
- await writeFile(join(tempDir, 'ktx.yaml'), [
1893
- 'connections:',
1894
- ' analytics:',
1895
- ' driver: bigquery',
1896
- ' dataset_id: analytics',
1897
- ' credentials_json: env:BIGQUERY_CREDENTIALS_JSON',
1898
- '',
1899
- ].join('\n'), 'utf-8');
1900
- const io = makeIo();
1901
- const result = await runKtxSetupDatabasesStep({
1902
- projectDir: tempDir,
1903
- inputMode: 'disabled',
1904
- databaseConnectionIds: ['analytics'],
1905
- databaseSchemas: [],
1906
- enableQueryHistory: true,
1907
- queryHistoryWindowDays: 45,
1908
- skipDatabases: false,
1909
- }, io.io, {
1910
- testConnection: vi.fn(async () => 0),
1911
- scanConnection: vi.fn(async () => 0),
1912
- });
1913
- expect(result.status).toBe('ready');
1914
- const configText = await readFile(join(tempDir, 'ktx.yaml'), 'utf-8');
1915
- const config = parseKtxProjectConfig(configText);
1916
- expect(config.connections.analytics).toMatchObject({
1917
- context: {
1918
- queryHistory: {
1919
- enabled: true,
1920
- windowDays: 45,
1921
- filters: {
1922
- dropTrivialProbes: true,
1923
- },
1924
- redactionPatterns: [],
1925
- },
1926
- },
1927
- });
1928
- expect(configText).not.toContain('live-database');
1929
- expect(configText).not.toContain('historic-sql');
1930
- expect(configText).not.toMatch(/^\s+adapters:/m);
1931
- expect(config.ingest.adapters).toEqual([]);
1932
- });
1933
- it('enables query history on an existing Postgres connection', async () => {
1934
- await writeFile(join(tempDir, 'ktx.yaml'), [
1935
- 'connections:',
1936
- ' warehouse:',
1937
- ' driver: postgres',
1938
- ' url: env:DATABASE_URL',
1939
- '',
1940
- ].join('\n'), 'utf-8');
1941
- const io = makeIo();
1942
- const result = await runKtxSetupDatabasesStep({
1943
- projectDir: tempDir,
1944
- inputMode: 'disabled',
1945
- databaseConnectionIds: ['warehouse'],
1946
- databaseSchemas: [],
1947
- enableQueryHistory: true,
1948
- queryHistoryMinExecutions: 8,
1949
- skipDatabases: false,
1950
- }, io.io, {
1951
- testConnection: vi.fn(async () => 0),
1952
- scanConnection: vi.fn(async () => 0),
1953
- historicSqlProbe: vi.fn(async () => ({ ok: true, lines: [' OK pg_stat_statements ready (PostgreSQL 16.4)'] })),
1954
- });
1955
- expect(result.status).toBe('ready');
1956
- const config = parseKtxProjectConfig(await readFile(join(tempDir, 'ktx.yaml'), 'utf-8'));
1957
- expect(config.connections.warehouse).toMatchObject({
1958
- context: {
1959
- queryHistory: {
1960
- enabled: true,
1961
- minExecutions: 8,
1962
- filters: {
1963
- dropTrivialProbes: true,
1964
- },
1965
- },
1966
- },
1967
- });
1968
- });
1969
- it('prints a non-blocking Postgres query history probe failure after connection test succeeds', async () => {
1970
- const io = makeIo();
1971
- const historicSqlProbe = vi.fn(async () => ({
1972
- ok: false,
1973
- lines: [
1974
- ' FAIL pg_stat_statements extension is not installed in the connection database',
1975
- ' Fix: Run (against this database): CREATE EXTENSION pg_stat_statements;',
1976
- " Fix: Ensure shared_preload_libraries includes 'pg_stat_statements'.",
1977
- ],
1978
- }));
1979
- const result = await runKtxSetupDatabasesStep({
1980
- projectDir: tempDir,
1981
- inputMode: 'disabled',
1982
- databaseDrivers: ['postgres'],
1983
- databaseConnectionId: 'warehouse',
1984
- databaseUrl: 'env:DATABASE_URL',
1985
- databaseSchemas: [],
1986
- enableQueryHistory: true,
1987
- skipDatabases: false,
1988
- }, io.io, {
1989
- testConnection: vi.fn(async () => 0),
1990
- scanConnection: vi.fn(async () => 0),
1991
- historicSqlProbe,
1992
- });
1993
- expect(result.status).toBe('ready');
1994
- expect(historicSqlProbe).toHaveBeenCalledWith(expect.objectContaining({
1995
- projectDir: tempDir,
1996
- connectionId: 'warehouse',
1997
- dialect: 'postgres',
1998
- }));
1999
- expect(io.stdout()).toContain('Query history probe...');
2000
- expect(io.stdout()).not.toContain('Historic SQL probe...');
2001
- expect(io.stdout()).toContain('pg_stat_statements extension is not installed');
2002
- expect(io.stdout()).toContain('Setup written; query history will be skipped until fixed.');
2003
- });
2004
- it('prints a non-blocking Snowflake query history probe failure with the grants remediation', async () => {
2005
- const io = makeIo();
2006
- const historicSqlProbe = vi.fn(async () => ({
2007
- ok: false,
2008
- lines: [
2009
- ' FAIL Snowflake role cannot read SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY',
2010
- ' Fix: Run (as ACCOUNTADMIN): GRANT IMPORTED PRIVILEGES ON DATABASE SNOWFLAKE TO ROLE <connection role>;',
2011
- ],
2012
- }));
2013
- const result = await runKtxSetupDatabasesStep({
2014
- projectDir: tempDir,
2015
- inputMode: 'disabled',
2016
- databaseDrivers: ['snowflake'],
2017
- databaseConnectionId: 'warehouse',
2018
- databaseSchemas: [],
2019
- enableQueryHistory: true,
2020
- skipDatabases: false,
2021
- }, io.io, {
2022
- testConnection: vi.fn(async () => 0),
2023
- scanConnection: vi.fn(async () => 0),
2024
- historicSqlProbe,
2025
- prompts: makePromptAdapter({
2026
- textValues: ['env:SNOWFLAKE_ACCOUNT', 'WH', 'ANALYTICS', 'reader', ''],
2027
- passwordValues: ['env:SNOWFLAKE_PASSWORD'],
2028
- }),
2029
- });
2030
- expect(result.status).toBe('ready');
2031
- expect(historicSqlProbe).toHaveBeenCalledWith(expect.objectContaining({
2032
- projectDir: tempDir,
2033
- connectionId: 'warehouse',
2034
- dialect: 'snowflake',
2035
- }));
2036
- expect(io.stdout()).toContain('Query history probe...');
2037
- expect(io.stdout()).toContain('Snowflake role cannot read SNOWFLAKE.ACCOUNT_USAGE.QUERY_HISTORY');
2038
- expect(io.stdout()).toContain('GRANT IMPORTED PRIVILEGES ON DATABASE SNOWFLAKE');
2039
- expect(io.stdout()).toContain('Setup written; query history will be skipped until fixed.');
2040
- });
2041
- it('does not run the query history probe when the regular connection test fails', async () => {
2042
- const io = makeIo();
2043
- const historicSqlProbe = vi.fn(async () => ({ ok: true, lines: [] }));
2044
- const result = await runKtxSetupDatabasesStep({
2045
- projectDir: tempDir,
2046
- inputMode: 'disabled',
2047
- databaseDrivers: ['postgres'],
2048
- databaseConnectionId: 'warehouse',
2049
- databaseUrl: 'env:DATABASE_URL',
2050
- databaseSchemas: [],
2051
- enableQueryHistory: true,
2052
- skipDatabases: false,
2053
- }, io.io, {
2054
- testConnection: vi.fn(async () => 1),
2055
- scanConnection: vi.fn(async () => 0),
2056
- historicSqlProbe,
2057
- });
2058
- expect(result.status).toBe('failed');
2059
- expect(historicSqlProbe).not.toHaveBeenCalled();
2060
- });
2061
- it('returns missing input when non-interactive database flags are incomplete', async () => {
2062
- const io = makeIo();
2063
- const result = await runKtxSetupDatabasesStep({
2064
- projectDir: tempDir,
2065
- inputMode: 'disabled',
2066
- databaseDrivers: ['postgres'],
2067
- databaseSchemas: [],
2068
- skipDatabases: false,
2069
- }, io.io);
2070
- expect(result.status).toBe('missing-input');
2071
- expect(io.stderr()).toContain('Missing database connection id');
2072
- });
2073
- it('accepts former ingest subcommand names as non-interactive database connection ids', async () => {
2074
- const io = makeIo();
2075
- const result = await runKtxSetupDatabasesStep({
2076
- projectDir: tempDir,
2077
- inputMode: 'disabled',
2078
- databaseDrivers: ['postgres'],
2079
- databaseConnectionId: 'replay',
2080
- databaseUrl: 'env:DATABASE_URL',
2081
- databaseSchemas: [],
2082
- skipDatabases: false,
2083
- }, io.io, {
2084
- testConnection: vi.fn(async () => 0),
2085
- scanConnection: vi.fn(async () => 0),
2086
- });
2087
- expect(result.status).toBe('ready');
2088
- const config = parseKtxProjectConfig(await readFile(join(tempDir, 'ktx.yaml'), 'utf-8'));
2089
- expect(config.connections.replay).toMatchObject({
2090
- driver: 'postgres',
2091
- url: 'env:DATABASE_URL',
2092
- });
2093
- });
2094
- it('leaves setup incomplete when databases are skipped', async () => {
2095
- const io = makeIo();
2096
- const result = await runKtxSetupDatabasesStep({ projectDir: tempDir, inputMode: 'disabled', databaseSchemas: [], skipDatabases: true }, io.io);
2097
- expect(result.status).toBe('skipped');
2098
- expect(io.stdout()).toContain('KTX cannot work until you add a database.');
2099
- expect(await readFile(join(tempDir, 'ktx.yaml'), 'utf-8')).not.toContain('completed_steps:');
2100
- });
2101
- });