@intentius/chant-lexicon-grafana 0.97.0 → 0.99.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 (451) hide show
  1. package/README.md +15 -5
  2. package/dist/actions/grafana.d.ts +24 -0
  3. package/dist/actions/grafana.d.ts.map +1 -0
  4. package/dist/actions/index.d.ts +2 -0
  5. package/dist/actions/index.d.ts.map +1 -0
  6. package/dist/alerting-build.d.ts +128 -0
  7. package/dist/alerting-build.d.ts.map +1 -0
  8. package/dist/alerting.d.ts +273 -0
  9. package/dist/alerting.d.ts.map +1 -0
  10. package/dist/api/apply.d.ts +156 -0
  11. package/dist/api/apply.d.ts.map +1 -0
  12. package/dist/api/bind.d.ts +32 -0
  13. package/dist/api/bind.d.ts.map +1 -0
  14. package/dist/api/client.d.ts +76 -0
  15. package/dist/api/client.d.ts.map +1 -0
  16. package/dist/api/converge.d.ts +33 -0
  17. package/dist/api/converge.d.ts.map +1 -0
  18. package/dist/api/dashboards.d.ts +102 -0
  19. package/dist/api/dashboards.d.ts.map +1 -0
  20. package/dist/api/datasources.d.ts +26 -0
  21. package/dist/api/datasources.d.ts.map +1 -0
  22. package/dist/api/fake-grafana-writes.d.ts +41 -0
  23. package/dist/api/fake-grafana-writes.d.ts.map +1 -0
  24. package/dist/api/fake-grafana.d.ts +61 -0
  25. package/dist/api/fake-grafana.d.ts.map +1 -0
  26. package/dist/api/folders.d.ts +110 -0
  27. package/dist/api/folders.d.ts.map +1 -0
  28. package/dist/api/library-panels.d.ts +52 -0
  29. package/dist/api/library-panels.d.ts.map +1 -0
  30. package/dist/build.d.ts +30 -2
  31. package/dist/build.d.ts.map +1 -1
  32. package/dist/catalog.d.ts +1 -1
  33. package/dist/catalog.d.ts.map +1 -1
  34. package/dist/codegen/docs.d.ts.map +1 -1
  35. package/dist/codegen/schema-types.d.ts +3 -3
  36. package/dist/codegen/schema-types.d.ts.map +1 -1
  37. package/dist/composites/agent-dashboard.d.ts.map +1 -1
  38. package/dist/composites/catalog.d.ts.map +1 -1
  39. package/dist/composites/index.d.ts +4 -2
  40. package/dist/composites/index.d.ts.map +1 -1
  41. package/dist/composites/red-dashboard.d.ts +21 -2
  42. package/dist/composites/red-dashboard.d.ts.map +1 -1
  43. package/dist/composites/shared.d.ts +7 -0
  44. package/dist/composites/shared.d.ts.map +1 -1
  45. package/dist/composites/slo-alert-rules.d.ts +92 -0
  46. package/dist/composites/slo-alert-rules.d.ts.map +1 -0
  47. package/dist/config.d.ts +111 -0
  48. package/dist/config.d.ts.map +1 -0
  49. package/dist/contact-point-secrets.d.ts +17 -0
  50. package/dist/contact-point-secrets.d.ts.map +1 -0
  51. package/dist/dashboard.d.ts +16 -2
  52. package/dist/dashboard.d.ts.map +1 -1
  53. package/dist/datasource-refs.d.ts +95 -0
  54. package/dist/datasource-refs.d.ts.map +1 -0
  55. package/dist/datasource.d.ts +29 -0
  56. package/dist/datasource.d.ts.map +1 -1
  57. package/dist/deep-observe-hooks.d.ts +64 -0
  58. package/dist/deep-observe-hooks.d.ts.map +1 -0
  59. package/dist/deep-observe.d.ts +42 -0
  60. package/dist/deep-observe.d.ts.map +1 -0
  61. package/dist/describe-resources.d.ts +49 -0
  62. package/dist/describe-resources.d.ts.map +1 -0
  63. package/dist/detect.d.ts +28 -1
  64. package/dist/detect.d.ts.map +1 -1
  65. package/dist/export-resources.d.ts +38 -0
  66. package/dist/export-resources.d.ts.map +1 -0
  67. package/dist/import/alerting.d.ts +40 -0
  68. package/dist/import/alerting.d.ts.map +1 -0
  69. package/dist/import/edits.d.ts +47 -0
  70. package/dist/import/edits.d.ts.map +1 -0
  71. package/dist/import/embedded.d.ts +14 -0
  72. package/dist/import/embedded.d.ts.map +1 -0
  73. package/dist/import/generator.d.ts +60 -0
  74. package/dist/import/generator.d.ts.map +1 -0
  75. package/dist/import/live-export.d.ts +108 -0
  76. package/dist/import/live-export.d.ts.map +1 -0
  77. package/dist/import/mappings.d.ts +83 -0
  78. package/dist/import/mappings.d.ts.map +1 -0
  79. package/dist/import/model.d.ts +98 -0
  80. package/dist/import/model.d.ts.map +1 -0
  81. package/dist/import/normalize-alerting.d.ts +26 -0
  82. package/dist/import/normalize-alerting.d.ts.map +1 -0
  83. package/dist/import/normalize.d.ts +74 -0
  84. package/dist/import/normalize.d.ts.map +1 -0
  85. package/dist/import/parser.d.ts +110 -0
  86. package/dist/import/parser.d.ts.map +1 -0
  87. package/dist/import/report.d.ts +26 -0
  88. package/dist/import/report.d.ts.map +1 -0
  89. package/dist/import/testdata/fixtures.d.ts +35 -0
  90. package/dist/import/testdata/fixtures.d.ts.map +1 -0
  91. package/dist/import/v2.d.ts +72 -0
  92. package/dist/import/v2.d.ts.map +1 -0
  93. package/dist/index.d.ts +26 -9
  94. package/dist/index.d.ts.map +1 -1
  95. package/dist/integrity.json +18 -10
  96. package/dist/k8s.d.ts +107 -0
  97. package/dist/k8s.d.ts.map +1 -0
  98. package/dist/lint/audit-catalog.d.ts +2 -1
  99. package/dist/lint/audit-catalog.d.ts.map +1 -1
  100. package/dist/lint/post-synth/graf101.d.ts +2 -2
  101. package/dist/lint/post-synth/graf102.d.ts +1 -1
  102. package/dist/lint/post-synth/graf107.d.ts +1 -1
  103. package/dist/lint/post-synth/graf108.d.ts +8 -0
  104. package/dist/lint/post-synth/graf108.d.ts.map +1 -0
  105. package/dist/lint/post-synth/graf109.d.ts +8 -0
  106. package/dist/lint/post-synth/graf109.d.ts.map +1 -0
  107. package/dist/lint/post-synth/graf111.d.ts +8 -0
  108. package/dist/lint/post-synth/graf111.d.ts.map +1 -0
  109. package/dist/lint/post-synth/graf112.d.ts +8 -0
  110. package/dist/lint/post-synth/graf112.d.ts.map +1 -0
  111. package/dist/lint/post-synth/graf113.d.ts +8 -0
  112. package/dist/lint/post-synth/graf113.d.ts.map +1 -0
  113. package/dist/lint/post-synth/graf114.d.ts +8 -0
  114. package/dist/lint/post-synth/graf114.d.ts.map +1 -0
  115. package/dist/lint/post-synth/graf115.d.ts +8 -0
  116. package/dist/lint/post-synth/graf115.d.ts.map +1 -0
  117. package/dist/lint/post-synth/grafana-helpers.d.ts +7 -5
  118. package/dist/lint/post-synth/grafana-helpers.d.ts.map +1 -1
  119. package/dist/lint/post-synth/index.d.ts.map +1 -1
  120. package/dist/lint/rules/literal-secret.d.ts +7 -3
  121. package/dist/lint/rules/literal-secret.d.ts.map +1 -1
  122. package/dist/lsp/hover.d.ts.map +1 -1
  123. package/dist/manifest.json +1 -1
  124. package/dist/members.d.ts +27 -0
  125. package/dist/members.d.ts.map +1 -0
  126. package/dist/meta.json +135 -0
  127. package/dist/okf/index.md +36 -2
  128. package/dist/okf/rules/GRAF002.md +3 -2
  129. package/dist/okf/rules/GRAF101.md +3 -2
  130. package/dist/okf/rules/GRAF102.md +1 -0
  131. package/dist/okf/rules/GRAF108.md +11 -0
  132. package/dist/okf/rules/GRAF109.md +15 -0
  133. package/dist/okf/rules/GRAF111.md +11 -0
  134. package/dist/okf/rules/GRAF112.md +16 -0
  135. package/dist/okf/rules/GRAF113.md +16 -0
  136. package/dist/okf/rules/GRAF114.md +11 -0
  137. package/dist/okf/rules/GRAF115.md +11 -0
  138. package/dist/okf/types/AlertListPanel.md +9 -0
  139. package/dist/okf/types/AlertQuery.md +9 -0
  140. package/dist/okf/types/AlertRule.md +9 -0
  141. package/dist/okf/types/AlertRuleGroup.md +9 -0
  142. package/dist/okf/types/BarChartPanel.md +9 -0
  143. package/dist/okf/types/BarGaugePanel.md +9 -0
  144. package/dist/okf/types/CanvasPanel.md +9 -0
  145. package/dist/okf/types/ClassicConditionsExpression.md +9 -0
  146. package/dist/okf/types/ContactPoint.md +14 -0
  147. package/dist/okf/types/Dashboard.md +1 -0
  148. package/dist/okf/types/Datasource.md +3 -2
  149. package/dist/okf/types/ExternalDatasource.md +15 -0
  150. package/dist/okf/types/FlameGraphPanel.md +9 -0
  151. package/dist/okf/types/GeomapPanel.md +9 -0
  152. package/dist/okf/types/HistogramPanel.md +9 -0
  153. package/dist/okf/types/MathExpression.md +9 -0
  154. package/dist/okf/types/MuteTiming.md +13 -0
  155. package/dist/okf/types/NodeGraphPanel.md +9 -0
  156. package/dist/okf/types/NotificationPolicy.md +9 -0
  157. package/dist/okf/types/NotificationTemplate.md +9 -0
  158. package/dist/okf/types/PieChartPanel.md +9 -0
  159. package/dist/okf/types/ReduceExpression.md +9 -0
  160. package/dist/okf/types/ResampleExpression.md +9 -0
  161. package/dist/okf/types/SqlExpression.md +9 -0
  162. package/dist/okf/types/StateTimelinePanel.md +9 -0
  163. package/dist/okf/types/StatusHistoryPanel.md +9 -0
  164. package/dist/okf/types/ThresholdExpression.md +9 -0
  165. package/dist/okf/types/TrendPanel.md +9 -0
  166. package/dist/okf/types/XYChartPanel.md +9 -0
  167. package/dist/op/activities/grafana-apply.d.ts +75 -0
  168. package/dist/op/activities/grafana-apply.d.ts.map +1 -0
  169. package/dist/op/activities/index.d.ts +9 -0
  170. package/dist/op/activities/index.d.ts.map +1 -0
  171. package/dist/op/builders.d.ts +24 -0
  172. package/dist/op/builders.d.ts.map +1 -0
  173. package/dist/ownership.d.ts +67 -0
  174. package/dist/ownership.d.ts.map +1 -0
  175. package/dist/panel-options.d.ts +65 -0
  176. package/dist/panel-options.d.ts.map +1 -0
  177. package/dist/panels.d.ts +46 -9
  178. package/dist/panels.d.ts.map +1 -1
  179. package/dist/pin.d.ts +29 -7
  180. package/dist/pin.d.ts.map +1 -1
  181. package/dist/plugin.d.ts.map +1 -1
  182. package/dist/promql-check.d.ts +51 -0
  183. package/dist/promql-check.d.ts.map +1 -0
  184. package/dist/query.d.ts +9 -8
  185. package/dist/query.d.ts.map +1 -1
  186. package/dist/rules/graf101.ts +3 -3
  187. package/dist/rules/graf102.ts +1 -1
  188. package/dist/rules/graf107.ts +1 -1
  189. package/dist/rules/graf108.ts +17 -0
  190. package/dist/rules/graf109.ts +17 -0
  191. package/dist/rules/graf111.ts +17 -0
  192. package/dist/rules/graf112.ts +17 -0
  193. package/dist/rules/graf113.ts +17 -0
  194. package/dist/rules/graf114.ts +17 -0
  195. package/dist/rules/graf115.ts +17 -0
  196. package/dist/rules/grafana-helpers.ts +15 -8
  197. package/dist/rules/literal-secret.ts +60 -6
  198. package/dist/schema/barchart.gen.d.ts +198 -0
  199. package/dist/schema/barchart.gen.d.ts.map +1 -0
  200. package/dist/schema/bargauge.gen.d.ts +97 -0
  201. package/dist/schema/bargauge.gen.d.ts.map +1 -0
  202. package/dist/schema/canvas.gen.d.ts +152 -0
  203. package/dist/schema/canvas.gen.d.ts.map +1 -0
  204. package/dist/schema/dashboard.gen.d.ts +196 -21
  205. package/dist/schema/dashboard.gen.d.ts.map +1 -1
  206. package/dist/schema/expr.gen.d.ts +606 -0
  207. package/dist/schema/expr.gen.d.ts.map +1 -0
  208. package/dist/schema/gauge.gen.d.ts.map +1 -1
  209. package/dist/schema/geomap.gen.d.ts +100 -0
  210. package/dist/schema/geomap.gen.d.ts.map +1 -0
  211. package/dist/schema/heatmap.gen.d.ts.map +1 -1
  212. package/dist/schema/histogram.gen.d.ts +132 -0
  213. package/dist/schema/histogram.gen.d.ts.map +1 -0
  214. package/dist/schema/index.d.ts +12 -0
  215. package/dist/schema/index.d.ts.map +1 -1
  216. package/dist/schema/logs.gen.d.ts +1 -0
  217. package/dist/schema/logs.gen.d.ts.map +1 -1
  218. package/dist/schema/loki.gen.d.ts.map +1 -1
  219. package/dist/schema/nodegraph.gen.d.ts +49 -0
  220. package/dist/schema/nodegraph.gen.d.ts.map +1 -0
  221. package/dist/schema/piechart.gen.d.ts +127 -0
  222. package/dist/schema/piechart.gen.d.ts.map +1 -0
  223. package/dist/schema/prometheus.gen.d.ts.map +1 -1
  224. package/dist/schema/stat.gen.d.ts.map +1 -1
  225. package/dist/schema/statetimeline.gen.d.ts +124 -0
  226. package/dist/schema/statetimeline.gen.d.ts.map +1 -0
  227. package/dist/schema/statushistory.gen.d.ts +114 -0
  228. package/dist/schema/statushistory.gen.d.ts.map +1 -0
  229. package/dist/schema/table.gen.d.ts +22 -3
  230. package/dist/schema/table.gen.d.ts.map +1 -1
  231. package/dist/schema/tempo.gen.d.ts.map +1 -1
  232. package/dist/schema/text.gen.d.ts.map +1 -1
  233. package/dist/schema/timeseries.gen.d.ts +5 -1
  234. package/dist/schema/timeseries.gen.d.ts.map +1 -1
  235. package/dist/schema/trend.gen.d.ts +180 -0
  236. package/dist/schema/trend.gen.d.ts.map +1 -0
  237. package/dist/schema/xychart.gen.d.ts +145 -0
  238. package/dist/schema/xychart.gen.d.ts.map +1 -0
  239. package/dist/schema-validate.d.ts +30 -4
  240. package/dist/schema-validate.d.ts.map +1 -1
  241. package/dist/serializer.d.ts +11 -3
  242. package/dist/serializer.d.ts.map +1 -1
  243. package/dist/skill-defs.d.ts.map +1 -1
  244. package/dist/skills/chant-grafana-alerting.md +49 -0
  245. package/dist/skills/chant-grafana-provisioning.md +3 -3
  246. package/dist/skills/chant-grafana.md +12 -6
  247. package/dist/spec/fetch-units-cli.d.ts +3 -0
  248. package/dist/spec/fetch-units-cli.d.ts.map +1 -0
  249. package/dist/spec/overlay.d.ts +43 -0
  250. package/dist/spec/overlay.d.ts.map +1 -0
  251. package/dist/spec/schemas.d.ts +13 -7
  252. package/dist/spec/schemas.d.ts.map +1 -1
  253. package/dist/spec/units.d.ts +35 -0
  254. package/dist/spec/units.d.ts.map +1 -0
  255. package/dist/spec/units.gen.d.ts +2 -0
  256. package/dist/spec/units.gen.d.ts.map +1 -0
  257. package/dist/transformations.d.ts +459 -0
  258. package/dist/transformations.d.ts.map +1 -0
  259. package/dist/util.d.ts +6 -0
  260. package/dist/util.d.ts.map +1 -1
  261. package/dist/validate-alerting.d.ts +65 -0
  262. package/dist/validate-alerting.d.ts.map +1 -0
  263. package/dist/validate-output.d.ts +49 -9
  264. package/dist/validate-output.d.ts.map +1 -1
  265. package/dist/validate.d.ts.map +1 -1
  266. package/dist/variables.d.ts +7 -2
  267. package/dist/variables.d.ts.map +1 -1
  268. package/package.json +12 -4
  269. package/src/actions/actions.test.ts +24 -0
  270. package/src/actions/grafana.ts +40 -0
  271. package/src/actions/index.ts +1 -0
  272. package/src/alerting-build.ts +376 -0
  273. package/src/alerting.e2e.test.ts +212 -0
  274. package/src/alerting.test.ts +272 -0
  275. package/src/alerting.ts +435 -0
  276. package/src/api/apply.test.ts +238 -0
  277. package/src/api/apply.ts +384 -0
  278. package/src/api/bind.ts +58 -0
  279. package/src/api/client.ts +148 -0
  280. package/src/api/converge.ts +63 -0
  281. package/src/api/dashboards.ts +247 -0
  282. package/src/api/datasources.ts +33 -0
  283. package/src/api/fake-grafana-writes.ts +201 -0
  284. package/src/api/fake-grafana.ts +186 -0
  285. package/src/api/folders.ts +265 -0
  286. package/src/api/library-panels.ts +101 -0
  287. package/src/build.test.ts +210 -5
  288. package/src/build.ts +180 -43
  289. package/src/catalog.ts +86 -2
  290. package/src/codegen/docs.ts +42 -9
  291. package/src/codegen/schema-types.ts +16 -6
  292. package/src/composites/agent-dashboard.ts +6 -3
  293. package/src/composites/catalog.test.ts +2 -2
  294. package/src/composites/catalog.ts +82 -0
  295. package/src/composites/composites.test.ts +73 -9
  296. package/src/composites/index.ts +4 -2
  297. package/src/composites/queries.e2e.test.ts +374 -0
  298. package/src/composites/red-dashboard.ts +83 -12
  299. package/src/composites/shared.ts +11 -0
  300. package/src/composites/slo-alert-rules.ts +149 -0
  301. package/src/config.test.ts +61 -0
  302. package/src/config.ts +138 -0
  303. package/src/contact-point-secrets.ts +63 -0
  304. package/src/dashboard.ts +16 -2
  305. package/src/datasource-refs.test.ts +56 -0
  306. package/src/datasource-refs.ts +189 -0
  307. package/src/datasource.ts +52 -0
  308. package/src/deep-observe-hooks.ts +252 -0
  309. package/src/deep-observe.test.ts +265 -0
  310. package/src/deep-observe.ts +164 -0
  311. package/src/describe-resources.test.ts +255 -0
  312. package/src/describe-resources.ts +209 -0
  313. package/src/detect.ts +79 -2
  314. package/src/export-resources.test.ts +129 -0
  315. package/src/export-resources.ts +84 -0
  316. package/src/generated/lexicon-grafana.json +135 -0
  317. package/src/import/alerting-roundtrip.test.ts +161 -0
  318. package/src/import/alerting.ts +500 -0
  319. package/src/import/edits.ts +95 -0
  320. package/src/import/embedded.test.ts +45 -0
  321. package/src/import/embedded.ts +47 -0
  322. package/src/import/generated-types.e2e.test.ts +118 -0
  323. package/src/import/generator.test.ts +210 -0
  324. package/src/import/generator.ts +462 -0
  325. package/src/import/live-export.ts +264 -0
  326. package/src/import/mappings.ts +264 -0
  327. package/src/import/model.ts +110 -0
  328. package/src/import/normalize-alerting.ts +101 -0
  329. package/src/import/normalize.test.ts +117 -0
  330. package/src/import/normalize.ts +364 -0
  331. package/src/import/parser.test.ts +440 -0
  332. package/src/import/parser.ts +1236 -0
  333. package/src/import/report.ts +62 -0
  334. package/src/import/roundtrip.test.ts +468 -0
  335. package/src/import/testdata/fixtures.ts +129 -0
  336. package/src/import/v2.test.ts +292 -0
  337. package/src/import/v2.ts +803 -0
  338. package/src/import.e2e.test.ts +31 -84
  339. package/src/import.test.ts +9 -3
  340. package/src/index.ts +190 -5
  341. package/src/k8s.test.ts +98 -0
  342. package/src/k8s.ts +197 -0
  343. package/src/lint/audit-catalog.ts +60 -3
  344. package/src/lint/core-lint.test.ts +43 -0
  345. package/src/lint/post-synth/graf101.ts +3 -3
  346. package/src/lint/post-synth/graf102.ts +1 -1
  347. package/src/lint/post-synth/graf107.ts +1 -1
  348. package/src/lint/post-synth/graf108.ts +17 -0
  349. package/src/lint/post-synth/graf109.ts +17 -0
  350. package/src/lint/post-synth/graf111.ts +17 -0
  351. package/src/lint/post-synth/graf112.ts +17 -0
  352. package/src/lint/post-synth/graf113.ts +17 -0
  353. package/src/lint/post-synth/graf114.ts +17 -0
  354. package/src/lint/post-synth/graf115.ts +17 -0
  355. package/src/lint/post-synth/grafana-helpers.ts +15 -8
  356. package/src/lint/post-synth/index.ts +14 -0
  357. package/src/lint/post-synth/post-synth.test.ts +235 -11
  358. package/src/lint/rules/literal-secret.ts +60 -6
  359. package/src/lint/rules/rules.test.ts +27 -0
  360. package/src/lsp/completions.test.ts +5 -0
  361. package/src/lsp/hover.test.ts +7 -0
  362. package/src/lsp/hover.ts +8 -1
  363. package/src/members.ts +68 -0
  364. package/src/observe.e2e.test.ts +255 -0
  365. package/src/op/activities/grafana-apply.e2e.test.ts +175 -0
  366. package/src/op/activities/grafana-apply.test.ts +229 -0
  367. package/src/op/activities/grafana-apply.ts +171 -0
  368. package/src/op/activities/index.ts +8 -0
  369. package/src/op/builders.test.ts +40 -0
  370. package/src/op/builders.ts +34 -0
  371. package/src/ownership.ts +103 -0
  372. package/src/panel-options.ts +66 -0
  373. package/src/panels.test.ts +51 -0
  374. package/src/panels.ts +148 -10
  375. package/src/pin.ts +57 -6
  376. package/src/plugin.test.ts +5 -2
  377. package/src/plugin.ts +66 -0
  378. package/src/promql-check.test.ts +108 -0
  379. package/src/promql-check.ts +189 -0
  380. package/src/query.ts +13 -8
  381. package/src/schema/barchart.gen.ts +221 -0
  382. package/src/schema/bargauge.gen.ts +111 -0
  383. package/src/schema/canvas.gen.ts +176 -0
  384. package/src/schema/dashboard.gen.ts +206 -22
  385. package/src/schema/expr.gen.ts +618 -0
  386. package/src/schema/gauge.gen.ts +2 -1
  387. package/src/schema/geomap.gen.ts +112 -0
  388. package/src/schema/heatmap.gen.ts +2 -1
  389. package/src/schema/histogram.gen.ts +151 -0
  390. package/src/schema/index.ts +12 -0
  391. package/src/schema/logs.gen.ts +3 -1
  392. package/src/schema/loki.gen.ts +2 -1
  393. package/src/schema/nodegraph.gen.ts +58 -0
  394. package/src/schema/piechart.gen.ts +146 -0
  395. package/src/schema/prometheus.gen.ts +2 -1
  396. package/src/schema/stat.gen.ts +2 -1
  397. package/src/schema/statetimeline.gen.ts +143 -0
  398. package/src/schema/statushistory.gen.ts +132 -0
  399. package/src/schema/table.gen.ts +27 -4
  400. package/src/schema/tempo.gen.ts +2 -1
  401. package/src/schema/text.gen.ts +2 -1
  402. package/src/schema/timeseries.gen.ts +7 -2
  403. package/src/schema/trend.gen.ts +208 -0
  404. package/src/schema/xychart.gen.ts +167 -0
  405. package/src/schema-overlay.test.ts +282 -0
  406. package/src/schema-validate.ts +141 -50
  407. package/src/schema.test.ts +4 -2
  408. package/src/serializer.test.ts +13 -1
  409. package/src/serializer.ts +11 -3
  410. package/src/skill-defs.ts +17 -0
  411. package/src/skills/chant-grafana-alerting.md +49 -0
  412. package/src/skills/chant-grafana-provisioning.md +3 -3
  413. package/src/skills/chant-grafana.md +12 -6
  414. package/src/spec/fetch-cli.ts +3 -3
  415. package/src/spec/fetch-units-cli.ts +37 -0
  416. package/src/spec/overlay/canvas.overlay.json +142 -0
  417. package/src/spec/overlay/dashboard.overlay.json +378 -0
  418. package/src/spec/overlay/expr.overlay.json +14 -0
  419. package/src/spec/overlay/geomap.overlay.json +35 -0
  420. package/src/spec/overlay/logs.overlay.json +14 -0
  421. package/src/spec/overlay/nodegraph.overlay.json +31 -0
  422. package/src/spec/overlay/piechart.overlay.json +17 -0
  423. package/src/spec/overlay/table.overlay.json +84 -0
  424. package/src/spec/overlay/timeseries.overlay.json +21 -0
  425. package/src/spec/overlay/trend.overlay.json +17 -0
  426. package/src/spec/overlay/xychart.overlay.json +16 -0
  427. package/src/spec/overlay.ts +105 -0
  428. package/src/spec/schemas/barchart.jsonschema.json +419 -0
  429. package/src/spec/schemas/bargauge.jsonschema.json +224 -0
  430. package/src/spec/schemas/canvas.jsonschema.json +356 -0
  431. package/src/spec/schemas/dashboardv2.jsonschema.json +3387 -0
  432. package/src/spec/schemas/expr.jsonschema.json +987 -0
  433. package/src/spec/schemas/geomap.jsonschema.json +220 -0
  434. package/src/spec/schemas/histogram.jsonschema.json +308 -0
  435. package/src/spec/schemas/nodegraph.jsonschema.json +77 -0
  436. package/src/spec/schemas/piechart.jsonschema.json +275 -0
  437. package/src/spec/schemas/statetimeline.jsonschema.json +324 -0
  438. package/src/spec/schemas/statushistory.jsonschema.json +292 -0
  439. package/src/spec/schemas/trend.jsonschema.json +449 -0
  440. package/src/spec/schemas/xychart.jsonschema.json +428 -0
  441. package/src/spec/schemas.ts +22 -10
  442. package/src/spec/units.gen.ts +281 -0
  443. package/src/spec/units.ts +77 -0
  444. package/src/transformations.test.ts +65 -0
  445. package/src/transformations.ts +574 -0
  446. package/src/util.ts +8 -0
  447. package/src/validate-alerting.test.ts +255 -0
  448. package/src/validate-alerting.ts +428 -0
  449. package/src/validate-output.ts +332 -101
  450. package/src/validate.ts +14 -0
  451. package/src/variables.ts +7 -2
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Dashboard JSON embedded in another lexicon's resource, for `chant import`
3
+ * (#2962): a k8s ConfigMap holding a dashboard, as the Grafana dashboard
4
+ * sidecar (the `grafana_dashboard` label) and file provisioning both mount
5
+ * them. Matching is by content, so a dashboard in an unlabelled ConfigMap
6
+ * is imported too.
7
+ *
8
+ * The dashboard is imported exactly as `chant import dashboard.json` would
9
+ * import it, into a directory of its own, and the host's value becomes
10
+ * `dashboardJson(dashboard)`, the JSON text the grafana serializer writes.
11
+ */
12
+
13
+ import type { EmbeddedContentImporter, EmbeddedImport } from "@intentius/chant/import/embedded";
14
+ import { looksLikeDashboard, looksLikeV2Dashboard } from "../detect";
15
+ import { DASHBOARD_RESOURCE_TYPE, parseGrafana, type PlanResourceProperties } from "./parser";
16
+ import { generatePlanModules } from "./generator";
17
+
18
+ const PACKAGE = "@intentius/chant-lexicon-grafana";
19
+
20
+ export const dashboardImporter: EmbeddedContentImporter = {
21
+ what: "a Grafana dashboard",
22
+
23
+ matches(content) {
24
+ if (typeof content.text !== "string" || content.select !== undefined) return false;
25
+ return looksLikeDashboard(content.document) && !looksLikeV2Dashboard(content.document);
26
+ },
27
+
28
+ import(content): EmbeddedImport {
29
+ const ir = parseGrafana(content.text!);
30
+ const resource = ir.resources.find((r) => r.type === DASHBOARD_RESOURCE_TYPE);
31
+ if (!resource) throw new Error((ir.warnings ?? []).join(" ") || "no dashboard was read");
32
+ const { plan } = resource.properties as unknown as PlanResourceProperties;
33
+ // Core gives the content a directory of its own; the plan's would nest a second one inside it.
34
+ const { files, exported } = generatePlanModules({ ...plan, directory: "" });
35
+ const dashboard = exported.get("dashboard");
36
+ if (!dashboard) throw new Error("the import declared no dashboard");
37
+ return {
38
+ files,
39
+ value: {
40
+ bindings: [{ from: dashboard.path, name: dashboard.name }],
41
+ shape: "single",
42
+ through: { from: PACKAGE, name: "dashboardJson" },
43
+ },
44
+ warnings: ir.warnings ?? [],
45
+ };
46
+ },
47
+ };
@@ -0,0 +1,118 @@
1
+ /**
2
+ * The generated source type-checks against the lexicon's types. Compiling
3
+ * the corpus takes longer than the unit-test budget allows, so this runs
4
+ * with the e2e tests.
5
+ *
6
+ * Grafana's UI exports, the alerting provisioning corpus and what the
7
+ * examples build type-check clean. A
8
+ * community dashboard can carry keys the pinned schemas do not list (query
9
+ * fields from older Grafana versions such as `step` and `metric`, panel
10
+ * options added after the pin): it still imports and builds back to the
11
+ * same JSON, GRAF107 warns about each key, and tsc points at the const or
12
+ * constructor that holds it. Those are pinned here, per fixture, so a
13
+ * change to the generator or the types that adds or removes one is seen.
14
+ */
15
+ import { describe, expect, test } from "vitest";
16
+ import { mkdirSync, mkdtempSync } from "fs";
17
+ import { join } from "path";
18
+ import * as ts from "typescript";
19
+ import { GrafanaParser } from "./parser";
20
+ import { GrafanaGenerator } from "./generator";
21
+ import { ALERTING, COMMUNITY, KUBE_PROMETHEUS, UI_EXPORTS, V2_EXPORTS, exampleOutputs, pkgDir, read, removeDir, repoRoot, writeFiles } from "./testdata/fixtures";
22
+
23
+ type Files = Array<{ path: string; content: string }>;
24
+
25
+ /** Type errors in generated projects, compiled together against the lexicon's source with the repo's compiler options. */
26
+ function typeErrors(projects: Record<string, Files>): string[] {
27
+ const dir = mkdtempSync(join(pkgDir, ".roundtrip-tmp-"));
28
+ try {
29
+ const files: string[] = [];
30
+ for (const [name, generated] of Object.entries(projects)) {
31
+ const sub = join(dir, name.replace(/[^A-Za-z0-9]+/g, "-"));
32
+ mkdirSync(sub);
33
+ files.push(...writeFiles(sub, generated));
34
+ }
35
+ const configFile = ts.readConfigFile(join(repoRoot, "tsconfig.json"), ts.sys.readFile);
36
+ const parsed = ts.parseJsonConfigFileContent(configFile.config, ts.sys, repoRoot);
37
+ const program = ts.createProgram(files, { ...parsed.options, noEmit: true });
38
+ const real = (f: string) => ts.sys.realpath?.(f) ?? f;
39
+ const fileSet = new Set(files.map(real));
40
+ const diagnostics = ts.getPreEmitDiagnostics(program).filter((d) => d.file && fileSet.has(real(d.file.fileName)));
41
+ // A program that saw none of the files would pass vacuously.
42
+ expect(files.every((f) => program.getSourceFile(f) !== undefined)).toBe(true);
43
+ return diagnostics.map((d) => {
44
+ const at = real(d.file!.fileName).slice(real(dir).length + 1);
45
+ return `${at}: ${ts.flattenDiagnosticMessageText(d.messageText, " ")}`;
46
+ });
47
+ } finally {
48
+ removeDir(dir);
49
+ }
50
+ }
51
+
52
+ const generate = (content: string): Files => new GrafanaGenerator().generate(new GrafanaParser().parse(content));
53
+
54
+ /**
55
+ * Error counts by the property tsc names, for a compact pin of what a
56
+ * fixture carries outside the types. tsc names only the first unknown key
57
+ * of an object literal, so a count is a lower bound (GRAF107 lists them all).
58
+ */
59
+ function byProperty(errors: string[]): Record<string, number> {
60
+ const out: Record<string, number> = {};
61
+ for (const e of errors) {
62
+ const m = /'([^']+)' does not exist in type/.exec(e);
63
+ const key = m ? m[1] : e.replace(/^[^:]+: /, "");
64
+ out[key] = (out[key] ?? 0) + 1;
65
+ }
66
+ return out;
67
+ }
68
+
69
+ describe("the generated source type-checks against the lexicon's types", () => {
70
+ test("Grafana's UI exports, the v2 dashboards and the examples' dashboards type-check clean", async () => {
71
+ const projects: Record<string, Files> = Object.fromEntries([...UI_EXPORTS, ...V2_EXPORTS].map((f) => [f, generate(read(f))]));
72
+ for (const [name, text] of await exampleOutputs()) {
73
+ if (/^[^/]+\/dashboards\/.*\.json$/.test(name)) projects[name] = generate(text);
74
+ }
75
+ expect(typeErrors(projects)).toEqual([]);
76
+ }, 180_000);
77
+
78
+ test("alerting provisioning files, and what the alerting example builds, type-check clean", async () => {
79
+ const projects: Record<string, Files> = Object.fromEntries(ALERTING.map((f) => [f, generate(read(f))]));
80
+ for (const [name, text] of await exampleOutputs()) {
81
+ if (name.endsWith("provisioning/alerting/chant.yaml")) projects[name] = generate(text);
82
+ }
83
+ expect(Object.keys(projects).some((n) => n.startsWith("alerting/"))).toBe(true);
84
+ expect(typeErrors(projects)).toEqual([]);
85
+ }, 180_000);
86
+
87
+ test("community dashboards: what they carry outside the pinned types is where tsc points", () => {
88
+ const found: Record<string, Record<string, number>> = {};
89
+ for (const file of COMMUNITY) found[file] = byProperty(typeErrors({ [file]: generate(read(file)) }));
90
+ expect(found).toEqual({
91
+ "community/node-exporter-full.json": { step: 273, metric: 1 },
92
+ "community/k8s-views-global.json": {},
93
+ "community/k8s-views-pods.json": { footer: 3 },
94
+ "community/traefik.json": {},
95
+ "community/redis.json": { metric: 11, step: 6, time_options: 1, unitScale: 13 },
96
+ "community/prometheus-2-stats.json": { metric: 3, now: 1, step: 18 },
97
+ "community/prometheus-2-stats.grafana-12.4.11.json": { metric: 3, step: 18 },
98
+ });
99
+ }, 300_000);
100
+
101
+ test("kube-prometheus's dashboards: what they carry outside the pinned types is where tsc points", () => {
102
+ const projects: Record<string, Files> = Object.fromEntries(KUBE_PROMETHEUS.map((f) => [f, generate(read(f))]));
103
+ // Compiled as one program: a project's files are under its fixture path, made a directory name.
104
+ const errors = typeErrors(projects);
105
+ const found = Object.fromEntries(
106
+ KUBE_PROMETHEUS.map((f) => [f, byProperty(errors.filter((e) => e.startsWith(`${f.replace(/[^A-Za-z0-9]+/g, "-")}/`)))] as const).filter(([, v]) => Object.keys(v).length > 0),
107
+ );
108
+ expect(Object.values(found).reduce((n, v) => n + Object.values(v).reduce((a, b) => a + b, 0), 0)).toBe(errors.length);
109
+ // Values GRAF107 reports in the source dashboards (roundtrip.test.ts), plus keys the pinned schemas do not list. The node
110
+ // dashboards' merge transformation without options type-checks: the typed transformation classes do not require it.
111
+ expect(found).toEqual({
112
+ "kube-prometheus/grafana-overview.json": { "Type 'string' is not assignable to type 'string[]'.": 1, alertThreshold: 2 },
113
+ "kube-prometheus/k8s-resources-pod.json": { "Type '\"thresholds\"' is not assignable to type 'AxisColorMode | undefined'.": 1 },
114
+ "kube-prometheus/namespace-by-pod.json": { index: 6 },
115
+ "kube-prometheus/pod-total.json": { index: 6 },
116
+ });
117
+ }, 300_000);
118
+ });
@@ -0,0 +1,210 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { GrafanaGenerator, generatePlan, DECLARABLES_PER_FILE } from "./generator";
3
+ import { callValue, declRef, type Declaration, type Plan } from "./model";
4
+
5
+ function plan(declarations: Declaration[], extra: Partial<Plan> = {}): Plan {
6
+ const keys = [...new Set(declarations.map((d) => d.module))];
7
+ return {
8
+ directory: "",
9
+ modules: keys.map((key) => ({ key, file: key, summary: `The ${key}` })),
10
+ declarations,
11
+ customClasses: [],
12
+ exports: ["dashboard"],
13
+ ...extra,
14
+ };
15
+ }
16
+
17
+ const panel = (n: number, props: Record<string, unknown> = {}, module = "panels"): Declaration => ({
18
+ id: `panel:${n}`,
19
+ kind: "new",
20
+ className: "StatPanel",
21
+ props: { title: `Panel ${n}`, ...props },
22
+ name: `Panel ${n}`,
23
+ module,
24
+ unit: `panel:${n}`,
25
+ });
26
+
27
+ const query = (n: number, refId: string, module = "panels"): Declaration => ({
28
+ id: `query:${n}:${refId}`,
29
+ kind: "new",
30
+ className: "PromQuery",
31
+ props: { expr: "up", refId },
32
+ name: { of: `panel:${n}`, suffix: refId },
33
+ module,
34
+ unit: `panel:${n}`,
35
+ });
36
+
37
+ const dash = (panels: string[]): Declaration => ({
38
+ id: "dashboard",
39
+ kind: "new",
40
+ className: "Dashboard",
41
+ props: { title: "D", panels: panels.map(declRef) },
42
+ name: "D",
43
+ module: "dashboard",
44
+ });
45
+
46
+ const file = (files: Array<{ path: string; content: string }>, path: string) => {
47
+ const f = files.find((x) => x.path === path);
48
+ if (!f) throw new Error(`no ${path} in ${files.map((x) => x.path).join(", ")}`);
49
+ return f.content;
50
+ };
51
+
52
+ describe("names", () => {
53
+ test("from titles, with a query named after its panel and refId", () => {
54
+ const files = generatePlan(plan([query(1, "A"), panel(1, { targets: [declRef("query:1:A")] }), dash(["panel:1"])]));
55
+ const panels = file(files, "panels.ts");
56
+ expect(panels).toContain('const panel1A = new PromQuery({ expr: "up", refId: "A" });');
57
+ expect(panels).toContain('const panel1 = new StatPanel({ title: "Panel 1", targets: [panel1A] });');
58
+ });
59
+
60
+ test("an all-caps name is one word; reserved words, clashes and leading digits are avoided", () => {
61
+ const decls: Declaration[] = [
62
+ { id: "variable:DS_PROMETHEUS", kind: "new", className: "DatasourceVariable", props: { name: "DS_PROMETHEUS", pluginType: "prometheus" }, name: "DS_PROMETHEUS", module: "variables" },
63
+ { id: "variable:default", kind: "new", className: "TextboxVariable", props: { name: "default" }, name: "default", module: "variables" },
64
+ { id: "variable:x", kind: "new", className: "TextboxVariable", props: { name: "x" }, name: "404 errors", module: "variables" },
65
+ { id: "variable:y", kind: "new", className: "TextboxVariable", props: { name: "y" }, name: "default", module: "variables" },
66
+ dash([]),
67
+ ];
68
+ const body = file(generatePlan(plan(decls)), "variables.ts");
69
+ expect(body).toContain("const dsPrometheus = new DatasourceVariable(");
70
+ expect(body).toContain("const default2 = new TextboxVariable(");
71
+ expect(body).toContain("const variable404Errors = new TextboxVariable(");
72
+ expect(body).toContain("const default3 = new TextboxVariable(");
73
+ });
74
+ });
75
+
76
+ describe("literals", () => {
77
+ test("a nested value is lifted into a const typed by the class's props (COR001)", () => {
78
+ const files = generatePlan(plan([panel(1, { gridPos: { x: 0, y: 0, w: 6, h: 4 }, options: { reduceOptions: { calcs: ["last"] } } }), dash(["panel:1"])]));
79
+ const panels = file(files, "panels.ts");
80
+ expect(panels).toContain('import { type PropsOf, StatPanel } from "@intentius/chant-lexicon-grafana";');
81
+ expect(panels).toContain('const panel1GridPos: PropsOf<typeof StatPanel>["gridPos"] = { x: 0, y: 0, w: 6, h: 4 };');
82
+ expect(panels).toContain('const panel1Options: PropsOf<typeof StatPanel>["options"] = { reduceOptions: { calcs: ["last"] } };');
83
+ expect(panels).toContain("gridPos: panel1GridPos,");
84
+ });
85
+
86
+ test("lists of strings and of references stay inline", () => {
87
+ const files = generatePlan(plan([{ ...dash([]), props: { title: "D", tags: ["a", "b"] } }]));
88
+ expect(file(files, "dashboard.ts")).toContain('const d = new Dashboard({ title: "D", tags: ["a", "b"] });');
89
+ });
90
+
91
+ test("a value declaration carries its type annotation and imports the type", () => {
92
+ const files = generatePlan(
93
+ plan([
94
+ { id: "ds", kind: "value", value: { type: "prometheus", uid: "prom" }, type: { text: 'DatasourceRef<"prometheus">', imports: ["DatasourceRef"] }, name: "prom", module: "datasources" },
95
+ panel(1, { datasource: declRef("ds") }),
96
+ dash(["panel:1"]),
97
+ ]),
98
+ );
99
+ expect(file(files, "datasources.ts")).toContain('import { type DatasourceRef } from "@intentius/chant-lexicon-grafana";');
100
+ expect(file(files, "datasources.ts")).toContain('const prom: DatasourceRef<"prometheus"> = { type: "prometheus", uid: "prom" };');
101
+ expect(file(files, "panels.ts")).toContain('import { prom } from "./datasources";');
102
+ expect(file(files, "datasources.ts")).toMatch(/export \{ prom \};\n$/);
103
+ });
104
+
105
+ test("multi-line strings become template literals with ${ escaped; quotes pick the lighter form", () => {
106
+ const files = generatePlan(plan([panel(1, { description: "line one\nuses ${var} and `ticks`", title: 'say "hi"' }), dash(["panel:1"])]));
107
+ const body = file(files, "panels.ts");
108
+ expect(body).toContain("description: `line one\nuses \\${var} and \\`ticks\\``");
109
+ expect(body).toContain(`title: 'say "hi"'`);
110
+ });
111
+ });
112
+
113
+ describe("modules", () => {
114
+ test(`a group over ${DECLARABLES_PER_FILE} declarables is split, a panel kept with its queries (COR009)`, () => {
115
+ const decls: Declaration[] = [];
116
+ for (let n = 0; n < 7; n++) decls.push(query(n, "A"), query(n, "B"), panel(n, { targets: [declRef(`query:${n}:A`), declRef(`query:${n}:B`)] }));
117
+ decls.push(dash(Array.from({ length: 7 }, (_, n) => `panel:${n}`)));
118
+ const files = generatePlan(plan(decls));
119
+ const panelFiles = files.filter((f) => f.path.startsWith("panels-"));
120
+ expect(panelFiles.map((f) => f.path)).toEqual(["panels-1.ts", "panels-2.ts", "panels-3.ts", "panels-4.ts"]);
121
+ for (const f of panelFiles) {
122
+ expect((f.content.match(/ = new /g) ?? []).length).toBeLessThanOrEqual(DECLARABLES_PER_FILE);
123
+ // Every panel sits with its two queries.
124
+ const panels = [...f.content.matchAll(/const (panel\d+) = new StatPanel/g)].map((m) => m[1]);
125
+ for (const p of panels) expect(f.content).toContain(`const ${p}A = new PromQuery(`);
126
+ }
127
+ expect(panelFiles[0].content).toContain("/** The panels (1 of 4) */");
128
+ expect(file(files, "dashboard.ts")).toContain('import { panel0, panel1 } from "./panels-1";');
129
+ });
130
+
131
+ test("a panel with more queries than fit in one file is split across files", () => {
132
+ const decls: Declaration[] = [];
133
+ const refs = "ABCDEFGHIJ".split("");
134
+ for (const r of refs) decls.push(query(0, r));
135
+ decls.push(panel(0, { targets: refs.map((r) => declRef(`query:0:${r}`)) }), dash(["panel:0"]));
136
+ const files = generatePlan(plan(decls)).filter((f) => f.path.startsWith("panels-"));
137
+ expect(files.map((f) => (f.content.match(/ = new /g) ?? []).length)).toEqual([8, 3]);
138
+ expect(files[1].content).toContain('from "./panels-1";');
139
+ });
140
+
141
+ test("only what another module imports is exported, besides the plan's exports", () => {
142
+ const files = generatePlan(plan([query(1, "A"), panel(1, { targets: [declRef("query:1:A")] }), dash(["panel:1"])]));
143
+ expect(file(files, "panels.ts")).toMatch(/export \{ panel1 \};\n$/);
144
+ expect(file(files, "dashboard.ts")).toMatch(/export \{ d \};\n$/);
145
+ });
146
+
147
+ test("a directory holds the plan's modules, and imports stay relative to it", () => {
148
+ const files = generatePlan(plan([panel(1), dash(["panel:1"])], { directory: "my-board" }));
149
+ expect(files.map((f) => f.path)).toEqual(["my-board/panels.ts", "my-board/dashboard.ts"]);
150
+ expect(file(files, "my-board/dashboard.ts")).toContain('import { panel1 } from "./panels";');
151
+ });
152
+
153
+ test("a class the plan declares is written in plugins.ts and imported where it is used", () => {
154
+ const files = generatePlan(
155
+ plan([{ ...panel(1), className: "PiechartPanel", customClass: "panel-class:piechart" }, dash(["panel:1"])], {
156
+ customClasses: [
157
+ {
158
+ id: "panel-class:piechart",
159
+ className: "PiechartPanel",
160
+ factory: "definePanel",
161
+ definition: { type: "piechart", className: "PiechartPanel", defaultSize: { w: 12, h: 8 } },
162
+ comment: ["// not shipped"],
163
+ },
164
+ ],
165
+ }),
166
+ );
167
+ expect(file(files, "plugins.ts")).toBe(
168
+ [
169
+ "/** Classes for plugins chant has no class for */",
170
+ 'import { definePanel } from "@intentius/chant-lexicon-grafana";',
171
+ "",
172
+ "// not shipped",
173
+ "const PiechartPanel = definePanel()({",
174
+ ' type: "piechart",',
175
+ ' className: "PiechartPanel",',
176
+ " defaultSize: { w: 12, h: 8 },",
177
+ "});",
178
+ "",
179
+ "export { PiechartPanel };",
180
+ "",
181
+ ].join("\n"),
182
+ );
183
+ expect(file(files, "panels.ts")).toContain('import { PiechartPanel } from "./plugins";');
184
+ expect(file(files, "panels.ts")).not.toContain("@intentius/chant-lexicon-grafana");
185
+ });
186
+
187
+ test("a reference to something the plan does not declare is an error", () => {
188
+ expect(() => generatePlan(plan([dash(["panel:9"])]))).toThrow(/refers to "panel:9"/);
189
+ });
190
+ });
191
+
192
+ test("an IR with no dashboard (a v2 one, reported by the parser) generates nothing", () => {
193
+ expect(new GrafanaGenerator().generate({ resources: [], parameters: [] })).toEqual([]);
194
+ });
195
+
196
+ test("the generator owns its layout, so core writes its files as they are (#2964)", () => {
197
+ expect(new GrafanaGenerator().ownsLayout).toBe(true);
198
+ });
199
+
200
+ describe("call values (#2954)", () => {
201
+ test("a call is written with the package function imported, its last argument laid out like any value", () => {
202
+ const long = { excludeByName: Object.fromEntries(Array.from({ length: 8 }, (_, i) => [`field number ${i}`, true])) };
203
+ const transformations = [{ id: "merge", options: {} }, callValue("customTransformation", ["sortBy", { fields: {} }]), callValue("customTransformation", ["organize", long])];
204
+ const files = generatePlan(plan([panel(1, { transformations }), dash(["panel:1"])]));
205
+ const text = files.map((f) => f.content).join("\n");
206
+ expect(text).toMatch(/import \{[^}]*customTransformation[^}]*\} from "@intentius\/chant-lexicon-grafana";/);
207
+ expect(text).toContain(' customTransformation("sortBy", { fields: {} }),');
208
+ expect(text).toContain(' customTransformation("organize", {\n excludeByName: {\n "field number 0": true,');
209
+ });
210
+ });