@kaelio/ktx 0.1.0-rc.5 → 0.1.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 (180) hide show
  1. package/assets/python/kaelio_ktx-0.1.0-py3-none-any.whl +0 -0
  2. package/assets/python/manifest.json +2 -2
  3. package/dist/clack.d.ts +6 -0
  4. package/dist/clack.js +23 -0
  5. package/dist/cli-program.js +5 -2
  6. package/dist/cli-program.test.js +7 -1
  7. package/dist/cli-runtime.d.ts +4 -0
  8. package/dist/cli-runtime.js +8 -1
  9. package/dist/command-schemas.d.ts +1 -1
  10. package/dist/commands/ingest-commands.js +1 -0
  11. package/dist/commands/knowledge-commands.js +5 -0
  12. package/dist/commands/mcp-commands.js +11 -3
  13. package/dist/commands/mcp-commands.test.js +30 -1
  14. package/dist/commands/sql-commands.d.ts +3 -0
  15. package/dist/commands/sql-commands.js +43 -0
  16. package/dist/commands/sql-commands.test.d.ts +1 -0
  17. package/dist/commands/sql-commands.test.js +68 -0
  18. package/dist/context-build-view.js +5 -1
  19. package/dist/dev.test.js +27 -0
  20. package/dist/index.d.ts +1 -0
  21. package/dist/index.js +1 -0
  22. package/dist/index.test.js +56 -21
  23. package/dist/ingest.js +123 -18
  24. package/dist/ingest.test.js +206 -0
  25. package/dist/io/print-list.d.ts +2 -1
  26. package/dist/io/print-list.js +7 -0
  27. package/dist/io/print-list.test.js +13 -11
  28. package/dist/io/symbols.d.ts +2 -2
  29. package/dist/knowledge.d.ts +1 -0
  30. package/dist/knowledge.js +34 -16
  31. package/dist/knowledge.test.js +27 -0
  32. package/dist/managed-python-command.d.ts +2 -0
  33. package/dist/managed-python-command.js +17 -9
  34. package/dist/managed-python-command.test.js +59 -4
  35. package/dist/next-steps.js +1 -1
  36. package/dist/next-steps.test.js +2 -0
  37. package/dist/print-command-tree.js +7 -1
  38. package/dist/public-ingest.d.ts +9 -1
  39. package/dist/public-ingest.js +50 -7
  40. package/dist/public-ingest.test.js +69 -2
  41. package/dist/release-version.d.ts +5 -0
  42. package/dist/release-version.js +44 -0
  43. package/dist/runtime-requirements.d.ts +23 -0
  44. package/dist/runtime-requirements.js +99 -0
  45. package/dist/runtime-requirements.test.d.ts +1 -0
  46. package/dist/runtime-requirements.test.js +63 -0
  47. package/dist/setup-agents.d.ts +11 -3
  48. package/dist/setup-agents.js +397 -134
  49. package/dist/setup-agents.test.js +359 -61
  50. package/dist/setup-embeddings.js +3 -6
  51. package/dist/setup-embeddings.test.js +18 -2
  52. package/dist/setup-models.js +2 -2
  53. package/dist/setup-models.test.js +5 -3
  54. package/dist/setup-ready-menu.d.ts +1 -1
  55. package/dist/setup-ready-menu.js +2 -0
  56. package/dist/setup-ready-menu.test.js +3 -0
  57. package/dist/setup-runtime.d.ts +45 -0
  58. package/dist/setup-runtime.js +47 -0
  59. package/dist/setup-runtime.test.d.ts +1 -0
  60. package/dist/setup-runtime.test.js +110 -0
  61. package/dist/setup-sources-notion.test.d.ts +1 -0
  62. package/dist/setup-sources-notion.test.js +107 -0
  63. package/dist/setup-sources.js +5 -2
  64. package/dist/setup.d.ts +19 -1
  65. package/dist/setup.js +104 -29
  66. package/dist/setup.test.js +221 -57
  67. package/dist/sl.js +2 -2
  68. package/dist/sl.test.js +10 -0
  69. package/dist/source-mapping.js +9 -1
  70. package/dist/source-mapping.test.d.ts +1 -0
  71. package/dist/source-mapping.test.js +65 -0
  72. package/dist/sql.d.ts +22 -0
  73. package/dist/sql.js +125 -0
  74. package/dist/sql.test.d.ts +1 -0
  75. package/dist/sql.test.js +226 -0
  76. package/node_modules/@ktx/connector-clickhouse/dist/package-exports.test.js +1 -1
  77. package/node_modules/@ktx/context/dist/connections/connection-type.d.ts +4 -4
  78. package/node_modules/@ktx/context/dist/core/git.service.d.ts +3 -0
  79. package/node_modules/@ktx/context/dist/core/git.service.js +47 -1
  80. package/node_modules/@ktx/context/dist/core/git.service.patch.test.d.ts +1 -0
  81. package/node_modules/@ktx/context/dist/core/git.service.patch.test.js +40 -0
  82. package/node_modules/@ktx/context/dist/ingest/adapters/historic-sql/types.d.ts +5 -5
  83. package/node_modules/@ktx/context/dist/ingest/adapters/looker/looker.adapter.d.ts +2 -2
  84. package/node_modules/@ktx/context/dist/ingest/adapters/looker/tools/looker-query-to-sl.tool.d.ts +2 -2
  85. package/node_modules/@ktx/context/dist/ingest/adapters/looker/types.d.ts +16 -16
  86. package/node_modules/@ktx/context/dist/ingest/adapters/lookml/pull-config.d.ts +1 -1
  87. package/node_modules/@ktx/context/dist/ingest/adapters/metabase/fetch.js +16 -0
  88. package/node_modules/@ktx/context/dist/ingest/adapters/metabase/fetch.test.js +41 -0
  89. package/node_modules/@ktx/context/dist/ingest/adapters/metricflow/metricflow.adapter.d.ts +2 -1
  90. package/node_modules/@ktx/context/dist/ingest/adapters/metricflow/metricflow.adapter.js +40 -0
  91. package/node_modules/@ktx/context/dist/ingest/adapters/metricflow/metricflow.adapter.test.js +116 -1
  92. package/node_modules/@ktx/context/dist/ingest/adapters/metricflow/projection-config.d.ts +29 -0
  93. package/node_modules/@ktx/context/dist/ingest/adapters/metricflow/projection-config.js +40 -0
  94. package/node_modules/@ktx/context/dist/ingest/adapters/metricflow/pull-config.d.ts +1 -1
  95. package/node_modules/@ktx/context/dist/ingest/artifact-gates.d.ts +25 -0
  96. package/node_modules/@ktx/context/dist/ingest/artifact-gates.js +149 -0
  97. package/node_modules/@ktx/context/dist/ingest/artifact-gates.test.d.ts +1 -0
  98. package/node_modules/@ktx/context/dist/ingest/artifact-gates.test.js +167 -0
  99. package/node_modules/@ktx/context/dist/ingest/final-gate-repair.d.ts +29 -0
  100. package/node_modules/@ktx/context/dist/ingest/final-gate-repair.js +178 -0
  101. package/node_modules/@ktx/context/dist/ingest/final-gate-repair.test.d.ts +1 -0
  102. package/node_modules/@ktx/context/dist/ingest/final-gate-repair.test.js +109 -0
  103. package/node_modules/@ktx/context/dist/ingest/index.d.ts +8 -1
  104. package/node_modules/@ktx/context/dist/ingest/index.js +7 -0
  105. package/node_modules/@ktx/context/dist/ingest/ingest-bundle.runner.d.ts +18 -2
  106. package/node_modules/@ktx/context/dist/ingest/ingest-bundle.runner.isolated-diff.test.d.ts +1 -0
  107. package/node_modules/@ktx/context/dist/ingest/ingest-bundle.runner.isolated-diff.test.js +1761 -0
  108. package/node_modules/@ktx/context/dist/ingest/ingest-bundle.runner.js +1695 -901
  109. package/node_modules/@ktx/context/dist/ingest/ingest-bundle.runner.test.js +135 -118
  110. package/node_modules/@ktx/context/dist/ingest/ingest-trace.d.ts +50 -0
  111. package/node_modules/@ktx/context/dist/ingest/ingest-trace.js +88 -0
  112. package/node_modules/@ktx/context/dist/ingest/ingest-trace.test.d.ts +1 -0
  113. package/node_modules/@ktx/context/dist/ingest/ingest-trace.test.js +76 -0
  114. package/node_modules/@ktx/context/dist/ingest/isolated-diff/git-patch.d.ts +16 -0
  115. package/node_modules/@ktx/context/dist/ingest/isolated-diff/git-patch.js +78 -0
  116. package/node_modules/@ktx/context/dist/ingest/isolated-diff/git-patch.test.d.ts +1 -0
  117. package/node_modules/@ktx/context/dist/ingest/isolated-diff/git-patch.test.js +76 -0
  118. package/node_modules/@ktx/context/dist/ingest/isolated-diff/patch-integrator.d.ts +58 -0
  119. package/node_modules/@ktx/context/dist/ingest/isolated-diff/patch-integrator.js +223 -0
  120. package/node_modules/@ktx/context/dist/ingest/isolated-diff/patch-integrator.test.d.ts +1 -0
  121. package/node_modules/@ktx/context/dist/ingest/isolated-diff/patch-integrator.test.js +369 -0
  122. package/node_modules/@ktx/context/dist/ingest/isolated-diff/textual-conflict-resolver.d.ts +23 -0
  123. package/node_modules/@ktx/context/dist/ingest/isolated-diff/textual-conflict-resolver.js +190 -0
  124. package/node_modules/@ktx/context/dist/ingest/isolated-diff/textual-conflict-resolver.test.d.ts +1 -0
  125. package/node_modules/@ktx/context/dist/ingest/isolated-diff/textual-conflict-resolver.test.js +101 -0
  126. package/node_modules/@ktx/context/dist/ingest/isolated-diff/work-unit-executor.d.ts +15 -0
  127. package/node_modules/@ktx/context/dist/ingest/isolated-diff/work-unit-executor.js +61 -0
  128. package/node_modules/@ktx/context/dist/ingest/isolated-diff/work-unit-executor.test.d.ts +1 -0
  129. package/node_modules/@ktx/context/dist/ingest/isolated-diff/work-unit-executor.test.js +137 -0
  130. package/node_modules/@ktx/context/dist/ingest/local-bundle-ingest.test.js +7 -0
  131. package/node_modules/@ktx/context/dist/ingest/local-bundle-runtime.js +54 -10
  132. package/node_modules/@ktx/context/dist/ingest/local-bundle-runtime.test.js +65 -0
  133. package/node_modules/@ktx/context/dist/ingest/memory-flow/schema.d.ts +23 -5
  134. package/node_modules/@ktx/context/dist/ingest/memory-flow/schema.js +17 -0
  135. package/node_modules/@ktx/context/dist/ingest/memory-flow/schema.test.js +1 -0
  136. package/node_modules/@ktx/context/dist/ingest/memory-flow/types.d.ts +6 -0
  137. package/node_modules/@ktx/context/dist/ingest/parsed-target-table.d.ts +1 -1
  138. package/node_modules/@ktx/context/dist/ingest/ports.d.ts +3 -0
  139. package/node_modules/@ktx/context/dist/ingest/report-snapshot.d.ts +32 -7
  140. package/node_modules/@ktx/context/dist/ingest/report-snapshot.js +25 -0
  141. package/node_modules/@ktx/context/dist/ingest/report-snapshot.test.js +124 -0
  142. package/node_modules/@ktx/context/dist/ingest/reports.d.ts +23 -0
  143. package/node_modules/@ktx/context/dist/ingest/semantic-layer-target-policy.d.ts +11 -0
  144. package/node_modules/@ktx/context/dist/ingest/semantic-layer-target-policy.js +26 -0
  145. package/node_modules/@ktx/context/dist/ingest/semantic-layer-target-policy.test.d.ts +1 -0
  146. package/node_modules/@ktx/context/dist/ingest/semantic-layer-target-policy.test.js +25 -0
  147. package/node_modules/@ktx/context/dist/ingest/stages/stage-3-work-units.d.ts +4 -0
  148. package/node_modules/@ktx/context/dist/ingest/stages/stage-3-work-units.js +4 -0
  149. package/node_modules/@ktx/context/dist/ingest/stages/stage-3-work-units.test.js +29 -0
  150. package/node_modules/@ktx/context/dist/ingest/tools/emit-unmapped-fallback.tool.d.ts +1 -1
  151. package/node_modules/@ktx/context/dist/ingest/types.d.ts +24 -0
  152. package/node_modules/@ktx/context/dist/ingest/wiki-body-refs.d.ts +24 -0
  153. package/node_modules/@ktx/context/dist/ingest/wiki-body-refs.js +111 -0
  154. package/node_modules/@ktx/context/dist/ingest/wiki-body-refs.test.d.ts +1 -0
  155. package/node_modules/@ktx/context/dist/ingest/wiki-body-refs.test.js +138 -0
  156. package/node_modules/@ktx/context/dist/llm/claude-code-runtime.js +19 -2
  157. package/node_modules/@ktx/context/dist/llm/claude-code-runtime.test.js +33 -0
  158. package/node_modules/@ktx/context/dist/project/setup-config.d.ts +1 -1
  159. package/node_modules/@ktx/context/dist/project/setup-config.js +10 -1
  160. package/node_modules/@ktx/context/dist/project/setup-config.test.js +3 -2
  161. package/node_modules/@ktx/context/dist/sl/tools/sl-edit-source.tool.js +5 -1
  162. package/node_modules/@ktx/context/dist/sl/tools/sl-edit-source.tool.test.js +15 -0
  163. package/node_modules/@ktx/context/dist/sl/tools/sl-write-source.tool.js +5 -1
  164. package/node_modules/@ktx/context/dist/sl/tools/sl-write-source.tool.test.js +22 -0
  165. package/node_modules/@ktx/context/dist/tools/action-target-connection.d.ts +9 -0
  166. package/node_modules/@ktx/context/dist/tools/action-target-connection.js +14 -0
  167. package/node_modules/@ktx/context/dist/tools/context-candidate-write.tool.d.ts +4 -4
  168. package/node_modules/@ktx/context/dist/tools/index.d.ts +1 -0
  169. package/node_modules/@ktx/context/dist/tools/index.js +1 -0
  170. package/node_modules/@ktx/context/dist/wiki/local-knowledge.js +4 -1
  171. package/node_modules/@ktx/context/dist/wiki/local-knowledge.test.js +44 -0
  172. package/node_modules/@ktx/context/dist/wiki/tools/wiki-write.tool.js +3 -48
  173. package/node_modules/@ktx/context/dist/wiki/tools/wiki-write.tool.test.js +28 -0
  174. package/node_modules/@ktx/context/dist/wiki/wiki-ref-validation.d.ts +17 -0
  175. package/node_modules/@ktx/context/dist/wiki/wiki-ref-validation.js +79 -0
  176. package/node_modules/@ktx/context/dist/wiki/wiki-ref-validation.test.d.ts +1 -0
  177. package/node_modules/@ktx/context/dist/wiki/wiki-ref-validation.test.js +64 -0
  178. package/node_modules/@ktx/context/prompts/memory_agent_bundle_ingest_work_unit.md +23 -4
  179. package/node_modules/@ktx/context/skills/ingest_triage/SKILL.md +7 -3
  180. package/package.json +4 -4
@@ -13,13 +13,13 @@ export declare const lookerPullConfigSchema: z.ZodObject<{
13
13
  lookUpdatedSince: z.ZodOptional<z.ZodNullable<z.ZodISODateTime>>;
14
14
  connectionMappings: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
15
15
  connectionTypes: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodEnum<{
16
- SNOWFLAKE: "SNOWFLAKE";
16
+ POSTGRESQL: "POSTGRESQL";
17
17
  BIGQUERY: "BIGQUERY";
18
+ SNOWFLAKE: "SNOWFLAKE";
18
19
  MYSQL: "MYSQL";
19
- CLICKHOUSE: "CLICKHOUSE";
20
- POSTGRESQL: "POSTGRESQL";
21
- SQLITE: "SQLITE";
22
20
  SQLSERVER: "SQLSERVER";
21
+ SQLITE: "SQLITE";
22
+ CLICKHOUSE: "CLICKHOUSE";
23
23
  CENTRALREACH: "CENTRALREACH";
24
24
  EPIC: "EPIC";
25
25
  CERNER: "CERNER";
@@ -44,9 +44,9 @@ export declare const lookerPullConfigSchema: z.ZodObject<{
44
44
  }, z.core.$strip>, z.ZodObject<{
45
45
  ok: z.ZodLiteral<false>;
46
46
  reason: z.ZodEnum<{
47
+ no_connection_mapping: "no_connection_mapping";
47
48
  looker_template_unresolved: "looker_template_unresolved";
48
49
  derived_table_not_supported: "derived_table_not_supported";
49
- no_connection_mapping: "no_connection_mapping";
50
50
  no_physical_table: "no_physical_table";
51
51
  multiple_table_references: "multiple_table_references";
52
52
  unsupported_dialect: "unsupported_dialect";
@@ -89,9 +89,9 @@ export declare const stagedLookerQuerySchema: z.ZodObject<{
89
89
  }, z.core.$strip>, z.ZodObject<{
90
90
  ok: z.ZodLiteral<false>;
91
91
  reason: z.ZodEnum<{
92
+ no_connection_mapping: "no_connection_mapping";
92
93
  looker_template_unresolved: "looker_template_unresolved";
93
94
  derived_table_not_supported: "derived_table_not_supported";
94
- no_connection_mapping: "no_connection_mapping";
95
95
  no_physical_table: "no_physical_table";
96
96
  multiple_table_references: "multiple_table_references";
97
97
  unsupported_dialect: "unsupported_dialect";
@@ -131,9 +131,9 @@ export declare const stagedDashboardFileSchema: z.ZodObject<{
131
131
  }, z.core.$strip>, z.ZodObject<{
132
132
  ok: z.ZodLiteral<false>;
133
133
  reason: z.ZodEnum<{
134
+ no_connection_mapping: "no_connection_mapping";
134
135
  looker_template_unresolved: "looker_template_unresolved";
135
136
  derived_table_not_supported: "derived_table_not_supported";
136
- no_connection_mapping: "no_connection_mapping";
137
137
  no_physical_table: "no_physical_table";
138
138
  multiple_table_references: "multiple_table_references";
139
139
  unsupported_dialect: "unsupported_dialect";
@@ -171,9 +171,9 @@ export declare const stagedLookFileSchema: z.ZodObject<{
171
171
  }, z.core.$strip>, z.ZodObject<{
172
172
  ok: z.ZodLiteral<false>;
173
173
  reason: z.ZodEnum<{
174
+ no_connection_mapping: "no_connection_mapping";
174
175
  looker_template_unresolved: "looker_template_unresolved";
175
176
  derived_table_not_supported: "derived_table_not_supported";
176
- no_connection_mapping: "no_connection_mapping";
177
177
  no_physical_table: "no_physical_table";
178
178
  multiple_table_references: "multiple_table_references";
179
179
  unsupported_dialect: "unsupported_dialect";
@@ -255,9 +255,9 @@ export declare const stagedExploreFileSchema: z.ZodObject<{
255
255
  }, z.core.$strip>, z.ZodObject<{
256
256
  ok: z.ZodLiteral<false>;
257
257
  reason: z.ZodEnum<{
258
+ no_connection_mapping: "no_connection_mapping";
258
259
  looker_template_unresolved: "looker_template_unresolved";
259
260
  derived_table_not_supported: "derived_table_not_supported";
260
- no_connection_mapping: "no_connection_mapping";
261
261
  no_physical_table: "no_physical_table";
262
262
  multiple_table_references: "multiple_table_references";
263
263
  unsupported_dialect: "unsupported_dialect";
@@ -276,9 +276,9 @@ export declare const stagedExploreFileSchema: z.ZodObject<{
276
276
  }, z.core.$strip>, z.ZodObject<{
277
277
  ok: z.ZodLiteral<false>;
278
278
  reason: z.ZodEnum<{
279
+ no_connection_mapping: "no_connection_mapping";
279
280
  looker_template_unresolved: "looker_template_unresolved";
280
281
  derived_table_not_supported: "derived_table_not_supported";
281
- no_connection_mapping: "no_connection_mapping";
282
282
  no_physical_table: "no_physical_table";
283
283
  multiple_table_references: "multiple_table_references";
284
284
  unsupported_dialect: "unsupported_dialect";
@@ -352,10 +352,10 @@ export declare const stagedLookerFetchIssueSchema: z.ZodObject<{
352
352
  retryRecommended: z.ZodDefault<z.ZodBoolean>;
353
353
  kind: z.ZodOptional<z.ZodEnum<{
354
354
  lookml_connection_mismatch: "lookml_connection_mismatch";
355
- unmapped_looker_connection: "unmapped_looker_connection";
356
- unparseable_sql_table_name: "unparseable_sql_table_name";
357
355
  looker_template_unresolved: "looker_template_unresolved";
358
356
  derived_table_not_supported: "derived_table_not_supported";
357
+ unmapped_looker_connection: "unmapped_looker_connection";
358
+ unparseable_sql_table_name: "unparseable_sql_table_name";
359
359
  }>>;
360
360
  details: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
361
361
  }, z.core.$strip>;
@@ -386,10 +386,10 @@ export declare const stagedLookerFetchReportSchema: z.ZodObject<{
386
386
  retryRecommended: z.ZodDefault<z.ZodBoolean>;
387
387
  kind: z.ZodOptional<z.ZodEnum<{
388
388
  lookml_connection_mismatch: "lookml_connection_mismatch";
389
- unmapped_looker_connection: "unmapped_looker_connection";
390
- unparseable_sql_table_name: "unparseable_sql_table_name";
391
389
  looker_template_unresolved: "looker_template_unresolved";
392
390
  derived_table_not_supported: "derived_table_not_supported";
391
+ unmapped_looker_connection: "unmapped_looker_connection";
392
+ unparseable_sql_table_name: "unparseable_sql_table_name";
393
393
  }>>;
394
394
  details: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
395
395
  }, z.core.$strip>>>;
@@ -413,10 +413,10 @@ export declare const stagedLookerFetchReportSchema: z.ZodObject<{
413
413
  retryRecommended: z.ZodDefault<z.ZodBoolean>;
414
414
  kind: z.ZodOptional<z.ZodEnum<{
415
415
  lookml_connection_mismatch: "lookml_connection_mismatch";
416
- unmapped_looker_connection: "unmapped_looker_connection";
417
- unparseable_sql_table_name: "unparseable_sql_table_name";
418
416
  looker_template_unresolved: "looker_template_unresolved";
419
417
  derived_table_not_supported: "derived_table_not_supported";
418
+ unmapped_looker_connection: "unmapped_looker_connection";
419
+ unparseable_sql_table_name: "unparseable_sql_table_name";
420
420
  }>>;
421
421
  details: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
422
422
  }, z.core.$strip>>>;
@@ -14,9 +14,9 @@ export declare const lookmlPullConfigSchema: z.ZodObject<{
14
14
  }, z.core.$strip>, z.ZodObject<{
15
15
  ok: z.ZodLiteral<false>;
16
16
  reason: z.ZodEnum<{
17
+ no_connection_mapping: "no_connection_mapping";
17
18
  looker_template_unresolved: "looker_template_unresolved";
18
19
  derived_table_not_supported: "derived_table_not_supported";
19
- no_connection_mapping: "no_connection_mapping";
20
20
  no_physical_table: "no_physical_table";
21
21
  multiple_table_references: "multiple_table_references";
22
22
  unsupported_dialect: "unsupported_dialect";
@@ -46,6 +46,15 @@ function resolvePath(index, collectionId) {
46
46
  export async function fetchMetabaseBundle(params) {
47
47
  const pullConfig = parseMetabasePullConfig(params.pullConfig);
48
48
  const logger = params.logger ?? noopMetabaseFetchLogger;
49
+ const emitFetchProgress = (percent, message, transient = false) => {
50
+ params.ctx.memoryFlow?.emit({
51
+ type: 'stage_progress',
52
+ stage: 'source',
53
+ percent,
54
+ message,
55
+ ...(transient ? { transient } : {}),
56
+ });
57
+ };
49
58
  const syncState = await params.sourceStateReader.getSourceState(pullConfig.metabaseConnectionId);
50
59
  const mapping = syncState.mappings.find((m) => m.metabaseDatabaseId === pullConfig.metabaseDatabaseId && m.syncEnabled);
51
60
  if (!mapping?.targetConnectionId) {
@@ -56,6 +65,7 @@ export async function fetchMetabaseBundle(params) {
56
65
  }
57
66
  const client = await params.clientFactory.createClient(pullConfig, params.ctx);
58
67
  try {
68
+ emitFetchProgress(26, `Fetching Metabase database ${pullConfig.metabaseDatabaseId} metadata`);
59
69
  let mappingDatabaseName = mapping.metabaseDatabaseName;
60
70
  let mappingEngine = mapping.metabaseEngine;
61
71
  if (mappingDatabaseName === null) {
@@ -86,6 +96,7 @@ export async function fetchMetabaseBundle(params) {
86
96
  await mkdir(join(params.stagedDir, STAGED_FILES.collectionsDir), { recursive: true });
87
97
  await mkdir(join(params.stagedDir, STAGED_FILES.databasesDir), { recursive: true });
88
98
  const cardIdsToFetch = await resolveCardIdsToFetch(client, scope, pullConfig.metabaseDatabaseId, logger);
99
+ emitFetchProgress(28, `Fetching ${cardIdsToFetch.length} Metabase card${cardIdsToFetch.length === 1 ? '' : 's'} for database ${pullConfig.metabaseDatabaseId}`);
89
100
  const referencedCollectionIds = new Set();
90
101
  let writtenCards = 0;
91
102
  const fetched = new Set();
@@ -154,7 +165,12 @@ export async function fetchMetabaseBundle(params) {
154
165
  }
155
166
  }
156
167
  }
168
+ const knownTotal = Math.max(cardIdsToFetch.length, fetched.size + queue.length);
169
+ if (fetched.size === 1 || fetched.size % 10 === 0 || queue.length === 0) {
170
+ emitFetchProgress(30, `Checked ${fetched.size}/${knownTotal} Metabase cards for database ${pullConfig.metabaseDatabaseId}; wrote ${writtenCards}`, true);
171
+ }
157
172
  }
173
+ emitFetchProgress(32, `Fetched Metabase database ${pullConfig.metabaseDatabaseId}: ${writtenCards} cards, ${unresolvedCards.length} unresolved`);
158
174
  for (const colId of referencedCollectionIds) {
159
175
  const node = collectionIndex.get(colId);
160
176
  if (!node) {
@@ -119,6 +119,47 @@ describe('fetchMetabaseBundle', () => {
119
119
  expect(log).not.toHaveBeenCalled();
120
120
  expect(warn).not.toHaveBeenCalled();
121
121
  });
122
+ it('emits memory-flow progress while fetching Metabase cards', async () => {
123
+ const events = [];
124
+ await fetchMetabaseBundle({
125
+ pullConfig: { metabaseConnectionId, metabaseDatabaseId: 42 },
126
+ stagedDir,
127
+ ctx: {
128
+ ...makeFetchContext(),
129
+ memoryFlow: {
130
+ emit: (event) => events.push(event),
131
+ update: vi.fn(),
132
+ finish: vi.fn(),
133
+ snapshot: vi.fn(),
134
+ },
135
+ },
136
+ clientFactory,
137
+ sourceStateReader,
138
+ });
139
+ expect(events).toEqual(expect.arrayContaining([
140
+ expect.objectContaining({
141
+ type: 'stage_progress',
142
+ stage: 'source',
143
+ message: 'Fetching Metabase database 42 metadata',
144
+ }),
145
+ expect.objectContaining({
146
+ type: 'stage_progress',
147
+ stage: 'source',
148
+ message: 'Fetching 1 Metabase card for database 42',
149
+ }),
150
+ expect.objectContaining({
151
+ type: 'stage_progress',
152
+ stage: 'source',
153
+ message: 'Checked 1/1 Metabase cards for database 42; wrote 1',
154
+ transient: true,
155
+ }),
156
+ expect.objectContaining({
157
+ type: 'stage_progress',
158
+ stage: 'source',
159
+ message: 'Fetched Metabase database 42: 1 cards, 0 unresolved',
160
+ }),
161
+ ]));
162
+ });
122
163
  it('routes Metabase fetch warnings through the injected logger', async () => {
123
164
  const logger = {
124
165
  log: vi.fn(),
@@ -1,4 +1,4 @@
1
- import type { ChunkResult, DiffSet, FetchContext, SourceAdapter } from '../../types.js';
1
+ import type { ChunkResult, DeterministicProjectionContext, DiffSet, FetchContext, ProjectionResult, SourceAdapter } from '../../types.js';
2
2
  export interface MetricflowSourceAdapterDeps {
3
3
  homeDir: string;
4
4
  targetConnectionIds?: string[];
@@ -12,5 +12,6 @@ export declare class MetricflowSourceAdapter implements SourceAdapter {
12
12
  fetch(pullConfig: unknown, stagedDir: string, ctx: FetchContext): Promise<void>;
13
13
  listTargetConnectionIds(_stagedDir: string): Promise<string[]>;
14
14
  chunk(stagedDir: string, diffSet?: DiffSet): Promise<ChunkResult>;
15
+ project(ctx: DeterministicProjectionContext): Promise<ProjectionResult>;
15
16
  private resolveCacheDir;
16
17
  }
@@ -3,7 +3,9 @@ import { chunkMetricFlowProject } from './chunk.js';
3
3
  import { detectMetricFlowStagedDir } from './detect.js';
4
4
  import { parseMetricflowFiles } from './deep-parse.js';
5
5
  import { fetchMetricflowRepo } from './fetch.js';
6
+ import { importMetricflowSemanticModels } from './import-semantic-models.js';
6
7
  import { parseMetricFlowStagedDir } from './parse.js';
8
+ import { metricflowHostTablesFromParsedTargets, readMetricflowProjectionConfig, writeMetricflowProjectionConfig, } from './projection-config.js';
7
9
  import { parseMetricflowPullConfig } from './pull-config.js';
8
10
  function uniqueSorted(values) {
9
11
  return [...new Set(values ?? [])].sort((left, right) => left.localeCompare(right));
@@ -25,6 +27,9 @@ export class MetricflowSourceAdapter {
25
27
  cacheDir: this.resolveCacheDir(ctx.connectionId),
26
28
  stagedDir,
27
29
  });
30
+ await writeMetricflowProjectionConfig(stagedDir, {
31
+ parsedTargetTables: config.parsedTargetTables,
32
+ });
28
33
  }
29
34
  async listTargetConnectionIds(_stagedDir) {
30
35
  return uniqueSorted(this.deps.targetConnectionIds);
@@ -35,6 +40,31 @@ export class MetricflowSourceAdapter {
35
40
  const parseArtifacts = parseMetricflowStagedDirForImport(project);
36
41
  return { ...chunk, parseArtifacts };
37
42
  }
43
+ async project(ctx) {
44
+ if (!isMetricFlowParseResult(ctx.parseArtifacts)) {
45
+ return {
46
+ warnings: [],
47
+ errors: ['MetricFlow deterministic projection requires parseArtifacts from chunk()'],
48
+ touchedSources: [],
49
+ changedWikiPageKeys: [],
50
+ };
51
+ }
52
+ const projectionConfig = await readMetricflowProjectionConfig(ctx.stagedDir);
53
+ const result = await importMetricflowSemanticModels({ semanticLayerService: ctx.semanticLayerService }, {
54
+ connectionId: ctx.connectionId,
55
+ parseResult: ctx.parseArtifacts,
56
+ targetSchema: null,
57
+ hostTables: metricflowHostTablesFromParsedTargets(projectionConfig.parsedTargetTables),
58
+ workdir: ctx.workdir,
59
+ });
60
+ return {
61
+ result,
62
+ warnings: result.warnings,
63
+ errors: result.errors,
64
+ touchedSources: result.touchedSources,
65
+ changedWikiPageKeys: [],
66
+ };
67
+ }
38
68
  resolveCacheDir(connectionId) {
39
69
  return join(this.deps.homeDir, 'ingest-metricflow-repos', connectionId);
40
70
  }
@@ -42,3 +72,13 @@ export class MetricflowSourceAdapter {
42
72
  function parseMetricflowStagedDirForImport(project) {
43
73
  return parseMetricflowFiles(project.files);
44
74
  }
75
+ function isMetricFlowParseResult(value) {
76
+ if (!value || typeof value !== 'object') {
77
+ return false;
78
+ }
79
+ const candidate = value;
80
+ return (Array.isArray(candidate.semanticModels) &&
81
+ Array.isArray(candidate.crossModelMetrics) &&
82
+ Array.isArray(candidate.relationships) &&
83
+ Array.isArray(candidate.warnings));
84
+ }
@@ -1,9 +1,10 @@
1
1
  import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
2
2
  import { tmpdir } from 'node:os';
3
3
  import { join } from 'node:path';
4
- import { afterEach, beforeEach, describe, expect, it } from 'vitest';
4
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
5
5
  import { makeLocalGitRepo } from '../../../test/make-local-git-repo.js';
6
6
  import { MetricflowSourceAdapter } from './metricflow.adapter.js';
7
+ import { readMetricflowProjectionConfig, writeMetricflowProjectionConfig } from './projection-config.js';
7
8
  function compileOnlyRequiredDepsCheck() {
8
9
  // @ts-expect-error MetricflowSourceAdapter requires an explicit cache home.
9
10
  new MetricflowSourceAdapter();
@@ -18,6 +19,24 @@ async function makeRepo(tmpRoot, files) {
18
19
  }
19
20
  return makeLocalGitRepo(fixtureDir, join(tmpRoot, 'origin'));
20
21
  }
22
+ function metricflowParseResult() {
23
+ return {
24
+ semanticModels: [
25
+ {
26
+ name: 'orders',
27
+ description: 'Orders',
28
+ modelRef: 'orders',
29
+ dimensions: [{ name: 'status', column: 'status', type: 'string', label: 'Status' }],
30
+ measures: [{ type: 'simple', name: 'order_count', column: 'id', aggregation: 'count' }],
31
+ entities: [{ name: 'customer', type: 'foreign', expr: 'customer_id' }],
32
+ defaultTimeDimension: null,
33
+ },
34
+ ],
35
+ crossModelMetrics: [],
36
+ relationships: [],
37
+ warnings: ['parser warning'],
38
+ };
39
+ }
21
40
  describe('MetricflowSourceAdapter', () => {
22
41
  let tmpRoot;
23
42
  let stagedDir;
@@ -94,4 +113,100 @@ describe('MetricflowSourceAdapter', () => {
94
113
  await expect(readFile(join(stagedDir, 'models/orders.yml'), 'utf-8')).resolves.toContain('semantic_models');
95
114
  expect(await adapter.detect(stagedDir)).toBe(true);
96
115
  });
116
+ it('persists parsed target tables for deterministic projection during fetch', async () => {
117
+ const repo = await makeRepo(tmpRoot, {
118
+ 'dbt_project.yml': 'name: analytics\n',
119
+ 'models/orders.yml': 'semantic_models:\n - name: orders\n model: ref("orders")\n',
120
+ });
121
+ await adapter.fetch?.({
122
+ repoUrl: repo.repoUrl,
123
+ branch: 'main',
124
+ path: null,
125
+ authToken: null,
126
+ parsedTargetTables: {
127
+ orders: {
128
+ ok: true,
129
+ catalog: null,
130
+ schema: 'analytics',
131
+ name: 'orders',
132
+ canonicalTable: 'analytics.orders',
133
+ },
134
+ },
135
+ }, stagedDir, { connectionId: 'warehouse-1', sourceKey: 'metricflow' });
136
+ await expect(readMetricflowProjectionConfig(stagedDir)).resolves.toMatchObject({
137
+ parsedTargetTables: {
138
+ orders: {
139
+ ok: true,
140
+ schema: 'analytics',
141
+ name: 'orders',
142
+ },
143
+ },
144
+ });
145
+ });
146
+ it('projects parsed MetricFlow semantic models in the integration worktree', async () => {
147
+ await writeMetricflowProjectionConfig(stagedDir, {
148
+ parsedTargetTables: {
149
+ orders: {
150
+ ok: true,
151
+ catalog: null,
152
+ schema: 'analytics',
153
+ name: 'orders',
154
+ canonicalTable: 'analytics.orders',
155
+ },
156
+ },
157
+ });
158
+ const scoped = {
159
+ getManifestEntry: vi.fn().mockResolvedValue(null),
160
+ isManifestBacked: vi.fn().mockResolvedValue(false),
161
+ loadAllSources: vi.fn().mockResolvedValue({ sources: [], loadErrors: [] }),
162
+ loadSource: vi.fn().mockResolvedValue(null),
163
+ writeSource: vi.fn().mockResolvedValue({ warnings: [] }),
164
+ };
165
+ const semanticLayerService = {
166
+ forWorktree: vi.fn().mockReturnValue(scoped),
167
+ getManifestEntry: vi.fn(),
168
+ isManifestBacked: vi.fn(),
169
+ loadAllSources: vi.fn(),
170
+ loadSource: vi.fn(),
171
+ writeSource: vi.fn(),
172
+ };
173
+ const result = await adapter.project?.({
174
+ connectionId: 'warehouse-1',
175
+ sourceKey: 'metricflow',
176
+ syncId: 'sync-1',
177
+ jobId: 'job-1',
178
+ runId: 'run-1',
179
+ stagedDir,
180
+ workdir: '/tmp/metricflow-integration',
181
+ parseArtifacts: metricflowParseResult(),
182
+ semanticLayerService: semanticLayerService,
183
+ });
184
+ expect(semanticLayerService.forWorktree).toHaveBeenCalledWith('/tmp/metricflow-integration');
185
+ expect(scoped.writeSource).toHaveBeenCalledWith('warehouse-1', expect.objectContaining({ name: 'orders' }), 'dbt MetricFlow', expect.any(String), 'dbt MetricFlow sync: create source orders', { skipValidation: true });
186
+ expect(result).toMatchObject({
187
+ warnings: ['parser warning'],
188
+ errors: [],
189
+ touchedSources: [{ connectionId: 'warehouse-1', sourceName: 'orders' }],
190
+ changedWikiPageKeys: [],
191
+ });
192
+ });
193
+ it('returns a projection error when parse artifacts are missing', async () => {
194
+ const result = await adapter.project?.({
195
+ connectionId: 'warehouse-1',
196
+ sourceKey: 'metricflow',
197
+ syncId: 'sync-1',
198
+ jobId: 'job-1',
199
+ runId: 'run-1',
200
+ stagedDir,
201
+ workdir: '/tmp/metricflow-integration',
202
+ parseArtifacts: undefined,
203
+ semanticLayerService: {},
204
+ });
205
+ expect(result).toMatchObject({
206
+ warnings: [],
207
+ errors: ['MetricFlow deterministic projection requires parseArtifacts from chunk()'],
208
+ touchedSources: [],
209
+ changedWikiPageKeys: [],
210
+ });
211
+ });
97
212
  });
@@ -0,0 +1,29 @@
1
+ import { z } from 'zod';
2
+ import { type ParsedTargetTable } from '../../parsed-target-table.js';
3
+ import type { MetricflowHostTable } from './semantic-models.js';
4
+ declare const metricflowProjectionConfigSchema: z.ZodObject<{
5
+ parsedTargetTables: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodDiscriminatedUnion<[z.ZodObject<{
6
+ ok: z.ZodLiteral<true>;
7
+ catalog: z.ZodNullable<z.ZodString>;
8
+ schema: z.ZodNullable<z.ZodString>;
9
+ name: z.ZodString;
10
+ canonicalTable: z.ZodString;
11
+ }, z.core.$strip>, z.ZodObject<{
12
+ ok: z.ZodLiteral<false>;
13
+ reason: z.ZodEnum<{
14
+ no_connection_mapping: "no_connection_mapping";
15
+ looker_template_unresolved: "looker_template_unresolved";
16
+ derived_table_not_supported: "derived_table_not_supported";
17
+ no_physical_table: "no_physical_table";
18
+ multiple_table_references: "multiple_table_references";
19
+ unsupported_dialect: "unsupported_dialect";
20
+ parse_error: "parse_error";
21
+ }>;
22
+ detail: z.ZodOptional<z.ZodString>;
23
+ }, z.core.$strip>], "ok">>>;
24
+ }, z.core.$strip>;
25
+ export type MetricflowProjectionConfig = z.infer<typeof metricflowProjectionConfigSchema>;
26
+ export declare function writeMetricflowProjectionConfig(stagedDir: string, config: MetricflowProjectionConfig): Promise<void>;
27
+ export declare function readMetricflowProjectionConfig(stagedDir: string): Promise<MetricflowProjectionConfig>;
28
+ export declare function metricflowHostTablesFromParsedTargets(parsedTargetTables: Record<string, ParsedTargetTable>): MetricflowHostTable[];
29
+ export {};
@@ -0,0 +1,40 @@
1
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { z } from 'zod';
4
+ import { parsedTargetTableSchema } from '../../parsed-target-table.js';
5
+ const METRICFLOW_PROJECTION_CONFIG_FILE = 'sync-config.json';
6
+ const metricflowProjectionConfigSchema = z.object({
7
+ parsedTargetTables: z.record(z.string(), parsedTargetTableSchema).default({}),
8
+ });
9
+ export async function writeMetricflowProjectionConfig(stagedDir, config) {
10
+ const parsed = metricflowProjectionConfigSchema.parse(config);
11
+ await mkdir(stagedDir, { recursive: true });
12
+ await writeFile(join(stagedDir, METRICFLOW_PROJECTION_CONFIG_FILE), `${JSON.stringify(parsed, null, 2)}\n`, 'utf-8');
13
+ }
14
+ export async function readMetricflowProjectionConfig(stagedDir) {
15
+ const path = join(stagedDir, METRICFLOW_PROJECTION_CONFIG_FILE);
16
+ try {
17
+ return metricflowProjectionConfigSchema.parse(JSON.parse(await readFile(path, 'utf-8')));
18
+ }
19
+ catch (error) {
20
+ if (error && typeof error === 'object' && 'code' in error && error.code === 'ENOENT') {
21
+ return { parsedTargetTables: {} };
22
+ }
23
+ throw error;
24
+ }
25
+ }
26
+ export function metricflowHostTablesFromParsedTargets(parsedTargetTables) {
27
+ return Object.entries(parsedTargetTables)
28
+ .flatMap(([id, table]) => table.ok
29
+ ? [
30
+ {
31
+ id,
32
+ name: table.name,
33
+ catalog: table.catalog,
34
+ db: table.schema,
35
+ columns: [],
36
+ },
37
+ ]
38
+ : [])
39
+ .sort((left, right) => left.id.localeCompare(right.id));
40
+ }
@@ -14,9 +14,9 @@ export declare const metricflowPullConfigSchema: z.ZodObject<{
14
14
  }, z.core.$strip>, z.ZodObject<{
15
15
  ok: z.ZodLiteral<false>;
16
16
  reason: z.ZodEnum<{
17
+ no_connection_mapping: "no_connection_mapping";
17
18
  looker_template_unresolved: "looker_template_unresolved";
18
19
  derived_table_not_supported: "derived_table_not_supported";
19
- no_connection_mapping: "no_connection_mapping";
20
20
  no_physical_table: "no_physical_table";
21
21
  multiple_table_references: "multiple_table_references";
22
22
  unsupported_dialect: "unsupported_dialect";
@@ -0,0 +1,25 @@
1
+ import type { SemanticLayerService } from '../sl/index.js';
2
+ import type { TouchedSlSource } from '../tools/index.js';
3
+ import type { KnowledgeWikiService } from '../wiki/index.js';
4
+ export interface TouchedValidationResult {
5
+ invalidSources: string[];
6
+ validSources: string[];
7
+ }
8
+ export interface FinalArtifactGateInput {
9
+ connectionIds: string[];
10
+ changedWikiPageKeys: string[];
11
+ touchedSlSources: TouchedSlSource[];
12
+ wikiService: KnowledgeWikiService;
13
+ semanticLayerService: SemanticLayerService;
14
+ validateTouchedSources(touched: TouchedSlSource[]): Promise<TouchedValidationResult>;
15
+ tableExists(connectionId: string, tableRef: string): Promise<boolean>;
16
+ }
17
+ export interface ProvenanceRawPathValidationInput {
18
+ rows: Array<{
19
+ rawPath: string;
20
+ }>;
21
+ currentRawPaths: Set<string>;
22
+ deletedRawPaths: Set<string>;
23
+ }
24
+ export declare function validateFinalIngestArtifacts(input: FinalArtifactGateInput): Promise<void>;
25
+ export declare function validateProvenanceRawPaths(input: ProvenanceRawPathValidationInput): void;