@alchemy.run/axiom 0.0.0-stage → 2.0.0-beta.84

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 (83) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +58 -2
  3. package/lib/Annotation.d.ts +51 -0
  4. package/lib/Annotation.d.ts.map +1 -0
  5. package/lib/Annotation.js +97 -0
  6. package/lib/Annotation.js.map +1 -0
  7. package/lib/ApiToken.d.ts +64 -0
  8. package/lib/ApiToken.d.ts.map +1 -0
  9. package/lib/ApiToken.js +123 -0
  10. package/lib/ApiToken.js.map +1 -0
  11. package/lib/AuthProvider.d.ts +40 -0
  12. package/lib/AuthProvider.d.ts.map +1 -0
  13. package/lib/AuthProvider.js +99 -0
  14. package/lib/AuthProvider.js.map +1 -0
  15. package/lib/Chart.d.ts +130 -0
  16. package/lib/Chart.d.ts.map +1 -0
  17. package/lib/Chart.js +14 -0
  18. package/lib/Chart.js.map +1 -0
  19. package/lib/Contributions.d.ts +7 -0
  20. package/lib/Contributions.d.ts.map +1 -0
  21. package/lib/Contributions.js +6 -0
  22. package/lib/Contributions.js.map +1 -0
  23. package/lib/Credentials.d.ts +10 -0
  24. package/lib/Credentials.d.ts.map +1 -0
  25. package/lib/Credentials.js +30 -0
  26. package/lib/Credentials.js.map +1 -0
  27. package/lib/Dashboard.d.ts +112 -0
  28. package/lib/Dashboard.d.ts.map +1 -0
  29. package/lib/Dashboard.js +157 -0
  30. package/lib/Dashboard.js.map +1 -0
  31. package/lib/Dataset.d.ts +120 -0
  32. package/lib/Dataset.d.ts.map +1 -0
  33. package/lib/Dataset.js +215 -0
  34. package/lib/Dataset.js.map +1 -0
  35. package/lib/Monitor.d.ts +76 -0
  36. package/lib/Monitor.d.ts.map +1 -0
  37. package/lib/Monitor.js +126 -0
  38. package/lib/Monitor.js.map +1 -0
  39. package/lib/Notifier.d.ts +65 -0
  40. package/lib/Notifier.d.ts.map +1 -0
  41. package/lib/Notifier.js +112 -0
  42. package/lib/Notifier.js.map +1 -0
  43. package/lib/OtelHeaders.d.ts +9 -0
  44. package/lib/OtelHeaders.d.ts.map +1 -0
  45. package/lib/OtelHeaders.js +9 -0
  46. package/lib/OtelHeaders.js.map +1 -0
  47. package/lib/Providers.d.ts +15 -0
  48. package/lib/Providers.d.ts.map +1 -0
  49. package/lib/Providers.js +34 -0
  50. package/lib/Providers.js.map +1 -0
  51. package/lib/Telemetry.d.ts +66 -0
  52. package/lib/Telemetry.d.ts.map +1 -0
  53. package/lib/Telemetry.js +68 -0
  54. package/lib/Telemetry.js.map +1 -0
  55. package/lib/View.d.ts +57 -0
  56. package/lib/View.d.ts.map +1 -0
  57. package/lib/View.js +100 -0
  58. package/lib/View.js.map +1 -0
  59. package/lib/VirtualField.d.ts +44 -0
  60. package/lib/VirtualField.d.ts.map +1 -0
  61. package/lib/VirtualField.js +97 -0
  62. package/lib/VirtualField.js.map +1 -0
  63. package/lib/index.d.ts +14 -0
  64. package/lib/index.d.ts.map +1 -0
  65. package/lib/index.js +14 -0
  66. package/lib/index.js.map +1 -0
  67. package/package.json +68 -3
  68. package/src/Annotation.ts +121 -0
  69. package/src/ApiToken.ts +148 -0
  70. package/src/AuthProvider.ts +135 -0
  71. package/src/Chart.ts +156 -0
  72. package/src/Contributions.ts +9 -0
  73. package/src/Credentials.ts +68 -0
  74. package/src/Dashboard.ts +192 -0
  75. package/src/Dataset.ts +326 -0
  76. package/src/Monitor.ts +148 -0
  77. package/src/Notifier.ts +136 -0
  78. package/src/OtelHeaders.ts +9 -0
  79. package/src/Providers.ts +60 -0
  80. package/src/Telemetry.ts +116 -0
  81. package/src/View.ts +130 -0
  82. package/src/VirtualField.ts +123 -0
  83. package/src/index.ts +13 -0
@@ -0,0 +1,120 @@
1
+ import * as Axiom from "@distilled.cloud/axiom";
2
+ import * as Provider from "alchemy/Provider";
3
+ import { Resource } from "alchemy/Resource";
4
+ import { Stack } from "alchemy/Stack";
5
+ import { Stage } from "alchemy/Stage";
6
+ import type { Providers } from "./Providers.ts";
7
+ export type DatasetKind = "otel:metrics:v1" | "otel:traces:v1" | "otel:logs:v1" | "axiom:events:v1" | (string & {});
8
+ export type DatasetProps = {
9
+ /**
10
+ * Dataset name. Used as the dataset's stable identifier in Axiom — changing
11
+ * this triggers a replacement.
12
+ */
13
+ name: string;
14
+ /** Free-form description shown in the Axiom UI. */
15
+ description?: string;
16
+ /**
17
+ * Edge deployment where Axiom stores and processes the dataset's data.
18
+ * Defaults to the organization's default edge deployment.
19
+ *
20
+ * Cannot be changed after creation — triggers a replacement.
21
+ */
22
+ edgeDeployment?: string;
23
+ /**
24
+ * Dataset kind. Defaults to `axiom:events:v1`.
25
+ *
26
+ * For OTEL pipelines, choose:
27
+ * - `otel:traces:v1` for traces
28
+ * - `otel:logs:v1` for logs
29
+ * - `otel:metrics:v1` for metrics
30
+ *
31
+ * Cannot be changed after creation — triggers a replacement.
32
+ */
33
+ kind?: DatasetKind;
34
+ /** Retention in days. Plan-dependent — see Axiom docs. */
35
+ retentionDays?: number;
36
+ /** Whether to enforce the configured retention period. */
37
+ useRetentionPeriod?: boolean;
38
+ };
39
+ export type Dataset = Resource<"Axiom.Dataset", DatasetProps, {
40
+ id: string;
41
+ name: string;
42
+ kind: DatasetKind;
43
+ description: string;
44
+ created: string;
45
+ /** Edge deployment where the dataset's data is stored and processed. */
46
+ edgeDeployment: string;
47
+ /** Base URL for ingesting and querying data in the dataset's edge deployment. */
48
+ edgeDeploymentUrl: string;
49
+ /** Base URL for Axiom's centralized management API. */
50
+ apiBaseUrl: string;
51
+ /** Root OTLP endpoint. Most exporters auto-append the signal path. */
52
+ otelEndpoint: string;
53
+ /** OTLP/HTTP traces endpoint. */
54
+ otelTracesEndpoint: string;
55
+ /** OTLP/HTTP logs endpoint. */
56
+ otelLogsEndpoint: string;
57
+ /** OTLP/HTTP metrics endpoint. */
58
+ otelMetricsEndpoint: string;
59
+ /**
60
+ * Headers required for OTLP shipping aside from the bearer token:
61
+ * `X-Axiom-Dataset`, or `X-Axiom-Metrics-Dataset` for an
62
+ * `otel:metrics:v1` dataset.
63
+ * Add `Authorization: Bearer <AXIOM_TOKEN>` separately at runtime so the
64
+ * secret is never persisted in resource state.
65
+ */
66
+ otelHeaders: Record<string, string>;
67
+ }, never, Providers>;
68
+ /**
69
+ * An Axiom dataset — the top-level container that stores events, logs,
70
+ * traces, or metrics. Pick a `kind` up-front: it determines schema and how
71
+ * the data is shown in the UI, and **cannot be changed** after creation
72
+ * (changing it triggers a replacement, which deletes the data).
73
+ *
74
+ * Datasets expose their edge deployment and Axiom's OTLP/HTTP endpoints
75
+ * (`otelTracesEndpoint`, `otelLogsEndpoint`, `otelMetricsEndpoint`) as output
76
+ * attributes so you can inject them into a Worker / Lambda's env vars for
77
+ * OpenTelemetry shipping. The bearer token is **not** stored in resource state
78
+ * — supply `Authorization: Bearer <AXIOM_TOKEN>` separately at runtime.
79
+ * @see https://axiom.co/docs/reference/datasets
80
+ * @see https://axiom.co/docs/send-data/opentelemetry — OTLP endpoint reference
81
+ *
82
+ * ### Creating a Dataset
83
+ * **Example:** Logs dataset with 30-day retention
84
+ * ```typescript
85
+ * const logs = yield* Axiom.Dataset("app-logs", {
86
+ * name: "my-app-logs",
87
+ * kind: "otel:logs:v1",
88
+ * description: "Application logs from prod workers",
89
+ * retentionDays: 30,
90
+ * useRetentionPeriod: true,
91
+ * });
92
+ * ```
93
+ *
94
+ * **Example:** Separate datasets per OTEL signal
95
+ * ```typescript
96
+ * const traces = yield* Axiom.Dataset("traces", { name: "app-traces", kind: "otel:traces:v1" });
97
+ * const logs = yield* Axiom.Dataset("logs", { name: "app-logs", kind: "otel:logs:v1" });
98
+ * const metrics = yield* Axiom.Dataset("metrics", { name: "app-metrics", kind: "otel:metrics:v1" });
99
+ * ```
100
+ *
101
+ * ### Shipping OTEL data
102
+ * **Example:** Wire OTEL env vars into a Cloudflare Worker
103
+ * ```typescript
104
+ * yield* Cloudflare.Worker("api", {
105
+ * vars: {
106
+ * OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: traces.otelTracesEndpoint,
107
+ * OTEL_EXPORTER_OTLP_LOGS_ENDPOINT: logs.otelLogsEndpoint,
108
+ * // Bearer token must come from a secret store, not the dataset state.
109
+ * OTEL_EXPORTER_OTLP_HEADERS:
110
+ * `Authorization=Bearer ${env.AXIOM_TOKEN},X-Axiom-Dataset=${traces.name}`,
111
+ * },
112
+ * });
113
+ * ```
114
+ *
115
+ * @resource
116
+ * @product Dataset
117
+ */
118
+ export declare const Dataset: import("alchemy/Resource").ResourceClass<Dataset>;
119
+ export declare const DatasetProvider: () => import("effect/Layer").Layer<Provider.Provider<Dataset>, never, Stack | Stage | Axiom.AxiomOpContext>;
120
+ //# sourceMappingURL=Dataset.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Dataset.d.ts","sourceRoot":"","sources":["../src/Dataset.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,wBAAwB,CAAC;AAIhD,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAGtC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,MAAM,WAAW,GACnB,iBAAiB,GACjB,gBAAgB,GAChB,cAAc,GACd,iBAAiB,GACjB,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAElB,MAAM,MAAM,YAAY,GAAG;IACzB;;;OAGG;IACH,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;;;;;OASG;IACH,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,0DAA0D;IAC1D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,0DAA0D;IAC1D,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,OAAO,GAAG,QAAQ,CAC5B,eAAe,EACf,YAAY,EACZ;IACE,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,WAAW,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,cAAc,EAAE,MAAM,CAAC;IACvB,iFAAiF;IACjF,iBAAiB,EAAE,MAAM,CAAC;IAC1B,uDAAuD;IACvD,UAAU,EAAE,MAAM,CAAC;IACnB,sEAAsE;IACtE,YAAY,EAAE,MAAM,CAAC;IACrB,iCAAiC;IACjC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,+BAA+B;IAC/B,gBAAgB,EAAE,MAAM,CAAC;IACzB,kCAAkC;IAClC,mBAAmB,EAAE,MAAM,CAAC;IAC5B;;;;;;OAMG;IACH,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACrC,EACD,KAAK,EACL,SAAS,CACV,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,eAAO,MAAM,OAAO,mDAAqC,CAAC;AA8B1D,eAAO,MAAM,eAAe,6GA8JzB,CAAC"}
package/lib/Dataset.js ADDED
@@ -0,0 +1,215 @@
1
+ import * as Axiom from "@distilled.cloud/axiom";
2
+ import { Credentials } from "@distilled.cloud/axiom/Credentials";
3
+ import { Unowned } from "alchemy/AdoptPolicy";
4
+ import { isResolved } from "alchemy/Diff";
5
+ import * as Provider from "alchemy/Provider";
6
+ import { Resource } from "alchemy/Resource";
7
+ import { Stack } from "alchemy/Stack";
8
+ import { Stage } from "alchemy/Stage";
9
+ import * as Effect from "effect/Effect";
10
+ import { otelDatasetHeader } from "./OtelHeaders.js";
11
+ /**
12
+ * An Axiom dataset — the top-level container that stores events, logs,
13
+ * traces, or metrics. Pick a `kind` up-front: it determines schema and how
14
+ * the data is shown in the UI, and **cannot be changed** after creation
15
+ * (changing it triggers a replacement, which deletes the data).
16
+ *
17
+ * Datasets expose their edge deployment and Axiom's OTLP/HTTP endpoints
18
+ * (`otelTracesEndpoint`, `otelLogsEndpoint`, `otelMetricsEndpoint`) as output
19
+ * attributes so you can inject them into a Worker / Lambda's env vars for
20
+ * OpenTelemetry shipping. The bearer token is **not** stored in resource state
21
+ * — supply `Authorization: Bearer <AXIOM_TOKEN>` separately at runtime.
22
+ * @see https://axiom.co/docs/reference/datasets
23
+ * @see https://axiom.co/docs/send-data/opentelemetry — OTLP endpoint reference
24
+ *
25
+ * ### Creating a Dataset
26
+ * **Example:** Logs dataset with 30-day retention
27
+ * ```typescript
28
+ * const logs = yield* Axiom.Dataset("app-logs", {
29
+ * name: "my-app-logs",
30
+ * kind: "otel:logs:v1",
31
+ * description: "Application logs from prod workers",
32
+ * retentionDays: 30,
33
+ * useRetentionPeriod: true,
34
+ * });
35
+ * ```
36
+ *
37
+ * **Example:** Separate datasets per OTEL signal
38
+ * ```typescript
39
+ * const traces = yield* Axiom.Dataset("traces", { name: "app-traces", kind: "otel:traces:v1" });
40
+ * const logs = yield* Axiom.Dataset("logs", { name: "app-logs", kind: "otel:logs:v1" });
41
+ * const metrics = yield* Axiom.Dataset("metrics", { name: "app-metrics", kind: "otel:metrics:v1" });
42
+ * ```
43
+ *
44
+ * ### Shipping OTEL data
45
+ * **Example:** Wire OTEL env vars into a Cloudflare Worker
46
+ * ```typescript
47
+ * yield* Cloudflare.Worker("api", {
48
+ * vars: {
49
+ * OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: traces.otelTracesEndpoint,
50
+ * OTEL_EXPORTER_OTLP_LOGS_ENDPOINT: logs.otelLogsEndpoint,
51
+ * // Bearer token must come from a secret store, not the dataset state.
52
+ * OTEL_EXPORTER_OTLP_HEADERS:
53
+ * `Authorization=Bearer ${env.AXIOM_TOKEN},X-Axiom-Dataset=${traces.name}`,
54
+ * },
55
+ * });
56
+ * ```
57
+ *
58
+ * @resource
59
+ * @product Dataset
60
+ */
61
+ export const Dataset = Resource("Axiom.Dataset");
62
+ /**
63
+ * Axiom has no tags/labels API. The only writable field we can use to mark
64
+ * ownership is `description`. We append a deterministic marker on create so
65
+ * that on a re-apply (e.g. state was wiped) we can safely identify a dataset
66
+ * that **we** previously created and adopt it idempotently — without
67
+ * accidentally hijacking a dataset created by someone else with the same name.
68
+ */
69
+ const MARKER_RE = /\s*\[alchemy:stack=([^;]+);stage=([^;]+);id=([^\]]+)\]\s*$/;
70
+ const buildMarker = (stack, stage, id) => `[alchemy:stack=${stack};stage=${stage};id=${id}]`;
71
+ const augmentDescription = (description, marker) => {
72
+ const base = stripMarker(description ?? "");
73
+ return base.length > 0 ? `${base}\n${marker}` : marker;
74
+ };
75
+ const stripMarker = (description) => description.replace(MARKER_RE, "").trimEnd();
76
+ const parseMarker = (description) => {
77
+ if (!description)
78
+ return undefined;
79
+ const m = description.match(MARKER_RE);
80
+ if (!m)
81
+ return undefined;
82
+ return { stack: m[1], stage: m[2], id: m[3] };
83
+ };
84
+ export const DatasetProvider = () => Provider.effect(Dataset, Effect.gen(function* () {
85
+ const { apiBaseUrl } = yield* yield* Credentials;
86
+ const create = yield* Axiom.createDataset;
87
+ const update = yield* Axiom.updateDataset;
88
+ const get = yield* Axiom.getDataset;
89
+ const listDatasets = yield* Axiom.getDatasets;
90
+ const del = yield* Axiom.deleteDataset;
91
+ const toAttrs = Effect.fn(function* (dataset) {
92
+ if (!dataset.edgeDeployment || !dataset.edgeDeploymentUrl) {
93
+ return yield* Effect.fail(new Error(`Axiom dataset "${dataset.name}" is missing its edge deployment metadata`));
94
+ }
95
+ const apiRoot = apiBaseUrl.replace(/\/$/, "");
96
+ const otelRoot = dataset.edgeDeploymentUrl.replace(/\/$/, "");
97
+ return {
98
+ id: dataset.id,
99
+ name: dataset.name,
100
+ kind: dataset.kind,
101
+ description: stripMarker(dataset.description),
102
+ created: dataset.created,
103
+ edgeDeployment: dataset.edgeDeployment,
104
+ edgeDeploymentUrl: dataset.edgeDeploymentUrl,
105
+ apiBaseUrl: apiRoot,
106
+ otelEndpoint: otelRoot,
107
+ otelTracesEndpoint: `${otelRoot}/v1/traces`,
108
+ otelLogsEndpoint: `${otelRoot}/v1/logs`,
109
+ otelMetricsEndpoint: `${otelRoot}/v1/metrics`,
110
+ otelHeaders: {
111
+ [otelDatasetHeader(dataset.kind === "otel:metrics:v1")]: dataset.name,
112
+ },
113
+ };
114
+ });
115
+ return {
116
+ stables: ["id", "name", "kind", "edgeDeployment", "edgeDeploymentUrl"],
117
+ // Enumerate every dataset in the org. Axiom exposes a single
118
+ // account-wide `GET /v2/datasets` collection op (no pagination), so we
119
+ // fetch it once and hydrate each row into the exact `read`/`toAttrs`
120
+ // Attributes shape — directly usable by `delete` with no follow-up get.
121
+ list: () => Effect.gen(function* () {
122
+ const datasets = yield* listDatasets({});
123
+ return yield* Effect.forEach(datasets, toAttrs);
124
+ }),
125
+ diff: Effect.fn(function* ({ olds, news, output }) {
126
+ if (!isResolved(news))
127
+ return undefined;
128
+ if (output && news.name !== output.name) {
129
+ return { action: "replace" };
130
+ }
131
+ if (news.kind && output && news.kind !== output.kind) {
132
+ return { action: "replace" };
133
+ }
134
+ if (news.edgeDeployment && output && news.edgeDeployment !== output.edgeDeployment) {
135
+ return { action: "replace" };
136
+ }
137
+ if (news.description !== olds?.description ||
138
+ news.retentionDays !== olds?.retentionDays ||
139
+ news.useRetentionPeriod !== olds?.useRetentionPeriod) {
140
+ return { action: "update" };
141
+ }
142
+ return undefined;
143
+ }),
144
+ reconcile: Effect.fn(function* ({ id, news, output }) {
145
+ const stack = yield* Stack;
146
+ const stage = yield* Stage;
147
+ const marker = buildMarker(stack.name, stage, id);
148
+ // Observe — Axiom's dataset path identifier is `name`, which is
149
+ // also stable input. Probe for live state by name (preferring the
150
+ // cached `output.id` when present). `read` upstream has already
151
+ // surfaced foreign datasets as `Unowned`, so by the time we land
152
+ // here mutation is safe.
153
+ const datasetId = output?.id ?? news.name;
154
+ const observed = yield* get({ dataset_id: datasetId }).pipe(Effect.catchTag("NotFound", () => Effect.succeed(undefined)));
155
+ // Ensure — POST creates the dataset. Tolerate Conflict/
156
+ // UnprocessableEntity as a race with a peer reconciler (or with
157
+ // upstream read↔create), falling through to the sync path.
158
+ let current = observed;
159
+ if (current === undefined) {
160
+ current = yield* create({
161
+ name: news.name,
162
+ description: augmentDescription(news.description, marker),
163
+ edgeDeployment: news.edgeDeployment,
164
+ kind: news.kind,
165
+ retentionDays: news.retentionDays,
166
+ useRetentionPeriod: news.useRetentionPeriod,
167
+ }).pipe(Effect.catchIf((e) => e._tag === "Conflict" || e._tag === "UnprocessableEntity", () => update({
168
+ dataset_id: news.name,
169
+ description: augmentDescription(news.description, marker),
170
+ retentionDays: news.retentionDays,
171
+ useRetentionPeriod: news.useRetentionPeriod,
172
+ })));
173
+ return yield* toAttrs(current);
174
+ }
175
+ // Sync — the dataset exists. Apply mutable aspects (description,
176
+ // retentionDays, useRetentionPeriod) via PATCH. `kind` and `name`
177
+ // are stable and replacement-only via diff above.
178
+ const desiredDescription = augmentDescription(news.description, marker);
179
+ const needsSync = current.description !== desiredDescription ||
180
+ current.retentionDays !== news.retentionDays ||
181
+ current.useRetentionPeriod !== news.useRetentionPeriod;
182
+ if (!needsSync) {
183
+ return yield* toAttrs(current);
184
+ }
185
+ const updated = yield* update({
186
+ dataset_id: current.id,
187
+ description: desiredDescription,
188
+ retentionDays: news.retentionDays,
189
+ useRetentionPeriod: news.useRetentionPeriod,
190
+ });
191
+ return yield* toAttrs(updated);
192
+ }),
193
+ delete: Effect.fn(function* ({ output }) {
194
+ yield* del({ dataset_id: output.id }).pipe(Effect.catchTag("NotFound", () => Effect.void));
195
+ }),
196
+ read: Effect.fn(function* ({ id, olds, output }) {
197
+ const stack = yield* Stack;
198
+ const stage = yield* Stage;
199
+ const datasetId = output?.id ?? olds?.name;
200
+ if (!datasetId)
201
+ return undefined;
202
+ const existing = yield* get({ dataset_id: datasetId }).pipe(Effect.catchTag("NotFound", () => Effect.succeed(undefined)));
203
+ if (!existing)
204
+ return undefined;
205
+ const ownership = parseMarker(existing.description);
206
+ const isOurs = ownership !== undefined &&
207
+ ownership.stack === stack.name &&
208
+ ownership.stage === stage &&
209
+ ownership.id === id;
210
+ const attrs = yield* toAttrs(existing);
211
+ return isOurs ? attrs : Unowned(attrs);
212
+ }),
213
+ };
214
+ }));
215
+ //# sourceMappingURL=Dataset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Dataset.js","sourceRoot":"","sources":["../src/Dataset.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ import * as Axiom from "@distilled.cloud/axiom";
2
+ import * as Provider from "alchemy/Provider";
3
+ import { Resource } from "alchemy/Resource";
4
+ import type { Providers } from "./Providers.ts";
5
+ export type MonitorProps = Axiom.CreateMonitorRequest;
6
+ export type Monitor = Resource<"Axiom.Monitor", MonitorProps, Axiom.CreateMonitorResponse, never, Providers>;
7
+ /**
8
+ * An Axiom monitor — a scheduled APL/MPL query that evaluates on a fixed
9
+ * cadence and fires alerts via {@link Notifier notifiers} when its
10
+ * condition is met.
11
+ *
12
+ * Three monitor `type`s are supported:
13
+ *
14
+ * - **`Threshold`** — fires when an aggregate result crosses a static
15
+ * `threshold` (compared with `operator`).
16
+ * - **`MatchEvent`** — fires for every event matching the query.
17
+ * - **`AnomalyDetection`** — fires when results deviate from a learned
18
+ * baseline by more than `tolerance` over `compareDays`.
19
+ *
20
+ * Changing `type` triggers a replacement; everything else updates in place.
21
+ * @see https://axiom.co/docs/monitor-data/monitors
22
+ *
23
+ * ### Creating a Monitor
24
+ * **Example:** Threshold: alert on >100 errors per 5m
25
+ * ```typescript
26
+ * yield* Axiom.Monitor("error-rate", {
27
+ * name: "High error rate",
28
+ * description: "Fires when error count exceeds 100/5m",
29
+ * type: "Threshold",
30
+ * aplQuery: `
31
+ * ['my-app-traces']
32
+ * | where status >= 500
33
+ * | summarize count() by bin_auto(_time)
34
+ * `,
35
+ * operator: "Above",
36
+ * threshold: 100,
37
+ * intervalMinutes: 5,
38
+ * rangeMinutes: 5,
39
+ * alertOnNoData: false,
40
+ * resolvable: true,
41
+ * notifierIds: [slack.id, pagerduty.id],
42
+ * });
43
+ * ```
44
+ *
45
+ * **Example:** MatchEvent: alert on every panic
46
+ * ```typescript
47
+ * yield* Axiom.Monitor("panics", {
48
+ * name: "Service panic",
49
+ * type: "MatchEvent",
50
+ * aplQuery: `['my-app-logs'] | where message contains "panic:"`,
51
+ * intervalMinutes: 1,
52
+ * rangeMinutes: 1,
53
+ * notifierIds: [pagerduty.id],
54
+ * });
55
+ * ```
56
+ *
57
+ * **Example:** AnomalyDetection: deviation vs. last 7 days
58
+ * ```typescript
59
+ * yield* Axiom.Monitor("traffic-anomaly", {
60
+ * name: "Traffic anomaly",
61
+ * type: "AnomalyDetection",
62
+ * aplQuery: `['my-app-traces'] | summarize count() by bin_auto(_time)`,
63
+ * compareDays: 7,
64
+ * tolerance: 25, // %
65
+ * intervalMinutes: 15,
66
+ * rangeMinutes: 15,
67
+ * notifierIds: [slack.id],
68
+ * });
69
+ * ```
70
+ *
71
+ * @resource
72
+ * @product Monitor
73
+ */
74
+ export declare const Monitor: import("alchemy/Resource").ResourceClass<Monitor>;
75
+ export declare const MonitorProvider: () => import("effect/Layer").Layer<Provider.Provider<Monitor>, never, Axiom.AxiomOpContext>;
76
+ //# sourceMappingURL=Monitor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Monitor.d.ts","sourceRoot":"","sources":["../src/Monitor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,wBAAwB,CAAC;AAEhD,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,MAAM,YAAY,GAAG,KAAK,CAAC,oBAAoB,CAAC;AAEtD,MAAM,MAAM,OAAO,GAAG,QAAQ,CAC5B,eAAe,EACf,YAAY,EACZ,KAAK,CAAC,qBAAqB,EAC3B,KAAK,EACL,SAAS,CACV,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEG;AACH,eAAO,MAAM,OAAO,mDAAqC,CAAC;AAE1D,eAAO,MAAM,eAAe,6FA6DzB,CAAC"}
package/lib/Monitor.js ADDED
@@ -0,0 +1,126 @@
1
+ import * as Axiom from "@distilled.cloud/axiom";
2
+ import { isResolved } from "alchemy/Diff";
3
+ import * as Provider from "alchemy/Provider";
4
+ import { Resource } from "alchemy/Resource";
5
+ import * as Effect from "effect/Effect";
6
+ /**
7
+ * An Axiom monitor — a scheduled APL/MPL query that evaluates on a fixed
8
+ * cadence and fires alerts via {@link Notifier notifiers} when its
9
+ * condition is met.
10
+ *
11
+ * Three monitor `type`s are supported:
12
+ *
13
+ * - **`Threshold`** — fires when an aggregate result crosses a static
14
+ * `threshold` (compared with `operator`).
15
+ * - **`MatchEvent`** — fires for every event matching the query.
16
+ * - **`AnomalyDetection`** — fires when results deviate from a learned
17
+ * baseline by more than `tolerance` over `compareDays`.
18
+ *
19
+ * Changing `type` triggers a replacement; everything else updates in place.
20
+ * @see https://axiom.co/docs/monitor-data/monitors
21
+ *
22
+ * ### Creating a Monitor
23
+ * **Example:** Threshold: alert on >100 errors per 5m
24
+ * ```typescript
25
+ * yield* Axiom.Monitor("error-rate", {
26
+ * name: "High error rate",
27
+ * description: "Fires when error count exceeds 100/5m",
28
+ * type: "Threshold",
29
+ * aplQuery: `
30
+ * ['my-app-traces']
31
+ * | where status >= 500
32
+ * | summarize count() by bin_auto(_time)
33
+ * `,
34
+ * operator: "Above",
35
+ * threshold: 100,
36
+ * intervalMinutes: 5,
37
+ * rangeMinutes: 5,
38
+ * alertOnNoData: false,
39
+ * resolvable: true,
40
+ * notifierIds: [slack.id, pagerduty.id],
41
+ * });
42
+ * ```
43
+ *
44
+ * **Example:** MatchEvent: alert on every panic
45
+ * ```typescript
46
+ * yield* Axiom.Monitor("panics", {
47
+ * name: "Service panic",
48
+ * type: "MatchEvent",
49
+ * aplQuery: `['my-app-logs'] | where message contains "panic:"`,
50
+ * intervalMinutes: 1,
51
+ * rangeMinutes: 1,
52
+ * notifierIds: [pagerduty.id],
53
+ * });
54
+ * ```
55
+ *
56
+ * **Example:** AnomalyDetection: deviation vs. last 7 days
57
+ * ```typescript
58
+ * yield* Axiom.Monitor("traffic-anomaly", {
59
+ * name: "Traffic anomaly",
60
+ * type: "AnomalyDetection",
61
+ * aplQuery: `['my-app-traces'] | summarize count() by bin_auto(_time)`,
62
+ * compareDays: 7,
63
+ * tolerance: 25, // %
64
+ * intervalMinutes: 15,
65
+ * rangeMinutes: 15,
66
+ * notifierIds: [slack.id],
67
+ * });
68
+ * ```
69
+ *
70
+ * @resource
71
+ * @product Monitor
72
+ */
73
+ export const Monitor = Resource("Axiom.Monitor");
74
+ export const MonitorProvider = () => Provider.effect(Monitor, Effect.gen(function* () {
75
+ const create = yield* Axiom.createMonitor;
76
+ const update = yield* Axiom.updateMonitor;
77
+ const get = yield* Axiom.getMonitor;
78
+ const list = yield* Axiom.getMonitors;
79
+ const del = yield* Axiom.deleteMonitor;
80
+ return {
81
+ stables: ["id"],
82
+ // Enumerate every monitor in the org. Axiom exposes a single
83
+ // account-wide `GET /v2/monitors` collection op (no pagination) whose
84
+ // item schema is identical to `getMonitor`/`createMonitor`'s output, so
85
+ // we fetch it once and each row already carries the exact `read`
86
+ // Attributes shape — directly usable by `delete` with no follow-up get.
87
+ list: () => Effect.gen(function* () {
88
+ const monitors = yield* list({});
89
+ return monitors.map((m) => m);
90
+ }),
91
+ diff: Effect.fn(function* ({ news, output }) {
92
+ if (!isResolved(news))
93
+ return undefined;
94
+ if (output && news.type !== output.type) {
95
+ return { action: "replace" };
96
+ }
97
+ return undefined;
98
+ }),
99
+ reconcile: Effect.fn(function* ({ news, output }) {
100
+ // Observe — Axiom assigns the monitor id server-side, so the only
101
+ // handle to a previously-created monitor is the cached
102
+ // `output.id`. Probe for live state with that id; treat NotFound
103
+ // (deleted out-of-band) as "no observed state" so we converge by
104
+ // re-creating.
105
+ const observed = output?.id
106
+ ? yield* get({ id: output.id }).pipe(Effect.catchTag("NotFound", () => Effect.succeed(undefined)))
107
+ : undefined;
108
+ // Ensure — POST mints a new monitor with a fresh id.
109
+ if (observed === undefined) {
110
+ return yield* create(news);
111
+ }
112
+ // Sync — the monitor exists; PATCH against its id with the
113
+ // desired props. `type` is replacement-only (handled in diff).
114
+ return yield* update({ ...news, id: observed.id });
115
+ }),
116
+ delete: Effect.fn(function* ({ output }) {
117
+ yield* del({ id: output.id }).pipe(Effect.catchTag("NotFound", () => Effect.void));
118
+ }),
119
+ read: Effect.fn(function* ({ output }) {
120
+ if (!output?.id)
121
+ return undefined;
122
+ return yield* get({ id: output.id }).pipe(Effect.catchTag("NotFound", () => Effect.succeed(undefined)));
123
+ }),
124
+ };
125
+ }));
126
+ //# sourceMappingURL=Monitor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Monitor.js","sourceRoot":"","sources":["../src/Monitor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ import * as Axiom from "@distilled.cloud/axiom";
2
+ import * as Provider from "alchemy/Provider";
3
+ import { Resource } from "alchemy/Resource";
4
+ import type { Providers } from "./Providers.ts";
5
+ export type NotifierProps = Axiom.CreateNotifierRequest;
6
+ export type Notifier = Resource<"Axiom.Notifier", NotifierProps, Axiom.CreateNotifierResponse & {
7
+ id: string;
8
+ }, never, Providers>;
9
+ /**
10
+ * An Axiom notifier — an alert destination (Slack, email, PagerDuty,
11
+ * Opsgenie, Discord, Microsoft Teams, generic webhook, or a fully custom
12
+ * webhook with templated body/headers) that {@link Monitor monitors} target
13
+ * via `notifierIds`. Exactly one channel under `properties` should be set.
14
+ * @see https://axiom.co/docs/monitor-data/notifiers
15
+ *
16
+ * ### Creating a Notifier
17
+ * **Example:** Slack incoming webhook
18
+ * ```typescript
19
+ * const slack = yield* Axiom.Notifier("ops-slack", {
20
+ * name: "ops-channel",
21
+ * properties: {
22
+ * slack: { slackUrl: process.env.SLACK_WEBHOOK_URL! },
23
+ * },
24
+ * });
25
+ * ```
26
+ *
27
+ * **Example:** Email distribution list
28
+ * ```typescript
29
+ * yield* Axiom.Notifier("ops-email", {
30
+ * name: "ops-team",
31
+ * properties: { email: { emails: ["sre@example.com", "oncall@example.com"] } },
32
+ * });
33
+ * ```
34
+ *
35
+ * **Example:** PagerDuty integration
36
+ * ```typescript
37
+ * yield* Axiom.Notifier("pagerduty", {
38
+ * name: "primary-oncall",
39
+ * properties: {
40
+ * pagerduty: { routingKey: process.env.PAGERDUTY_ROUTING_KEY!, token: "" },
41
+ * },
42
+ * });
43
+ * ```
44
+ *
45
+ * **Example:** Custom webhook with templated body
46
+ * ```typescript
47
+ * yield* Axiom.Notifier("incident-webhook", {
48
+ * name: "incident.io",
49
+ * properties: {
50
+ * customWebhook: {
51
+ * url: "https://api.incident.io/v2/alert_events",
52
+ * headers: { "Content-Type": "application/json" },
53
+ * secretHeaders: { Authorization: `Bearer ${process.env.INCIDENT_TOKEN}` },
54
+ * body: '{"title": "{{.Monitor.Name}}", "status": "firing"}',
55
+ * },
56
+ * },
57
+ * });
58
+ * ```
59
+ *
60
+ * @resource
61
+ * @product Monitor
62
+ */
63
+ export declare const Notifier: import("alchemy/Resource").ResourceClass<Notifier>;
64
+ export declare const NotifierProvider: () => import("effect/Layer").Layer<Provider.Provider<Notifier>, never, Axiom.AxiomOpContext>;
65
+ //# sourceMappingURL=Notifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Notifier.d.ts","sourceRoot":"","sources":["../src/Notifier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,wBAAwB,CAAC;AAChD,OAAO,KAAK,QAAQ,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,MAAM,aAAa,GAAG,KAAK,CAAC,qBAAqB,CAAC;AAExD,MAAM,MAAM,QAAQ,GAAG,QAAQ,CAC7B,gBAAgB,EAChB,aAAa,EACb,KAAK,CAAC,sBAAsB,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,EAC7C,KAAK,EACL,SAAS,CACV,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,eAAO,MAAM,QAAQ,oDAAuC,CAAC;AAE7D,eAAO,MAAM,gBAAgB,8FA+D1B,CAAC"}