@intentius/chant-lexicon-grafana 0.98.0 → 0.100.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 (532) hide show
  1. package/README.md +24 -8
  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/annotations.d.ts +61 -0
  11. package/dist/annotations.d.ts.map +1 -0
  12. package/dist/api/apply.d.ts +160 -0
  13. package/dist/api/apply.d.ts.map +1 -0
  14. package/dist/api/bind.d.ts +32 -0
  15. package/dist/api/bind.d.ts.map +1 -0
  16. package/dist/api/client.d.ts +76 -0
  17. package/dist/api/client.d.ts.map +1 -0
  18. package/dist/api/converge.d.ts +33 -0
  19. package/dist/api/converge.d.ts.map +1 -0
  20. package/dist/api/dashboards.d.ts +102 -0
  21. package/dist/api/dashboards.d.ts.map +1 -0
  22. package/dist/api/datasources.d.ts +26 -0
  23. package/dist/api/datasources.d.ts.map +1 -0
  24. package/dist/api/fake-grafana-writes.d.ts +41 -0
  25. package/dist/api/fake-grafana-writes.d.ts.map +1 -0
  26. package/dist/api/fake-grafana.d.ts +63 -0
  27. package/dist/api/fake-grafana.d.ts.map +1 -0
  28. package/dist/api/folders.d.ts +146 -0
  29. package/dist/api/folders.d.ts.map +1 -0
  30. package/dist/api/library-panels.d.ts +52 -0
  31. package/dist/api/library-panels.d.ts.map +1 -0
  32. package/dist/build.d.ts +55 -6
  33. package/dist/build.d.ts.map +1 -1
  34. package/dist/catalog.d.ts +1 -1
  35. package/dist/catalog.d.ts.map +1 -1
  36. package/dist/codegen/docs.d.ts.map +1 -1
  37. package/dist/codegen/generate.d.ts +7 -1
  38. package/dist/codegen/generate.d.ts.map +1 -1
  39. package/dist/codegen/schema-types.d.ts +3 -3
  40. package/dist/codegen/schema-types.d.ts.map +1 -1
  41. package/dist/composites/agent-dashboard.d.ts.map +1 -1
  42. package/dist/composites/catalog.d.ts.map +1 -1
  43. package/dist/composites/index.d.ts +4 -2
  44. package/dist/composites/index.d.ts.map +1 -1
  45. package/dist/composites/red-dashboard.d.ts +21 -2
  46. package/dist/composites/red-dashboard.d.ts.map +1 -1
  47. package/dist/composites/shared.d.ts +11 -3
  48. package/dist/composites/shared.d.ts.map +1 -1
  49. package/dist/composites/slo-alert-rules.d.ts +92 -0
  50. package/dist/composites/slo-alert-rules.d.ts.map +1 -0
  51. package/dist/config.d.ts +111 -0
  52. package/dist/config.d.ts.map +1 -0
  53. package/dist/contact-point-secrets.d.ts +17 -0
  54. package/dist/contact-point-secrets.d.ts.map +1 -0
  55. package/dist/dashboard.d.ts +51 -9
  56. package/dist/dashboard.d.ts.map +1 -1
  57. package/dist/datasource-refs.d.ts +99 -0
  58. package/dist/datasource-refs.d.ts.map +1 -0
  59. package/dist/datasource-settings.d.ts +834 -0
  60. package/dist/datasource-settings.d.ts.map +1 -0
  61. package/dist/datasource.d.ts +82 -11
  62. package/dist/datasource.d.ts.map +1 -1
  63. package/dist/deep-observe-hooks.d.ts +64 -0
  64. package/dist/deep-observe-hooks.d.ts.map +1 -0
  65. package/dist/deep-observe.d.ts +47 -0
  66. package/dist/deep-observe.d.ts.map +1 -0
  67. package/dist/describe-resources.d.ts +50 -0
  68. package/dist/describe-resources.d.ts.map +1 -0
  69. package/dist/detect.d.ts +32 -3
  70. package/dist/detect.d.ts.map +1 -1
  71. package/dist/export-resources.d.ts +38 -0
  72. package/dist/export-resources.d.ts.map +1 -0
  73. package/dist/folder.d.ts +68 -0
  74. package/dist/folder.d.ts.map +1 -0
  75. package/dist/import/alerting.d.ts +40 -0
  76. package/dist/import/alerting.d.ts.map +1 -0
  77. package/dist/import/edits.d.ts +47 -0
  78. package/dist/import/edits.d.ts.map +1 -0
  79. package/dist/import/embedded.d.ts +14 -0
  80. package/dist/import/embedded.d.ts.map +1 -0
  81. package/dist/import/generator.d.ts +60 -0
  82. package/dist/import/generator.d.ts.map +1 -0
  83. package/dist/import/live-export.d.ts +108 -0
  84. package/dist/import/live-export.d.ts.map +1 -0
  85. package/dist/import/mappings.d.ts +89 -0
  86. package/dist/import/mappings.d.ts.map +1 -0
  87. package/dist/import/model.d.ts +105 -0
  88. package/dist/import/model.d.ts.map +1 -0
  89. package/dist/import/normalize-alerting.d.ts +26 -0
  90. package/dist/import/normalize-alerting.d.ts.map +1 -0
  91. package/dist/import/normalize.d.ts +74 -0
  92. package/dist/import/normalize.d.ts.map +1 -0
  93. package/dist/import/parser.d.ts +125 -0
  94. package/dist/import/parser.d.ts.map +1 -0
  95. package/dist/import/report.d.ts +26 -0
  96. package/dist/import/report.d.ts.map +1 -0
  97. package/dist/import/testdata/fixtures.d.ts +41 -0
  98. package/dist/import/testdata/fixtures.d.ts.map +1 -0
  99. package/dist/import/v2.d.ts +73 -0
  100. package/dist/import/v2.d.ts.map +1 -0
  101. package/dist/index.d.ts +31 -12
  102. package/dist/index.d.ts.map +1 -1
  103. package/dist/init-templates.d.ts +27 -0
  104. package/dist/init-templates.d.ts.map +1 -0
  105. package/dist/integrity.json +22 -12
  106. package/dist/k8s.d.ts +107 -0
  107. package/dist/k8s.d.ts.map +1 -0
  108. package/dist/lint/audit-catalog.d.ts +2 -1
  109. package/dist/lint/audit-catalog.d.ts.map +1 -1
  110. package/dist/lint/post-synth/graf101.d.ts +2 -2
  111. package/dist/lint/post-synth/graf102.d.ts +1 -1
  112. package/dist/lint/post-synth/graf104.d.ts +2 -2
  113. package/dist/lint/post-synth/graf106.d.ts +2 -2
  114. package/dist/lint/post-synth/graf107.d.ts +1 -1
  115. package/dist/lint/post-synth/graf108.d.ts +8 -0
  116. package/dist/lint/post-synth/graf108.d.ts.map +1 -0
  117. package/dist/lint/post-synth/graf109.d.ts +8 -0
  118. package/dist/lint/post-synth/graf109.d.ts.map +1 -0
  119. package/dist/lint/post-synth/graf110.d.ts +8 -0
  120. package/dist/lint/post-synth/graf110.d.ts.map +1 -0
  121. package/dist/lint/post-synth/graf111.d.ts +8 -0
  122. package/dist/lint/post-synth/graf111.d.ts.map +1 -0
  123. package/dist/lint/post-synth/graf112.d.ts +8 -0
  124. package/dist/lint/post-synth/graf112.d.ts.map +1 -0
  125. package/dist/lint/post-synth/graf113.d.ts +8 -0
  126. package/dist/lint/post-synth/graf113.d.ts.map +1 -0
  127. package/dist/lint/post-synth/graf114.d.ts +8 -0
  128. package/dist/lint/post-synth/graf114.d.ts.map +1 -0
  129. package/dist/lint/post-synth/graf115.d.ts +8 -0
  130. package/dist/lint/post-synth/graf115.d.ts.map +1 -0
  131. package/dist/lint/post-synth/grafana-helpers.d.ts +7 -5
  132. package/dist/lint/post-synth/grafana-helpers.d.ts.map +1 -1
  133. package/dist/lint/post-synth/index.d.ts.map +1 -1
  134. package/dist/lint/rules/literal-secret.d.ts +7 -3
  135. package/dist/lint/rules/literal-secret.d.ts.map +1 -1
  136. package/dist/lsp/hover.d.ts.map +1 -1
  137. package/dist/manifest.json +1 -1
  138. package/dist/members.d.ts +27 -0
  139. package/dist/members.d.ts.map +1 -0
  140. package/dist/meta.json +205 -0
  141. package/dist/okf/index.md +53 -4
  142. package/dist/okf/rules/GRAF002.md +3 -2
  143. package/dist/okf/rules/GRAF101.md +3 -2
  144. package/dist/okf/rules/GRAF102.md +1 -0
  145. package/dist/okf/rules/GRAF104.md +2 -2
  146. package/dist/okf/rules/GRAF106.md +2 -2
  147. package/dist/okf/rules/GRAF108.md +11 -0
  148. package/dist/okf/rules/GRAF109.md +15 -0
  149. package/dist/okf/rules/GRAF110.md +11 -0
  150. package/dist/okf/rules/GRAF111.md +11 -0
  151. package/dist/okf/rules/GRAF112.md +16 -0
  152. package/dist/okf/rules/GRAF113.md +16 -0
  153. package/dist/okf/rules/GRAF114.md +11 -0
  154. package/dist/okf/rules/GRAF115.md +11 -0
  155. package/dist/okf/types/AdhocVariable.md +9 -0
  156. package/dist/okf/types/AlertListPanel.md +9 -0
  157. package/dist/okf/types/AlertQuery.md +9 -0
  158. package/dist/okf/types/AlertRule.md +9 -0
  159. package/dist/okf/types/AlertRuleGroup.md +9 -0
  160. package/dist/okf/types/AzureMonitorQuery.md +9 -0
  161. package/dist/okf/types/BarChartPanel.md +9 -0
  162. package/dist/okf/types/BarGaugePanel.md +9 -0
  163. package/dist/okf/types/BigQueryQuery.md +9 -0
  164. package/dist/okf/types/CanvasPanel.md +9 -0
  165. package/dist/okf/types/ClassicConditionsExpression.md +9 -0
  166. package/dist/okf/types/CloudMonitoringQuery.md +9 -0
  167. package/dist/okf/types/CloudWatchQuery.md +9 -0
  168. package/dist/okf/types/ContactPoint.md +14 -0
  169. package/dist/okf/types/Dashboard.md +2 -1
  170. package/dist/okf/types/Datasource.md +3 -2
  171. package/dist/okf/types/DatasourceProvisioning.md +9 -0
  172. package/dist/okf/types/ElasticsearchQuery.md +9 -0
  173. package/dist/okf/types/ExternalDatasource.md +15 -0
  174. package/dist/okf/types/FlameGraphPanel.md +9 -0
  175. package/dist/okf/types/Folder.md +9 -0
  176. package/dist/okf/types/GeomapPanel.md +9 -0
  177. package/dist/okf/types/GroupByVariable.md +9 -0
  178. package/dist/okf/types/HistogramPanel.md +9 -0
  179. package/dist/okf/types/MSSQLQuery.md +9 -0
  180. package/dist/okf/types/MathExpression.md +9 -0
  181. package/dist/okf/types/MuteTiming.md +13 -0
  182. package/dist/okf/types/MySQLQuery.md +9 -0
  183. package/dist/okf/types/NodeGraphPanel.md +9 -0
  184. package/dist/okf/types/NotificationPolicy.md +9 -0
  185. package/dist/okf/types/NotificationTemplate.md +9 -0
  186. package/dist/okf/types/PieChartPanel.md +9 -0
  187. package/dist/okf/types/PostgresQuery.md +9 -0
  188. package/dist/okf/types/PyroscopeQuery.md +9 -0
  189. package/dist/okf/types/ReduceExpression.md +9 -0
  190. package/dist/okf/types/ResampleExpression.md +9 -0
  191. package/dist/okf/types/SqlExpression.md +9 -0
  192. package/dist/okf/types/StateTimelinePanel.md +9 -0
  193. package/dist/okf/types/StatusHistoryPanel.md +9 -0
  194. package/dist/okf/types/SwitchVariable.md +9 -0
  195. package/dist/okf/types/ThresholdExpression.md +9 -0
  196. package/dist/okf/types/TrendPanel.md +9 -0
  197. package/dist/okf/types/XYChartPanel.md +9 -0
  198. package/dist/op/activities/grafana-apply.d.ts +86 -0
  199. package/dist/op/activities/grafana-apply.d.ts.map +1 -0
  200. package/dist/op/activities/index.d.ts +9 -0
  201. package/dist/op/activities/index.d.ts.map +1 -0
  202. package/dist/op/builders.d.ts +24 -0
  203. package/dist/op/builders.d.ts.map +1 -0
  204. package/dist/ownership.d.ts +78 -0
  205. package/dist/ownership.d.ts.map +1 -0
  206. package/dist/panel-options.d.ts +65 -0
  207. package/dist/panel-options.d.ts.map +1 -0
  208. package/dist/panels.d.ts +46 -9
  209. package/dist/panels.d.ts.map +1 -1
  210. package/dist/pin.d.ts +29 -7
  211. package/dist/pin.d.ts.map +1 -1
  212. package/dist/plugin.d.ts.map +1 -1
  213. package/dist/promql-check.d.ts +51 -0
  214. package/dist/promql-check.d.ts.map +1 -0
  215. package/dist/query-models.d.ts +95 -0
  216. package/dist/query-models.d.ts.map +1 -0
  217. package/dist/query.d.ts +116 -18
  218. package/dist/query.d.ts.map +1 -1
  219. package/dist/rules/graf101.ts +3 -3
  220. package/dist/rules/graf102.ts +1 -1
  221. package/dist/rules/graf104.ts +3 -3
  222. package/dist/rules/graf106.ts +3 -3
  223. package/dist/rules/graf107.ts +1 -1
  224. package/dist/rules/graf108.ts +17 -0
  225. package/dist/rules/graf109.ts +17 -0
  226. package/dist/rules/graf110.ts +17 -0
  227. package/dist/rules/graf111.ts +17 -0
  228. package/dist/rules/graf112.ts +17 -0
  229. package/dist/rules/graf113.ts +17 -0
  230. package/dist/rules/graf114.ts +17 -0
  231. package/dist/rules/graf115.ts +17 -0
  232. package/dist/rules/grafana-helpers.ts +27 -10
  233. package/dist/rules/literal-secret.ts +60 -6
  234. package/dist/schema/azuremonitor.gen.d.ts +354 -0
  235. package/dist/schema/azuremonitor.gen.d.ts.map +1 -0
  236. package/dist/schema/barchart.gen.d.ts +198 -0
  237. package/dist/schema/barchart.gen.d.ts.map +1 -0
  238. package/dist/schema/bargauge.gen.d.ts +97 -0
  239. package/dist/schema/bargauge.gen.d.ts.map +1 -0
  240. package/dist/schema/bigquery.gen.d.ts +93 -0
  241. package/dist/schema/bigquery.gen.d.ts.map +1 -0
  242. package/dist/schema/canvas.gen.d.ts +152 -0
  243. package/dist/schema/canvas.gen.d.ts.map +1 -0
  244. package/dist/schema/cloudwatch.gen.d.ts +394 -0
  245. package/dist/schema/cloudwatch.gen.d.ts.map +1 -0
  246. package/dist/schema/dashboard.gen.d.ts +196 -21
  247. package/dist/schema/dashboard.gen.d.ts.map +1 -1
  248. package/dist/schema/elasticsearch.gen.d.ts +444 -0
  249. package/dist/schema/elasticsearch.gen.d.ts.map +1 -0
  250. package/dist/schema/expr.gen.d.ts +606 -0
  251. package/dist/schema/expr.gen.d.ts.map +1 -0
  252. package/dist/schema/gauge.gen.d.ts.map +1 -1
  253. package/dist/schema/geomap.gen.d.ts +100 -0
  254. package/dist/schema/geomap.gen.d.ts.map +1 -0
  255. package/dist/schema/googlecloudmonitoring.gen.d.ts +312 -0
  256. package/dist/schema/googlecloudmonitoring.gen.d.ts.map +1 -0
  257. package/dist/schema/grafanapyroscope.gen.d.ts +60 -0
  258. package/dist/schema/grafanapyroscope.gen.d.ts.map +1 -0
  259. package/dist/schema/heatmap.gen.d.ts.map +1 -1
  260. package/dist/schema/histogram.gen.d.ts +132 -0
  261. package/dist/schema/histogram.gen.d.ts.map +1 -0
  262. package/dist/schema/index.d.ts +18 -0
  263. package/dist/schema/index.d.ts.map +1 -1
  264. package/dist/schema/logs.gen.d.ts +1 -0
  265. package/dist/schema/logs.gen.d.ts.map +1 -1
  266. package/dist/schema/loki.gen.d.ts.map +1 -1
  267. package/dist/schema/nodegraph.gen.d.ts +49 -0
  268. package/dist/schema/nodegraph.gen.d.ts.map +1 -0
  269. package/dist/schema/piechart.gen.d.ts +127 -0
  270. package/dist/schema/piechart.gen.d.ts.map +1 -0
  271. package/dist/schema/prometheus.gen.d.ts.map +1 -1
  272. package/dist/schema/stat.gen.d.ts.map +1 -1
  273. package/dist/schema/statetimeline.gen.d.ts +124 -0
  274. package/dist/schema/statetimeline.gen.d.ts.map +1 -0
  275. package/dist/schema/statushistory.gen.d.ts +114 -0
  276. package/dist/schema/statushistory.gen.d.ts.map +1 -0
  277. package/dist/schema/table.gen.d.ts +35 -3
  278. package/dist/schema/table.gen.d.ts.map +1 -1
  279. package/dist/schema/tempo.gen.d.ts.map +1 -1
  280. package/dist/schema/text.gen.d.ts.map +1 -1
  281. package/dist/schema/timeseries.gen.d.ts +5 -1
  282. package/dist/schema/timeseries.gen.d.ts.map +1 -1
  283. package/dist/schema/trend.gen.d.ts +180 -0
  284. package/dist/schema/trend.gen.d.ts.map +1 -0
  285. package/dist/schema/xychart.gen.d.ts +145 -0
  286. package/dist/schema/xychart.gen.d.ts.map +1 -0
  287. package/dist/schema-validate.d.ts +39 -4
  288. package/dist/schema-validate.d.ts.map +1 -1
  289. package/dist/serializer.d.ts +11 -3
  290. package/dist/serializer.d.ts.map +1 -1
  291. package/dist/skill-defs.d.ts.map +1 -1
  292. package/dist/skills/chant-grafana-alerting.md +49 -0
  293. package/dist/skills/chant-grafana-operations.md +108 -0
  294. package/dist/skills/chant-grafana-provisioning.md +3 -3
  295. package/dist/skills/chant-grafana.md +15 -8
  296. package/dist/spec/fetch-units-cli.d.ts +3 -0
  297. package/dist/spec/fetch-units-cli.d.ts.map +1 -0
  298. package/dist/spec/overlay.d.ts +43 -0
  299. package/dist/spec/overlay.d.ts.map +1 -0
  300. package/dist/spec/schemas.d.ts +21 -13
  301. package/dist/spec/schemas.d.ts.map +1 -1
  302. package/dist/spec/schemas.gen.d.ts +3 -0
  303. package/dist/spec/schemas.gen.d.ts.map +1 -0
  304. package/dist/spec/units.d.ts +35 -0
  305. package/dist/spec/units.d.ts.map +1 -0
  306. package/dist/spec/units.gen.d.ts +2 -0
  307. package/dist/spec/units.gen.d.ts.map +1 -0
  308. package/dist/transformations.d.ts +459 -0
  309. package/dist/transformations.d.ts.map +1 -0
  310. package/dist/util.d.ts +6 -0
  311. package/dist/util.d.ts.map +1 -1
  312. package/dist/validate-alerting.d.ts +65 -0
  313. package/dist/validate-alerting.d.ts.map +1 -0
  314. package/dist/validate-output.d.ts +62 -9
  315. package/dist/validate-output.d.ts.map +1 -1
  316. package/dist/validate.d.ts.map +1 -1
  317. package/dist/validation.d.ts +14 -0
  318. package/dist/validation.d.ts.map +1 -0
  319. package/dist/variables.d.ts +109 -7
  320. package/dist/variables.d.ts.map +1 -1
  321. package/package.json +12 -4
  322. package/src/actions/actions.test.ts +24 -0
  323. package/src/actions/grafana.ts +40 -0
  324. package/src/actions/index.ts +1 -0
  325. package/src/alerting-build.ts +376 -0
  326. package/src/alerting.e2e.test.ts +212 -0
  327. package/src/alerting.test.ts +272 -0
  328. package/src/alerting.ts +435 -0
  329. package/src/annotations.ts +84 -0
  330. package/src/api/apply.test.ts +256 -0
  331. package/src/api/apply.ts +394 -0
  332. package/src/api/bind.ts +58 -0
  333. package/src/api/client.ts +148 -0
  334. package/src/api/converge.ts +63 -0
  335. package/src/api/dashboards.ts +247 -0
  336. package/src/api/datasources.ts +33 -0
  337. package/src/api/fake-grafana-writes.ts +201 -0
  338. package/src/api/fake-grafana.ts +190 -0
  339. package/src/api/folders.ts +339 -0
  340. package/src/api/library-panels.ts +101 -0
  341. package/src/build.test.ts +351 -10
  342. package/src/build.ts +294 -55
  343. package/src/catalog.ts +104 -2
  344. package/src/codegen/docs.ts +50 -10
  345. package/src/codegen/generate.ts +27 -1
  346. package/src/codegen/schema-types.ts +16 -6
  347. package/src/composites/agent-dashboard.ts +6 -3
  348. package/src/composites/catalog.test.ts +2 -2
  349. package/src/composites/catalog.ts +82 -0
  350. package/src/composites/composites.test.ts +73 -9
  351. package/src/composites/index.ts +4 -2
  352. package/src/composites/queries.e2e.test.ts +374 -0
  353. package/src/composites/red-dashboard.ts +83 -12
  354. package/src/composites/shared.ts +14 -2
  355. package/src/composites/slo-alert-rules.ts +149 -0
  356. package/src/config.test.ts +61 -0
  357. package/src/config.ts +138 -0
  358. package/src/contact-point-secrets.ts +63 -0
  359. package/src/dashboard.ts +68 -9
  360. package/src/datasource-refs.test.ts +56 -0
  361. package/src/datasource-refs.ts +213 -0
  362. package/src/datasource-settings.test.ts +106 -0
  363. package/src/datasource-settings.ts +941 -0
  364. package/src/datasource.ts +123 -11
  365. package/src/deep-observe-hooks.ts +267 -0
  366. package/src/deep-observe.test.ts +311 -0
  367. package/src/deep-observe.ts +187 -0
  368. package/src/describe-resources.test.ts +271 -0
  369. package/src/describe-resources.ts +244 -0
  370. package/src/detect.ts +82 -5
  371. package/src/export-resources.test.ts +129 -0
  372. package/src/export-resources.ts +84 -0
  373. package/src/folder.ts +118 -0
  374. package/src/generated/lexicon-grafana.json +205 -0
  375. package/src/import/alerting-roundtrip.test.ts +161 -0
  376. package/src/import/alerting.ts +500 -0
  377. package/src/import/edits.ts +95 -0
  378. package/src/import/embedded.test.ts +45 -0
  379. package/src/import/embedded.ts +47 -0
  380. package/src/import/generated-types.e2e.test.ts +130 -0
  381. package/src/import/generator.test.ts +210 -0
  382. package/src/import/generator.ts +463 -0
  383. package/src/import/live-export.ts +264 -0
  384. package/src/import/mappings.ts +336 -0
  385. package/src/import/model.ts +117 -0
  386. package/src/import/normalize-alerting.ts +101 -0
  387. package/src/import/normalize.test.ts +117 -0
  388. package/src/import/normalize.ts +393 -0
  389. package/src/import/parser.test.ts +516 -0
  390. package/src/import/parser.ts +1387 -0
  391. package/src/import/report.ts +62 -0
  392. package/src/import/roundtrip.test.ts +563 -0
  393. package/src/import/testdata/fixtures.ts +137 -0
  394. package/src/import/v2.test.ts +292 -0
  395. package/src/import/v2.ts +804 -0
  396. package/src/import.e2e.test.ts +31 -84
  397. package/src/import.test.ts +9 -3
  398. package/src/index.ts +308 -14
  399. package/src/init-templates.test.ts +74 -0
  400. package/src/init-templates.ts +349 -0
  401. package/src/k8s.test.ts +98 -0
  402. package/src/k8s.ts +197 -0
  403. package/src/lint/audit-catalog.ts +68 -3
  404. package/src/lint/core-lint.test.ts +43 -0
  405. package/src/lint/post-synth/graf101.ts +3 -3
  406. package/src/lint/post-synth/graf102.ts +1 -1
  407. package/src/lint/post-synth/graf104.ts +3 -3
  408. package/src/lint/post-synth/graf106.ts +3 -3
  409. package/src/lint/post-synth/graf107.ts +1 -1
  410. package/src/lint/post-synth/graf108.ts +17 -0
  411. package/src/lint/post-synth/graf109.ts +17 -0
  412. package/src/lint/post-synth/graf110.ts +17 -0
  413. package/src/lint/post-synth/graf111.ts +17 -0
  414. package/src/lint/post-synth/graf112.ts +17 -0
  415. package/src/lint/post-synth/graf113.ts +17 -0
  416. package/src/lint/post-synth/graf114.ts +17 -0
  417. package/src/lint/post-synth/graf115.ts +17 -0
  418. package/src/lint/post-synth/grafana-helpers.ts +27 -10
  419. package/src/lint/post-synth/index.ts +16 -0
  420. package/src/lint/post-synth/post-synth.test.ts +333 -12
  421. package/src/lint/rules/literal-secret.ts +60 -6
  422. package/src/lint/rules/rules.test.ts +27 -0
  423. package/src/load-cost.test.ts +35 -0
  424. package/src/lsp/completions.test.ts +5 -0
  425. package/src/lsp/hover.test.ts +7 -0
  426. package/src/lsp/hover.ts +8 -1
  427. package/src/members.ts +68 -0
  428. package/src/observe.e2e.test.ts +255 -0
  429. package/src/op/activities/grafana-apply.e2e.test.ts +184 -0
  430. package/src/op/activities/grafana-apply.test.ts +243 -0
  431. package/src/op/activities/grafana-apply.ts +197 -0
  432. package/src/op/activities/index.ts +8 -0
  433. package/src/op/builders.test.ts +40 -0
  434. package/src/op/builders.ts +34 -0
  435. package/src/ownership.ts +122 -0
  436. package/src/panel-options.ts +66 -0
  437. package/src/panels.test.ts +53 -0
  438. package/src/panels.ts +148 -10
  439. package/src/pin.ts +69 -6
  440. package/src/plugin.test.ts +12 -2
  441. package/src/plugin.ts +75 -0
  442. package/src/promql-check.test.ts +108 -0
  443. package/src/promql-check.ts +193 -0
  444. package/src/query-models.ts +94 -0
  445. package/src/query.test.ts +166 -0
  446. package/src/query.ts +190 -20
  447. package/src/schema/azuremonitor.gen.ts +381 -0
  448. package/src/schema/barchart.gen.ts +221 -0
  449. package/src/schema/bargauge.gen.ts +111 -0
  450. package/src/schema/bigquery.gen.ts +110 -0
  451. package/src/schema/canvas.gen.ts +176 -0
  452. package/src/schema/cloudwatch.gen.ts +423 -0
  453. package/src/schema/dashboard.gen.ts +206 -22
  454. package/src/schema/elasticsearch.gen.ts +508 -0
  455. package/src/schema/expr.gen.ts +618 -0
  456. package/src/schema/gauge.gen.ts +2 -1
  457. package/src/schema/geomap.gen.ts +112 -0
  458. package/src/schema/googlecloudmonitoring.gen.ts +330 -0
  459. package/src/schema/grafanapyroscope.gen.ts +66 -0
  460. package/src/schema/heatmap.gen.ts +2 -1
  461. package/src/schema/histogram.gen.ts +151 -0
  462. package/src/schema/index.ts +18 -0
  463. package/src/schema/logs.gen.ts +3 -1
  464. package/src/schema/loki.gen.ts +2 -1
  465. package/src/schema/nodegraph.gen.ts +58 -0
  466. package/src/schema/piechart.gen.ts +146 -0
  467. package/src/schema/prometheus.gen.ts +2 -1
  468. package/src/schema/stat.gen.ts +2 -1
  469. package/src/schema/statetimeline.gen.ts +143 -0
  470. package/src/schema/statushistory.gen.ts +132 -0
  471. package/src/schema/table.gen.ts +41 -4
  472. package/src/schema/tempo.gen.ts +2 -1
  473. package/src/schema/text.gen.ts +2 -1
  474. package/src/schema/timeseries.gen.ts +7 -2
  475. package/src/schema/trend.gen.ts +208 -0
  476. package/src/schema/xychart.gen.ts +167 -0
  477. package/src/schema-overlay.test.ts +282 -0
  478. package/src/schema-validate.ts +199 -57
  479. package/src/schema.test.ts +4 -2
  480. package/src/serializer.test.ts +13 -1
  481. package/src/serializer.ts +11 -3
  482. package/src/skill-defs.ts +34 -0
  483. package/src/skills/chant-grafana-alerting.md +49 -0
  484. package/src/skills/chant-grafana-operations.md +108 -0
  485. package/src/skills/chant-grafana-provisioning.md +3 -3
  486. package/src/skills/chant-grafana.md +15 -8
  487. package/src/spec/fetch-cli.ts +3 -3
  488. package/src/spec/fetch-units-cli.ts +37 -0
  489. package/src/spec/overlay/canvas.overlay.json +142 -0
  490. package/src/spec/overlay/dashboard.overlay.json +378 -0
  491. package/src/spec/overlay/expr.overlay.json +14 -0
  492. package/src/spec/overlay/geomap.overlay.json +35 -0
  493. package/src/spec/overlay/logs.overlay.json +14 -0
  494. package/src/spec/overlay/nodegraph.overlay.json +31 -0
  495. package/src/spec/overlay/piechart.overlay.json +17 -0
  496. package/src/spec/overlay/table.overlay.json +103 -0
  497. package/src/spec/overlay/timeseries.overlay.json +21 -0
  498. package/src/spec/overlay/trend.overlay.json +17 -0
  499. package/src/spec/overlay/xychart.overlay.json +16 -0
  500. package/src/spec/overlay.ts +105 -0
  501. package/src/spec/schemas/azuremonitor.jsonschema.json +679 -0
  502. package/src/spec/schemas/barchart.jsonschema.json +419 -0
  503. package/src/spec/schemas/bargauge.jsonschema.json +224 -0
  504. package/src/spec/schemas/bigquery.jsonschema.json +290 -0
  505. package/src/spec/schemas/canvas.jsonschema.json +356 -0
  506. package/src/spec/schemas/cloudwatch.jsonschema.json +721 -0
  507. package/src/spec/schemas/dashboardv2.jsonschema.json +3387 -0
  508. package/src/spec/schemas/elasticsearch.jsonschema.json +1695 -0
  509. package/src/spec/schemas/expr.jsonschema.json +987 -0
  510. package/src/spec/schemas/geomap.jsonschema.json +220 -0
  511. package/src/spec/schemas/googlecloudmonitoring.jsonschema.json +488 -0
  512. package/src/spec/schemas/grafanapyroscope.jsonschema.json +93 -0
  513. package/src/spec/schemas/histogram.jsonschema.json +308 -0
  514. package/src/spec/schemas/nodegraph.jsonschema.json +77 -0
  515. package/src/spec/schemas/piechart.jsonschema.json +275 -0
  516. package/src/spec/schemas/statetimeline.jsonschema.json +324 -0
  517. package/src/spec/schemas/statushistory.jsonschema.json +292 -0
  518. package/src/spec/schemas/trend.jsonschema.json +449 -0
  519. package/src/spec/schemas/xychart.jsonschema.json +428 -0
  520. package/src/spec/schemas.gen.ts +40 -0
  521. package/src/spec/schemas.ts +30 -16
  522. package/src/spec/units.gen.ts +281 -0
  523. package/src/spec/units.ts +77 -0
  524. package/src/transformations.test.ts +65 -0
  525. package/src/transformations.ts +574 -0
  526. package/src/util.ts +8 -0
  527. package/src/validate-alerting.test.ts +255 -0
  528. package/src/validate-alerting.ts +430 -0
  529. package/src/validate-output.ts +427 -104
  530. package/src/validate.ts +28 -0
  531. package/src/validation.ts +27 -0
  532. package/src/variables.ts +120 -8
package/src/build.ts CHANGED
@@ -10,15 +10,28 @@
10
10
 
11
11
  import { dump } from "js-yaml";
12
12
  import type { Declarable } from "@intentius/chant/declarable";
13
- import { isDatasourceEntity, type DatasourceEntity, type DatasourceRef } from "./datasource";
13
+ import {
14
+ isDatasourceDeclaration,
15
+ isDatasourceEntity,
16
+ isDatasourceProvisioningEntity,
17
+ isExternalDatasource,
18
+ type DatasourceEntity,
19
+ type DatasourceProvisioningProps,
20
+ type DatasourceRef,
21
+ type DeletedDatasource,
22
+ type ExternalDatasourceEntity,
23
+ } from "./datasource";
14
24
  import {
15
25
  isDashboardEntity,
16
26
  isDashboardProviderEntity,
17
27
  DEFAULT_DASHBOARDS_PATH,
18
28
  type DashboardEntity,
19
29
  type DashboardLinkInput,
20
- type DashboardProviderProps,
30
+ type DashboardProviderEntityProps,
21
31
  } from "./dashboard";
32
+ import { annotationJson } from "./annotations";
33
+ import { folderLevels, isFolderEntity, type FolderEntity } from "./folder";
34
+ import { resolveFolders, type FolderPlan } from "./api/folders";
22
35
  import { isPanelEntity, isRowEntity, type PanelEntity, type RowEntity, type PanelLink } from "./panels";
23
36
  import { isQueryEntity, type QueryEntity, type DatasourceInput } from "./query";
24
37
  import { isDatasourceVariable, isVariableEntity, type VariableEntity, type VariableHide } from "./variables";
@@ -34,6 +47,8 @@ import type {
34
47
  import { DASHBOARD_SCHEMA_VERSION } from "./schema/dashboard.gen";
35
48
  import { GRAFANA_SCHEMA_PIN } from "./pin";
36
49
  import { compact, slugUid } from "./util";
50
+ import { ALERTING_FILE, alertingYaml, buildAlerting, type AlertingFile, type AlertingIndex } from "./alerting-build";
51
+ import { DEFAULT_PROVIDER_NAME } from "./ownership";
37
52
 
38
53
  export type { DashboardJson, PanelJson, RowPanelJson, VariableModel, DataSourceRef };
39
54
 
@@ -65,6 +80,12 @@ export interface ProvisionedDatasource {
65
80
  version?: number;
66
81
  }
67
82
 
83
+ /** The settings of the datasource provisioning file besides its datasources. */
84
+ export interface DatasourceFileSettings {
85
+ prune: boolean;
86
+ deleteDatasources: DeletedDatasource[];
87
+ }
88
+
68
89
  /** One entry of `providers:` in a dashboard provisioning file. */
69
90
  export interface ProvisionedProvider {
70
91
  name: string;
@@ -82,6 +103,8 @@ export interface BuiltDashboard {
82
103
  uid: string;
83
104
  title: string;
84
105
  folder?: string;
106
+ /** The uid of its folder, when it is in one. */
107
+ folderUid?: string;
85
108
  /** Path of its JSON file, relative to the output directory. */
86
109
  file: string;
87
110
  json: DashboardJson;
@@ -90,7 +113,15 @@ export interface BuiltDashboard {
90
113
  export interface BuiltGrafana {
91
114
  dashboards: BuiltDashboard[];
92
115
  datasources: ProvisionedDatasource[];
116
+ /** `ExternalDatasource` declarations: the checks count them, the provisioning file leaves them out. */
117
+ externalDatasources: ExternalDatasourceRecord[];
93
118
  providers: ProvisionedProvider[];
119
+ /** Every folder the dashboards and `Folder`s name, each level of a path its own, sorted by path. */
120
+ folders: FolderPlan[];
121
+ /** `prune` and `deleteDatasources` of the datasource provisioning file. */
122
+ datasourceFile: DatasourceFileSettings;
123
+ /** The alerting provisioning file, when the build declares any alerting. */
124
+ alerting?: AlertingFile;
94
125
  /** Every output file by path, ready to write. */
95
126
  files: Record<string, string>;
96
127
  /** A short summary of what was built: the serializer's primary output. */
@@ -99,11 +130,24 @@ export interface BuiltGrafana {
99
130
 
100
131
  export interface GrafanaIndex {
101
132
  grafanaSchema: string;
102
- dashboards: Array<{ uid: string; title: string; folder?: string; file: string }>;
133
+ dashboards: Array<{ uid: string; title: string; folder?: string; folderUid?: string; file: string }>;
134
+ /** The folders the dashboards are in, and any `Folder` declared: what the API applier creates, parents first. */
135
+ folders?: FolderPlan[];
103
136
  datasources: Array<{ name: string; type: string; uid: string }>;
137
+ /** Datasources declared with `ExternalDatasource`: referenced, never provisioned. */
138
+ externalDatasources?: ExternalDatasourceRecord[];
139
+ /** Rule groups, contact points, policies, mute timings and templates, when the build declares any. */
140
+ alerting?: AlertingIndex;
104
141
  files: string[];
105
142
  }
106
143
 
144
+ /** An `ExternalDatasource` as the checks and the index see it. */
145
+ export interface ExternalDatasourceRecord {
146
+ type: string;
147
+ uid: string;
148
+ name?: string;
149
+ }
150
+
107
151
  // ── References ──────────────────────────────────────────────────
108
152
 
109
153
  function isRef(value: unknown): value is DatasourceRef {
@@ -116,13 +160,13 @@ function isRef(value: unknown): value is DatasourceRef {
116
160
  );
117
161
  }
118
162
 
119
- /** The `{ type, uid }` Grafana stores for a datasource, a datasource variable, or a ref. */
163
+ /** The `{ type, uid }` Grafana stores for a datasource (declared or external), a datasource variable, or a ref. */
120
164
  export function datasourceRef(input: DatasourceInput | undefined): DataSourceRef | undefined {
121
165
  if (input === undefined || input === null) return undefined;
122
- if (isDatasourceEntity(input)) return { type: input.datasourceType, uid: input.uid };
166
+ if (isDatasourceDeclaration(input)) return { type: input.datasourceType, uid: input.uid };
123
167
  if (isDatasourceVariable(input)) return { type: input.pluginType, uid: `\${${input.variableName}}` };
124
168
  if (isRef(input)) return { type: input.type, uid: input.uid };
125
- throw new Error("grafana: a datasource must be a Datasource, a DatasourceVariable or a { type, uid } ref");
169
+ throw new Error("grafana: a datasource must be a Datasource, an ExternalDatasource, a DatasourceVariable or a { type, uid } ref");
126
170
  }
127
171
 
128
172
  function sameRef(a: DataSourceRef | undefined, b: DataSourceRef | undefined): boolean {
@@ -154,8 +198,24 @@ function dashboardLink(link: DashboardLinkInput | PanelLink): DashboardLink {
154
198
  const HIDE: Record<VariableHide, 0 | 1 | 2> = { label: 0, valueOnly: 1, hidden: 2 };
155
199
  const REFRESH = { never: 0, onLoad: 1, onTimeRangeChange: 2 } as const;
156
200
 
201
+ /** A custom variable value in its `query`: a comma is escaped as `\\,`, and one already escaped is left alone. */
157
202
  function escapeCustom(v: string): string {
158
- return v.replace(/,/g, "\\,");
203
+ return v.replace(/\\?,/g, "\\,");
204
+ }
205
+
206
+ /**
207
+ * The options Grafana makes of a custom variable's `query`: split on commas
208
+ * not escaped as `\\,`, and `text : value` (spaces around the colon) read as
209
+ * an option showing `text` that sets `value`. The same parse as Grafana's
210
+ * custom variable (public/app/features/variables/custom/reducer.ts, and
211
+ * CustomVariable in @grafana/scenes).
212
+ */
213
+ export function customVariableOptions(query: string): Array<{ text: string; value: string }> {
214
+ return (query.match(/(?:\\,|[^,])+/g) ?? []).map((item) => {
215
+ const text = item.replace(/\\,/g, ",");
216
+ const kv = /^\s*(.+)\s:\s(.+)$/.exec(text);
217
+ return kv ? { text: kv[1].trim(), value: kv[2].trim() } : { text: text.trim(), value: text.trim() };
218
+ });
159
219
  }
160
220
 
161
221
  function optionsOf(values: string[], current?: { text: string | string[]; value: string | string[] }) {
@@ -182,7 +242,7 @@ export function variableModel(variable: VariableEntity): VariableModel {
182
242
  ...common,
183
243
  datasource: datasourceRef(p.datasource),
184
244
  query: p.query,
185
- definition: p.query,
245
+ definition: p.definition ?? queryText(p.query),
186
246
  regex: p.regex,
187
247
  refresh: REFRESH[(p.refresh ?? "onLoad") as keyof typeof REFRESH],
188
248
  sort: p.sort,
@@ -192,13 +252,16 @@ export function variableModel(variable: VariableEntity): VariableModel {
192
252
  }) as VariableModel;
193
253
  case "custom": {
194
254
  const values: string[] = p.values;
195
- const current = p.current ?? (values[0] !== undefined ? { text: values[0], value: values[0] } : undefined);
255
+ const query = values.map(escapeCustom).join(",");
256
+ const options = customVariableOptions(query);
257
+ const current = p.current ?? (options[0] !== undefined ? { text: options[0].text, value: options[0].value } : undefined);
258
+ const selected = current ? (Array.isArray(current.value) ? current.value : [current.value]) : [];
196
259
  return compact({
197
260
  ...common,
198
- query: values.map(escapeCustom).join(","),
261
+ query,
199
262
  ...multi,
200
263
  current,
201
- options: optionsOf(values, current),
264
+ options: options.map((o) => ({ selected: selected.includes(o.value), ...o })),
202
265
  }) as VariableModel;
203
266
  }
204
267
  case "interval": {
@@ -238,9 +301,51 @@ export function variableModel(variable: VariableEntity): VariableModel {
238
301
  query: p.value ?? "",
239
302
  current: { text: p.value ?? "", value: p.value ?? "" },
240
303
  }) as VariableModel;
304
+ case "adhoc":
305
+ return compact({
306
+ ...common,
307
+ datasource: datasourceRef(p.datasource),
308
+ filters: p.filters ?? [],
309
+ baseFilters: p.baseFilters ?? [],
310
+ defaultKeys: p.defaultKeys,
311
+ allowCustomValue: p.allowCustomValue,
312
+ enableGroupBy: p.enableGroupBy,
313
+ }) as VariableModel;
314
+ case "groupby": {
315
+ const options = p.options?.map((o: string | { text: string; value: string }) => (typeof o === "string" ? { text: o, value: o } : o));
316
+ const defaultValue = Array.isArray(p.defaultValue) ? { text: p.defaultValue, value: p.defaultValue } : p.defaultValue;
317
+ return compact({
318
+ ...common,
319
+ datasource: datasourceRef(p.datasource),
320
+ options,
321
+ current: p.current ?? defaultValue,
322
+ defaultValue,
323
+ allowCustomValue: p.allowCustomValue,
324
+ }) as VariableModel;
325
+ }
326
+ case "switch": {
327
+ const on: string = p.enabledValue ?? "true";
328
+ const off: string = p.disabledValue ?? "false";
329
+ const value = p.enabled ? on : off;
330
+ return compact({
331
+ ...common,
332
+ current: { text: value, value },
333
+ options: [
334
+ { text: on, value: on },
335
+ { text: off, value: off },
336
+ ],
337
+ }) as VariableModel;
338
+ }
241
339
  }
242
340
  }
243
341
 
342
+ /** The text of a variable query: the string, or an object query's `query`. */
343
+ function queryText(query: unknown): string | undefined {
344
+ if (typeof query === "string") return query;
345
+ if (query && typeof query === "object" && typeof (query as { query?: unknown }).query === "string") return (query as { query: string }).query;
346
+ return undefined;
347
+ }
348
+
244
349
  // ── Panels and layout ───────────────────────────────────────────
245
350
 
246
351
  function repeatName(repeat: VariableEntity | string | undefined): string | undefined {
@@ -266,47 +371,110 @@ export function targetJson(query: QueryEntity, index: number, panelDatasource?:
266
371
  });
267
372
  }
268
373
 
374
+ /** A bitmask of grid columns [x, x + w), clipped to the grid. */
375
+ function columns(x: number, w: number): number {
376
+ const from = Math.max(0, x);
377
+ const to = Math.min(GRID_COLUMNS, x + w);
378
+ return to > from ? ((1 << (to - from)) - 1) << from : 0;
379
+ }
380
+
381
+ /**
382
+ * Places panels on Grafana's grid. It keeps an occupancy grid, so a panel
383
+ * placed automatically never lands on one placed explicitly (reserved up
384
+ * front) or on one placed before it.
385
+ */
269
386
  class Layout {
270
387
  private x = 0;
271
- private y = 0;
388
+ private y: number;
272
389
  private lineH = 0;
273
- private bottom = 0;
390
+ private bottom: number;
391
+ /** Taken cells: one column bitmask per grid line. */
392
+ private readonly taken: number[] = [];
393
+
394
+ constructor(top = 0) {
395
+ this.y = top;
396
+ this.bottom = top;
397
+ }
398
+
399
+ /** Marks the cells of a panel with both `x` and `y` as taken, before anything is placed. */
400
+ reserve(gp: Partial<GridPos> | undefined, size: { w: number; h: number }): void {
401
+ if (gp?.x === undefined || gp?.y === undefined) return;
402
+ this.take(gp.x, gp.y, gp.w ?? size.w, gp.h ?? size.h);
403
+ }
274
404
 
275
405
  place(gp: Partial<GridPos> | undefined, size: { w: number; h: number }): GridPos {
276
406
  const w = gp?.w ?? size.w;
277
407
  const h = gp?.h ?? size.h;
278
408
  if (gp?.x !== undefined && gp?.y !== undefined) {
409
+ this.take(gp.x, gp.y, w, h);
279
410
  this.bottom = Math.max(this.bottom, gp.y + h);
280
411
  return { h, w, x: gp.x, y: gp.y };
281
412
  }
282
- if (this.x + w > GRID_COLUMNS && this.x > 0) {
283
- this.y += this.lineH;
413
+ if (gp?.y !== undefined) {
414
+ // Only `y`: the first free column on that line, else on the first line below with room.
415
+ for (let y = Math.max(0, gp.y); ; y++) {
416
+ const x = this.freeColumn(0, y, w, h);
417
+ if (x === undefined) continue;
418
+ this.take(x, y, w, h);
419
+ this.bottom = Math.max(this.bottom, y + h);
420
+ return { h, w, x, y };
421
+ }
422
+ }
423
+ // Flow: the first free spot on the current line after the previous panel, else the next line.
424
+ // Only `x`: that column, on the first line where it is free.
425
+ for (;;) {
426
+ const x =
427
+ gp?.x === undefined
428
+ ? this.freeColumn(this.x, this.y, w, h)
429
+ : gp.x >= this.x && this.free(gp.x, this.y, w, h)
430
+ ? gp.x
431
+ : undefined;
432
+ if (x !== undefined) {
433
+ const pos = { h, w, x, y: this.y };
434
+ this.take(x, this.y, w, h);
435
+ this.x = x + w;
436
+ this.lineH = Math.max(this.lineH, h);
437
+ this.bottom = Math.max(this.bottom, this.y + h);
438
+ return pos;
439
+ }
440
+ this.y += this.lineH || 1;
284
441
  this.x = 0;
285
442
  this.lineH = 0;
286
443
  }
287
- const pos = { h, w, x: this.x, y: this.y };
288
- this.x += w;
289
- this.lineH = Math.max(this.lineH, h);
290
- this.bottom = Math.max(this.bottom, this.y + h);
291
- return pos;
292
444
  }
293
445
 
294
- /** A full-width row header below everything placed so far. */
295
- row(): { pos: GridPos; mark: () => void } {
296
- const y = Math.max(this.bottom, this.y + this.lineH);
297
- const pos = { h: 1, w: GRID_COLUMNS, x: 0, y };
446
+ /** Marks the line of a row header with an explicit `y` as taken, before anything is placed. */
447
+ reserveRow(y: number | undefined): void {
448
+ if (y !== undefined) this.take(0, y, GRID_COLUMNS, 1);
449
+ }
450
+
451
+ /** A full-width row header on line `explicitY`, else on the first free line below everything placed so far. */
452
+ row(explicitY?: number): GridPos {
453
+ let y = explicitY ?? Math.max(this.bottom, this.y + this.lineH);
454
+ if (explicitY === undefined) while (!this.free(0, y, GRID_COLUMNS, 1)) y++;
455
+ this.take(0, y, GRID_COLUMNS, 1);
298
456
  this.x = 0;
299
457
  this.y = y + 1;
300
458
  this.lineH = 0;
301
459
  this.bottom = y + 1;
302
- // Called after a collapsed row's panels: the next thing starts right under the header.
303
- const mark = () => {
304
- this.x = 0;
305
- this.y = y + 1;
306
- this.lineH = 0;
307
- this.bottom = y + 1;
308
- };
309
- return { pos, mark };
460
+ return { h: 1, w: GRID_COLUMNS, x: 0, y };
461
+ }
462
+
463
+ /** The first column at or after `from` where a w×h panel fits on line `y`. A panel wider than the grid fits only at 0. */
464
+ private freeColumn(from: number, y: number, w: number, h: number): number | undefined {
465
+ for (let x = from; x === 0 || x + w <= GRID_COLUMNS; x++) if (this.free(x, y, w, h)) return x;
466
+ return undefined;
467
+ }
468
+
469
+ private free(x: number, y: number, w: number, h: number): boolean {
470
+ const mask = columns(x, w);
471
+ for (let line = Math.max(0, y); line < y + h; line++) if ((this.taken[line] ?? 0) & mask) return false;
472
+ return true;
473
+ }
474
+
475
+ private take(x: number, y: number, w: number, h: number): void {
476
+ const mask = columns(x, w);
477
+ for (let line = Math.max(0, y); line < y + h; line++) this.taken[line] = (this.taken[line] ?? 0) | mask;
310
478
  }
311
479
  }
312
480
 
@@ -334,7 +502,8 @@ function panelJson(panel: PanelEntity, gridPos: GridPos, id: number, inherited?:
334
502
  const def = panel.panelDefinition;
335
503
  const own = datasourceRef(p.datasource) ?? inherited;
336
504
  const queries = (p.targets ?? []).filter(isQueryEntity);
337
- const targets = queries.map((q, i) => targetJson(q, i, own));
505
+ // A query under a Mixed panel names its own datasource; one that names none goes to the default, not to Mixed.
506
+ const targets = queries.map((q, i) => targetJson(q, i, sameRef(own, MIXED) ? undefined : own));
338
507
  const refs = targets.map((t) => t.datasource as DataSourceRef | undefined);
339
508
  let datasource = own;
340
509
  if (!datasource && refs.length > 0) {
@@ -374,19 +543,29 @@ function panelJson(panel: PanelEntity, gridPos: GridPos, id: number, inherited?:
374
543
  /** A dashboard's `panels` array: rows and panels, laid out, with ids. */
375
544
  export function panelsJson(items: Array<PanelEntity | RowEntity>): Array<PanelJson | RowPanelJson> {
376
545
  const layout = new Layout();
546
+ // Panels on the dashboard's grid with both x and y, and rows with a y, take their cells first; a collapsed row's panels are laid out on their own.
547
+ for (const item of items) {
548
+ if (isRowEntity(item)) layout.reserveRow(item.props.gridPos?.y);
549
+ const onGrid = isRowEntity(item) ? (item.props.collapsed ? [] : (item.props.panels ?? [])) : [item];
550
+ for (const p of onGrid.filter(isPanelEntity)) layout.reserve(p.props.gridPos, p.panelDefinition.defaultSize);
551
+ }
377
552
  const ids = new Ids(explicitIds(items));
378
553
  const out: Array<PanelJson | RowPanelJson> = [];
379
554
  for (const item of items) {
380
555
  if (isRowEntity(item)) {
381
556
  const r = item.props;
382
- const { pos, mark } = layout.row();
557
+ const pos = layout.row(r.gridPos?.y);
383
558
  const rowId = ids.take(r.id);
384
559
  const rowRef = datasourceRef(r.datasource);
385
- const children = (r.panels ?? []).filter(isPanelEntity).map((p) => {
386
- const gp = layout.place(p.props.gridPos, p.panelDefinition.defaultSize);
560
+ const collapsed = r.collapsed ?? false;
561
+ // A collapsed row's panels sit under its header on a grid of their own; the next item starts right under the header.
562
+ const rowLayout = collapsed ? new Layout(pos.y + 1) : layout;
563
+ const panels = (r.panels ?? []).filter(isPanelEntity);
564
+ if (collapsed) for (const p of panels) rowLayout.reserve(p.props.gridPos, p.panelDefinition.defaultSize);
565
+ const children = panels.map((p) => {
566
+ const gp = rowLayout.place(p.props.gridPos, p.panelDefinition.defaultSize);
387
567
  return panelJson(p, gp, ids.take(p.props.id), rowRef);
388
568
  });
389
- const collapsed = r.collapsed ?? false;
390
569
  out.push(
391
570
  compact({
392
571
  type: "row" as const,
@@ -399,8 +578,7 @@ export function panelsJson(items: Array<PanelEntity | RowEntity>): Array<PanelJs
399
578
  repeat: repeatName(r.repeat),
400
579
  }),
401
580
  );
402
- if (collapsed) mark();
403
- else out.push(...children);
581
+ if (!collapsed) out.push(...children);
404
582
  } else if (isPanelEntity(item)) {
405
583
  const gp = layout.place(item.props.gridPos, item.panelDefinition.defaultSize);
406
584
  out.push(panelJson(item, gp, ids.take(item.props.id)));
@@ -422,7 +600,7 @@ export function dashboardUid(dashboard: DashboardEntity, exportName?: string): s
422
600
  export function renderDashboard(dashboard: DashboardEntity, exportName?: string): DashboardJson {
423
601
  const p = dashboard.props;
424
602
  return compact({
425
- annotations: { list: [] },
603
+ annotations: { list: (p.annotations ?? []).map((a) => annotationJson(a, datasourceRef)) },
426
604
  description: p.description,
427
605
  editable: p.editable ?? true,
428
606
  fiscalYearStartMonth: p.fiscalYearStartMonth ?? 0,
@@ -431,7 +609,7 @@ export function renderDashboard(dashboard: DashboardEntity, exportName?: string)
431
609
  liveNow: p.liveNow,
432
610
  panels: panelsJson(p.panels ?? []),
433
611
  refresh: p.refresh,
434
- schemaVersion: DASHBOARD_SCHEMA_VERSION,
612
+ schemaVersion: p.schemaVersion ?? DASHBOARD_SCHEMA_VERSION,
435
613
  tags: p.tags ?? [],
436
614
  templating: { list: (p.variables ?? []).filter(isVariableEntity).map(variableModel) },
437
615
  time: p.time ?? { from: "now-6h", to: "now" },
@@ -450,9 +628,9 @@ export function dashboardJson(dashboard: DashboardEntity, exportName?: string):
450
628
 
451
629
  // ── Datasources and providers ───────────────────────────────────
452
630
 
453
- /** Replace every declared `Datasource` inside plugin settings with its uid. */
631
+ /** Replace every declared or external datasource inside plugin settings with its uid. */
454
632
  function resolveNested(value: unknown): unknown {
455
- if (isDatasourceEntity(value)) return value.uid;
633
+ if (isDatasourceDeclaration(value)) return value.uid;
456
634
  if (Array.isArray(value)) return value.map(resolveNested);
457
635
  if (value && typeof value === "object" && Object.getPrototypeOf(value) === Object.prototype) {
458
636
  return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, resolveNested(v)]));
@@ -483,8 +661,13 @@ export function provisionedDatasource(ds: DatasourceEntity): ProvisionedDatasour
483
661
  });
484
662
  }
485
663
 
664
+ /** One `ExternalDatasource` as the checks and the index see it. */
665
+ export function externalDatasourceRecord(ds: ExternalDatasourceEntity): ExternalDatasourceRecord {
666
+ return compact({ type: ds.datasourceType, uid: ds.uid, name: ds.props.name });
667
+ }
668
+
486
669
  /** One dashboard provider as a provisioning entry. */
487
- export function provisionedProvider(p: DashboardProviderProps): ProvisionedProvider {
670
+ export function provisionedProvider(p: DashboardProviderEntityProps): ProvisionedProvider {
488
671
  const pinnedFolder = p.folder !== undefined || p.folderUid !== undefined;
489
672
  return compact({
490
673
  name: p.name,
@@ -506,9 +689,26 @@ function yamlFile(header: string, body: Record<string, unknown>): string {
506
689
  return `# ${header}\n${dump(body, { lineWidth: -1, noRefs: true, quotingType: '"' })}`;
507
690
  }
508
691
 
509
- /** The datasource provisioning file for these datasources. */
510
- export function datasourcesYaml(datasources: ProvisionedDatasource[]): string {
511
- return yamlFile("Grafana datasource provisioning, generated by chant.", { apiVersion: 1, datasources });
692
+ /** `prune` and `deleteDatasources` as a build writes them: pruning on unless a `DatasourceProvisioning` turns it off. */
693
+ export function datasourceFileSettings(props: DatasourceProvisioningProps | undefined): DatasourceFileSettings {
694
+ return {
695
+ prune: props?.prune ?? true,
696
+ deleteDatasources: (props?.deleteDatasources ?? []).map((d) => ({ name: d.name, ...(d.orgId !== undefined ? { orgId: d.orgId } : {}) })),
697
+ };
698
+ }
699
+
700
+ /**
701
+ * The datasource provisioning file for these datasources. It prunes by
702
+ * default (`prune: true`), so a datasource removed from the build is removed
703
+ * from Grafana; `deleteDatasources` comes first, as Grafana runs it first.
704
+ */
705
+ export function datasourcesYaml(datasources: ProvisionedDatasource[], settings: DatasourceFileSettings = datasourceFileSettings(undefined)): string {
706
+ return yamlFile("Grafana datasource provisioning, generated by chant.", {
707
+ apiVersion: 1,
708
+ ...(settings.prune ? { prune: true } : {}),
709
+ ...(settings.deleteDatasources.length > 0 ? { deleteDatasources: settings.deleteDatasources } : {}),
710
+ datasources,
711
+ });
512
712
  }
513
713
 
514
714
  /** The dashboard provisioning file for these providers. */
@@ -516,8 +716,20 @@ export function dashboardProvidersYaml(providers: ProvisionedProvider[]): string
516
716
  return yamlFile("Grafana dashboard provisioning, generated by chant.", { apiVersion: 1, providers });
517
717
  }
518
718
 
719
+ /**
720
+ * A dashboard's `folder` as its directory under `dashboards/`: one
721
+ * subdirectory per level (`folderLevels`), which a provider with
722
+ * `foldersFromFilesStructure` turns into nested folders on Grafana 13.1 and
723
+ * later (GRAF109 says what earlier versions do). Empty for the General
724
+ * folder.
725
+ */
519
726
  function folderDir(folder: string): string {
520
- return folder.replace(/[/\\]+/g, "-").replace(/^\.+/, "").trim() || "General";
727
+ return folderLevels(folder).join("/");
728
+ }
729
+
730
+ /** A `Folder` and every folder above it. */
731
+ function withAncestors(folder: FolderEntity, into: Set<FolderEntity>): void {
732
+ for (let f: FolderEntity | undefined = folder; f; f = f.props.parent) into.add(f);
521
733
  }
522
734
 
523
735
  // ── The whole build ─────────────────────────────────────────────
@@ -534,37 +746,64 @@ export function buildGrafana(entities: Map<string, Declarable> | Iterable<Declar
534
746
  .map(([, e]) => provisionedDatasource(e as DatasourceEntity))
535
747
  .sort((a, b) => a.name.localeCompare(b.name));
536
748
 
749
+ const externalDatasources: ExternalDatasourceRecord[] = named
750
+ .filter(([, e]) => isExternalDatasource(e))
751
+ .map(([, e]) => externalDatasourceRecord(e as ExternalDatasourceEntity))
752
+ .sort((a, b) => a.uid.localeCompare(b.uid));
753
+
754
+ // Folders: every declared `Folder` and every one a dashboard holds, with their parents, pin uids; every path a dashboard names is a folder per level.
755
+ const declaredFolders = new Set<FolderEntity>();
756
+ for (const [, e] of named) {
757
+ if (isFolderEntity(e)) withAncestors(e, declaredFolders);
758
+ else if (isDashboardEntity(e) && e.folderEntity) withAncestors(e.folderEntity, declaredFolders);
759
+ }
760
+ const resolved = resolveFolders({
761
+ declared: [...declaredFolders].map((f) => ({ uid: f.uid, path: f.path })),
762
+ paths: named.flatMap(([, e]) => (isDashboardEntity(e) && e.props.folder ? [e.props.folder] : [])),
763
+ }, { duplicateUids: "keep" });
764
+
537
765
  const dashboards: BuiltDashboard[] = [];
538
766
  for (const [name, e] of named) {
539
767
  if (!isDashboardEntity(e)) continue;
540
768
  const json = renderDashboard(e, name);
541
769
  const uid = json.uid as string;
542
770
  const folder = e.props.folder;
543
- const dir = folder ? `${DASHBOARDS_DIR}/${folderDir(folder)}` : DASHBOARDS_DIR;
771
+ const sub = folder ? folderDir(folder) : "";
772
+ const dir = sub ? `${DASHBOARDS_DIR}/${sub}` : DASHBOARDS_DIR;
544
773
  let file = `${dir}/${uid}.json`;
545
774
  for (let n = 2; file in files; n++) file = `${dir}/${uid}-${n}.json`;
546
775
  files[file] = `${JSON.stringify(json, null, 2)}\n`;
547
- dashboards.push({ uid, title: e.props.title, ...(folder ? { folder } : {}), file, json });
776
+ const folderUid = sub ? resolved.uidOf(sub) : undefined;
777
+ dashboards.push({ uid, title: e.props.title, ...(folder ? { folder } : {}), ...(folderUid ? { folderUid } : {}), file, json });
548
778
  }
549
779
 
550
- const declaredProviders = named.filter(([, e]) => isDashboardProviderEntity(e)).map(([, e]) => (e as unknown as { props: DashboardProviderProps }).props);
780
+ const settings = named.map(([, e]) => e).filter(isDatasourceProvisioningEntity);
781
+ if (settings.length > 1) throw new Error("grafana: declare at most one DatasourceProvisioning; the build writes one datasource provisioning file");
782
+ const datasourceFile = datasourceFileSettings(settings[0]?.props);
783
+
784
+ const declaredProviders = named.filter(([, e]) => isDashboardProviderEntity(e)).map(([, e]) => (e as unknown as { props: DashboardProviderEntityProps }).props);
551
785
  const providers =
552
786
  declaredProviders.length > 0
553
787
  ? declaredProviders.map(provisionedProvider)
554
788
  : dashboards.length > 0
555
- ? [provisionedProvider({ name: "chant" })]
789
+ ? [provisionedProvider({ name: DEFAULT_PROVIDER_NAME })]
556
790
  : [];
557
791
 
558
- if (datasources.length > 0) files[DATASOURCES_FILE] = datasourcesYaml(datasources);
792
+ if (datasources.length > 0 || datasourceFile.deleteDatasources.length > 0) files[DATASOURCES_FILE] = datasourcesYaml(datasources, datasourceFile);
559
793
  if (providers.length > 0) files[DASHBOARD_PROVIDERS_FILE] = dashboardProvidersYaml(providers);
794
+ const alerting = buildAlerting(named.map(([, e]) => e));
795
+ if (alerting) files[ALERTING_FILE] = alertingYaml(alerting.file);
560
796
 
561
797
  const index: GrafanaIndex = {
562
798
  grafanaSchema: `${GRAFANA_SCHEMA_PIN.source}@${GRAFANA_SCHEMA_PIN.ref}`,
563
- dashboards: dashboards.map(({ uid, title, folder, file }) => ({ uid, title, ...(folder ? { folder } : {}), file })),
799
+ dashboards: dashboards.map(({ uid, title, folder, folderUid, file }) => ({ uid, title, ...(folder ? { folder } : {}), ...(folderUid ? { folderUid } : {}), file })),
800
+ ...(resolved.folders.length > 0 ? { folders: resolved.folders } : {}),
564
801
  datasources: datasources.map(({ name, type, uid }) => ({ name, type, uid })),
802
+ ...(externalDatasources.length > 0 ? { externalDatasources } : {}),
803
+ ...(alerting ? { alerting: alerting.index } : {}),
565
804
  files: Object.keys(files).sort(),
566
805
  };
567
- return { dashboards, datasources, providers, files, index };
806
+ return { dashboards, datasources, externalDatasources, providers, folders: resolved.folders, datasourceFile, ...(alerting ? { alerting: alerting.file } : {}), files, index };
568
807
  }
569
808
 
570
809
  /** Every file the grafana entities render to, by path relative to the output directory. */