@homeflare/alchemy 0.34.0 → 0.36.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 (65) hide show
  1. package/dist/grafana/alert-rule-group-drop-warning.d.ts +25 -0
  2. package/dist/grafana/alert-rule-group-drop-warning.d.ts.map +1 -0
  3. package/dist/grafana/alert-rule-group-fixtures.d.ts +73 -0
  4. package/dist/grafana/alert-rule-group-fixtures.d.ts.map +1 -0
  5. package/dist/grafana/alert-rule-group-model.d.ts +28 -0
  6. package/dist/grafana/alert-rule-group-model.d.ts.map +1 -0
  7. package/dist/grafana/alert-rule-group.d.ts +60 -0
  8. package/dist/grafana/alert-rule-group.d.ts.map +1 -0
  9. package/dist/grafana/index.d.ts +6 -4
  10. package/dist/grafana/index.d.ts.map +1 -1
  11. package/dist/grafana/index.js +355 -139
  12. package/dist/grafana/index.js.map +14 -8
  13. package/dist/grafana/notification-policy-drop-warning.d.ts +27 -0
  14. package/dist/grafana/notification-policy-drop-warning.d.ts.map +1 -0
  15. package/dist/grafana/notification-policy-fixtures.d.ts +34 -0
  16. package/dist/grafana/notification-policy-fixtures.d.ts.map +1 -0
  17. package/dist/grafana/notification-policy-model.d.ts +53 -0
  18. package/dist/grafana/notification-policy-model.d.ts.map +1 -0
  19. package/dist/grafana/notification-policy.d.ts +57 -0
  20. package/dist/grafana/notification-policy.d.ts.map +1 -0
  21. package/dist/grafana/providers.d.ts +1 -1
  22. package/dist/grafana/providers.d.ts.map +1 -1
  23. package/dist/proxmox/index.js +349 -287
  24. package/dist/proxmox/index.js.map +8 -7
  25. package/dist/proxmox/network-apply-read.d.ts +12 -0
  26. package/dist/proxmox/network-apply-read.d.ts.map +1 -1
  27. package/dist/proxmox/network-apply.d.ts +2 -0
  28. package/dist/proxmox/network-apply.d.ts.map +1 -1
  29. package/dist/proxmox/node-network-form.d.ts +86 -54
  30. package/dist/proxmox/node-network-form.d.ts.map +1 -1
  31. package/dist/proxmox/node-network-wire.d.ts +100 -0
  32. package/dist/proxmox/node-network-wire.d.ts.map +1 -0
  33. package/dist/proxmox/node-network.d.ts +21 -106
  34. package/dist/proxmox/node-network.d.ts.map +1 -1
  35. package/docs/grafana-alerting-rules.md +143 -0
  36. package/docs/grafana-alerting.md +7 -4
  37. package/docs/grafana-notification-policy.md +127 -0
  38. package/docs/grafana.md +12 -6
  39. package/docs/upstream-conformance.md +30 -4
  40. package/package.json +1 -1
  41. package/src/grafana/alert-rule-group-drop-warning.test.ts +107 -0
  42. package/src/grafana/alert-rule-group-drop-warning.ts +41 -0
  43. package/src/grafana/alert-rule-group-fixtures.ts +46 -0
  44. package/src/grafana/alert-rule-group-model.test.ts +75 -0
  45. package/src/grafana/alert-rule-group-model.ts +66 -0
  46. package/src/grafana/alert-rule-group-provenance.test.ts +116 -0
  47. package/src/grafana/alert-rule-group.test.ts +164 -0
  48. package/src/grafana/alert-rule-group.ts +231 -0
  49. package/src/grafana/index.ts +18 -4
  50. package/src/grafana/notification-policy-drop-warning.test.ts +163 -0
  51. package/src/grafana/notification-policy-drop-warning.ts +60 -0
  52. package/src/grafana/notification-policy-fixtures.ts +27 -0
  53. package/src/grafana/notification-policy-model.test.ts +104 -0
  54. package/src/grafana/notification-policy-model.ts +100 -0
  55. package/src/grafana/notification-policy-refusals.test.ts +119 -0
  56. package/src/grafana/notification-policy.test.ts +86 -0
  57. package/src/grafana/notification-policy.ts +191 -0
  58. package/src/grafana/providers.ts +4 -0
  59. package/src/proxmox/constraints-host-forms.test.ts +2 -2
  60. package/src/proxmox/network-apply-read.ts +12 -0
  61. package/src/proxmox/network-apply.ts +2 -0
  62. package/src/proxmox/node-network-form.ts +93 -99
  63. package/src/proxmox/node-network-read-failure.test.ts +206 -0
  64. package/src/proxmox/node-network-wire.ts +239 -0
  65. package/src/proxmox/node-network.ts +155 -167
@@ -1,17 +1,77 @@
1
- // src/grafana/datasource.ts
1
+ // src/grafana/alert-rule-group.ts
2
+ import { isResolved as isResolved2 } from "alchemy/Diff";
2
3
  import { Resource } from "alchemy";
3
4
  import * as Provider from "alchemy/Provider";
4
5
  import * as grafana from "@distilled.cloud/grafana";
6
+ import * as Effect5 from "effect/Effect";
7
+
8
+ // src/grafana/alert-rule-group-drop-warning.ts
9
+ import * as Effect from "effect/Effect";
10
+ var warnOnDroppedRules = (folderUid, group, declaredUids, liveRules) => Effect.gen(function* () {
11
+ const dropped = (liveRules ?? []).filter((rule) => typeof rule.uid === "string").filter((rule) => !declaredUids.has(rule.uid));
12
+ if (dropped.length === 0)
13
+ return;
14
+ const names = dropped.map((rule) => `${typeof rule.title === "string" ? rule.title : "(untitled)"} (${rule.uid})`).join(", ");
15
+ yield* Effect.logWarning(`Grafana.AlertRuleGroup ${folderUid}/${group}: this update REMOVES ${dropped.length} ` + `rule(s) not in the declaration: ${names}. Whole-group replacement is by design — add ` + `them to the declaration to keep them, or give them their own group.`);
16
+ });
17
+
18
+ // src/grafana/alert-rule-group-model.ts
19
+ var RULE_VOLATILE_FIELDS = ["id", "updated", "provenance"];
20
+ var normalizeRule = (rule, folderUid, group) => {
21
+ const out = {};
22
+ for (const [key, value] of Object.entries(rule)) {
23
+ if (key === "folderUID" || key === "ruleGroup" || key === "orgID")
24
+ continue;
25
+ if (RULE_VOLATILE_FIELDS.includes(key))
26
+ continue;
27
+ out[key] = value;
28
+ }
29
+ out.folderUID = folderUid;
30
+ out.ruleGroup = group;
31
+ out.orgID = 1;
32
+ return out;
33
+ };
34
+ var toWireRule = (rule, folderUid, group) => ({
35
+ ...rule,
36
+ folderUID: folderUid,
37
+ orgID: 1,
38
+ ruleGroup: group
39
+ });
40
+
41
+ // src/grafana/alerting-provenance.ts
5
42
  import * as Effect3 from "effect/Effect";
6
43
 
44
+ // src/grafana/provisioned.ts
45
+ import * as Data from "effect/Data";
46
+ import * as Effect2 from "effect/Effect";
47
+
48
+ class GrafanaProvisionedObjectError extends Data.TaggedError("GrafanaProvisionedObjectError") {
49
+ }
50
+ var refuseIfProvisioned = (kind, uid, owner) => {
51
+ const ownerClause = owner === undefined || owner === "" ? `(this Grafana instance did not report an owning file)` : `by ${owner}`;
52
+ return Effect2.fail(new GrafanaProvisionedObjectError({
53
+ message: `${kind} '${uid}' is file-provisioned ${ownerClause} and read-only through the classic ` + `write API this family calls. Edit the provisioning file instead — declaring this ` + `object read-only (a declaration matching what is live) is safe and will never reach ` + `this refusal.`
54
+ }));
55
+ };
56
+
57
+ // src/grafana/alerting-provenance.ts
58
+ var WRITABLE_PROVENANCES = new Set(["", "api"]);
59
+ var isForeignProvenance = (provenance) => provenance === undefined || !WRITABLE_PROVENANCES.has(provenance);
60
+ var refuseIfForeignProvenance = (kind, id, provenance) => {
61
+ const reason = provenance === undefined ? `did not report a provenance at all — this family cannot tell whether it is safe to own` : `has provenance '${provenance}', not '(none)' or 'api'`;
62
+ return Effect3.fail(new GrafanaProvisionedObjectError({
63
+ message: `${kind} '${id}' ${reason} — something other than this API may own it (file ` + `provisioning, the Prometheus-rules importer, or similar), and writing here could take ` + `over or corrupt an object this family does not actually control. Edit it at its actual ` + `source instead — declaring this object read-only (a declaration matching what is live) ` + `is safe and will never reach this refusal.`
64
+ }));
65
+ };
66
+
7
67
  // src/grafana/resource.ts
8
68
  import { isResolved } from "alchemy/Diff";
9
- import * as Effect from "effect/Effect";
69
+ import * as Effect4 from "effect/Effect";
10
70
  var grafanaOperations = (spec) => {
11
- const read = (props) => spec.fetchLive(props).pipe(Effect.map((live) => live === undefined ? undefined : spec.attributes(live, props)));
71
+ const read = (props) => spec.fetchLive(props).pipe(Effect4.map((live) => live === undefined ? undefined : spec.attributes(live, props)));
12
72
  return {
13
73
  read,
14
- diff: (news, output) => Effect.gen(function* () {
74
+ diff: (news, output) => Effect4.gen(function* () {
15
75
  if (output === undefined || !isResolved(news))
16
76
  return;
17
77
  const live = yield* read(news);
@@ -21,7 +81,7 @@ var grafanaOperations = (spec) => {
21
81
  return { action: "noop" };
22
82
  return spec.update === undefined ? { action: "replace" } : { action: "update" };
23
83
  }),
24
- reconcile: (news) => Effect.gen(function* () {
84
+ reconcile: (news) => Effect4.gen(function* () {
25
85
  const before = yield* spec.fetchLive(news);
26
86
  if (before === undefined) {
27
87
  yield* spec.create(news);
@@ -33,11 +93,11 @@ var grafanaOperations = (spec) => {
33
93
  }
34
94
  const after = yield* read(news);
35
95
  if (after === undefined) {
36
- return yield* Effect.die(new Error("grafanaOperations: the write returned no error but the object is still absent. " + "Read back rather than trusting the response status."));
96
+ return yield* Effect4.die(new Error("grafanaOperations: the write returned no error but the object is still absent. " + "Read back rather than trusting the response status."));
37
97
  }
38
98
  return after;
39
99
  }),
40
- destroy: (olds) => Effect.gen(function* () {
100
+ destroy: (olds) => Effect4.gen(function* () {
41
101
  const live = yield* spec.fetchLive(olds);
42
102
  if (live === undefined)
43
103
  return;
@@ -48,7 +108,7 @@ var grafanaOperations = (spec) => {
48
108
  var grafanaHandlers = (spec) => {
49
109
  const ops = grafanaOperations(spec);
50
110
  return {
51
- list: () => Effect.succeed([]),
111
+ list: () => Effect4.succeed([]),
52
112
  read: ({ olds }) => ops.read(olds),
53
113
  diff: ({ news, output }) => ops.diff(news, output),
54
114
  reconcile: ({ news }) => ops.reconcile(news),
@@ -56,20 +116,212 @@ var grafanaHandlers = (spec) => {
56
116
  };
57
117
  };
58
118
 
119
+ // src/grafana/subset-match.ts
120
+ var isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
121
+ var declaredContentMatches = (declared, live) => {
122
+ if (declared == null)
123
+ return true;
124
+ if (Array.isArray(declared)) {
125
+ return Array.isArray(live) && declared.length === live.length && declared.every((item, index) => declaredContentMatches(item, live[index]));
126
+ }
127
+ if (isPlainObject(declared)) {
128
+ return isPlainObject(live) && Object.entries(declared).every(([key, value]) => declaredContentMatches(value, live[key]));
129
+ }
130
+ return declared === live;
131
+ };
132
+
133
+ // src/grafana/alert-rule-group.ts
134
+ var GrafanaAlertRuleGroup = Resource("Grafana.AlertRuleGroup", {
135
+ defaultRemovalPolicy: "retain"
136
+ });
137
+ var findForeignRule = (rules) => (rules ?? []).find((rule) => isForeignProvenance(rule.provenance));
138
+ var body = (props) => ({
139
+ folderUid: props.folderUid,
140
+ interval: props.interval,
141
+ rules: props.rules.map((rule) => toWireRule(rule, props.folderUid, props.group)),
142
+ title: props.group
143
+ });
144
+ var refuseForForeignRule = (props, foreign) => refuseIfForeignProvenance("Grafana.AlertRuleGroup", `${props.folderUid}/${props.group}`, foreign.provenance);
145
+ var declaredUids = (props) => new Set(props.rules.map((rule) => rule.uid));
146
+ var spec = {
147
+ attributes: (live, props) => ({
148
+ folderUid: live.folderUid ?? props.folderUid,
149
+ group: props.group,
150
+ interval: live.interval ?? 0,
151
+ rules: (live.rules ?? []).map((rule) => normalizeRule(rule, props.folderUid, props.group))
152
+ }),
153
+ create: (props) => grafana.routePutAlertRuleGroup({
154
+ FolderUID: props.folderUid,
155
+ Group: props.group,
156
+ ...body(props)
157
+ }),
158
+ destroy: (props, live) => Effect5.gen(function* () {
159
+ const foreign = findForeignRule(live.rules);
160
+ if (foreign !== undefined)
161
+ return yield* refuseForForeignRule(props, foreign);
162
+ yield* grafana.routeDeleteAlertRuleGroup({ FolderUID: props.folderUid, Group: props.group });
163
+ }),
164
+ fetchLive: (props) => grafana.routeGetAlertRuleGroup({ FolderUID: props.folderUid, Group: props.group }).pipe(Effect5.catchTag("NotFound", () => Effect5.succeed(undefined))),
165
+ matches: (attributes, props) => attributes.interval === props.interval && declaredContentMatches(props.rules.map((rule) => normalizeRule(rule, props.folderUid, props.group)), attributes.rules),
166
+ update: (props, live) => Effect5.gen(function* () {
167
+ const foreign = findForeignRule(live.rules);
168
+ if (foreign !== undefined)
169
+ return yield* refuseForForeignRule(props, foreign);
170
+ yield* warnOnDroppedRules(props.folderUid, props.group, declaredUids(props), live.rules);
171
+ yield* grafana.routePutAlertRuleGroup({
172
+ FolderUID: props.folderUid,
173
+ Group: props.group,
174
+ ...body(props)
175
+ });
176
+ })
177
+ };
178
+ var diff = ({
179
+ news,
180
+ output
181
+ }) => Effect5.gen(function* () {
182
+ if (output === undefined || !isResolved2(news))
183
+ return;
184
+ const live = yield* spec.fetchLive(news);
185
+ if (live === undefined)
186
+ return { action: "update" };
187
+ yield* warnOnDroppedRules(news.folderUid, news.group, declaredUids(news), live.rules);
188
+ const mapped = spec.attributes(live, news);
189
+ return mapped !== undefined && spec.matches(mapped, news) ? { action: "noop" } : { action: "update" };
190
+ });
191
+ var handlers = { ...grafanaHandlers(spec), diff };
192
+ var GrafanaAlertRuleGroupProvider = () => Provider.effect(GrafanaAlertRuleGroup, Effect5.succeed(GrafanaAlertRuleGroup.Provider.of(handlers)));
193
+ // src/grafana/notification-policy.ts
194
+ import { isResolved as isResolved3 } from "alchemy/Diff";
195
+ import { Resource as Resource2 } from "alchemy";
196
+ import * as Provider2 from "alchemy/Provider";
197
+ import * as grafana2 from "@distilled.cloud/grafana";
198
+ import * as Effect7 from "effect/Effect";
199
+
200
+ // src/grafana/notification-policy-model.ts
201
+ var ROUTE_VOLATILE_FIELDS = ["provenance"];
202
+ var normalizeRoute = (route) => {
203
+ const out = {};
204
+ for (const [key, value] of Object.entries(route)) {
205
+ if (ROUTE_VOLATILE_FIELDS.includes(key))
206
+ continue;
207
+ if (key === "routes" && Array.isArray(value)) {
208
+ out.routes = value.map((child) => normalizeRoute(child));
209
+ continue;
210
+ }
211
+ out[key] = value;
212
+ }
213
+ return out;
214
+ };
215
+ var ownFieldsJson = (route) => {
216
+ const { routes: _routes, ...ownFields } = route;
217
+ return JSON.stringify(ownFields);
218
+ };
219
+ var flattenRoutes = (route, path = "root", ancestorKey = "") => {
220
+ const children = Array.isArray(route.routes) ? route.routes : [];
221
+ const ownJson = ownFieldsJson(route);
222
+ const key = ancestorKey === "" ? ownJson : `${ancestorKey}\x00${ownJson}`;
223
+ const self = {
224
+ key,
225
+ path,
226
+ receiver: typeof route.receiver === "string" ? route.receiver : "(inherited)"
227
+ };
228
+ return [
229
+ self,
230
+ ...children.flatMap((child, index) => flattenRoutes(child, `${path}.${index}`, key))
231
+ ];
232
+ };
233
+
234
+ // src/grafana/notification-policy-drop-warning.ts
235
+ import * as Effect6 from "effect/Effect";
236
+ var countByKey = (routes) => {
237
+ const counts = new Map;
238
+ for (const route of routes)
239
+ counts.set(route.key, (counts.get(route.key) ?? 0) + 1);
240
+ return counts;
241
+ };
242
+ var warnOnDroppedRoutes = (declared, live) => Effect6.gen(function* () {
243
+ const declaredNodes = flattenRoutes(normalizeRoute(declared)).filter((r) => r.path !== "root");
244
+ const liveNodes = flattenRoutes(normalizeRoute(live)).filter((r) => r.path !== "root");
245
+ const declaredCounts = countByKey(declaredNodes);
246
+ const liveCounts = countByKey(liveNodes);
247
+ const dropped = [];
248
+ for (const [key, liveCount] of liveCounts) {
249
+ const extra = liveCount - (declaredCounts.get(key) ?? 0);
250
+ if (extra <= 0)
251
+ continue;
252
+ const occurrences = liveNodes.filter((route) => route.key === key);
253
+ dropped.push(...occurrences.slice(-extra));
254
+ }
255
+ if (dropped.length === 0)
256
+ return;
257
+ const names = dropped.map((route) => `${route.receiver} (${route.path})`).join(", ");
258
+ yield* Effect6.logWarning(`Grafana.NotificationPolicy: this update REMOVES ${dropped.length} route(s)/matcher(s) not ` + `in the declaration: ${names}. Whole-tree replacement is by design — add them to the ` + `declared tree to keep them.`);
259
+ });
260
+
261
+ // src/grafana/notification-policy.ts
262
+ var GrafanaNotificationPolicy = Resource2("Grafana.NotificationPolicy", { defaultRemovalPolicy: "retain" });
263
+ var body2 = (props) => props.route;
264
+ var spec2 = {
265
+ attributes: (live) => ({
266
+ provenance: live.provenance,
267
+ route: normalizeRoute(live)
268
+ }),
269
+ create: (props) => grafana2.routePutPolicyTree(body2(props)),
270
+ destroy: (props, live) => Effect7.gen(function* () {
271
+ if (isForeignProvenance(live.provenance)) {
272
+ return yield* refuseIfForeignProvenance("Grafana.NotificationPolicy", "default", live.provenance);
273
+ }
274
+ if (props.allowReset !== true) {
275
+ yield* Effect7.logWarning("Grafana.NotificationPolicy: destroy requested but allowReset is not set -- the policy " + "tree is RETAINED, not reset. Grafana always needs a root route; resetting wipes " + "every declared route/receiver/matcher back to Grafana's own default for the WHOLE " + "instance. Set allowReset: true to actually call routeResetPolicyTree.");
276
+ return;
277
+ }
278
+ yield* grafana2.routeResetPolicyTree({});
279
+ }),
280
+ fetchLive: (_props) => grafana2.routeGetPolicyTree({}),
281
+ matches: (attributes, props) => declaredContentMatches(normalizeRoute(props.route), attributes.route),
282
+ update: (props, live) => Effect7.gen(function* () {
283
+ if (isForeignProvenance(live.provenance)) {
284
+ return yield* refuseIfForeignProvenance("Grafana.NotificationPolicy", "default", live.provenance);
285
+ }
286
+ yield* warnOnDroppedRoutes(props.route, live);
287
+ yield* grafana2.routePutPolicyTree(body2(props));
288
+ })
289
+ };
290
+ var diff2 = ({
291
+ news,
292
+ output
293
+ }) => Effect7.gen(function* () {
294
+ if (output === undefined || !isResolved3(news))
295
+ return;
296
+ const live = yield* spec2.fetchLive(news);
297
+ if (live === undefined)
298
+ return { action: "update" };
299
+ yield* warnOnDroppedRoutes(news.route, live);
300
+ const mapped = spec2.attributes(live, news);
301
+ return mapped !== undefined && spec2.matches(mapped, news) ? { action: "noop" } : { action: "update" };
302
+ });
303
+ var handlers2 = { ...grafanaHandlers(spec2), diff: diff2 };
304
+ var GrafanaNotificationPolicyProvider = () => Provider2.effect(GrafanaNotificationPolicy, Effect7.succeed(GrafanaNotificationPolicy.Provider.of(handlers2)));
305
+ // src/grafana/datasource.ts
306
+ import { Resource as Resource3 } from "alchemy";
307
+ import * as Provider3 from "alchemy/Provider";
308
+ import * as grafana3 from "@distilled.cloud/grafana";
309
+ import * as Effect9 from "effect/Effect";
310
+
59
311
  // src/grafana/secret-refs.ts
60
- import * as Data from "effect/Data";
61
- import * as Effect2 from "effect/Effect";
312
+ import * as Data2 from "effect/Data";
313
+ import * as Effect8 from "effect/Effect";
62
314
 
63
- class GrafanaSecretRefUnsetError extends Data.TaggedError("GrafanaSecretRefUnsetError") {
315
+ class GrafanaSecretRefUnsetError extends Data2.TaggedError("GrafanaSecretRefUnsetError") {
64
316
  }
65
- var resolveSecretRefs = (refs, describeField) => Effect2.gen(function* () {
317
+ var resolveSecretRefs = (refs, describeField) => Effect8.gen(function* () {
66
318
  if (refs === undefined)
67
319
  return;
68
320
  const out = {};
69
321
  for (const [field, envVar] of Object.entries(refs)) {
70
322
  const raw = process.env[envVar];
71
323
  if (raw === undefined || raw.trim() === "") {
72
- return yield* Effect2.fail(new GrafanaSecretRefUnsetError({
324
+ return yield* Effect8.fail(new GrafanaSecretRefUnsetError({
73
325
  message: `${envVar} is unset. Export the value of ${describeField(field)} there — ` + "never as an Alchemy prop."
74
326
  }));
75
327
  }
@@ -79,7 +331,7 @@ var resolveSecretRefs = (refs, describeField) => Effect2.gen(function* () {
79
331
  });
80
332
 
81
333
  // src/grafana/datasource.ts
82
- var GrafanaDatasource = Resource("Grafana.Datasource");
334
+ var GrafanaDatasource = Resource3("Grafana.Datasource");
83
335
  var resolveSecureJsonData = (refs) => resolveSecretRefs(refs, (field) => `secureJsonData.${field}`);
84
336
  var fields = (props) => ({
85
337
  name: props.name,
@@ -94,7 +346,7 @@ var fields = (props) => ({
94
346
  ...props.user === undefined ? {} : { user: props.user },
95
347
  ...props.withCredentials === undefined ? {} : { withCredentials: props.withCredentials }
96
348
  });
97
- var spec = {
349
+ var spec3 = {
98
350
  attributes: (live) => ({
99
351
  access: live.access ?? "",
100
352
  id: live.id ?? 0,
@@ -107,42 +359,28 @@ var spec = {
107
359
  url: live.url ?? "",
108
360
  version: live.version ?? 0
109
361
  }),
110
- create: (props) => Effect3.flatMap(resolveSecureJsonData(props.secureJsonDataRefs), (secureJsonData) => grafana.addDataSource({
362
+ create: (props) => Effect9.flatMap(resolveSecureJsonData(props.secureJsonDataRefs), (secureJsonData) => grafana3.addDataSource({
111
363
  ...fields(props),
112
364
  ...secureJsonData === undefined ? {} : { secureJsonData },
113
365
  uid: props.uid
114
366
  })),
115
- destroy: (props) => grafana.deleteDataSourceByUID({ uid: props.uid }),
116
- fetchLive: (props) => grafana.getDataSourceByUID({ uid: props.uid }).pipe(Effect3.catchTag("NotFound", () => Effect3.succeed(undefined))),
367
+ destroy: (props) => grafana3.deleteDataSourceByUID({ uid: props.uid }),
368
+ fetchLive: (props) => grafana3.getDataSourceByUID({ uid: props.uid }).pipe(Effect9.catchTag("NotFound", () => Effect9.succeed(undefined))),
117
369
  matches: (attributes, props) => (props.name === undefined || attributes.name === props.name) && (props.type === undefined || attributes.type === props.type) && (props.url === undefined || attributes.url === props.url) && (props.access === undefined || attributes.access === props.access) && (props.isDefault === undefined || attributes.isDefault === props.isDefault),
118
- update: (props, live) => Effect3.flatMap(resolveSecureJsonData(props.secureJsonDataRefs), (secureJsonData) => grafana.updateDataSourceByUID({
370
+ update: (props, live) => Effect9.flatMap(resolveSecureJsonData(props.secureJsonDataRefs), (secureJsonData) => grafana3.updateDataSourceByUID({
119
371
  ...fields(props),
120
372
  ...secureJsonData === undefined ? {} : { secureJsonData },
121
373
  ...live.version === undefined ? {} : { version: live.version },
122
374
  uid: props.uid
123
375
  }))
124
376
  };
125
- var handlers = grafanaHandlers(spec);
126
- var GrafanaDatasourceProvider = () => Provider.effect(GrafanaDatasource, Effect3.succeed(GrafanaDatasource.Provider.of(handlers)));
377
+ var handlers3 = grafanaHandlers(spec3);
378
+ var GrafanaDatasourceProvider = () => Provider3.effect(GrafanaDatasource, Effect9.succeed(GrafanaDatasource.Provider.of(handlers3)));
127
379
  // src/grafana/dashboard.ts
128
- import { Resource as Resource2 } from "alchemy";
129
- import * as Provider2 from "alchemy/Provider";
130
- import * as grafana2 from "@distilled.cloud/grafana";
131
- import * as Effect5 from "effect/Effect";
132
-
133
- // src/grafana/subset-match.ts
134
- var isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
135
- var declaredContentMatches = (declared, live) => {
136
- if (declared == null)
137
- return true;
138
- if (Array.isArray(declared)) {
139
- return Array.isArray(live) && declared.length === live.length && declared.every((item, index) => declaredContentMatches(item, live[index]));
140
- }
141
- if (isPlainObject(declared)) {
142
- return isPlainObject(live) && Object.entries(declared).every(([key, value]) => declaredContentMatches(value, live[key]));
143
- }
144
- return declared === live;
145
- };
380
+ import { Resource as Resource4 } from "alchemy";
381
+ import * as Provider4 from "alchemy/Provider";
382
+ import * as grafana4 from "@distilled.cloud/grafana";
383
+ import * as Effect10 from "effect/Effect";
146
384
 
147
385
  // src/grafana/dashboard-model.ts
148
386
  var VOLATILE_FIELDS = ["id", "version", "iteration"];
@@ -160,23 +398,10 @@ var normalizeModel = (model, uid) => {
160
398
  };
161
399
  var modelTitle = (model) => typeof model.title === "string" ? model.title : "";
162
400
 
163
- // src/grafana/provisioned.ts
164
- import * as Data2 from "effect/Data";
165
- import * as Effect4 from "effect/Effect";
166
-
167
- class GrafanaProvisionedObjectError extends Data2.TaggedError("GrafanaProvisionedObjectError") {
168
- }
169
- var refuseIfProvisioned = (kind, uid, owner) => {
170
- const ownerClause = owner === undefined || owner === "" ? `(this Grafana instance did not report an owning file)` : `by ${owner}`;
171
- return Effect4.fail(new GrafanaProvisionedObjectError({
172
- message: `${kind} '${uid}' is file-provisioned ${ownerClause} and read-only through the classic ` + `write API this family calls. Edit the provisioning file instead — declaring this ` + `object read-only (a declaration matching what is live) is safe and will never reach ` + `this refusal.`
173
- }));
174
- };
175
-
176
401
  // src/grafana/dashboard.ts
177
- var GrafanaDashboard = Resource2("Grafana.Dashboard");
402
+ var GrafanaDashboard = Resource4("Grafana.Dashboard");
178
403
  var modelOf = (live) => live.dashboard !== null && typeof live.dashboard === "object" ? live.dashboard : {};
179
- var spec2 = {
404
+ var spec4 = {
180
405
  attributes: (live, props) => {
181
406
  const model = normalizeModel(modelOf(live), props.uid);
182
407
  return {
@@ -190,23 +415,23 @@ var spec2 = {
190
415
  version: live.meta?.version ?? 0
191
416
  };
192
417
  },
193
- create: (props) => grafana2.postDashboard({
418
+ create: (props) => grafana4.postDashboard({
194
419
  dashboard: normalizeModel(props.dashboard, props.uid),
195
420
  ...props.folderUid === undefined ? {} : { folderUid: props.folderUid }
196
421
  }),
197
- destroy: (props, live) => Effect5.gen(function* () {
422
+ destroy: (props, live) => Effect10.gen(function* () {
198
423
  if (live.meta?.provisioned) {
199
424
  return yield* refuseIfProvisioned("Grafana.Dashboard", props.uid, live.meta.provisionedExternalId);
200
425
  }
201
- yield* grafana2.deleteDashboard({ uid: props.uid });
426
+ yield* grafana4.deleteDashboard({ uid: props.uid });
202
427
  }),
203
- fetchLive: (props) => grafana2.getDashboard({ uid: props.uid }).pipe(Effect5.catchTag("NotFound", () => Effect5.succeed(undefined))),
428
+ fetchLive: (props) => grafana4.getDashboard({ uid: props.uid }).pipe(Effect10.catchTag("NotFound", () => Effect10.succeed(undefined))),
204
429
  matches: (attributes, props) => (props.folderUid === undefined || attributes.folderUid === props.folderUid) && declaredContentMatches(normalizeModel(props.dashboard, props.uid), attributes.dashboard),
205
- update: (props, live) => Effect5.gen(function* () {
430
+ update: (props, live) => Effect10.gen(function* () {
206
431
  if (live.meta?.provisioned) {
207
432
  return yield* refuseIfProvisioned("Grafana.Dashboard", props.uid, live.meta.provisionedExternalId);
208
433
  }
209
- return yield* grafana2.postDashboard({
434
+ return yield* grafana4.postDashboard({
210
435
  dashboard: {
211
436
  ...normalizeModel(props.dashboard, props.uid),
212
437
  ...live.meta?.version === undefined ? {} : { version: live.meta.version }
@@ -215,21 +440,21 @@ var spec2 = {
215
440
  });
216
441
  })
217
442
  };
218
- var handlers2 = grafanaHandlers(spec2);
219
- var GrafanaDashboardProvider = () => Provider2.effect(GrafanaDashboard, Effect5.succeed(GrafanaDashboard.Provider.of(handlers2)));
443
+ var handlers4 = grafanaHandlers(spec4);
444
+ var GrafanaDashboardProvider = () => Provider4.effect(GrafanaDashboard, Effect10.succeed(GrafanaDashboard.Provider.of(handlers4)));
220
445
  // src/grafana/folder.ts
221
- import { Resource as Resource3 } from "alchemy";
222
- import * as Provider3 from "alchemy/Provider";
223
- import * as grafana3 from "@distilled.cloud/grafana";
446
+ import { Resource as Resource5 } from "alchemy";
447
+ import * as Provider5 from "alchemy/Provider";
448
+ import * as grafana5 from "@distilled.cloud/grafana";
224
449
  import * as Data3 from "effect/Data";
225
- import * as Effect6 from "effect/Effect";
226
- var GrafanaFolder = Resource3("Grafana.Folder", {
450
+ import * as Effect11 from "effect/Effect";
451
+ var GrafanaFolder = Resource5("Grafana.Folder", {
227
452
  defaultRemovalPolicy: "retain"
228
453
  });
229
454
 
230
455
  class GrafanaFolderReparentError extends Data3.TaggedError("GrafanaFolderReparentError") {
231
456
  }
232
- var spec3 = {
457
+ var spec5 = {
233
458
  attributes: (live) => ({
234
459
  id: live.id ?? 0,
235
460
  managedBy: live.managedBy ?? "",
@@ -239,30 +464,30 @@ var spec3 = {
239
464
  url: live.url ?? "",
240
465
  version: live.version ?? 0
241
466
  }),
242
- create: (props) => grafana3.createFolder({
467
+ create: (props) => grafana5.createFolder({
243
468
  uid: props.uid,
244
469
  title: props.title,
245
470
  ...props.description === undefined ? {} : { description: props.description },
246
471
  ...props.parentUid === undefined ? {} : { parentUid: props.parentUid }
247
472
  }),
248
- destroy: (props, live) => Effect6.gen(function* () {
473
+ destroy: (props, live) => Effect11.gen(function* () {
249
474
  if (live.managedBy) {
250
475
  return yield* refuseIfProvisioned("Grafana.Folder", props.uid, live.managedBy);
251
476
  }
252
- yield* grafana3.deleteFolder({ folder_uid: props.uid });
477
+ yield* grafana5.deleteFolder({ folder_uid: props.uid });
253
478
  }),
254
- fetchLive: (props) => grafana3.getFolderByUID({ folder_uid: props.uid }).pipe(Effect6.catchTag("NotFound", () => Effect6.succeed(undefined))),
479
+ fetchLive: (props) => grafana5.getFolderByUID({ folder_uid: props.uid }).pipe(Effect11.catchTag("NotFound", () => Effect11.succeed(undefined))),
255
480
  matches: (attributes, props) => attributes.title === props.title && (props.parentUid === undefined || attributes.parentUid === props.parentUid),
256
- update: (props, live) => Effect6.gen(function* () {
481
+ update: (props, live) => Effect11.gen(function* () {
257
482
  if (live.managedBy) {
258
483
  return yield* refuseIfProvisioned("Grafana.Folder", props.uid, live.managedBy);
259
484
  }
260
485
  if (props.parentUid !== undefined && props.parentUid !== (live.parentUid ?? undefined)) {
261
- return yield* Effect6.fail(new GrafanaFolderReparentError({
486
+ return yield* Effect11.fail(new GrafanaFolderReparentError({
262
487
  message: `Grafana.Folder '${props.uid}' declares parentUid '${props.parentUid}' but is ` + `live under '${live.parentUid ?? "(root)"}'. PUT /folders/{uid} cannot reparent a ` + `folder — move it by hand in Grafana, then update the declaration to match.`
263
488
  }));
264
489
  }
265
- return yield* grafana3.updateFolder({
490
+ return yield* grafana5.updateFolder({
266
491
  folder_uid: props.uid,
267
492
  title: props.title,
268
493
  ...props.description === undefined ? {} : { description: props.description },
@@ -270,27 +495,14 @@ var spec3 = {
270
495
  });
271
496
  })
272
497
  };
273
- var handlers3 = grafanaHandlers(spec3);
274
- var GrafanaFolderProvider = () => Provider3.effect(GrafanaFolder, Effect6.succeed(GrafanaFolder.Provider.of(handlers3)));
275
- // src/grafana/contact-point.ts
276
- import { Resource as Resource4 } from "alchemy";
277
- import * as Provider4 from "alchemy/Provider";
278
- import * as grafana4 from "@distilled.cloud/grafana";
279
- import * as Effect8 from "effect/Effect";
280
-
281
- // src/grafana/alerting-provenance.ts
282
- import * as Effect7 from "effect/Effect";
283
- var WRITABLE_PROVENANCES = new Set(["", "api"]);
284
- var isForeignProvenance = (provenance) => provenance === undefined || !WRITABLE_PROVENANCES.has(provenance);
285
- var refuseIfForeignProvenance = (kind, id, provenance) => {
286
- const reason = provenance === undefined ? `did not report a provenance at all — this family cannot tell whether it is safe to own` : `has provenance '${provenance}', not '(none)' or 'api'`;
287
- return Effect7.fail(new GrafanaProvisionedObjectError({
288
- message: `${kind} '${id}' ${reason} — something other than this API may own it (file ` + `provisioning, the Prometheus-rules importer, or similar), and writing here could take ` + `over or corrupt an object this family does not actually control. Edit it at its actual ` + `source instead — declaring this object read-only (a declaration matching what is live) ` + `is safe and will never reach this refusal.`
289
- }));
290
- };
291
-
498
+ var handlers5 = grafanaHandlers(spec5);
499
+ var GrafanaFolderProvider = () => Provider5.effect(GrafanaFolder, Effect11.succeed(GrafanaFolder.Provider.of(handlers5)));
292
500
  // src/grafana/contact-point.ts
293
- var GrafanaContactPoint = Resource4("Grafana.ContactPoint");
501
+ import { Resource as Resource6 } from "alchemy";
502
+ import * as Provider6 from "alchemy/Provider";
503
+ import * as grafana6 from "@distilled.cloud/grafana";
504
+ import * as Effect12 from "effect/Effect";
505
+ var GrafanaContactPoint = Resource6("Grafana.ContactPoint");
294
506
  var nonSecretSettings = (props) => {
295
507
  const refs = props.secureSettingsRefs;
296
508
  if (refs === undefined)
@@ -302,8 +514,8 @@ var nonSecretSettings = (props) => {
302
514
  }
303
515
  return out;
304
516
  };
305
- var resolveSettings = (props) => Effect8.map(resolveSecretRefs(props.secureSettingsRefs, (field) => `settings.${field}`), (secrets) => secrets === undefined ? props.settings : { ...props.settings, ...secrets });
306
- var spec4 = {
517
+ var resolveSettings = (props) => Effect12.map(resolveSecretRefs(props.secureSettingsRefs, (field) => `settings.${field}`), (secrets) => secrets === undefined ? props.settings : { ...props.settings, ...secrets });
518
+ var spec6 = {
307
519
  attributes: (live) => ({
308
520
  disableResolveMessage: live.disableResolveMessage ?? false,
309
521
  name: live.name ?? "",
@@ -312,27 +524,27 @@ var spec4 = {
312
524
  type: live.type,
313
525
  uid: live.uid ?? ""
314
526
  }),
315
- create: (props) => Effect8.flatMap(resolveSettings(props), (settings) => grafana4.routePostContactpoints({
527
+ create: (props) => Effect12.flatMap(resolveSettings(props), (settings) => grafana6.routePostContactpoints({
316
528
  name: props.name,
317
529
  settings,
318
530
  type: props.type,
319
531
  uid: props.uid,
320
532
  ...props.disableResolveMessage === undefined ? {} : { disableResolveMessage: props.disableResolveMessage }
321
533
  })),
322
- destroy: (props, live) => Effect8.gen(function* () {
534
+ destroy: (props, live) => Effect12.gen(function* () {
323
535
  if (isForeignProvenance(live.provenance)) {
324
536
  return yield* refuseIfForeignProvenance("Grafana.ContactPoint", props.uid, live.provenance);
325
537
  }
326
- yield* grafana4.routeDeleteContactpoints({ UID: props.uid });
538
+ yield* grafana6.routeDeleteContactpoints({ UID: props.uid });
327
539
  }),
328
- fetchLive: (props) => grafana4.routeGetContactpoints({}).pipe(Effect8.map((points) => points.find((point) => point.uid === props.uid))),
540
+ fetchLive: (props) => grafana6.routeGetContactpoints({}).pipe(Effect12.map((points) => points.find((point) => point.uid === props.uid))),
329
541
  matches: (attributes, props) => attributes.name === props.name && attributes.type === props.type && attributes.disableResolveMessage === (props.disableResolveMessage ?? false) && declaredContentMatches(nonSecretSettings(props), attributes.settings),
330
- update: (props, live) => Effect8.gen(function* () {
542
+ update: (props, live) => Effect12.gen(function* () {
331
543
  if (isForeignProvenance(live.provenance)) {
332
544
  return yield* refuseIfForeignProvenance("Grafana.ContactPoint", props.uid, live.provenance);
333
545
  }
334
546
  const settings = yield* resolveSettings(props);
335
- yield* grafana4.routePutContactpoint({
547
+ yield* grafana6.routePutContactpoint({
336
548
  UID: props.uid,
337
549
  name: props.name,
338
550
  settings,
@@ -342,99 +554,101 @@ var spec4 = {
342
554
  });
343
555
  })
344
556
  };
345
- var handlers4 = grafanaHandlers(spec4);
346
- var GrafanaContactPointProvider = () => Provider4.effect(GrafanaContactPoint, Effect8.succeed(GrafanaContactPoint.Provider.of(handlers4)));
557
+ var handlers6 = grafanaHandlers(spec6);
558
+ var GrafanaContactPointProvider = () => Provider6.effect(GrafanaContactPoint, Effect12.succeed(GrafanaContactPoint.Provider.of(handlers6)));
347
559
  // src/grafana/message-template.ts
348
- import { Resource as Resource5 } from "alchemy";
349
- import * as Provider5 from "alchemy/Provider";
350
- import * as grafana5 from "@distilled.cloud/grafana";
351
- import * as Effect9 from "effect/Effect";
352
- var GrafanaMessageTemplate = Resource5("Grafana.MessageTemplate");
353
- var spec5 = {
560
+ import { Resource as Resource7 } from "alchemy";
561
+ import * as Provider7 from "alchemy/Provider";
562
+ import * as grafana7 from "@distilled.cloud/grafana";
563
+ import * as Effect13 from "effect/Effect";
564
+ var GrafanaMessageTemplate = Resource7("Grafana.MessageTemplate");
565
+ var spec7 = {
354
566
  attributes: (live) => ({
355
567
  name: live.name ?? "",
356
568
  provenance: live.provenance,
357
569
  template: live.template ?? ""
358
570
  }),
359
- create: (props) => grafana5.routePutTemplate({ name: props.name, template: props.template }),
360
- destroy: (props, live) => Effect9.gen(function* () {
571
+ create: (props) => grafana7.routePutTemplate({ name: props.name, template: props.template }),
572
+ destroy: (props, live) => Effect13.gen(function* () {
361
573
  if (isForeignProvenance(live.provenance)) {
362
574
  return yield* refuseIfForeignProvenance("Grafana.MessageTemplate", props.name, live.provenance);
363
575
  }
364
- yield* grafana5.routeDeleteTemplate({ name: props.name });
576
+ yield* grafana7.routeDeleteTemplate({ name: props.name });
365
577
  }),
366
- fetchLive: (props) => grafana5.routeGetTemplate({ name: props.name }).pipe(Effect9.catchTag("NotFound", () => Effect9.succeed(undefined))),
578
+ fetchLive: (props) => grafana7.routeGetTemplate({ name: props.name }).pipe(Effect13.catchTag("NotFound", () => Effect13.succeed(undefined))),
367
579
  matches: (attributes, props) => attributes.template === props.template,
368
- update: (props, live) => Effect9.gen(function* () {
580
+ update: (props, live) => Effect13.gen(function* () {
369
581
  if (isForeignProvenance(live.provenance)) {
370
582
  return yield* refuseIfForeignProvenance("Grafana.MessageTemplate", props.name, live.provenance);
371
583
  }
372
- yield* grafana5.routePutTemplate({
584
+ yield* grafana7.routePutTemplate({
373
585
  name: props.name,
374
586
  template: props.template,
375
587
  ...live.version === undefined ? {} : { version: live.version }
376
588
  });
377
589
  })
378
590
  };
379
- var handlers5 = grafanaHandlers(spec5);
380
- var GrafanaMessageTemplateProvider = () => Provider5.effect(GrafanaMessageTemplate, Effect9.succeed(GrafanaMessageTemplate.Provider.of(handlers5)));
591
+ var handlers7 = grafanaHandlers(spec7);
592
+ var GrafanaMessageTemplateProvider = () => Provider7.effect(GrafanaMessageTemplate, Effect13.succeed(GrafanaMessageTemplate.Provider.of(handlers7)));
381
593
  // src/grafana/mute-timing.ts
382
- import { Resource as Resource6 } from "alchemy";
383
- import * as Provider6 from "alchemy/Provider";
384
- import * as grafana6 from "@distilled.cloud/grafana";
385
- import * as Effect10 from "effect/Effect";
386
- var GrafanaMuteTiming = Resource6("Grafana.MuteTiming");
387
- var spec6 = {
594
+ import { Resource as Resource8 } from "alchemy";
595
+ import * as Provider8 from "alchemy/Provider";
596
+ import * as grafana8 from "@distilled.cloud/grafana";
597
+ import * as Effect14 from "effect/Effect";
598
+ var GrafanaMuteTiming = Resource8("Grafana.MuteTiming");
599
+ var spec8 = {
388
600
  attributes: (live) => ({
389
601
  name: live.name ?? "",
390
602
  provenance: live.provenance,
391
603
  timeIntervals: live.time_intervals ?? []
392
604
  }),
393
- create: (props) => grafana6.routePostMuteTiming({
605
+ create: (props) => grafana8.routePostMuteTiming({
394
606
  name: props.name,
395
607
  time_intervals: props.timeIntervals
396
608
  }),
397
- destroy: (props, live) => Effect10.gen(function* () {
609
+ destroy: (props, live) => Effect14.gen(function* () {
398
610
  if (isForeignProvenance(live.provenance)) {
399
611
  return yield* refuseIfForeignProvenance("Grafana.MuteTiming", props.name, live.provenance);
400
612
  }
401
- yield* grafana6.routeDeleteMuteTiming({
613
+ yield* grafana8.routeDeleteMuteTiming({
402
614
  name: props.name,
403
615
  ...live.version === undefined ? {} : { version: live.version }
404
616
  });
405
617
  }),
406
- fetchLive: (props) => grafana6.routeGetMuteTiming({ name: props.name }).pipe(Effect10.map((live) => live), Effect10.catchTag("NotFound", () => Effect10.succeed(undefined))),
618
+ fetchLive: (props) => grafana8.routeGetMuteTiming({ name: props.name }).pipe(Effect14.map((live) => live), Effect14.catchTag("NotFound", () => Effect14.succeed(undefined))),
407
619
  matches: (attributes, props) => declaredContentMatches(props.timeIntervals, attributes.timeIntervals),
408
- update: (props, live) => Effect10.gen(function* () {
620
+ update: (props, live) => Effect14.gen(function* () {
409
621
  if (isForeignProvenance(live.provenance)) {
410
622
  return yield* refuseIfForeignProvenance("Grafana.MuteTiming", props.name, live.provenance);
411
623
  }
412
- yield* grafana6.routePutMuteTiming({
624
+ yield* grafana8.routePutMuteTiming({
413
625
  name: props.name,
414
626
  time_intervals: props.timeIntervals
415
627
  });
416
628
  })
417
629
  };
418
- var handlers6 = grafanaHandlers(spec6);
419
- var GrafanaMuteTimingProvider = () => Provider6.effect(GrafanaMuteTiming, Effect10.succeed(GrafanaMuteTiming.Provider.of(handlers6)));
630
+ var handlers8 = grafanaHandlers(spec8);
631
+ var GrafanaMuteTimingProvider = () => Provider8.effect(GrafanaMuteTiming, Effect14.succeed(GrafanaMuteTiming.Provider.of(handlers8)));
420
632
  // src/grafana/credentials.ts
421
633
  import { Credentials } from "@distilled.cloud/grafana/Credentials";
422
634
  import { ConfigError } from "@distilled.cloud/grafana/Errors";
423
- import * as Effect11 from "effect/Effect";
635
+ import * as Effect15 from "effect/Effect";
424
636
  import * as Layer from "effect/Layer";
425
637
  import * as Redacted from "effect/Redacted";
426
- var resolve = (target) => Effect11.sync(() => process.env[target.tokenEnv]).pipe(Effect11.flatMap((raw) => raw === undefined || raw.trim() === "" ? Effect11.fail(new ConfigError({
638
+ var resolve = (target) => Effect15.sync(() => process.env[target.tokenEnv]).pipe(Effect15.flatMap((raw) => raw === undefined || raw.trim() === "" ? Effect15.fail(new ConfigError({
427
639
  message: `${target.tokenEnv} is not set. Export the Grafana service-account token there ` + `— never as an Alchemy prop — before running a stack that declares a resource ` + `against ${target.baseUrl}.`
428
- })) : Effect11.succeed({
640
+ })) : Effect15.succeed({
429
641
  apiBaseUrl: target.baseUrl.replace(/\/+$/, ""),
430
642
  token: Redacted.make(raw.trim()),
431
643
  ...target.orgId === undefined ? {} : { orgId: target.orgId }
432
- })), Effect11.orDie);
644
+ })), Effect15.orDie);
433
645
  var grafanaCredentials = (target) => Layer.succeed(Credentials, resolve(target));
434
646
  // src/grafana/providers.ts
435
647
  import * as Layer2 from "effect/Layer";
436
- var grafanaProviders = (target) => Layer2.mergeAll(GrafanaDatasourceProvider(), GrafanaFolderProvider(), GrafanaDashboardProvider(), GrafanaContactPointProvider(), GrafanaMuteTimingProvider(), GrafanaMessageTemplateProvider()).pipe(Layer2.provide(grafanaCredentials(target)));
648
+ var grafanaProviders = (target) => Layer2.mergeAll(GrafanaDatasourceProvider(), GrafanaFolderProvider(), GrafanaDashboardProvider(), GrafanaContactPointProvider(), GrafanaMuteTimingProvider(), GrafanaMessageTemplateProvider(), GrafanaAlertRuleGroupProvider(), GrafanaNotificationPolicyProvider()).pipe(Layer2.provide(grafanaCredentials(target)));
437
649
  export {
650
+ GrafanaAlertRuleGroup,
651
+ GrafanaAlertRuleGroupProvider,
438
652
  GrafanaContactPoint,
439
653
  GrafanaContactPointProvider,
440
654
  GrafanaDashboard,
@@ -448,11 +662,13 @@ export {
448
662
  GrafanaMessageTemplateProvider,
449
663
  GrafanaMuteTiming,
450
664
  GrafanaMuteTimingProvider,
665
+ GrafanaNotificationPolicy,
666
+ GrafanaNotificationPolicyProvider,
451
667
  GrafanaProvisionedObjectError,
452
668
  GrafanaSecretRefUnsetError,
453
669
  grafanaCredentials,
454
670
  grafanaProviders
455
671
  };
456
672
 
457
- //# debugId=D93AD79265681B5F64756E2164756E21
673
+ //# debugId=5578DCEDF956037C64756E2164756E21
458
674
  //# sourceMappingURL=index.js.map