@homeflare/alchemy 0.32.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.
- package/dist/grafana/alerting-provenance.d.ts +80 -0
- package/dist/grafana/alerting-provenance.d.ts.map +1 -0
- package/dist/grafana/contact-point-fixtures.d.ts +28 -0
- package/dist/grafana/contact-point-fixtures.d.ts.map +1 -0
- package/dist/grafana/contact-point.d.ts +99 -0
- package/dist/grafana/contact-point.d.ts.map +1 -0
- package/dist/grafana/dashboard-model.d.ts +47 -0
- package/dist/grafana/dashboard-model.d.ts.map +1 -0
- package/dist/grafana/dashboard.d.ts +98 -0
- package/dist/grafana/dashboard.d.ts.map +1 -0
- package/dist/grafana/datasource.d.ts +2 -9
- package/dist/grafana/datasource.d.ts.map +1 -1
- package/dist/grafana/folder.d.ts +100 -0
- package/dist/grafana/folder.d.ts.map +1 -0
- package/dist/grafana/index.d.ts +16 -9
- package/dist/grafana/index.d.ts.map +1 -1
- package/dist/grafana/index.js +325 -16
- package/dist/grafana/index.js.map +15 -5
- package/dist/grafana/message-template.d.ts +70 -0
- package/dist/grafana/message-template.d.ts.map +1 -0
- package/dist/grafana/mute-timing.d.ts +112 -0
- package/dist/grafana/mute-timing.d.ts.map +1 -0
- package/dist/grafana/providers.d.ts +1 -1
- package/dist/grafana/providers.d.ts.map +1 -1
- package/dist/grafana/provisioned.d.ts +11 -0
- package/dist/grafana/provisioned.d.ts.map +1 -0
- package/dist/grafana/secret-refs.d.ts +19 -0
- package/dist/grafana/secret-refs.d.ts.map +1 -0
- package/dist/grafana/subset-match.d.ts +2 -0
- package/dist/grafana/subset-match.d.ts.map +1 -0
- package/dist/linux/index.d.ts +1 -1
- package/dist/linux/index.d.ts.map +1 -1
- package/dist/linux/index.js +3 -1
- package/dist/linux/index.js.map +1 -1
- package/dist/proxmox/index.js +76 -45
- package/dist/proxmox/index.js.map +6 -6
- package/dist/proxmox/zfs-pool-form.d.ts +124 -0
- package/dist/proxmox/zfs-pool-form.d.ts.map +1 -0
- package/dist/proxmox/zfs-pool-wire.d.ts +78 -0
- package/dist/proxmox/zfs-pool-wire.d.ts.map +1 -0
- package/dist/proxmox/zfs-pool.d.ts +27 -52
- package/dist/proxmox/zfs-pool.d.ts.map +1 -1
- package/docs/grafana-alerting.md +145 -0
- package/docs/grafana-folder-dashboard.md +145 -0
- package/docs/grafana.md +12 -10
- package/docs/upstream-conformance.md +31 -4
- package/package.json +1 -1
- package/src/grafana/alerting-provenance.ts +109 -0
- package/src/grafana/contact-point-fixtures.ts +25 -0
- package/src/grafana/contact-point-provenance.test.ts +113 -0
- package/src/grafana/contact-point-secrets.test.ts +117 -0
- package/src/grafana/contact-point.test.ts +123 -0
- package/src/grafana/contact-point.ts +179 -0
- package/src/grafana/dashboard-model.test.ts +111 -0
- package/src/grafana/dashboard-model.ts +62 -0
- package/src/grafana/dashboard.test.ts +232 -0
- package/src/grafana/dashboard.ts +161 -0
- package/src/grafana/datasource.ts +5 -27
- package/src/grafana/folder.test.ts +212 -0
- package/src/grafana/folder.ts +161 -0
- package/src/grafana/index.ts +42 -9
- package/src/grafana/message-template.test.ts +212 -0
- package/src/grafana/message-template.ts +114 -0
- package/src/grafana/mute-timing.test.ts +227 -0
- package/src/grafana/mute-timing.ts +160 -0
- package/src/grafana/providers.ts +13 -1
- package/src/grafana/provisioned.ts +49 -0
- package/src/grafana/secret-refs.ts +48 -0
- package/src/grafana/subset-match.ts +71 -0
- package/src/linux/index-export.test.ts +25 -0
- package/src/linux/index.ts +15 -1
- package/src/proxmox/constraints-host-forms.test.ts +1 -1
- package/src/proxmox/zfs-pool-adopt.test.ts +26 -0
- package/src/proxmox/zfs-pool-form.ts +236 -0
- package/src/proxmox/zfs-pool-read-failure.test.ts +118 -0
- package/src/proxmox/zfs-pool-wire.ts +132 -0
- package/src/proxmox/zfs-pool.ts +106 -76
- package/dist/proxmox/zfs-pool-spec.d.ts +0 -27
- package/dist/proxmox/zfs-pool-spec.d.ts.map +0 -1
- package/dist/proxmox/zfs-pool-write.d.ts +0 -128
- package/dist/proxmox/zfs-pool-write.d.ts.map +0 -1
- package/src/proxmox/zfs-pool-spec.ts +0 -76
- package/src/proxmox/zfs-pool-write.ts +0 -196
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shared refusal for a foreign-provenance ALERTING-PROVISIONING object — `Grafana.ContactPoint`,
|
|
3
|
+
* `Grafana.MessageTemplate`, `Grafana.AlertRuleGroup` and `Grafana.NotificationPolicy` each read a
|
|
4
|
+
* `provenance` string off what Grafana returns (`EmbeddedContactPoint.provenance`,
|
|
5
|
+
* `NotificationTemplate.provenance`, `ProvisionedAlertRule.provenance` per rule, `Route.provenance`
|
|
6
|
+
* on the policy tree — all measured against `@distilled.cloud/grafana`'s generated types,
|
|
7
|
+
* `packages/distilled-grafana/src/services/grafana.ts`) and refuse a write when it disagrees with
|
|
8
|
+
* what this family is allowed to own. Sibling to `provisioned.ts`, not a rename of it — the two
|
|
9
|
+
* refusals guard different fields with different value shapes (`managedBy`/`provisioned` there,
|
|
10
|
+
* `provenance` here) for two different Grafana API surfaces (classic folder/dashboard vs. alerting
|
|
11
|
+
* provisioning), but both end in the SAME tagged error, reused from `provisioned.ts` rather than
|
|
12
|
+
* declared twice: "a write was refused because something else owns this object" is one condition
|
|
13
|
+
* across the whole family, the spirit PR 245's provisioned refusal already established.
|
|
14
|
+
*
|
|
15
|
+
* ★ THE ALLOWLIST, MEASURED AGAINST GRAFANA'S OWN SOURCE (not this SDK, which only echoes the
|
|
16
|
+
* value back on read — never re-fetched live in this PR): Grafana's `pkg/services/ngalert/
|
|
17
|
+
* models` package defines a `Provenance` type with `ProvenanceNone = ""` (nothing has claimed
|
|
18
|
+
* the object — fresh, or written through the classic non-provisioning path), `ProvenanceAPI =
|
|
19
|
+
* "api"` (created or last written through THIS provisioning API — what this family's own
|
|
20
|
+
* create/update leaves behind), `ProvenanceFile = "file"` (Grafana's file-based provisioning),
|
|
21
|
+
* and `ProvenanceConvertedPrometheus = "converted_prometheus"` (the Prometheus-rules importer).
|
|
22
|
+
* Corroborated by Grafana's own docs (grafana.com/docs/grafana/v13.1/alerting/set-up/
|
|
23
|
+
* provision-alerting-resources/http-api-provisioning/), whose example payloads show
|
|
24
|
+
* `"provenance": "api"` on an alert rule this API created and `"provenance": "file"` on a
|
|
25
|
+
* file-provisioned mute timing and template.
|
|
26
|
+
*
|
|
27
|
+
* `WRITABLE_PROVENANCES` is therefore an ALLOWLIST of `{"", "api"}`, not a denylist of the two
|
|
28
|
+
* named foreign values — a provenance source not yet named above (a plugin importer, a future
|
|
29
|
+
* Grafana release) refuses by default instead of silently being treated as writable, the safer
|
|
30
|
+
* failure direction for a control-plane write.
|
|
31
|
+
*
|
|
32
|
+
* ⛔ A MISSING `provenance` FIELD IS TREATED AS FOREIGN, NEVER AS `ProvenanceNone` — fixed after an
|
|
33
|
+
* adversarial review of this PR found the first version failed OPEN: every resource mapped
|
|
34
|
+
* `live.provenance ?? ''`, and `''` (`ProvenanceNone`, writable) is indistinguishable from a
|
|
35
|
+
* field the response simply never included once both collapse through `??`. Every `provenance`
|
|
36
|
+
* field in this family is optional on the wire (`EmbeddedContactPoint.provenance?: string`, etc.
|
|
37
|
+
* — genuinely absent is a real, typed possibility, not a hypothetical), and `Grafana.MuteTiming`'s
|
|
38
|
+
* depends on an unmodeled-key passthrough this family has never observed against a real Grafana
|
|
39
|
+
* response (see its own file header) — a version, edge case or bug in that decode path that drops
|
|
40
|
+
* the field would, under the old `?? ''` rule, silently make a file-provisioned object look
|
|
41
|
+
* writable. `isForeignProvenance`/`refuseIfForeignProvenance` below keep `undefined` as its own
|
|
42
|
+
* value all the way through: ONLY a field Grafana explicitly reported as `""` or `"api"` is
|
|
43
|
+
* writable; missing entirely refuses, with a message that says so rather than naming a fabricated
|
|
44
|
+
* provenance value.
|
|
45
|
+
*
|
|
46
|
+
* ⚠️ CLIENT-SIDE DEFENSE, NOT THE ONLY GUARD — AND NOT RE-MEASURED LIVE IN THIS PR. Grafana's own
|
|
47
|
+
* alerting provisioning API server ALSO enforces this (unlike the classic folder/dashboard write
|
|
48
|
+
* API `provisioned.ts` guards, which has no such server-side check at all): grafana/grafana#103597
|
|
49
|
+
* ("Unable to delete file provisioned alert rules") reports exactly this block firing for a
|
|
50
|
+
* `file`-provenance object, and the `X-Disable-Provenance` header's own documented purpose is "by
|
|
51
|
+
* default, you cannot edit API-provisioned alerting resources in Grafana" (same doc page). This
|
|
52
|
+
* function exists anyway, for the same reason `provisioned.ts` does: a clear, typed, house error
|
|
53
|
+
* instead of whatever 400-with-a-string-message Grafana's server happens to return this version —
|
|
54
|
+
* and as a defense against the known server-side bug that same issue documents
|
|
55
|
+
* (`X-Disable-Provenance` failing to actually unlock a `file`-provenance object), which could
|
|
56
|
+
* otherwise leave a write silently rejected with only a generic `BadRequest` and no explanation
|
|
57
|
+
* of why.
|
|
58
|
+
*
|
|
59
|
+
* ⚠️ `Grafana.MuteTiming` ALSO USES THIS FUNCTION, BUT THROUGH A WIDENED LIVE TYPE — the SDK's
|
|
60
|
+
* generated `MuteTimeInterval` interface (the TS type for both `GET .../mute-timings` and
|
|
61
|
+
* `GET .../mute-timings/{name}`) declares no `provenance` field at all, unlike every other type
|
|
62
|
+
* named above. A first pass at this family assumed that meant the DATA was gone; it is not —
|
|
63
|
+
* MEASURED (see mute-timing.ts's own file header for the full account): `@distilled.cloud/core`'s
|
|
64
|
+
* response decoding never runs a strict schema decode, only `JSON.parse` + a key-rename pass that
|
|
65
|
+
* passes any key a type doesn't model through verbatim, so `provenance` survives on the actual
|
|
66
|
+
* runtime value even though the TS type doesn't know about it. `mute-timing.ts` reads it through
|
|
67
|
+
* a small local `& { provenance?: string }` intersection type rather than the SDK's own.
|
|
68
|
+
*/
|
|
69
|
+
import * as Effect from 'effect/Effect';
|
|
70
|
+
import { GrafanaProvisionedObjectError } from './provisioned.ts';
|
|
71
|
+
export { GrafanaProvisionedObjectError };
|
|
72
|
+
/**
|
|
73
|
+
* True when `provenance` names an owner other than "this API" — INCLUDING when Grafana did not
|
|
74
|
+
* report the field at all (`undefined`). `undefined` is deliberately NEVER folded into `''`
|
|
75
|
+
* (`ProvenanceNone`) here — see the file header's "fails open" note. Only a value Grafana
|
|
76
|
+
* explicitly reported as `""` or `"api"` is writable.
|
|
77
|
+
*/
|
|
78
|
+
export declare const isForeignProvenance: (provenance: string | undefined) => boolean;
|
|
79
|
+
export declare const refuseIfForeignProvenance: (kind: 'Grafana.ContactPoint' | 'Grafana.MessageTemplate' | 'Grafana.MuteTiming' | 'Grafana.AlertRuleGroup' | 'Grafana.NotificationPolicy', id: string, provenance: string | undefined) => Effect.Effect<never, GrafanaProvisionedObjectError>;
|
|
80
|
+
//# sourceMappingURL=alerting-provenance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"alerting-provenance.d.ts","sourceRoot":"","sources":["../../src/grafana/alerting-provenance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmEG;AACH,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,6BAA6B,EAAE,MAAM,kBAAkB,CAAC;AAEjE,OAAO,EAAE,6BAA6B,EAAE,CAAC;AAIzC;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,eAAgB,MAAM,GAAG,SAAS,KAAG,OACF,CAAC;AAEpE,eAAO,MAAM,yBAAyB,SAEhC,sBAAsB,GACtB,yBAAyB,GACzB,oBAAoB,GACpB,wBAAwB,GACxB,4BAA4B,MAC5B,MAAM,cACE,MAAM,GAAG,SAAS,KAC7B,MAAM,CAAC,MAAM,CAAC,KAAK,EAAE,6BAA6B,CAepD,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared fixtures for `contact-point.test.ts`/`contact-point-secrets.test.ts`/
|
|
3
|
+
* `contact-point-provenance.test.ts` — split across three files to keep each under the house's
|
|
4
|
+
* 250-line cap. Deliberately NOT named `*.test.ts`: importing one test file from another would
|
|
5
|
+
* make `bun test`'s glob discovery execute its `describe` blocks twice (once directly, once as an
|
|
6
|
+
* import side effect of the importing file) — this file has none to run, only plain data.
|
|
7
|
+
*/
|
|
8
|
+
export declare const LIST_PATH = "/api/v1/provisioning/contact-points";
|
|
9
|
+
export declare const ITEM_PATH = "/api/v1/provisioning/contact-points/slack-oncall";
|
|
10
|
+
export declare const liveJson: {
|
|
11
|
+
disableResolveMessage: boolean;
|
|
12
|
+
name: string;
|
|
13
|
+
provenance: string;
|
|
14
|
+
settings: {
|
|
15
|
+
recipient: string;
|
|
16
|
+
};
|
|
17
|
+
type: string;
|
|
18
|
+
uid: string;
|
|
19
|
+
};
|
|
20
|
+
export declare const props: {
|
|
21
|
+
name: string;
|
|
22
|
+
settings: {
|
|
23
|
+
recipient: string;
|
|
24
|
+
};
|
|
25
|
+
type: string;
|
|
26
|
+
uid: string;
|
|
27
|
+
};
|
|
28
|
+
//# sourceMappingURL=contact-point-fixtures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contact-point-fixtures.d.ts","sourceRoot":"","sources":["../../src/grafana/contact-point-fixtures.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,eAAO,MAAM,SAAS,wCAAwC,CAAC;AAC/D,eAAO,MAAM,SAAS,qDAAqD,CAAC;AAE5E,eAAO,MAAM,QAAQ;IACnB,qBAAqB;IACrB,IAAI;IACJ,UAAU;IACV,QAAQ;QAAI,SAAS;;IACrB,IAAI;IACJ,GAAG;CACJ,CAAC;AAEF,eAAO,MAAM,KAAK;IAChB,IAAI;IACJ,QAAQ;QAAI,SAAS;;IACrB,IAAI;IACJ,GAAG;CACJ,CAAC"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Grafana.ContactPoint` — one Grafana alerting contact point, keyed by `uid`.
|
|
3
|
+
*
|
|
4
|
+
* ⚠️ NO GET-BY-UID ROUTE EXISTS — MEASURED against the SDK's generated operations (grep across
|
|
5
|
+
* `services/grafana.ts` for `routeGet.*[Cc]ontactpoint`: only `routeGetContactpoints` (list,
|
|
6
|
+
* with an optional `name` filter) and `routeGetContactpointsExport`). Unlike
|
|
7
|
+
* `Grafana.Datasource`/`Grafana.Folder`/`Grafana.Dashboard`, `fetchLive` cannot key its read
|
|
8
|
+
* directly on `uid`. Filtering server-side by `name` is NOT a substitute: `name` is a grouping
|
|
9
|
+
* label a declaration can rename (`uid` is the only field this resource keys on, and Grafana's
|
|
10
|
+
* own doc calls `name` "used as grouping key in the UI" — several contact points can share one),
|
|
11
|
+
* so narrowing the GET to the DECLARED name would miss a live object still filed under an OLDER
|
|
12
|
+
* name mid-rename, reporting it absent and attempting a duplicate `create`. `fetchLive` instead
|
|
13
|
+
* lists every contact point (`routeGetContactpoints({})`, no filter) and matches `uid`
|
|
14
|
+
* client-side — the same ambiguity `resource.ts`'s header and netbox's `soleMatch` name, resolved
|
|
15
|
+
* the same way: requiring the one field the API is actually keyed on.
|
|
16
|
+
*
|
|
17
|
+
* ⛔ SECRET SETTINGS NEVER APPEAR AS A LITERAL PROP (S25) — same shape as `Grafana.Datasource`'s
|
|
18
|
+
* `secureJsonDataRefs` (`secret-refs.ts`, shared). Grafana does not use a separate top-level
|
|
19
|
+
* "secure settings" field for contact points the way it does for datasources; a secure key (a
|
|
20
|
+
* webhook URL with an embedded token, an API key, …) lives INSIDE `settings` on write, and comes
|
|
21
|
+
* back inside `settings` REDACTED on read — Grafana's own docs for the contact-points EXPORT
|
|
22
|
+
* route say as much explicitly ("Redacted settings will contain RedactedValue instead"; the
|
|
23
|
+
* plain, non-export GET this resource calls has no `decrypt` option at all, so it is redacted
|
|
24
|
+
* unconditionally). `secureSettingsRefs` therefore names which keys of `settings` are secret —
|
|
25
|
+
* value resolved from an env var NAME fresh inside `create`/`update`'s own effect (S24), merged
|
|
26
|
+
* into `settings` only at that point, never stored and never diffed (see `matches` below).
|
|
27
|
+
*
|
|
28
|
+
* ⛔ A FOREIGN-PROVENANCE CONTACT POINT REFUSES EVERY UPDATE AND DESTROY — `alerting-provenance.ts`,
|
|
29
|
+
* shared with `Grafana.MessageTemplate`/`Grafana.AlertRuleGroup`/`Grafana.NotificationPolicy`.
|
|
30
|
+
* See that file's header for the full reasoning and citations. `create` is never at risk: it only
|
|
31
|
+
* runs when `fetchLive` already found nothing with this `uid`.
|
|
32
|
+
*
|
|
33
|
+
* ⚠️ `X-Disable-Provenance` IS NEVER SET, BY DESIGN. Every create/update through this resource
|
|
34
|
+
* therefore leaves the contact point with `provenance: "api"` (Grafana's own default for an
|
|
35
|
+
* object this API just wrote) — which the docs describe as blocking that same object from being
|
|
36
|
+
* edited in the Grafana UI afterward. That is the intended outcome, not an oversight: an object
|
|
37
|
+
* this family declares is meant to be owned by the declaration, the same "one owner per resource"
|
|
38
|
+
* posture `provisioned.ts` establishes for folders/dashboards — handing it back to UI-editability
|
|
39
|
+
* by default would silently create the two-owner situation this whole file exists to prevent.
|
|
40
|
+
*/
|
|
41
|
+
import { Resource } from 'alchemy';
|
|
42
|
+
import * as Provider from 'alchemy/Provider';
|
|
43
|
+
import * as grafana from '@distilled.cloud/grafana';
|
|
44
|
+
import * as Effect from 'effect/Effect';
|
|
45
|
+
import { type GrafanaProvisionedObjectError } from './alerting-provenance.ts';
|
|
46
|
+
import { type GrafanaSpec } from './resource.ts';
|
|
47
|
+
import { GrafanaSecretRefUnsetError } from './secret-refs.ts';
|
|
48
|
+
export { GrafanaSecretRefUnsetError };
|
|
49
|
+
export interface ContactPointProps {
|
|
50
|
+
uid: string;
|
|
51
|
+
name: string;
|
|
52
|
+
/** Grafana integration id — `slack`, `webhook`, `email`, `pagerduty`, `opsgenie`, … */
|
|
53
|
+
type: string;
|
|
54
|
+
/** Non-secret settings — shape depends on `type`. Never put a secret VALUE here — declare it
|
|
55
|
+
* in `secureSettingsRefs` instead; see the file header. */
|
|
56
|
+
settings: Record<string, unknown>;
|
|
57
|
+
/** Secure field name (e.g. `url`, `token`, `password`) -> env var NAME holding its value. */
|
|
58
|
+
secureSettingsRefs?: Record<string, string>;
|
|
59
|
+
disableResolveMessage?: boolean;
|
|
60
|
+
}
|
|
61
|
+
export interface ContactPointAttributes {
|
|
62
|
+
uid: string;
|
|
63
|
+
name: string;
|
|
64
|
+
type: string;
|
|
65
|
+
/** As Grafana returns it — any secure key is a redaction placeholder, never the real value. */
|
|
66
|
+
settings: Record<string, unknown>;
|
|
67
|
+
disableResolveMessage: boolean;
|
|
68
|
+
/** `""`/`"api"` (this family may write); anything else, INCLUDING `undefined` (Grafana didn't
|
|
69
|
+
* report the field), refuses — see alerting-provenance.ts's "fails open" note. */
|
|
70
|
+
provenance: string | undefined;
|
|
71
|
+
}
|
|
72
|
+
export interface GrafanaContactPoint extends Resource<'Grafana.ContactPoint', ContactPointProps, ContactPointAttributes, never> {
|
|
73
|
+
}
|
|
74
|
+
export declare const GrafanaContactPoint: import("alchemy").ResourceClass<GrafanaContactPoint>;
|
|
75
|
+
export declare const spec: GrafanaSpec<ContactPointProps, grafana.EmbeddedContactPoint, ContactPointAttributes, grafana.RouteGetContactpointsError | grafana.RoutePostContactpointsError | grafana.RoutePutContactpointError | grafana.RouteDeleteContactpointsError | GrafanaProvisionedObjectError | GrafanaSecretRefUnsetError>;
|
|
76
|
+
export declare const handlers: {
|
|
77
|
+
list: () => Effect.Effect<never[], never, never>;
|
|
78
|
+
read: ({ olds }: {
|
|
79
|
+
olds: ContactPointProps;
|
|
80
|
+
}) => Effect.Effect<ContactPointAttributes | undefined, grafana.BadGateway | import("@distilled.cloud/caddy").BadRequest | grafana.BadRequest | grafana.ConfigError | import("@distilled.cloud/caddy").Conflict | grafana.Conflict | import("@distilled.cloud/caddy").Forbidden | grafana.Forbidden | grafana.GatewayTimeout | GrafanaProvisionedObjectError | GrafanaSecretRefUnsetError | import("effect/unstable/http/HttpClientError").HttpClientError | grafana.InternalServerError | grafana.Locked | import("@distilled.cloud/caddy").NotFound | grafana.ServiceUnavailable | grafana.TooManyRequests | grafana.Unauthorized | grafana.UnknownGrafanaError | import("@distilled.cloud/caddy").UnprocessableEntity, grafana.GrafanaOpContext>;
|
|
81
|
+
diff: ({ news, output }: {
|
|
82
|
+
news: import("alchemy").Input<ContactPointProps>;
|
|
83
|
+
output: ContactPointAttributes | undefined;
|
|
84
|
+
}) => Effect.Effect<{
|
|
85
|
+
readonly action: 'update';
|
|
86
|
+
} | {
|
|
87
|
+
readonly action: 'noop';
|
|
88
|
+
} | {
|
|
89
|
+
readonly action: 'replace';
|
|
90
|
+
} | undefined, grafana.BadGateway | import("@distilled.cloud/caddy").BadRequest | grafana.BadRequest | grafana.ConfigError | import("@distilled.cloud/caddy").Conflict | grafana.Conflict | import("@distilled.cloud/caddy").Forbidden | grafana.Forbidden | grafana.GatewayTimeout | GrafanaProvisionedObjectError | GrafanaSecretRefUnsetError | import("effect/unstable/http/HttpClientError").HttpClientError | grafana.InternalServerError | grafana.Locked | import("@distilled.cloud/caddy").NotFound | grafana.ServiceUnavailable | grafana.TooManyRequests | grafana.Unauthorized | grafana.UnknownGrafanaError | import("@distilled.cloud/caddy").UnprocessableEntity, grafana.GrafanaOpContext>;
|
|
91
|
+
reconcile: ({ news }: {
|
|
92
|
+
news: ContactPointProps;
|
|
93
|
+
}) => Effect.Effect<ContactPointAttributes, grafana.BadGateway | import("@distilled.cloud/caddy").BadRequest | grafana.BadRequest | grafana.ConfigError | import("@distilled.cloud/caddy").Conflict | grafana.Conflict | import("@distilled.cloud/caddy").Forbidden | grafana.Forbidden | grafana.GatewayTimeout | GrafanaProvisionedObjectError | GrafanaSecretRefUnsetError | import("effect/unstable/http/HttpClientError").HttpClientError | grafana.InternalServerError | grafana.Locked | import("@distilled.cloud/caddy").NotFound | grafana.ServiceUnavailable | grafana.TooManyRequests | grafana.Unauthorized | grafana.UnknownGrafanaError | import("@distilled.cloud/caddy").UnprocessableEntity, grafana.GrafanaOpContext>;
|
|
94
|
+
delete: ({ olds }: {
|
|
95
|
+
olds: ContactPointProps;
|
|
96
|
+
}) => Effect.Effect<void, grafana.BadGateway | import("@distilled.cloud/caddy").BadRequest | grafana.BadRequest | grafana.ConfigError | import("@distilled.cloud/caddy").Conflict | grafana.Conflict | import("@distilled.cloud/caddy").Forbidden | grafana.Forbidden | grafana.GatewayTimeout | GrafanaProvisionedObjectError | GrafanaSecretRefUnsetError | import("effect/unstable/http/HttpClientError").HttpClientError | grafana.InternalServerError | grafana.Locked | import("@distilled.cloud/caddy").NotFound | grafana.ServiceUnavailable | grafana.TooManyRequests | grafana.Unauthorized | grafana.UnknownGrafanaError | import("@distilled.cloud/caddy").UnprocessableEntity, grafana.GrafanaOpContext>;
|
|
97
|
+
};
|
|
98
|
+
export declare const GrafanaContactPointProvider: () => import("effect/Layer").Layer<Provider.Provider<GrafanaContactPoint>, never, grafana.GrafanaOpContext>;
|
|
99
|
+
//# sourceMappingURL=contact-point.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contact-point.d.ts","sourceRoot":"","sources":["../../src/grafana/contact-point.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,OAAO,MAAM,0BAA0B,CAAC;AACpD,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EACL,KAAK,6BAA6B,EAGnC,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,KAAK,WAAW,EAAmB,MAAM,eAAe,CAAC;AAClE,OAAO,EAAE,0BAA0B,EAAqB,MAAM,kBAAkB,CAAC;AAGjF,OAAO,EAAE,0BAA0B,EAAE,CAAC;AAEtC,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,uFAAuF;IACvF,IAAI,EAAE,MAAM,CAAC;IACb;gEAC4D;IAC5D,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,6FAA6F;IAC7F,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC5C,qBAAqB,CAAC,EAAE,OAAO,CAAC;CACjC;AAED,MAAM,WAAW,sBAAsB;IACrC,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,+FAA+F;IAC/F,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,qBAAqB,EAAE,OAAO,CAAC;IAC/B;uFACmF;IACnF,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC;AAED,MAAM,WAAW,mBAAoB,SAAQ,QAAQ,CACnD,sBAAsB,EACtB,iBAAiB,EACjB,sBAAsB,EACtB,KAAK,CACN;CAAG;AAEJ,eAAO,MAAM,mBAAmB,sDAAwD,CAAC;AAoBzF,eAAO,MAAM,IAAI,EAAE,WAAW,CAC5B,iBAAiB,EACjB,OAAO,CAAC,oBAAoB,EAC5B,sBAAsB,EACpB,OAAO,CAAC,0BAA0B,GAClC,OAAO,CAAC,2BAA2B,GACnC,OAAO,CAAC,yBAAyB,GACjC,OAAO,CAAC,6BAA6B,GACrC,6BAA6B,GAC7B,0BAA0B,CAyD7B,CAAC;AAEF,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;CAAwB,CAAC;AAE9C,eAAO,MAAM,2BAA2B,6GACyD,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure functions over a dashboard JSON model — no SDK call, no Effect. Split out of dashboard.ts
|
|
3
|
+
* so that file stays under the house's 250-line cap, and so `normalizeModel`'s no-change-is-noop
|
|
4
|
+
* behavior is exercised directly in dashboard-model.test.ts without a fake Grafana in the way.
|
|
5
|
+
*/
|
|
6
|
+
export type DashboardModel = Record<string, unknown>;
|
|
7
|
+
/**
|
|
8
|
+
* ★ NORMALIZES BOTH SIDES THE SAME WAY, so a no-change plan is a true noop
|
|
9
|
+
* (docs/grafana-folder-dashboard.md). Strips `id`/`version`/`iteration` (Grafana-managed, never
|
|
10
|
+
* declared content) and forces `uid` to this resource's OWN canonical value, wherever — or
|
|
11
|
+
* whether — the source object carried one. That second part is "uid placement": a dashboard
|
|
12
|
+
* pasted from Grafana's own "Export as JSON" always embeds the OLD live uid (or none, for a
|
|
13
|
+
* never-saved dashboard), while the live model this resource reads back always carries the
|
|
14
|
+
* CURRENT one; forcing both to `props.uid` before comparing is what makes those two shapes equal
|
|
15
|
+
* instead of permanently disagreeing on one field neither side is meant to declare independently
|
|
16
|
+
* (`DashboardProps.uid` — see dashboard.ts — is the single source of truth).
|
|
17
|
+
*
|
|
18
|
+
* ⚠️ EVERY OTHER DECLARED FIELD COMPARES STRUCTURALLY, ARRAY ORDER INCLUDED — dashboard.ts's
|
|
19
|
+
* `matches` runs this output through THIS FILE's own `declaredContentMatches` (below), which
|
|
20
|
+
* never reorders an array. That is correct here: `panels` is position-significant (screen layout
|
|
21
|
+
* via its own `gridPos`, but the array order also drives Grafana's internal panel indexing and
|
|
22
|
+
* any `repeat` behavior) and each panel's `targets` is execution/legend order. Nothing in a
|
|
23
|
+
* dashboard model is a genuinely unordered SET the way UniFi's `trustedDhcpServerIpAddresses` is
|
|
24
|
+
* (unifi/network-form.ts's `sortedSet`) — this family has no field that needs one.
|
|
25
|
+
* ⚠️ `schemaVersion` IS DELIBERATELY **NOT** NORMALIZED — NOT MEASURED against a live instance (no
|
|
26
|
+
* live calls in this PR). Community reports say Grafana can rewrite an old dashboard's
|
|
27
|
+
* `schemaVersion` upward on load; IF that also happens on every plain save of an
|
|
28
|
+
* already-current-schema dashboard, every plan against it would show a false `update` forever —
|
|
29
|
+
* but silently normalizing it away on a guess would just as easily hide a genuine schema
|
|
30
|
+
* mismatch. Left comparing until measured: add it to `VOLATILE_FIELDS` with a dated, measured
|
|
31
|
+
* comment the day it is seen live, the same standard every other row in this file meets.
|
|
32
|
+
*/
|
|
33
|
+
export declare const normalizeModel: (model: DashboardModel, uid: string) => DashboardModel;
|
|
34
|
+
/** The model's own declared title — reporting only. It is never compared on its own; the whole
|
|
35
|
+
* normalized model is, in dashboard.ts's `matches`, so a title-only mismatch still shows `update`. */
|
|
36
|
+
export declare const modelTitle: (model: DashboardModel) => string;
|
|
37
|
+
/**
|
|
38
|
+
* `declared` need only be a subset of `live` — dashboard.ts's `matches` runs `normalizeModel`'s
|
|
39
|
+
* output through this. Moved to `subset-match.ts` once `Grafana.ContactPoint`'s `settings` needed
|
|
40
|
+
* the identical "declaration silent about a field means don't compare it" rule; re-exported here
|
|
41
|
+
* under its original name so this file's own callers and dashboard-model.test.ts are unchanged.
|
|
42
|
+
* See subset-match.ts for the full reasoning, including the regression this exists to fix
|
|
43
|
+
* (Grafana's dashboard-save schema migration auto-decorating a panel's `datasource`) and the
|
|
44
|
+
* trade-off it accepts (an omitted field can no longer force-clear a previously-set one).
|
|
45
|
+
*/
|
|
46
|
+
export { declaredContentMatches } from './subset-match.ts';
|
|
47
|
+
//# sourceMappingURL=dashboard-model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard-model.d.ts","sourceRoot":"","sources":["../../src/grafana/dashboard-model.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAKrD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,eAAO,MAAM,cAAc,UAAW,cAAc,OAAO,MAAM,KAAG,cASnE,CAAC;AAEF;uGACuG;AACvG,eAAO,MAAM,UAAU,UAAW,cAAc,KAAG,MACC,CAAC;AAErD;;;;;;;;GAQG;AACH,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Grafana.Dashboard` — one Grafana dashboard, keyed by `uid`. Same uid-required doctrine as
|
|
3
|
+
* `datasource.ts`/`folder.ts` (`getDashboard`/`postDashboard`/`deleteDashboard` are all UID-keyed
|
|
4
|
+
* on the wire — `postDashboard`'s own `uid` travels INSIDE the `dashboard` JSON body, not as a
|
|
5
|
+
* request field, because Grafana's classic dashboard-save API has never had one; see
|
|
6
|
+
* `dashboard-model.ts`'s `normalizeModel`, which is what puts it there).
|
|
7
|
+
*
|
|
8
|
+
* ★ THE DECLARED CONTENT IS THE DASHBOARD JSON MODEL — `DashboardProps.dashboard`, the same shape
|
|
9
|
+
* Grafana's own "Export as JSON" produces. `dashboard-model.ts`'s `normalizeModel` strips the
|
|
10
|
+
* volatile fields (`id`, `version`, `iteration`) and forces `uid` to this resource's own value
|
|
11
|
+
* before ANY comparison, so a plan against an unchanged dashboard is a true noop despite those
|
|
12
|
+
* fields moving on every live save — see that file for the full reasoning, including which
|
|
13
|
+
* fields (`panels`, `targets`) are array-ORDER-significant and compared as such.
|
|
14
|
+
*
|
|
15
|
+
* ⛔ `matches` COMPARES "IS THE DECLARATION A SUBSET OF WHAT IS LIVE", NOT PLAIN EQUALITY —
|
|
16
|
+
* `dashboard-model.ts`'s `declaredContentMatches`, added after an adversarial review of this PR
|
|
17
|
+
* found plain `deepEqual` made an ordinary dashboard show `update` FOREVER: Grafana's own
|
|
18
|
+
* schema-migration on save decorates any panel/target whose `datasource` is absent with an
|
|
19
|
+
* explicit reference, so a declaration that never sets one would permanently disagree with what
|
|
20
|
+
* it reads back. See that function's own header for the full reasoning and the trade-off it
|
|
21
|
+
* accepts (an omitted field can no longer be used to CLEAR a previously-set one).
|
|
22
|
+
*
|
|
23
|
+
* ⛔ VERSION CONFLICTS (412) FAIL LOUDLY — NEVER OVERWRITTEN BLINDLY. `update` injects the
|
|
24
|
+
* `version` this resource just read (`live.meta.version`, from the SAME `fetchLive` call
|
|
25
|
+
* `resource.ts`'s `reconcile` already made before calling `update`) into the model it sends, and
|
|
26
|
+
* `overwrite` is never set. If Grafana's own version check then still fails — someone else saved
|
|
27
|
+
* in the gap between that read and this write — `postDashboard` answers `412` and
|
|
28
|
+
* `PreconditionFailed` is left IN the error union below, uncaught: the whole reconcile fails, the
|
|
29
|
+
* deploy stops, and nothing is written. The next run's `fetchLive` re-reads the new live version
|
|
30
|
+
* fresh, which is the "re-read" half of "re-read and fail loudly" — there is no separate retry
|
|
31
|
+
* loop in this file, because the next `plan`/`deploy` invocation already IS that re-read.
|
|
32
|
+
*
|
|
33
|
+
* ⛔ A PROVISIONED DASHBOARD (`meta.provisioned` — see provisioned.ts) REFUSES EVERY UPDATE AND
|
|
34
|
+
* DESTROY, naming `meta.provisionedExternalId` (the file) when Grafana reports one. `matches`
|
|
35
|
+
* still compares structurally regardless, so a mismatched declaration against a provisioned
|
|
36
|
+
* dashboard shows `update` in a plan rather than a silent, permanently-hidden noop.
|
|
37
|
+
*
|
|
38
|
+
* ⚠️ NO RETAIN DEFAULT, UNLIKE `Grafana.Folder`. Deleting one dashboard is a single-object blast
|
|
39
|
+
* radius — the same class `Grafana.Datasource` already accepts without a retain guard — not
|
|
40
|
+
* folder.ts's multi-object cascade (a folder's delete takes its dashboards AND their alert rules
|
|
41
|
+
* with it). See folder.ts's file header for the full contrast.
|
|
42
|
+
*/
|
|
43
|
+
import { Resource } from 'alchemy';
|
|
44
|
+
import * as Provider from 'alchemy/Provider';
|
|
45
|
+
import * as grafana from '@distilled.cloud/grafana';
|
|
46
|
+
import * as Effect from 'effect/Effect';
|
|
47
|
+
import { type DashboardModel } from './dashboard-model.ts';
|
|
48
|
+
import { type GrafanaProvisionedObjectError } from './provisioned.ts';
|
|
49
|
+
import { type GrafanaSpec } from './resource.ts';
|
|
50
|
+
export type { DashboardModel };
|
|
51
|
+
export interface DashboardProps {
|
|
52
|
+
uid: string;
|
|
53
|
+
/** Omit for the General/root folder. */
|
|
54
|
+
folderUid?: string;
|
|
55
|
+
/** The dashboard JSON model — see the file header. `id`/`version`/`iteration`/`uid` inside it
|
|
56
|
+
* are ignored; `uid` above is the single source of truth. */
|
|
57
|
+
dashboard: DashboardModel;
|
|
58
|
+
}
|
|
59
|
+
export interface DashboardAttributes {
|
|
60
|
+
uid: string;
|
|
61
|
+
title: string;
|
|
62
|
+
version: number;
|
|
63
|
+
folderUid: string;
|
|
64
|
+
url: string;
|
|
65
|
+
/** True when Grafana reports this dashboard as file-provisioned (read-only — see provisioned.ts). */
|
|
66
|
+
provisioned: boolean;
|
|
67
|
+
provisionedExternalId: string;
|
|
68
|
+
/** The live model, normalized the same way `matches` compares it. */
|
|
69
|
+
dashboard: DashboardModel;
|
|
70
|
+
}
|
|
71
|
+
export interface GrafanaDashboard extends Resource<'Grafana.Dashboard', DashboardProps, DashboardAttributes, never> {
|
|
72
|
+
}
|
|
73
|
+
export declare const GrafanaDashboard: import("alchemy").ResourceClass<GrafanaDashboard>;
|
|
74
|
+
export declare const spec: GrafanaSpec<DashboardProps, grafana.DashboardFullWithMeta, DashboardAttributes, grafana.GetDashboardError | grafana.PostDashboardError | grafana.DeleteDashboardError | GrafanaProvisionedObjectError>;
|
|
75
|
+
export declare const handlers: {
|
|
76
|
+
list: () => Effect.Effect<never[], never, never>;
|
|
77
|
+
read: ({ olds }: {
|
|
78
|
+
olds: DashboardProps;
|
|
79
|
+
}) => Effect.Effect<DashboardAttributes | undefined, grafana.BadGateway | import("@distilled.cloud/caddy").BadRequest | grafana.BadRequest | grafana.ConfigError | import("@distilled.cloud/caddy").Conflict | import("@distilled.cloud/caddy").Forbidden | grafana.Forbidden | grafana.GatewayTimeout | GrafanaProvisionedObjectError | import("effect/unstable/http/HttpClientError").HttpClientError | grafana.InternalServerError | grafana.Locked | import("@distilled.cloud/caddy").NotFound | grafana.NotFound | grafana.PreconditionFailed | grafana.ServiceUnavailable | grafana.TooManyRequests | grafana.Unauthorized | grafana.UnknownGrafanaError | import("@distilled.cloud/caddy").UnprocessableEntity | grafana.UnprocessableEntity, grafana.GrafanaOpContext>;
|
|
80
|
+
diff: ({ news, output }: {
|
|
81
|
+
news: import("alchemy").Input<DashboardProps>;
|
|
82
|
+
output: DashboardAttributes | undefined;
|
|
83
|
+
}) => Effect.Effect<{
|
|
84
|
+
readonly action: 'update';
|
|
85
|
+
} | {
|
|
86
|
+
readonly action: 'noop';
|
|
87
|
+
} | {
|
|
88
|
+
readonly action: 'replace';
|
|
89
|
+
} | undefined, grafana.BadGateway | import("@distilled.cloud/caddy").BadRequest | grafana.BadRequest | grafana.ConfigError | import("@distilled.cloud/caddy").Conflict | import("@distilled.cloud/caddy").Forbidden | grafana.Forbidden | grafana.GatewayTimeout | GrafanaProvisionedObjectError | import("effect/unstable/http/HttpClientError").HttpClientError | grafana.InternalServerError | grafana.Locked | import("@distilled.cloud/caddy").NotFound | grafana.NotFound | grafana.PreconditionFailed | grafana.ServiceUnavailable | grafana.TooManyRequests | grafana.Unauthorized | grafana.UnknownGrafanaError | import("@distilled.cloud/caddy").UnprocessableEntity | grafana.UnprocessableEntity, grafana.GrafanaOpContext>;
|
|
90
|
+
reconcile: ({ news }: {
|
|
91
|
+
news: DashboardProps;
|
|
92
|
+
}) => Effect.Effect<DashboardAttributes, grafana.BadGateway | import("@distilled.cloud/caddy").BadRequest | grafana.BadRequest | grafana.ConfigError | import("@distilled.cloud/caddy").Conflict | import("@distilled.cloud/caddy").Forbidden | grafana.Forbidden | grafana.GatewayTimeout | GrafanaProvisionedObjectError | import("effect/unstable/http/HttpClientError").HttpClientError | grafana.InternalServerError | grafana.Locked | import("@distilled.cloud/caddy").NotFound | grafana.NotFound | grafana.PreconditionFailed | grafana.ServiceUnavailable | grafana.TooManyRequests | grafana.Unauthorized | grafana.UnknownGrafanaError | import("@distilled.cloud/caddy").UnprocessableEntity | grafana.UnprocessableEntity, grafana.GrafanaOpContext>;
|
|
93
|
+
delete: ({ olds }: {
|
|
94
|
+
olds: DashboardProps;
|
|
95
|
+
}) => Effect.Effect<void, grafana.BadGateway | import("@distilled.cloud/caddy").BadRequest | grafana.BadRequest | grafana.ConfigError | import("@distilled.cloud/caddy").Conflict | import("@distilled.cloud/caddy").Forbidden | grafana.Forbidden | grafana.GatewayTimeout | GrafanaProvisionedObjectError | import("effect/unstable/http/HttpClientError").HttpClientError | grafana.InternalServerError | grafana.Locked | import("@distilled.cloud/caddy").NotFound | grafana.NotFound | grafana.PreconditionFailed | grafana.ServiceUnavailable | grafana.TooManyRequests | grafana.Unauthorized | grafana.UnknownGrafanaError | import("@distilled.cloud/caddy").UnprocessableEntity | grafana.UnprocessableEntity, grafana.GrafanaOpContext>;
|
|
96
|
+
};
|
|
97
|
+
export declare const GrafanaDashboardProvider: () => import("effect/Layer").Layer<Provider.Provider<GrafanaDashboard>, never, grafana.GrafanaOpContext>;
|
|
98
|
+
//# sourceMappingURL=dashboard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../../src/grafana/dashboard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,OAAO,MAAM,0BAA0B,CAAC;AACpD,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EACL,KAAK,cAAc,EAIpB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,KAAK,6BAA6B,EAAuB,MAAM,kBAAkB,CAAC;AAC3F,OAAO,EAAE,KAAK,WAAW,EAAmB,MAAM,eAAe,CAAC;AAElE,YAAY,EAAE,cAAc,EAAE,CAAC;AAE/B,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;kEAC8D;IAC9D,SAAS,EAAE,cAAc,CAAC;CAC3B;AAED,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,qGAAqG;IACrG,WAAW,EAAE,OAAO,CAAC;IACrB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,qEAAqE;IACrE,SAAS,EAAE,cAAc,CAAC;CAC3B;AAED,MAAM,WAAW,gBAAiB,SAAQ,QAAQ,CAChD,mBAAmB,EACnB,cAAc,EACd,mBAAmB,EACnB,KAAK,CACN;CAAG;AAEJ,eAAO,MAAM,gBAAgB,mDAAkD,CAAC;AAOhF,eAAO,MAAM,IAAI,EAAE,WAAW,CAC5B,cAAc,EACd,OAAO,CAAC,qBAAqB,EAC7B,mBAAmB,EACjB,OAAO,CAAC,iBAAiB,GACzB,OAAO,CAAC,kBAAkB,GAC1B,OAAO,CAAC,oBAAoB,GAC5B,6BAA6B,CAuDhC,CAAC;AAEF,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;CAAwB,CAAC;AAE9C,eAAO,MAAM,wBAAwB,0GACsD,CAAC"}
|
|
@@ -22,6 +22,8 @@ import * as Provider from 'alchemy/Provider';
|
|
|
22
22
|
import * as grafana from '@distilled.cloud/grafana';
|
|
23
23
|
import * as Effect from 'effect/Effect';
|
|
24
24
|
import { type GrafanaSpec } from './resource.ts';
|
|
25
|
+
import { GrafanaSecretRefUnsetError } from './secret-refs.ts';
|
|
26
|
+
export { GrafanaSecretRefUnsetError };
|
|
25
27
|
export interface DatasourceProps {
|
|
26
28
|
uid: string;
|
|
27
29
|
name: string;
|
|
@@ -54,14 +56,6 @@ export interface DatasourceAttributes {
|
|
|
54
56
|
export interface GrafanaDatasource extends Resource<'Grafana.Datasource', DatasourceProps, DatasourceAttributes, never> {
|
|
55
57
|
}
|
|
56
58
|
export declare const GrafanaDatasource: import("alchemy").ResourceClass<GrafanaDatasource>;
|
|
57
|
-
declare const GrafanaSecretRefUnsetError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
58
|
-
readonly _tag: "GrafanaSecretRefUnsetError";
|
|
59
|
-
} & Readonly<A>;
|
|
60
|
-
/** A named secure-field env var is unset — a domain refusal, not a distilled error. */
|
|
61
|
-
export declare class GrafanaSecretRefUnsetError extends GrafanaSecretRefUnsetError_base<{
|
|
62
|
-
readonly message: string;
|
|
63
|
-
}> {
|
|
64
|
-
}
|
|
65
59
|
export declare const spec: GrafanaSpec<DatasourceProps, grafana.DataSource, DatasourceAttributes, grafana.AddDataSourceError | grafana.GetDataSourceByUIDError | grafana.UpdateDataSourceByUIDError | grafana.DeleteDataSourceByUIDError | GrafanaSecretRefUnsetError>;
|
|
66
60
|
export declare const handlers: {
|
|
67
61
|
list: () => Effect.Effect<never[], never, never>;
|
|
@@ -86,5 +80,4 @@ export declare const handlers: {
|
|
|
86
80
|
}) => Effect.Effect<void, grafana.BadGateway | import("@distilled.cloud/caddy").BadRequest | grafana.BadRequest | grafana.ConfigError | import("@distilled.cloud/caddy").Conflict | grafana.Conflict | import("@distilled.cloud/caddy").Forbidden | grafana.Forbidden | grafana.GatewayTimeout | GrafanaSecretRefUnsetError | import("effect/unstable/http/HttpClientError").HttpClientError | grafana.InternalServerError | grafana.Locked | import("@distilled.cloud/caddy").NotFound | grafana.NotFound | grafana.ServiceUnavailable | grafana.TooManyRequests | grafana.Unauthorized | grafana.UnknownGrafanaError | import("@distilled.cloud/caddy").UnprocessableEntity, grafana.GrafanaOpContext>;
|
|
87
81
|
};
|
|
88
82
|
export declare const GrafanaDatasourceProvider: () => import("effect/Layer").Layer<Provider.Provider<GrafanaDatasource>, never, grafana.GrafanaOpContext>;
|
|
89
|
-
export {};
|
|
90
83
|
//# sourceMappingURL=datasource.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"datasource.d.ts","sourceRoot":"","sources":["../../src/grafana/datasource.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,OAAO,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"datasource.d.ts","sourceRoot":"","sources":["../../src/grafana/datasource.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,OAAO,MAAM,0BAA0B,CAAC;AACpD,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,KAAK,WAAW,EAAmB,MAAM,eAAe,CAAC;AAClE,OAAO,EAAE,0BAA0B,EAAqB,MAAM,kBAAkB,CAAC;AAEjF,OAAO,EAAE,0BAA0B,EAAE,CAAC;AAEtC,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,8FAA8F;IAC9F,kBAAkB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC7C;AAED,MAAM,WAAW,oBAAoB;IACnC,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAkB,SAAQ,QAAQ,CACjD,oBAAoB,EACpB,eAAe,EACf,oBAAoB,EACpB,KAAK,CACN;CAAG;AAEJ,eAAO,MAAM,iBAAiB,oDAAoD,CAAC;AAqBnF,eAAO,MAAM,IAAI,EAAE,WAAW,CAC5B,eAAe,EACf,OAAO,CAAC,UAAU,EAClB,oBAAoB,EAClB,OAAO,CAAC,kBAAkB,GAC1B,OAAO,CAAC,uBAAuB,GAC/B,OAAO,CAAC,0BAA0B,GAClC,OAAO,CAAC,0BAA0B,GAClC,0BAA0B,CAgD7B,CAAC;AAEF,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;CAAwB,CAAC;AAE9C,eAAO,MAAM,yBAAyB,2GACuD,CAAC"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Grafana.Folder` — one Grafana folder, keyed by `uid`. Mirrors `datasource.ts`'s shape and
|
|
3
|
+
* doctrine (uid required, not generated — same reasoning, `getFolderByUID`/`updateFolder`/
|
|
4
|
+
* `deleteFolder` are all UID-keyed and `getFolders` (list) has no per-uid filter).
|
|
5
|
+
*
|
|
6
|
+
* ⛔ `description` IS WRITE-ONLY — MEASURED against `@distilled.cloud/grafana`'s generated types
|
|
7
|
+
* (`Folder` in services/grafana.ts has no `description` field at all, though
|
|
8
|
+
* `CreateFolderRequest`/`UpdateFolderRequest` both accept one). Grafana's classic folder GET
|
|
9
|
+
* never returns it, not even as an is-it-set flag the way `DataSource.secureJsonFields` reports
|
|
10
|
+
* for a datasource's secure fields. So `description` is sent on every create/update that
|
|
11
|
+
* declares it but is NEVER part of `matches` — this resource cannot detect drift in a value it
|
|
12
|
+
* is never shown, the same treatment `datasource.ts` gives `secureJsonDataRefs`.
|
|
13
|
+
*
|
|
14
|
+
* ⚠️ NESTING (`parentUid`) IS CREATE-ONLY — MEASURED against the same generated types.
|
|
15
|
+
* `CreateFolderRequest.parentUid` exists; `UpdateFolderRequest` has no `parentUid` field at all
|
|
16
|
+
* (`folder_uid`, `description`, `overwrite`, `title`, `version` — reparenting is simply not a
|
|
17
|
+
* thing `PUT /folders/{uid}` can do). A declaration whose `parentUid` no longer matches the live
|
|
18
|
+
* folder therefore REFUSES with `GrafanaFolderReparentError` rather than either silently doing
|
|
19
|
+
* nothing (which would lie about the declaration taking effect) or deleting and recreating the
|
|
20
|
+
* folder under the new parent (which — see the ⛔ below — takes every dashboard and alert rule in
|
|
21
|
+
* it with it). Move a folder by hand in Grafana, then adjust the declaration to match.
|
|
22
|
+
*
|
|
23
|
+
* ⛔ DELETE DEFAULTS TO `retain` — DELETING A FOLDER DELETES ITS DASHBOARDS AND ALERT RULES IN
|
|
24
|
+
* GRAFANA, per `deleteFolder`'s own operation description ("along with all dashboards (and their
|
|
25
|
+
* alerts) stored in the folder... cannot be reverted... also deletes all the subfolders"). That
|
|
26
|
+
* is the same class of multi-object cascade `openbao/mount.ts` guards with
|
|
27
|
+
* `defaultRemovalPolicy: 'retain'` ("disabling a mount destroys every secret under it") — VERIFIED
|
|
28
|
+
* there against Alchemy's own `Apply.ts` (not re-verified here; same engine, same option). A
|
|
29
|
+
* stack that explicitly wants the cascade opts in with `.pipe(RemovalPolicy.destroy())` on the
|
|
30
|
+
* declared instance; `destroy` below still implements the real DELETE in full either way (S11).
|
|
31
|
+
* `Grafana.Dashboard` does NOT default to retain — deleting one dashboard is the same
|
|
32
|
+
* single-object blast radius `Grafana.Datasource` already accepts without a retain guard, not
|
|
33
|
+
* this family's multi-object cascade.
|
|
34
|
+
*
|
|
35
|
+
* ⛔ A PROVISIONED FOLDER (`managedBy` set — see provisioned.ts) REFUSES EVERY UPDATE AND DESTROY.
|
|
36
|
+
* `matches` still compares structurally either way, so a mismatched declaration against a
|
|
37
|
+
* provisioned folder shows `update` in a plan (never a silent, permanently-hidden noop) and then
|
|
38
|
+
* fails loudly, by design, when reconcile actually tries to write.
|
|
39
|
+
*/
|
|
40
|
+
import { Resource } from 'alchemy';
|
|
41
|
+
import * as Provider from 'alchemy/Provider';
|
|
42
|
+
import * as grafana from '@distilled.cloud/grafana';
|
|
43
|
+
import * as Effect from 'effect/Effect';
|
|
44
|
+
import { type GrafanaProvisionedObjectError } from './provisioned.ts';
|
|
45
|
+
import { type GrafanaSpec } from './resource.ts';
|
|
46
|
+
export interface FolderProps {
|
|
47
|
+
uid: string;
|
|
48
|
+
title: string;
|
|
49
|
+
/** Write-only — see the file header. Sent on every create/update that declares it; never diffed. */
|
|
50
|
+
description?: string;
|
|
51
|
+
/** Parent folder UID, for nested folders. Create-only — see the file header. */
|
|
52
|
+
parentUid?: string;
|
|
53
|
+
}
|
|
54
|
+
export interface FolderAttributes {
|
|
55
|
+
uid: string;
|
|
56
|
+
id: number;
|
|
57
|
+
title: string;
|
|
58
|
+
parentUid: string;
|
|
59
|
+
/** Non-empty when Grafana reports this folder as externally managed (e.g. provisioning). */
|
|
60
|
+
managedBy: string;
|
|
61
|
+
url: string;
|
|
62
|
+
version: number;
|
|
63
|
+
}
|
|
64
|
+
export interface GrafanaFolder extends Resource<'Grafana.Folder', FolderProps, FolderAttributes, never> {
|
|
65
|
+
}
|
|
66
|
+
export declare const GrafanaFolder: import("alchemy").ResourceClass<GrafanaFolder>;
|
|
67
|
+
declare const GrafanaFolderReparentError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
68
|
+
readonly _tag: "GrafanaFolderReparentError";
|
|
69
|
+
} & Readonly<A>;
|
|
70
|
+
/** A declared `parentUid` disagrees with the live folder's — `updateFolder` cannot reparent. */
|
|
71
|
+
export declare class GrafanaFolderReparentError extends GrafanaFolderReparentError_base<{
|
|
72
|
+
readonly message: string;
|
|
73
|
+
}> {
|
|
74
|
+
}
|
|
75
|
+
export declare const spec: GrafanaSpec<FolderProps, grafana.Folder, FolderAttributes, grafana.CreateFolderError | grafana.GetFolderByUIDError | grafana.UpdateFolderError | grafana.DeleteFolderError | GrafanaProvisionedObjectError | GrafanaFolderReparentError>;
|
|
76
|
+
export declare const handlers: {
|
|
77
|
+
list: () => Effect.Effect<never[], never, never>;
|
|
78
|
+
read: ({ olds }: {
|
|
79
|
+
olds: FolderProps;
|
|
80
|
+
}) => Effect.Effect<FolderAttributes | undefined, grafana.BadGateway | import("@distilled.cloud/caddy").BadRequest | grafana.BadRequest | grafana.ConfigError | import("@distilled.cloud/caddy").Conflict | grafana.Conflict | import("@distilled.cloud/caddy").Forbidden | grafana.Forbidden | grafana.GatewayTimeout | GrafanaFolderReparentError | GrafanaProvisionedObjectError | import("effect/unstable/http/HttpClientError").HttpClientError | grafana.InternalServerError | grafana.Locked | import("@distilled.cloud/caddy").NotFound | grafana.NotFound | grafana.ServiceUnavailable | grafana.TooManyRequests | grafana.Unauthorized | grafana.UnknownGrafanaError | import("@distilled.cloud/caddy").UnprocessableEntity, grafana.GrafanaOpContext>;
|
|
81
|
+
diff: ({ news, output }: {
|
|
82
|
+
news: import("alchemy").Input<FolderProps>;
|
|
83
|
+
output: FolderAttributes | undefined;
|
|
84
|
+
}) => Effect.Effect<{
|
|
85
|
+
readonly action: 'update';
|
|
86
|
+
} | {
|
|
87
|
+
readonly action: 'noop';
|
|
88
|
+
} | {
|
|
89
|
+
readonly action: 'replace';
|
|
90
|
+
} | undefined, grafana.BadGateway | import("@distilled.cloud/caddy").BadRequest | grafana.BadRequest | grafana.ConfigError | import("@distilled.cloud/caddy").Conflict | grafana.Conflict | import("@distilled.cloud/caddy").Forbidden | grafana.Forbidden | grafana.GatewayTimeout | GrafanaFolderReparentError | GrafanaProvisionedObjectError | import("effect/unstable/http/HttpClientError").HttpClientError | grafana.InternalServerError | grafana.Locked | import("@distilled.cloud/caddy").NotFound | grafana.NotFound | grafana.ServiceUnavailable | grafana.TooManyRequests | grafana.Unauthorized | grafana.UnknownGrafanaError | import("@distilled.cloud/caddy").UnprocessableEntity, grafana.GrafanaOpContext>;
|
|
91
|
+
reconcile: ({ news }: {
|
|
92
|
+
news: FolderProps;
|
|
93
|
+
}) => Effect.Effect<FolderAttributes, grafana.BadGateway | import("@distilled.cloud/caddy").BadRequest | grafana.BadRequest | grafana.ConfigError | import("@distilled.cloud/caddy").Conflict | grafana.Conflict | import("@distilled.cloud/caddy").Forbidden | grafana.Forbidden | grafana.GatewayTimeout | GrafanaFolderReparentError | GrafanaProvisionedObjectError | import("effect/unstable/http/HttpClientError").HttpClientError | grafana.InternalServerError | grafana.Locked | import("@distilled.cloud/caddy").NotFound | grafana.NotFound | grafana.ServiceUnavailable | grafana.TooManyRequests | grafana.Unauthorized | grafana.UnknownGrafanaError | import("@distilled.cloud/caddy").UnprocessableEntity, grafana.GrafanaOpContext>;
|
|
94
|
+
delete: ({ olds }: {
|
|
95
|
+
olds: FolderProps;
|
|
96
|
+
}) => Effect.Effect<void, grafana.BadGateway | import("@distilled.cloud/caddy").BadRequest | grafana.BadRequest | grafana.ConfigError | import("@distilled.cloud/caddy").Conflict | grafana.Conflict | import("@distilled.cloud/caddy").Forbidden | grafana.Forbidden | grafana.GatewayTimeout | GrafanaFolderReparentError | GrafanaProvisionedObjectError | import("effect/unstable/http/HttpClientError").HttpClientError | grafana.InternalServerError | grafana.Locked | import("@distilled.cloud/caddy").NotFound | grafana.NotFound | grafana.ServiceUnavailable | grafana.TooManyRequests | grafana.Unauthorized | grafana.UnknownGrafanaError | import("@distilled.cloud/caddy").UnprocessableEntity, grafana.GrafanaOpContext>;
|
|
97
|
+
};
|
|
98
|
+
export declare const GrafanaFolderProvider: () => import("effect/Layer").Layer<Provider.Provider<GrafanaFolder>, never, grafana.GrafanaOpContext>;
|
|
99
|
+
export {};
|
|
100
|
+
//# sourceMappingURL=folder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"folder.d.ts","sourceRoot":"","sources":["../../src/grafana/folder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,OAAO,MAAM,0BAA0B,CAAC;AAEpD,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,KAAK,6BAA6B,EAAuB,MAAM,kBAAkB,CAAC;AAC3F,OAAO,EAAE,KAAK,WAAW,EAAmB,MAAM,eAAe,CAAC;AAElE,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,oGAAoG;IACpG,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,4FAA4F;IAC5F,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAc,SAAQ,QAAQ,CAC7C,gBAAgB,EAChB,WAAW,EACX,gBAAgB,EAChB,KAAK,CACN;CAAG;AAEJ,eAAO,MAAM,aAAa,gDAExB,CAAC;;;;AAEH,gGAAgG;AAChG,qBAAa,0BAA2B,SAAQ,gCAA+C;IAC7F,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B,CAAC;CAAG;AAEL,eAAO,MAAM,IAAI,EAAE,WAAW,CAC5B,WAAW,EACX,OAAO,CAAC,MAAM,EACd,gBAAgB,EACd,OAAO,CAAC,iBAAiB,GACzB,OAAO,CAAC,mBAAmB,GAC3B,OAAO,CAAC,iBAAiB,GACzB,OAAO,CAAC,iBAAiB,GACzB,6BAA6B,GAC7B,0BAA0B,CA+D7B,CAAC;AAEF,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;CAAwB,CAAC;AAE9C,eAAO,MAAM,qBAAqB,uGACmD,CAAC"}
|
package/dist/grafana/index.d.ts
CHANGED
|
@@ -2,17 +2,24 @@
|
|
|
2
2
|
* Grafana providers for Alchemy.
|
|
3
3
|
*
|
|
4
4
|
* ⛔ THIS BARREL IS THE PUBLIC API, AND IT IS DELIBERATELY SMALLER THAN THE DIRECTORY. These are
|
|
5
|
-
* the symbols a real stack consumes; the rest of the files (resource.ts's engine,
|
|
6
|
-
* are internals a provider needs but a consumer should not
|
|
5
|
+
* the symbols a real stack consumes; the rest of the files (resource.ts's engine, provisioned.ts,
|
|
6
|
+
* dashboard-model.ts, fake-grafana.ts) are internals a provider needs but a consumer should not
|
|
7
|
+
* depend on.
|
|
7
8
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* see docs/grafana.md for what changed and what is still open.
|
|
9
|
+
* `Grafana.Datasource`, `Grafana.Folder`, `Grafana.Dashboard`, `Grafana.ContactPoint`,
|
|
10
|
+
* `Grafana.MuteTiming` and `Grafana.MessageTemplate` ship here. `Grafana.AlertRuleGroup` and
|
|
11
|
+
* `Grafana.NotificationPolicy` are stacked follow-up PRs on the same SDK operations — see
|
|
12
|
+
* docs/grafana.md, docs/grafana-folder-dashboard.md and docs/grafana-alerting.md for what shipped
|
|
13
|
+
* and what is still open.
|
|
14
14
|
*/
|
|
15
|
-
export { type DatasourceAttributes, type DatasourceProps, GrafanaDatasource, GrafanaDatasourceProvider,
|
|
15
|
+
export { type DatasourceAttributes, type DatasourceProps, GrafanaDatasource, GrafanaDatasourceProvider, } from './datasource.ts';
|
|
16
|
+
export { type DashboardAttributes, type DashboardModel, type DashboardProps, GrafanaDashboard, GrafanaDashboardProvider, } from './dashboard.ts';
|
|
17
|
+
export { type FolderAttributes, type FolderProps, GrafanaFolder, GrafanaFolderProvider, GrafanaFolderReparentError, } from './folder.ts';
|
|
18
|
+
export { type ContactPointAttributes, type ContactPointProps, GrafanaContactPoint, GrafanaContactPointProvider, } from './contact-point.ts';
|
|
19
|
+
export { type MessageTemplateAttributes, type MessageTemplateProps, GrafanaMessageTemplate, GrafanaMessageTemplateProvider, } from './message-template.ts';
|
|
20
|
+
export { type MuteTimingAttributes, type MuteTimingProps, GrafanaMuteTiming, GrafanaMuteTimingProvider, } from './mute-timing.ts';
|
|
21
|
+
export { GrafanaProvisionedObjectError } from './provisioned.ts';
|
|
22
|
+
export { GrafanaSecretRefUnsetError } from './secret-refs.ts';
|
|
16
23
|
export { type GrafanaTarget, grafanaCredentials } from './credentials.ts';
|
|
17
24
|
export { grafanaProviders } from './providers.ts';
|
|
18
25
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/grafana/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,iBAAiB,EACjB,yBAAyB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/grafana/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,iBAAiB,EACjB,yBAAyB,GAC1B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EACL,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,aAAa,EACb,qBAAqB,EACrB,0BAA0B,GAC3B,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,KAAK,sBAAsB,EAC3B,KAAK,iBAAiB,EACtB,mBAAmB,EACnB,2BAA2B,GAC5B,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,KAAK,yBAAyB,EAC9B,KAAK,oBAAoB,EACzB,sBAAsB,EACtB,8BAA8B,GAC/B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACpB,iBAAiB,EACjB,yBAAyB,GAC1B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,6BAA6B,EAAE,MAAM,kBAAkB,CAAC;AACjE,OAAO,EAAE,0BAA0B,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,KAAK,aAAa,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC"}
|