@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,51 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import * as panels from "./panels";
3
+ import { Dashboard } from "./dashboard";
4
+ import { renderDashboard } from "./build";
5
+ import { validateDashboardSchema } from "./schema-validate";
6
+ import { SCHEMA_NAMES } from "./pin";
7
+
8
+ const builtins = panels.registeredPanels().filter((d) => d.builtin);
9
+
10
+ describe("built-in panels", () => {
11
+ test("every panel type Grafana ships with a schema at the pin has a class", () => {
12
+ const panelSchemas = SCHEMA_NAMES.filter((n) => !["dashboard", "prometheus", "tempo", "loki", "expr"].includes(n));
13
+ expect(builtins.map((d) => d.schema).filter(Boolean).sort()).toEqual([...panelSchemas].sort());
14
+ });
15
+
16
+ test("the panels with no upstream schema are typed by hand and not validated beyond the Panel envelope", () => {
17
+ expect(builtins.filter((d) => !d.schema).map((d) => d.type).sort()).toEqual(["alertlist", "flamegraph", "traces"]);
18
+ });
19
+
20
+ test("the node graph keeps Grafana's camelCase id; a plugin chant does not ship may not", () => {
21
+ expect(panels.NodeGraphPanel.definition.type).toBe("nodeGraph");
22
+ expect(() => panels.definePanel()({ type: "myGraph", className: "MyGraph", defaultSize: { w: 1, h: 1 } })).toThrow(/not a panel plugin id/);
23
+ });
24
+
25
+ test.each(builtins.map((d) => [d.className, d] as const))("%s renders a dashboard GRAF107 accepts", (_name, def) => {
26
+ const Cls = (panels as unknown as Record<string, panels.PanelClass>)[def.className];
27
+ const json = renderDashboard(new Dashboard({ title: def.className, panels: [new Cls({ title: "p" })] }));
28
+ expect(json.panels![0]).toMatchObject({ type: def.type, gridPos: { w: def.defaultSize.w, h: def.defaultSize.h } });
29
+ expect(validateDashboardSchema(json as unknown as Record<string, unknown>)).toEqual([]);
30
+ });
31
+
32
+ test("typed options reach the JSON, and a value the schema rejects is a GRAF107 error", () => {
33
+ const pie = new panels.PieChartPanel({ options: { pieType: "donut", sort: "desc", displayLabels: ["percent"] } });
34
+ const ok = renderDashboard(new Dashboard({ title: "Pie", panels: [pie] }));
35
+ expect((ok.panels![0] as { options?: unknown }).options).toEqual({ pieType: "donut", sort: "desc", displayLabels: ["percent"] });
36
+ expect(validateDashboardSchema(ok as unknown as Record<string, unknown>)).toEqual([]);
37
+
38
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
39
+ const bad = new panels.BarGaugePanel({ options: { displayMode: "sparkle" as any } });
40
+ const problems = validateDashboardSchema(renderDashboard(new Dashboard({ title: "Bad", panels: [bad] })) as unknown as Record<string, unknown>);
41
+ expect(problems).toEqual([expect.objectContaining({ path: "/panels/0/options/displayMode", severity: "error" })]);
42
+ });
43
+
44
+ test("the hand-typed alert list options type what Grafana writes", () => {
45
+ const list = new panels.AlertListPanel({ options: { viewMode: "stat", sortOrder: 3, stateFilter: { firing: true, error: true }, folder: null } });
46
+ expect(renderDashboard(new Dashboard({ title: "Alerts", panels: [list] })).panels![0]).toMatchObject({
47
+ type: "alertlist",
48
+ options: { viewMode: "stat", sortOrder: 3, stateFilter: { firing: true, error: true }, folder: null },
49
+ });
50
+ });
51
+ });
package/src/panels.ts CHANGED
@@ -11,8 +11,10 @@
11
11
  *
12
12
  * `gridPos` is optional. A panel without `x` and `y` is placed by the
13
13
  * dashboard: left to right in declaration order, wrapping at Grafana's
14
- * 24-column width, below the previous row of panels. A panel with both is
15
- * placed exactly there, and GRAF105 reports overlaps.
14
+ * 24-column width, below the previous row of panels, and around any cells
15
+ * already taken. A panel with both is placed exactly there and reserves its
16
+ * cells first; GRAF105 reports overlaps between explicit panels. A panel
17
+ * with only `x` or only `y` keeps that column or line.
16
18
  */
17
19
 
18
20
  import { createProperty } from "@intentius/chant/runtime";
@@ -23,7 +25,6 @@ import type { VariableEntity } from "./variables";
23
25
  import type { SchemaName } from "./pin";
24
26
  import type {
25
27
  DashboardLink,
26
- DataTransformerConfig,
27
28
  FieldConfig,
28
29
  FieldConfigSource,
29
30
  GridPos,
@@ -35,6 +36,19 @@ import type * as table from "./schema/table.gen";
35
36
  import type * as logs from "./schema/logs.gen";
36
37
  import type * as heatmap from "./schema/heatmap.gen";
37
38
  import type * as text from "./schema/text.gen";
39
+ import type * as barchart from "./schema/barchart.gen";
40
+ import type * as bargauge from "./schema/bargauge.gen";
41
+ import type * as piechart from "./schema/piechart.gen";
42
+ import type * as statetimeline from "./schema/statetimeline.gen";
43
+ import type * as statushistory from "./schema/statushistory.gen";
44
+ import type * as histogram from "./schema/histogram.gen";
45
+ import type * as nodegraph from "./schema/nodegraph.gen";
46
+ import type * as xychart from "./schema/xychart.gen";
47
+ import type * as trend from "./schema/trend.gen";
48
+ import type * as canvas from "./schema/canvas.gen";
49
+ import type * as geomap from "./schema/geomap.gen";
50
+ import type { AlertListOptions, FlameGraphOptions } from "./panel-options";
51
+ import type { Transformation } from "./transformations";
38
52
 
39
53
  /** `fieldConfig` with `defaults.custom` typed for the panel. */
40
54
  export interface PanelFieldConfig<C> {
@@ -59,7 +73,8 @@ export interface PanelProps<O = Record<string, unknown>, C = Record<string, unkn
59
73
  /** The panel plugin's own options, typed from its schema. */
60
74
  options?: DeepPartial<O>;
61
75
  fieldConfig?: PanelFieldConfig<C>;
62
- transformations?: DataTransformerConfig[];
76
+ /** Typed per transformer: `{ id: "organize", options: { ... } }`, `transformation(id, options)`, or `customTransformation()` for an id or option the types don't know. */
77
+ transformations?: Transformation[];
63
78
  links?: PanelLink[];
64
79
  /** Repeat the panel once per value of this variable. */
65
80
  repeat?: VariableEntity | string;
@@ -118,9 +133,11 @@ export function panelDefinitionFor(type: string): PanelDefinition | undefined {
118
133
  }
119
134
 
120
135
  const PLUGIN_ID = /^[a-z0-9][a-z0-9-_]*$/;
136
+ /** Core panel ids predate the lowercase rule plugin ids follow: the node graph is `nodeGraph`. */
137
+ const CORE_PLUGIN_ID = /^[A-Za-z0-9][A-Za-z0-9-_]*$/;
121
138
 
122
139
  function makePanelClass<T extends string, O, C>(def: PanelDefinition<T>): PanelClass<T, O, C> {
123
- if (!PLUGIN_ID.test(def.type)) throw new Error(`grafana: "${def.type}" is not a panel plugin id`);
140
+ if (!(def.builtin ? CORE_PLUGIN_ID : PLUGIN_ID).test(def.type)) throw new Error(`grafana: "${def.type}" is not a panel plugin id`);
124
141
  if (def.type === "row") throw new Error('grafana: "row" is not a panel; use Row');
125
142
  const existing = registry().get(def.type);
126
143
  if (existing?.builtin && !def.builtin) {
@@ -145,12 +162,12 @@ function makePanelClass<T extends string, O, C>(def: PanelDefinition<T>): PanelC
145
162
  *
146
163
  * @example
147
164
  * ```ts
148
- * interface PieOptions { pieType?: "pie" | "donut"; legend?: { showLegend?: boolean } }
165
+ * interface ClockOptions { mode?: "time" | "countdown"; clockType?: "24 hour" | "12 hour" }
149
166
  *
150
- * export const PieChartPanel = definePanel<PieOptions>()({
151
- * type: "piechart",
152
- * className: "PieChartPanel",
153
- * defaultSize: { w: 8, h: 8 },
167
+ * export const ClockPanel = definePanel<ClockOptions>()({
168
+ * type: "grafana-clock-panel",
169
+ * className: "ClockPanel",
170
+ * defaultSize: { w: 6, h: 4 },
154
171
  * });
155
172
  * ```
156
173
  */
@@ -243,6 +260,121 @@ export const TextPanel = makePanelClass<"text", text.Options, Record<string, unk
243
260
  builtin: true,
244
261
  });
245
262
 
263
+ export const BarChartPanel = makePanelClass<"barchart", barchart.Options, barchart.FieldConfig>({
264
+ type: "barchart",
265
+ className: "BarChartPanel",
266
+ description: "Bar chart: categorical values as bars, grouped or stacked",
267
+ defaultSize: { w: 12, h: 8 },
268
+ schema: "barchart",
269
+ builtin: true,
270
+ });
271
+
272
+ export const BarGaugePanel = makePanelClass<"bargauge", bargauge.Options, Record<string, unknown>>({
273
+ type: "bargauge",
274
+ className: "BarGaugePanel",
275
+ description: "Bar gauge: one bar per series, filled against min, max and thresholds",
276
+ defaultSize: { w: 12, h: 8 },
277
+ schema: "bargauge",
278
+ builtin: true,
279
+ });
280
+
281
+ export const PieChartPanel = makePanelClass<"piechart", piechart.Options, piechart.FieldConfig>({
282
+ type: "piechart",
283
+ className: "PieChartPanel",
284
+ description: "Pie chart: each series' share of the total, as a pie or donut",
285
+ defaultSize: { w: 8, h: 8 },
286
+ schema: "piechart",
287
+ builtin: true,
288
+ });
289
+
290
+ export const StateTimelinePanel = makePanelClass<"state-timeline", statetimeline.Options, statetimeline.FieldConfig>({
291
+ type: "state-timeline",
292
+ className: "StateTimelinePanel",
293
+ description: "State timeline: state changes over time, one lane per series",
294
+ defaultSize: { w: 24, h: 8 },
295
+ schema: "statetimeline",
296
+ builtin: true,
297
+ });
298
+
299
+ export const StatusHistoryPanel = makePanelClass<"status-history", statushistory.Options, statushistory.FieldConfig>({
300
+ type: "status-history",
301
+ className: "StatusHistoryPanel",
302
+ description: "Status history: periodic states over time as a grid of cells",
303
+ defaultSize: { w: 24, h: 8 },
304
+ schema: "statushistory",
305
+ builtin: true,
306
+ });
307
+
308
+ export const HistogramPanel = makePanelClass<"histogram", histogram.Options, histogram.FieldConfig>({
309
+ type: "histogram",
310
+ className: "HistogramPanel",
311
+ description: "Histogram: the distribution of values, bucketed, over the whole time range",
312
+ defaultSize: { w: 12, h: 8 },
313
+ schema: "histogram",
314
+ builtin: true,
315
+ });
316
+
317
+ export const NodeGraphPanel = makePanelClass<"nodeGraph", nodegraph.Options, Record<string, unknown>>({
318
+ type: "nodeGraph",
319
+ className: "NodeGraphPanel",
320
+ description: "Node graph: a directed graph of nodes and edges, e.g. a service map",
321
+ defaultSize: { w: 24, h: 12 },
322
+ schema: "nodegraph",
323
+ builtin: true,
324
+ });
325
+
326
+ export const XYChartPanel = makePanelClass<"xychart", xychart.Options, xychart.FieldConfig>({
327
+ type: "xychart",
328
+ className: "XYChartPanel",
329
+ description: "XY chart: one field plotted against another, as points or lines",
330
+ defaultSize: { w: 12, h: 8 },
331
+ schema: "xychart",
332
+ builtin: true,
333
+ });
334
+
335
+ export const TrendPanel = makePanelClass<"trend", trend.Options, trend.FieldConfig>({
336
+ type: "trend",
337
+ className: "TrendPanel",
338
+ description: "Trend: values against a numeric x field that is not time",
339
+ defaultSize: { w: 12, h: 8 },
340
+ schema: "trend",
341
+ builtin: true,
342
+ });
343
+
344
+ export const CanvasPanel = makePanelClass<"canvas", canvas.Options, Record<string, unknown>>({
345
+ type: "canvas",
346
+ className: "CanvasPanel",
347
+ description: "Canvas: freely placed elements, text, icons and metrics bound to data",
348
+ defaultSize: { w: 12, h: 10 },
349
+ schema: "canvas",
350
+ builtin: true,
351
+ });
352
+
353
+ export const GeomapPanel = makePanelClass<"geomap", geomap.Options, Record<string, unknown>>({
354
+ type: "geomap",
355
+ className: "GeomapPanel",
356
+ description: "Geomap: data on a world map, as markers, heatmaps or routes",
357
+ defaultSize: { w: 12, h: 10 },
358
+ schema: "geomap",
359
+ builtin: true,
360
+ });
361
+
362
+ export const FlameGraphPanel = makePanelClass<"flamegraph", FlameGraphOptions, Record<string, unknown>>({
363
+ type: "flamegraph",
364
+ className: "FlameGraphPanel",
365
+ description: "Flame graph: profiling data, e.g. from Pyroscope, as a flame graph and table",
366
+ defaultSize: { w: 24, h: 12 },
367
+ builtin: true,
368
+ });
369
+
370
+ export const AlertListPanel = makePanelClass<"alertlist", AlertListOptions, Record<string, unknown>>({
371
+ type: "alertlist",
372
+ className: "AlertListPanel",
373
+ description: "Alert list: alert rules and their current state, no queries",
374
+ defaultSize: { w: 8, h: 10 },
375
+ builtin: true,
376
+ });
377
+
246
378
  // ── Rows ────────────────────────────────────────────────────────
247
379
 
248
380
  export interface RowProps {
@@ -256,6 +388,12 @@ export interface RowProps {
256
388
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
257
389
  datasource?: DatasourceInput<any>;
258
390
  id?: number;
391
+ /**
392
+ * The grid line the row header sits on. Without it, the row goes on the
393
+ * first free line below everything declared before it. A row header is
394
+ * always full width and one line high, so `y` is all it takes.
395
+ */
396
+ gridPos?: { y: number };
259
397
  }
260
398
 
261
399
  export interface RowEntity extends Declarable {
package/src/pin.ts CHANGED
@@ -5,12 +5,19 @@
5
5
  * kinds. `grafana/grafana-foundation-sdk` runs them through cog and
6
6
  * publishes, beside its builder libraries, one JSON Schema per kind under
7
7
  * `jsonschema/`. This lexicon vendors the files it uses into `src/spec/schemas/`
8
- * (exact bytes, at the commit below) and generates `src/schema/*.gen.ts`
9
- * from them. Nothing is fetched at build or test time.
8
+ * (exact bytes, at the commit below), applies the correction overlay in
9
+ * `src/spec/overlay/` (see `src/spec/overlay.ts`), and generates
10
+ * `src/schema/*.gen.ts` from the result. Nothing is fetched at build or test
11
+ * time.
12
+ *
13
+ * The types track Grafana 12.4 and 13.x: v0.0.20 is labelled with the
14
+ * v11.6.x kind registry, but its dashboard schema largely matches those
15
+ * releases, and the overlay covers the rest.
10
16
  *
11
17
  * Bumping the pin: change `ref` and `commit`, run `just fetch-schemas`
12
18
  * (downloads and prints the new digests), paste the digests here, run
13
- * `npm run generate`, and review the diff of `src/schema/`.
19
+ * `npm run generate` (it refuses overlay patches the new schemas already
20
+ * carry; delete those), and review the diff of `src/schema/`.
14
21
  */
15
22
 
16
23
  export interface GrafanaSchemaPin {
@@ -20,10 +27,13 @@ export interface GrafanaSchemaPin {
20
27
  ref: string;
21
28
  /** The exact commit the vendored files were taken from. */
22
29
  commit: string;
23
- /** The Grafana kind registry version cog generated the schemas from. */
30
+ /**
31
+ * The kind registry label the SDK publishes the schemas under. It lags the
32
+ * content: the v0.0.20 schemas are labelled v11.6.x and cover 12.4 and 13.x.
33
+ */
24
34
  kindRegistry: string;
25
35
  /** sha256 of each vendored file, keyed by schema name (`src/spec/schemas/<name>.jsonschema.json`). */
26
- files: Readonly<Record<SchemaName, string>>;
36
+ files: Readonly<Record<VendoredSchemaName, string>>;
27
37
  }
28
38
 
29
39
  export const SCHEMA_NAMES = [
@@ -35,13 +45,41 @@ export const SCHEMA_NAMES = [
35
45
  "logs",
36
46
  "heatmap",
37
47
  "text",
48
+ "barchart",
49
+ "bargauge",
50
+ "piechart",
51
+ "statetimeline",
52
+ "statushistory",
53
+ "histogram",
54
+ "nodegraph",
55
+ "xychart",
56
+ "trend",
57
+ "canvas",
58
+ "geomap",
38
59
  "prometheus",
39
60
  "tempo",
40
61
  "loki",
62
+ "expr",
41
63
  ] as const;
42
64
 
43
65
  export type SchemaName = (typeof SCHEMA_NAMES)[number];
44
66
 
67
+ /**
68
+ * Schemas vendored from the same commit for reading, not for types: no
69
+ * `.gen.ts` is generated from them and GRAF107 does not use them, since chant
70
+ * builds classic dashboards. `dashboardv2` is the v2 dashboard kind the
71
+ * importer reads (#2947); `src/import/v2.test.ts` checks the importer knows
72
+ * every key it defines.
73
+ */
74
+ export const IMPORT_ONLY_SCHEMA_NAMES = ["dashboardv2"] as const;
75
+
76
+ export type ImportOnlySchemaName = (typeof IMPORT_ONLY_SCHEMA_NAMES)[number];
77
+
78
+ /** Every schema file under `src/spec/schemas/`. */
79
+ export type VendoredSchemaName = SchemaName | ImportOnlySchemaName;
80
+
81
+ export const VENDORED_SCHEMA_NAMES: readonly VendoredSchemaName[] = [...SCHEMA_NAMES, ...IMPORT_ONLY_SCHEMA_NAMES];
82
+
45
83
  export const GRAFANA_SCHEMA_PIN: GrafanaSchemaPin = Object.freeze({
46
84
  source: "github.com/grafana/grafana-foundation-sdk/jsonschema",
47
85
  ref: "v0.0.20",
@@ -56,13 +94,26 @@ export const GRAFANA_SCHEMA_PIN: GrafanaSchemaPin = Object.freeze({
56
94
  logs: "0254600ef9720bd2c906628304a7c362c0182b4bff2fd7d91352d95a269b2dda",
57
95
  heatmap: "1e1905eef14ada48c121374ecad41412380b317e8183771d99a7504753d5005c",
58
96
  text: "db6d2e3e2c576d1c78ac2e08930f60dbc22bb7ee21abd0cd9204be49f0dbce09",
97
+ barchart: "36f16c10ac3529f88435b81af1d2e352bc915d228a0fb0418d4e46cc2615f6cb",
98
+ bargauge: "dbd22a7b71c4133b880ad39daeaf33c5bf0509fe0d5dc7a882fe3449207b636f",
99
+ piechart: "3edcd85eb8333db755562fc360e392795617582c8d03d11f5918247a4b0ceea9",
100
+ statetimeline: "4df9f488db587b074de51e2b10d6361e3dd390f110aee105029f334b863bd365",
101
+ statushistory: "3149edd7e3c9567913bb123ee764b118c816b9865ab7ec1f19275335b670f4b3",
102
+ histogram: "ef30d7b439b5ece72783be27575b736745aa3eedade37919fcf7560427eb09ac",
103
+ nodegraph: "76595130348310202d87c0efd1e248d60f391cf4cf39c0d3c8d30a251c1f9461",
104
+ xychart: "071b4866d0d292c772adb5946a6cae94cc2486b4a0acabb6247090b5f7a83f9f",
105
+ trend: "599a241e922217be840a65fbf731f7883a3304fd2b6074af01650d325ec0e629",
106
+ canvas: "53e5069cfeef461913cec759c120bc31167b7532be2b69d3c45ce7e4877290d7",
107
+ geomap: "8c8716e9bdbb6b0465b2e91ad1a1100d96a02b7f1be606bd90a6676d94897179",
59
108
  prometheus: "726fc97eeb1e37791dbbf988c3cf40de17bb8e623926e6979c4a3f947cb1af87",
60
109
  tempo: "21aec4c333c9b8e5e9228e85a6225c76161975abb54002e6f12e919e3b4e54ae",
61
110
  loki: "03b81d6b952e3d31785c4b52637e2b7b1c170cc2ea8ec7ec671c21599508e31b",
111
+ expr: "b1f1c1e4f6bf00f0fb4cb32b5bcc1b0e91fad9b7ad9354ecb791322bd97a5564",
112
+ dashboardv2: "a2cfb8b731ff9f48c41f5aa06ade134cf0d93ab9f6f0567a34a4bc39a9fd984e",
62
113
  }),
63
114
  });
64
115
 
65
116
  /** The raw URL of one schema file at the pinned commit. */
66
- export function schemaUrl(name: SchemaName, pin: GrafanaSchemaPin = GRAFANA_SCHEMA_PIN): string {
117
+ export function schemaUrl(name: VendoredSchemaName, pin: GrafanaSchemaPin = GRAFANA_SCHEMA_PIN): string {
67
118
  return `https://raw.githubusercontent.com/grafana/grafana-foundation-sdk/${pin.commit}/jsonschema/${name}.jsonschema.json`;
68
119
  }
@@ -16,7 +16,7 @@ describe("grafana plugin", () => {
16
16
 
17
17
  it("ships lint rules and post-synth checks, all under the GRAF prefix", () => {
18
18
  const ids = [...grafanaPlugin.lintRules!().map((r) => r.id), ...grafanaPlugin.postSynthChecks!().map((c) => c.id)];
19
- expect(ids).toEqual(["GRAF001", "GRAF002", "GRAF101", "GRAF102", "GRAF103", "GRAF104", "GRAF105", "GRAF106", "GRAF107"]);
19
+ expect(ids).toEqual(["GRAF001", "GRAF002", "GRAF101", "GRAF102", "GRAF103", "GRAF104", "GRAF105", "GRAF106", "GRAF107", "GRAF108", "GRAF109", "GRAF111", "GRAF112", "GRAF113", "GRAF114", "GRAF115"]);
20
20
  });
21
21
 
22
22
  it("catalogues every rule and check for chant audit, and nothing else", () => {
@@ -29,13 +29,16 @@ describe("grafana plugin", () => {
29
29
  expect(grafanaPlugin.detectTemplate!({ panels: [], schemaVersion: 41, title: "x" })).toBe(true);
30
30
  expect(grafanaPlugin.detectTemplate!({ apiVersion: 1, datasources: [] })).toBe(true);
31
31
  expect(grafanaPlugin.detectTemplate!({ apiVersion: 1, providers: [] })).toBe(true);
32
+ expect(grafanaPlugin.detectTemplate!({ apiVersion: 1, groups: [{ name: "g", folder: "F", rules: [] }] })).toBe(true);
33
+ expect(grafanaPlugin.detectTemplate!({ apiVersion: 1, contactPoints: [] })).toBe(true);
34
+ expect(grafanaPlugin.detectTemplate!({ groups: [{ name: "g", rules: [{ alert: "A", expr: "up == 0" }] }] })).toBe(false);
32
35
  expect(grafanaPlugin.detectTemplate!({ apiVersion: "v1", kind: "ConfigMap" })).toBe(false);
33
36
  expect(grafanaPlugin.detectTemplate!({ receivers: {}, service: { pipelines: {} } })).toBe(false);
34
37
  });
35
38
 
36
39
  it("loads its skills with content", () => {
37
40
  const skills = grafanaPlugin.skills!();
38
- expect(skills.map((s) => s.name)).toEqual(["chant-grafana", "chant-grafana-provisioning"]);
41
+ expect(skills.map((s) => s.name)).toEqual(["chant-grafana", "chant-grafana-provisioning", "chant-grafana-alerting"]);
39
42
  for (const s of skills) expect(s.content.length).toBeGreaterThan(200);
40
43
  });
41
44
 
package/src/plugin.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { LexiconPlugin } from "@intentius/chant/lexicon";
2
+ import type { OwnershipChannel } from "@intentius/chant/ownership";
2
3
  import type { CompletionContext, HoverContext } from "@intentius/chant/lsp/types";
3
4
  import type { McpResourceContribution } from "@intentius/chant/mcp/types";
4
5
  import { createDiffTool } from "@intentius/chant/lexicon-plugin-helpers";
@@ -9,10 +10,16 @@ import { grafanaAuditCatalog } from "./lint/audit-catalog";
9
10
  import { completions } from "./lsp/completions";
10
11
  import { hover } from "./lsp/hover";
11
12
  import { detectTemplate } from "./detect";
13
+ import { GrafanaParser } from "./import/parser";
14
+ import { GrafanaGenerator } from "./import/generator";
15
+ import { dashboardImporter } from "./import/embedded";
12
16
  import { grafanaSkills } from "./skill-defs";
13
17
  import { BUILTIN_CATALOG } from "./catalog";
14
18
  import { GRAFANA_SCHEMA_PIN } from "./pin";
15
19
  import { compositeCatalog } from "./composites/catalog";
20
+ import { grafanaConfigSchema } from "./config";
21
+ import { grafanaDeepNormalizationHooks } from "./deep-observe-hooks";
22
+ import { GRAFANA_OWNERSHIP_KEYS } from "./ownership";
16
23
 
17
24
  const catalogResource: McpResourceContribution = {
18
25
  uri: "grafana:resource-catalog",
@@ -24,6 +31,23 @@ const catalogResource: McpResourceContribution = {
24
31
  },
25
32
  };
26
33
 
34
+ /**
35
+ * Where chant's marker can be read (#2946, see ./ownership.ts).
36
+ *
37
+ * A dashboard read over `/apis/dashboard.grafana.app` resolves `owned` or
38
+ * `foreign` on all three paths: from chant's managed-by label (stack and env
39
+ * included) when it was written through the API, or from the manager
40
+ * annotation Grafana writes on a dashboard one of the project's providers
41
+ * loaded. What resolves nothing, and says `unknown`, is kind- and
42
+ * server-shaped rather than path-shaped: datasources (their API has no
43
+ * labels) and dashboards on Grafana 11 (read over `/api/dashboards/uid`).
44
+ * An `owned: true` read withholds those as `filtered`.
45
+ */
46
+ const ownershipChannel: OwnershipChannel = {
47
+ keys: GRAFANA_OWNERSHIP_KEYS,
48
+ reads: ["describeResources", "observeResourcesDeep", "exportResources"],
49
+ };
50
+
27
51
  /**
28
52
  * Grafana lexicon plugin.
29
53
  *
@@ -36,6 +60,8 @@ const catalogResource: McpResourceContribution = {
36
60
  export const grafanaPlugin: LexiconPlugin = {
37
61
  name: "grafana",
38
62
  serializer: grafanaSerializer,
63
+ configSchema: grafanaConfigSchema,
64
+ ownershipChannel,
39
65
 
40
66
  // ── Required lifecycle methods ────────────────────────────────
41
67
 
@@ -75,6 +101,12 @@ export const grafanaPlugin: LexiconPlugin = {
75
101
  return rules;
76
102
  },
77
103
 
104
+ // Panels, rows, queries and variables live inside a dashboard, so core
105
+ // lint does not count them as resources (chant #2957).
106
+ propertyClassNames() {
107
+ return BUILTIN_CATALOG.filter((e) => e.entityKind === "property").map((e) => e.className);
108
+ },
109
+
78
110
  postSynthChecks() {
79
111
  return postSynthChecks;
80
112
  },
@@ -101,6 +133,18 @@ export const grafanaPlugin: LexiconPlugin = {
101
133
  return detectTemplate(data);
102
134
  },
103
135
 
136
+ templateParser() {
137
+ return new GrafanaParser();
138
+ },
139
+
140
+ templateGenerator() {
141
+ return new GrafanaGenerator();
142
+ },
143
+
144
+ embeddedImporters() {
145
+ return [dashboardImporter];
146
+ },
147
+
104
148
  completionProvider(ctx: CompletionContext) {
105
149
  return completions(ctx);
106
150
  },
@@ -109,6 +153,28 @@ export const grafanaPlugin: LexiconPlugin = {
109
153
  return hover(ctx);
110
154
  },
111
155
 
156
+ // ── Live observation and export (#2946) ──────────────────────
157
+ // Behind dynamic imports, so `chant build` never loads a transport. The
158
+ // hooks are static: core normalizes the declared tree with them whether or
159
+ // not a live read happens.
160
+
161
+ async describeResources(options) {
162
+ const { describeResources } = await import("./describe-resources");
163
+ return describeResources(options);
164
+ },
165
+
166
+ async observeResourcesDeep(options) {
167
+ const { observeResourcesDeepGrafana } = await import("./deep-observe");
168
+ return observeResourcesDeepGrafana(options);
169
+ },
170
+
171
+ deepNormalizationHooks: grafanaDeepNormalizationHooks,
172
+
173
+ async exportResources(options) {
174
+ const { exportResources } = await import("./export-resources");
175
+ return exportResources(options);
176
+ },
177
+
112
178
  async docs(options?: { verbose?: boolean }) {
113
179
  const { generateDocs } = await import("./codegen/docs");
114
180
  return generateDocs(options);
@@ -0,0 +1,108 @@
1
+ /**
2
+ * GRAF108's pieces: template-variable substitution, the PromQL inside a
3
+ * Prometheus query variable, and which queries of a dashboard get parsed.
4
+ * The check itself, over built and hand-written dashboards, is in
5
+ * lint/post-synth/post-synth.test.ts.
6
+ */
7
+ import { describe, expect, test } from "vitest";
8
+ import { checkGrafanaPromql, prometheusQueries, substituteTemplateVariables, variablePromql } from "./promql-check";
9
+ import { knownDatasources } from "./datasource-refs";
10
+
11
+ describe("substituteTemplateVariables", () => {
12
+ test.each([
13
+ ['sum(rate(x{a=~"$a"}[$__rate_interval]))', 'sum(rate(x{a=~"$a"}[5m]))'],
14
+ ["rate(x[$__interval:$__interval])", "rate(x[5m:5m])"],
15
+ ["rate(x[${window}]) offset $shift", "rate(x[5m]) offset 5m"],
16
+ ["rate(x[${minutes}m])", "rate(x[1m])"],
17
+ ['$metric{job="a"} / $__range_s', 'grafana_var{job="a"} / 1'],
18
+ ["sum by ($group) (x) * $__interval_ms", "sum by (grafana_var) (x) * 1"],
19
+ ["x @ ${__to:date:seconds}", "x @ 1"],
20
+ ["rate([[metric]][[[w]]])", "rate(grafana_var[5m])"],
21
+ ["topk(${n:raw}, x)", "topk(grafana_var, x)"],
22
+ ["x{a='$a', b=`$b`}", "x{a='$a', b=`$b`}"],
23
+ ['x{a="\\"$a"}[$__range]', 'x{a="\\"$a"}[5m]'],
24
+ ])("%s", (query, text) => {
25
+ expect(substituteTemplateVariables(query).text).toBe(text);
26
+ });
27
+
28
+ test("maps offsets in the substituted text back to the query", () => {
29
+ const s = substituteTemplateVariables("rate(x[$__rate_interval]) +");
30
+ expect(s.text).toBe("rate(x[5m]) +");
31
+ expect(s.originalOffset(7)).toBe(7);
32
+ expect(s.originalOffset(8)).toBe(7);
33
+ expect(s.originalOffset(11)).toBe(25);
34
+ });
35
+ });
36
+
37
+ describe("checkGrafanaPromql", () => {
38
+ test("passes the queries Grafana's own dashboards use", () => {
39
+ for (const q of [
40
+ 'histogram_quantile(0.99, sum by (le) (rate(http_request_duration_seconds_bucket{route=~"$route"}[$__rate_interval])))',
41
+ 'sum by (route) (rate(http_requests_total{job=~"$job", env=~"$env"}[$window]))',
42
+ "sum(increase(x[$__range])) / ($__range_s)",
43
+ 'up{instance=~"[[instance]]"}',
44
+ ]) {
45
+ expect({ q, r: checkGrafanaPromql(q) }).toEqual({ q, r: { ok: true } });
46
+ }
47
+ });
48
+
49
+ test("reports an unbalanced expression against the original text", () => {
50
+ const r = checkGrafanaPromql('sum(x{cluster="$cluster"}');
51
+ expect(r).toEqual({ ok: false, message: expect.stringContaining('ends early (after "sum(x{cluster="$cluster"}")') });
52
+ });
53
+
54
+ test("reports the offset in the query as written, not as substituted", () => {
55
+ const r = checkGrafanaPromql("rate(x[$__rate_interval]) by");
56
+ expect(r.ok).toBe(false);
57
+ if (!r.ok) expect(r.message).toMatch(/offset 2\d: "rate\(x\[\$__rate_interval\]\) ?" >>> /);
58
+ });
59
+ });
60
+
61
+ describe("variablePromql", () => {
62
+ test.each([
63
+ ["label_names()", []],
64
+ ['label_names(up{job="a"})', ['up{job="a"}']],
65
+ ["label_values(job)", []],
66
+ ['label_values(up{env="$env"}, job)', ['up{env="$env"}']],
67
+ ["metrics(^http_.*)", []],
68
+ ["query_result(topk(5, sum by (job) (up)))", ["topk(5, sum by (job) (up))"]],
69
+ ['up{job="a"}', ['up{job="a"}']],
70
+ [" ", []],
71
+ ])("%s", (query, promql) => {
72
+ expect(variablePromql(query)).toEqual(promql);
73
+ });
74
+ });
75
+
76
+ describe("prometheusQueries", () => {
77
+ const known = knownDatasources([
78
+ { name: "Prometheus", type: "prometheus", uid: "prom" },
79
+ { name: "Loki", type: "loki", uid: "loki" },
80
+ ]);
81
+ const dashboard = {
82
+ templating: {
83
+ list: [
84
+ { type: "datasource", name: "ds", query: "prometheus" },
85
+ { type: "query", name: "job", datasource: { type: "prometheus", uid: "prom" }, query: { query: "label_values(up, job)", refId: "V" } },
86
+ { type: "query", name: "stream", datasource: { type: "loki", uid: "loki" }, query: "label_values(app)" },
87
+ ],
88
+ },
89
+ panels: [
90
+ { id: 1, title: "a", type: "stat", datasource: { type: "prometheus", uid: "prom" }, targets: [{ refId: "A", expr: "up" }, { refId: "B", expr: "" }] },
91
+ { id: 2, title: "b", type: "logs", datasource: { type: "loki", uid: "loki" }, targets: [{ refId: "A", expr: '{app="x"} |= "error"' }] },
92
+ { id: 3, title: "c", type: "stat", datasource: { type: "prometheus", uid: "${ds}" }, targets: [{ refId: "A", expr: "sum(up)" }] },
93
+ { id: 4, title: "d", type: "stat", targets: [{ refId: "A", expr: "no datasource" }] },
94
+ { id: 5, title: "e", type: "stat", datasource: { type: "datasource", uid: "-- Mixed --" }, targets: [{ refId: "A", expr: "x", datasource: { type: "loki", uid: "loki" } }, { refId: "B", expr: "y", datasource: { type: "prometheus", uid: "prom" } }] },
95
+ ],
96
+ __elements: { lib: { name: "lib", kind: 1, model: { type: "stat", datasource: { type: "prometheus", uid: "prom" }, targets: [{ refId: "A", expr: "rate(x[5m])" }] } } },
97
+ };
98
+
99
+ test("takes only the queries that reach a Prometheus, including library panels", () => {
100
+ expect(prometheusQueries(dashboard, known)).toEqual([
101
+ { where: 'panel "a" (id 1) query A', expr: "up" },
102
+ { where: 'panel "c" (id 3) query A', expr: "sum(up)" },
103
+ { where: 'panel "e" (id 5) query B', expr: "y" },
104
+ { where: 'variable "job" query', expr: "up" },
105
+ { where: 'library panel "lib" query A', expr: "rate(x[5m])" },
106
+ ]);
107
+ });
108
+ });