@homeflare/alchemy 0.33.0 → 0.34.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 (50) hide show
  1. package/dist/grafana/alerting-provenance.d.ts +80 -0
  2. package/dist/grafana/alerting-provenance.d.ts.map +1 -0
  3. package/dist/grafana/contact-point-fixtures.d.ts +28 -0
  4. package/dist/grafana/contact-point-fixtures.d.ts.map +1 -0
  5. package/dist/grafana/contact-point.d.ts +99 -0
  6. package/dist/grafana/contact-point.d.ts.map +1 -0
  7. package/dist/grafana/dashboard-model.d.ts +8 -38
  8. package/dist/grafana/dashboard-model.d.ts.map +1 -1
  9. package/dist/grafana/datasource.d.ts +2 -9
  10. package/dist/grafana/datasource.d.ts.map +1 -1
  11. package/dist/grafana/index.d.ts +10 -4
  12. package/dist/grafana/index.d.ts.map +1 -1
  13. package/dist/grafana/index.js +198 -41
  14. package/dist/grafana/index.js.map +12 -6
  15. package/dist/grafana/message-template.d.ts +70 -0
  16. package/dist/grafana/message-template.d.ts.map +1 -0
  17. package/dist/grafana/mute-timing.d.ts +112 -0
  18. package/dist/grafana/mute-timing.d.ts.map +1 -0
  19. package/dist/grafana/providers.d.ts +1 -1
  20. package/dist/grafana/providers.d.ts.map +1 -1
  21. package/dist/grafana/secret-refs.d.ts +19 -0
  22. package/dist/grafana/secret-refs.d.ts.map +1 -0
  23. package/dist/grafana/subset-match.d.ts +2 -0
  24. package/dist/grafana/subset-match.d.ts.map +1 -0
  25. package/dist/linux/index.d.ts +1 -1
  26. package/dist/linux/index.d.ts.map +1 -1
  27. package/dist/linux/index.js +3 -1
  28. package/dist/linux/index.js.map +1 -1
  29. package/docs/grafana-alerting.md +145 -0
  30. package/docs/grafana.md +4 -3
  31. package/docs/upstream-conformance.md +26 -4
  32. package/package.json +1 -1
  33. package/src/grafana/alerting-provenance.ts +109 -0
  34. package/src/grafana/contact-point-fixtures.ts +25 -0
  35. package/src/grafana/contact-point-provenance.test.ts +113 -0
  36. package/src/grafana/contact-point-secrets.test.ts +117 -0
  37. package/src/grafana/contact-point.test.ts +123 -0
  38. package/src/grafana/contact-point.ts +179 -0
  39. package/src/grafana/dashboard-model.ts +8 -57
  40. package/src/grafana/datasource.ts +5 -27
  41. package/src/grafana/index.ts +24 -4
  42. package/src/grafana/message-template.test.ts +212 -0
  43. package/src/grafana/message-template.ts +114 -0
  44. package/src/grafana/mute-timing.test.ts +227 -0
  45. package/src/grafana/mute-timing.ts +160 -0
  46. package/src/grafana/providers.ts +6 -0
  47. package/src/grafana/secret-refs.ts +48 -0
  48. package/src/grafana/subset-match.ts +71 -0
  49. package/src/linux/index-export.test.ts +25 -0
  50. package/src/linux/index.ts +15 -1
@@ -2,8 +2,7 @@
2
2
  import { Resource } from "alchemy";
3
3
  import * as Provider from "alchemy/Provider";
4
4
  import * as grafana from "@distilled.cloud/grafana";
5
- import * as Data from "effect/Data";
6
- import * as Effect2 from "effect/Effect";
5
+ import * as Effect3 from "effect/Effect";
7
6
 
8
7
  // src/grafana/resource.ts
9
8
  import { isResolved } from "alchemy/Diff";
@@ -57,12 +56,13 @@ var grafanaHandlers = (spec) => {
57
56
  };
58
57
  };
59
58
 
60
- // src/grafana/datasource.ts
61
- var GrafanaDatasource = Resource("Grafana.Datasource");
59
+ // src/grafana/secret-refs.ts
60
+ import * as Data from "effect/Data";
61
+ import * as Effect2 from "effect/Effect";
62
62
 
63
63
  class GrafanaSecretRefUnsetError extends Data.TaggedError("GrafanaSecretRefUnsetError") {
64
64
  }
65
- var resolveSecureJsonData = (refs) => Effect2.gen(function* () {
65
+ var resolveSecretRefs = (refs, describeField) => Effect2.gen(function* () {
66
66
  if (refs === undefined)
67
67
  return;
68
68
  const out = {};
@@ -70,13 +70,17 @@ var resolveSecureJsonData = (refs) => Effect2.gen(function* () {
70
70
  const raw = process.env[envVar];
71
71
  if (raw === undefined || raw.trim() === "") {
72
72
  return yield* Effect2.fail(new GrafanaSecretRefUnsetError({
73
- message: `${envVar} is unset. Export the value of secureJsonData.${field} there — ` + "never as an Alchemy prop."
73
+ message: `${envVar} is unset. Export the value of ${describeField(field)} there — ` + "never as an Alchemy prop."
74
74
  }));
75
75
  }
76
76
  out[field] = raw.trim();
77
77
  }
78
78
  return out;
79
79
  });
80
+
81
+ // src/grafana/datasource.ts
82
+ var GrafanaDatasource = Resource("Grafana.Datasource");
83
+ var resolveSecureJsonData = (refs) => resolveSecretRefs(refs, (field) => `secureJsonData.${field}`);
80
84
  var fields = (props) => ({
81
85
  name: props.name,
82
86
  type: props.type,
@@ -103,15 +107,15 @@ var spec = {
103
107
  url: live.url ?? "",
104
108
  version: live.version ?? 0
105
109
  }),
106
- create: (props) => Effect2.flatMap(resolveSecureJsonData(props.secureJsonDataRefs), (secureJsonData) => grafana.addDataSource({
110
+ create: (props) => Effect3.flatMap(resolveSecureJsonData(props.secureJsonDataRefs), (secureJsonData) => grafana.addDataSource({
107
111
  ...fields(props),
108
112
  ...secureJsonData === undefined ? {} : { secureJsonData },
109
113
  uid: props.uid
110
114
  })),
111
115
  destroy: (props) => grafana.deleteDataSourceByUID({ uid: props.uid }),
112
- fetchLive: (props) => grafana.getDataSourceByUID({ uid: props.uid }).pipe(Effect2.catchTag("NotFound", () => Effect2.succeed(undefined))),
116
+ fetchLive: (props) => grafana.getDataSourceByUID({ uid: props.uid }).pipe(Effect3.catchTag("NotFound", () => Effect3.succeed(undefined))),
113
117
  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),
114
- update: (props, live) => Effect2.flatMap(resolveSecureJsonData(props.secureJsonDataRefs), (secureJsonData) => grafana.updateDataSourceByUID({
118
+ update: (props, live) => Effect3.flatMap(resolveSecureJsonData(props.secureJsonDataRefs), (secureJsonData) => grafana.updateDataSourceByUID({
115
119
  ...fields(props),
116
120
  ...secureJsonData === undefined ? {} : { secureJsonData },
117
121
  ...live.version === undefined ? {} : { version: live.version },
@@ -119,12 +123,26 @@ var spec = {
119
123
  }))
120
124
  };
121
125
  var handlers = grafanaHandlers(spec);
122
- var GrafanaDatasourceProvider = () => Provider.effect(GrafanaDatasource, Effect2.succeed(GrafanaDatasource.Provider.of(handlers)));
126
+ var GrafanaDatasourceProvider = () => Provider.effect(GrafanaDatasource, Effect3.succeed(GrafanaDatasource.Provider.of(handlers)));
123
127
  // src/grafana/dashboard.ts
124
128
  import { Resource as Resource2 } from "alchemy";
125
129
  import * as Provider2 from "alchemy/Provider";
126
130
  import * as grafana2 from "@distilled.cloud/grafana";
127
- import * as Effect4 from "effect/Effect";
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
+ };
128
146
 
129
147
  // src/grafana/dashboard-model.ts
130
148
  var VOLATILE_FIELDS = ["id", "version", "iteration"];
@@ -141,28 +159,16 @@ var normalizeModel = (model, uid) => {
141
159
  return normalized;
142
160
  };
143
161
  var modelTitle = (model) => typeof model.title === "string" ? model.title : "";
144
- var isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
145
- var declaredContentMatches = (declared, live) => {
146
- if (declared == null)
147
- return true;
148
- if (Array.isArray(declared)) {
149
- return Array.isArray(live) && declared.length === live.length && declared.every((item, index) => declaredContentMatches(item, live[index]));
150
- }
151
- if (isPlainObject(declared)) {
152
- return isPlainObject(live) && Object.entries(declared).every(([key, value]) => declaredContentMatches(value, live[key]));
153
- }
154
- return declared === live;
155
- };
156
162
 
157
163
  // src/grafana/provisioned.ts
158
164
  import * as Data2 from "effect/Data";
159
- import * as Effect3 from "effect/Effect";
165
+ import * as Effect4 from "effect/Effect";
160
166
 
161
167
  class GrafanaProvisionedObjectError extends Data2.TaggedError("GrafanaProvisionedObjectError") {
162
168
  }
163
169
  var refuseIfProvisioned = (kind, uid, owner) => {
164
170
  const ownerClause = owner === undefined || owner === "" ? `(this Grafana instance did not report an owning file)` : `by ${owner}`;
165
- return Effect3.fail(new GrafanaProvisionedObjectError({
171
+ return Effect4.fail(new GrafanaProvisionedObjectError({
166
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.`
167
173
  }));
168
174
  };
@@ -188,15 +194,15 @@ var spec2 = {
188
194
  dashboard: normalizeModel(props.dashboard, props.uid),
189
195
  ...props.folderUid === undefined ? {} : { folderUid: props.folderUid }
190
196
  }),
191
- destroy: (props, live) => Effect4.gen(function* () {
197
+ destroy: (props, live) => Effect5.gen(function* () {
192
198
  if (live.meta?.provisioned) {
193
199
  return yield* refuseIfProvisioned("Grafana.Dashboard", props.uid, live.meta.provisionedExternalId);
194
200
  }
195
201
  yield* grafana2.deleteDashboard({ uid: props.uid });
196
202
  }),
197
- fetchLive: (props) => grafana2.getDashboard({ uid: props.uid }).pipe(Effect4.catchTag("NotFound", () => Effect4.succeed(undefined))),
203
+ fetchLive: (props) => grafana2.getDashboard({ uid: props.uid }).pipe(Effect5.catchTag("NotFound", () => Effect5.succeed(undefined))),
198
204
  matches: (attributes, props) => (props.folderUid === undefined || attributes.folderUid === props.folderUid) && declaredContentMatches(normalizeModel(props.dashboard, props.uid), attributes.dashboard),
199
- update: (props, live) => Effect4.gen(function* () {
205
+ update: (props, live) => Effect5.gen(function* () {
200
206
  if (live.meta?.provisioned) {
201
207
  return yield* refuseIfProvisioned("Grafana.Dashboard", props.uid, live.meta.provisionedExternalId);
202
208
  }
@@ -210,13 +216,13 @@ var spec2 = {
210
216
  })
211
217
  };
212
218
  var handlers2 = grafanaHandlers(spec2);
213
- var GrafanaDashboardProvider = () => Provider2.effect(GrafanaDashboard, Effect4.succeed(GrafanaDashboard.Provider.of(handlers2)));
219
+ var GrafanaDashboardProvider = () => Provider2.effect(GrafanaDashboard, Effect5.succeed(GrafanaDashboard.Provider.of(handlers2)));
214
220
  // src/grafana/folder.ts
215
221
  import { Resource as Resource3 } from "alchemy";
216
222
  import * as Provider3 from "alchemy/Provider";
217
223
  import * as grafana3 from "@distilled.cloud/grafana";
218
224
  import * as Data3 from "effect/Data";
219
- import * as Effect5 from "effect/Effect";
225
+ import * as Effect6 from "effect/Effect";
220
226
  var GrafanaFolder = Resource3("Grafana.Folder", {
221
227
  defaultRemovalPolicy: "retain"
222
228
  });
@@ -239,20 +245,20 @@ var spec3 = {
239
245
  ...props.description === undefined ? {} : { description: props.description },
240
246
  ...props.parentUid === undefined ? {} : { parentUid: props.parentUid }
241
247
  }),
242
- destroy: (props, live) => Effect5.gen(function* () {
248
+ destroy: (props, live) => Effect6.gen(function* () {
243
249
  if (live.managedBy) {
244
250
  return yield* refuseIfProvisioned("Grafana.Folder", props.uid, live.managedBy);
245
251
  }
246
252
  yield* grafana3.deleteFolder({ folder_uid: props.uid });
247
253
  }),
248
- fetchLive: (props) => grafana3.getFolderByUID({ folder_uid: props.uid }).pipe(Effect5.catchTag("NotFound", () => Effect5.succeed(undefined))),
254
+ fetchLive: (props) => grafana3.getFolderByUID({ folder_uid: props.uid }).pipe(Effect6.catchTag("NotFound", () => Effect6.succeed(undefined))),
249
255
  matches: (attributes, props) => attributes.title === props.title && (props.parentUid === undefined || attributes.parentUid === props.parentUid),
250
- update: (props, live) => Effect5.gen(function* () {
256
+ update: (props, live) => Effect6.gen(function* () {
251
257
  if (live.managedBy) {
252
258
  return yield* refuseIfProvisioned("Grafana.Folder", props.uid, live.managedBy);
253
259
  }
254
260
  if (props.parentUid !== undefined && props.parentUid !== (live.parentUid ?? undefined)) {
255
- return yield* Effect5.fail(new GrafanaFolderReparentError({
261
+ return yield* Effect6.fail(new GrafanaFolderReparentError({
256
262
  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.`
257
263
  }));
258
264
  }
@@ -265,25 +271,172 @@ var spec3 = {
265
271
  })
266
272
  };
267
273
  var handlers3 = grafanaHandlers(spec3);
268
- var GrafanaFolderProvider = () => Provider3.effect(GrafanaFolder, Effect5.succeed(GrafanaFolder.Provider.of(handlers3)));
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
+
292
+ // src/grafana/contact-point.ts
293
+ var GrafanaContactPoint = Resource4("Grafana.ContactPoint");
294
+ var nonSecretSettings = (props) => {
295
+ const refs = props.secureSettingsRefs;
296
+ if (refs === undefined)
297
+ return props.settings;
298
+ const out = {};
299
+ for (const [key, value] of Object.entries(props.settings)) {
300
+ if (!(key in refs))
301
+ out[key] = value;
302
+ }
303
+ return out;
304
+ };
305
+ var resolveSettings = (props) => Effect8.map(resolveSecretRefs(props.secureSettingsRefs, (field) => `settings.${field}`), (secrets) => secrets === undefined ? props.settings : { ...props.settings, ...secrets });
306
+ var spec4 = {
307
+ attributes: (live) => ({
308
+ disableResolveMessage: live.disableResolveMessage ?? false,
309
+ name: live.name ?? "",
310
+ provenance: live.provenance,
311
+ settings: live.settings ?? {},
312
+ type: live.type,
313
+ uid: live.uid ?? ""
314
+ }),
315
+ create: (props) => Effect8.flatMap(resolveSettings(props), (settings) => grafana4.routePostContactpoints({
316
+ name: props.name,
317
+ settings,
318
+ type: props.type,
319
+ uid: props.uid,
320
+ ...props.disableResolveMessage === undefined ? {} : { disableResolveMessage: props.disableResolveMessage }
321
+ })),
322
+ destroy: (props, live) => Effect8.gen(function* () {
323
+ if (isForeignProvenance(live.provenance)) {
324
+ return yield* refuseIfForeignProvenance("Grafana.ContactPoint", props.uid, live.provenance);
325
+ }
326
+ yield* grafana4.routeDeleteContactpoints({ UID: props.uid });
327
+ }),
328
+ fetchLive: (props) => grafana4.routeGetContactpoints({}).pipe(Effect8.map((points) => points.find((point) => point.uid === props.uid))),
329
+ 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* () {
331
+ if (isForeignProvenance(live.provenance)) {
332
+ return yield* refuseIfForeignProvenance("Grafana.ContactPoint", props.uid, live.provenance);
333
+ }
334
+ const settings = yield* resolveSettings(props);
335
+ yield* grafana4.routePutContactpoint({
336
+ UID: props.uid,
337
+ name: props.name,
338
+ settings,
339
+ type: props.type,
340
+ uid: props.uid,
341
+ ...props.disableResolveMessage === undefined ? {} : { disableResolveMessage: props.disableResolveMessage }
342
+ });
343
+ })
344
+ };
345
+ var handlers4 = grafanaHandlers(spec4);
346
+ var GrafanaContactPointProvider = () => Provider4.effect(GrafanaContactPoint, Effect8.succeed(GrafanaContactPoint.Provider.of(handlers4)));
347
+ // 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 = {
354
+ attributes: (live) => ({
355
+ name: live.name ?? "",
356
+ provenance: live.provenance,
357
+ template: live.template ?? ""
358
+ }),
359
+ create: (props) => grafana5.routePutTemplate({ name: props.name, template: props.template }),
360
+ destroy: (props, live) => Effect9.gen(function* () {
361
+ if (isForeignProvenance(live.provenance)) {
362
+ return yield* refuseIfForeignProvenance("Grafana.MessageTemplate", props.name, live.provenance);
363
+ }
364
+ yield* grafana5.routeDeleteTemplate({ name: props.name });
365
+ }),
366
+ fetchLive: (props) => grafana5.routeGetTemplate({ name: props.name }).pipe(Effect9.catchTag("NotFound", () => Effect9.succeed(undefined))),
367
+ matches: (attributes, props) => attributes.template === props.template,
368
+ update: (props, live) => Effect9.gen(function* () {
369
+ if (isForeignProvenance(live.provenance)) {
370
+ return yield* refuseIfForeignProvenance("Grafana.MessageTemplate", props.name, live.provenance);
371
+ }
372
+ yield* grafana5.routePutTemplate({
373
+ name: props.name,
374
+ template: props.template,
375
+ ...live.version === undefined ? {} : { version: live.version }
376
+ });
377
+ })
378
+ };
379
+ var handlers5 = grafanaHandlers(spec5);
380
+ var GrafanaMessageTemplateProvider = () => Provider5.effect(GrafanaMessageTemplate, Effect9.succeed(GrafanaMessageTemplate.Provider.of(handlers5)));
381
+ // 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 = {
388
+ attributes: (live) => ({
389
+ name: live.name ?? "",
390
+ provenance: live.provenance,
391
+ timeIntervals: live.time_intervals ?? []
392
+ }),
393
+ create: (props) => grafana6.routePostMuteTiming({
394
+ name: props.name,
395
+ time_intervals: props.timeIntervals
396
+ }),
397
+ destroy: (props, live) => Effect10.gen(function* () {
398
+ if (isForeignProvenance(live.provenance)) {
399
+ return yield* refuseIfForeignProvenance("Grafana.MuteTiming", props.name, live.provenance);
400
+ }
401
+ yield* grafana6.routeDeleteMuteTiming({
402
+ name: props.name,
403
+ ...live.version === undefined ? {} : { version: live.version }
404
+ });
405
+ }),
406
+ fetchLive: (props) => grafana6.routeGetMuteTiming({ name: props.name }).pipe(Effect10.map((live) => live), Effect10.catchTag("NotFound", () => Effect10.succeed(undefined))),
407
+ matches: (attributes, props) => declaredContentMatches(props.timeIntervals, attributes.timeIntervals),
408
+ update: (props, live) => Effect10.gen(function* () {
409
+ if (isForeignProvenance(live.provenance)) {
410
+ return yield* refuseIfForeignProvenance("Grafana.MuteTiming", props.name, live.provenance);
411
+ }
412
+ yield* grafana6.routePutMuteTiming({
413
+ name: props.name,
414
+ time_intervals: props.timeIntervals
415
+ });
416
+ })
417
+ };
418
+ var handlers6 = grafanaHandlers(spec6);
419
+ var GrafanaMuteTimingProvider = () => Provider6.effect(GrafanaMuteTiming, Effect10.succeed(GrafanaMuteTiming.Provider.of(handlers6)));
269
420
  // src/grafana/credentials.ts
270
421
  import { Credentials } from "@distilled.cloud/grafana/Credentials";
271
422
  import { ConfigError } from "@distilled.cloud/grafana/Errors";
272
- import * as Effect6 from "effect/Effect";
423
+ import * as Effect11 from "effect/Effect";
273
424
  import * as Layer from "effect/Layer";
274
425
  import * as Redacted from "effect/Redacted";
275
- var resolve = (target) => Effect6.sync(() => process.env[target.tokenEnv]).pipe(Effect6.flatMap((raw) => raw === undefined || raw.trim() === "" ? Effect6.fail(new ConfigError({
426
+ var resolve = (target) => Effect11.sync(() => process.env[target.tokenEnv]).pipe(Effect11.flatMap((raw) => raw === undefined || raw.trim() === "" ? Effect11.fail(new ConfigError({
276
427
  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}.`
277
- })) : Effect6.succeed({
428
+ })) : Effect11.succeed({
278
429
  apiBaseUrl: target.baseUrl.replace(/\/+$/, ""),
279
430
  token: Redacted.make(raw.trim()),
280
431
  ...target.orgId === undefined ? {} : { orgId: target.orgId }
281
- })), Effect6.orDie);
432
+ })), Effect11.orDie);
282
433
  var grafanaCredentials = (target) => Layer.succeed(Credentials, resolve(target));
283
434
  // src/grafana/providers.ts
284
435
  import * as Layer2 from "effect/Layer";
285
- var grafanaProviders = (target) => Layer2.mergeAll(GrafanaDatasourceProvider(), GrafanaFolderProvider(), GrafanaDashboardProvider()).pipe(Layer2.provide(grafanaCredentials(target)));
436
+ var grafanaProviders = (target) => Layer2.mergeAll(GrafanaDatasourceProvider(), GrafanaFolderProvider(), GrafanaDashboardProvider(), GrafanaContactPointProvider(), GrafanaMuteTimingProvider(), GrafanaMessageTemplateProvider()).pipe(Layer2.provide(grafanaCredentials(target)));
286
437
  export {
438
+ GrafanaContactPoint,
439
+ GrafanaContactPointProvider,
287
440
  GrafanaDashboard,
288
441
  GrafanaDashboardProvider,
289
442
  GrafanaDatasource,
@@ -291,11 +444,15 @@ export {
291
444
  GrafanaFolder,
292
445
  GrafanaFolderProvider,
293
446
  GrafanaFolderReparentError,
447
+ GrafanaMessageTemplate,
448
+ GrafanaMessageTemplateProvider,
449
+ GrafanaMuteTiming,
450
+ GrafanaMuteTimingProvider,
294
451
  GrafanaProvisionedObjectError,
295
452
  GrafanaSecretRefUnsetError,
296
453
  grafanaCredentials,
297
454
  grafanaProviders
298
455
  };
299
456
 
300
- //# debugId=D031E5F2BA23E8BC64756E2164756E21
457
+ //# debugId=D93AD79265681B5F64756E2164756E21
301
458
  //# sourceMappingURL=index.js.map