@alchemy.run/axiom 0.0.0 → 2.0.0-beta.85

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 +54 -4
  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 +74 -0
  8. package/lib/ApiToken.d.ts.map +1 -0
  9. package/lib/ApiToken.js +130 -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 +17 -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 +129 -0
  32. package/lib/Dataset.d.ts.map +1 -0
  33. package/lib/Dataset.js +221 -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 +53 -3
  68. package/src/Annotation.ts +121 -0
  69. package/src/ApiToken.ts +158 -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 +26 -0
  74. package/src/Dashboard.ts +192 -0
  75. package/src/Dataset.ts +333 -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,129 @@
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
+ declare const DatasetUnexpectedResponse_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 & {
8
+ readonly _tag: "DatasetUnexpectedResponse";
9
+ } & Readonly<A>;
10
+ export declare class DatasetUnexpectedResponse extends DatasetUnexpectedResponse_base<{
11
+ readonly message: string;
12
+ readonly cause?: unknown;
13
+ }> {
14
+ }
15
+ export type DatasetKind = "otel:metrics:v1" | "otel:traces:v1" | "otel:logs:v1" | "axiom:events:v1" | (string & {});
16
+ export type DatasetProps = {
17
+ /**
18
+ * Dataset name. Used as the dataset's stable identifier in Axiom — changing
19
+ * this triggers a replacement.
20
+ */
21
+ name: string;
22
+ /** Free-form description shown in the Axiom UI. */
23
+ description?: string;
24
+ /**
25
+ * Edge deployment where Axiom stores and processes the dataset's data.
26
+ * Defaults to the organization's default edge deployment.
27
+ *
28
+ * Cannot be changed after creation — triggers a replacement.
29
+ */
30
+ edgeDeployment?: string;
31
+ /**
32
+ * Dataset kind. Defaults to `axiom:events:v1`.
33
+ *
34
+ * For OTEL pipelines, choose:
35
+ * - `otel:traces:v1` for traces
36
+ * - `otel:logs:v1` for logs
37
+ * - `otel:metrics:v1` for metrics
38
+ *
39
+ * Cannot be changed after creation — triggers a replacement.
40
+ */
41
+ kind?: DatasetKind;
42
+ /** Retention in days. Plan-dependent — see Axiom docs. */
43
+ retentionDays?: number;
44
+ /** Whether to enforce the configured retention period. */
45
+ useRetentionPeriod?: boolean;
46
+ };
47
+ export type Dataset = Resource<"Axiom.Dataset", DatasetProps, {
48
+ id: string;
49
+ name: string;
50
+ kind: DatasetKind;
51
+ description: string;
52
+ created: string;
53
+ /** Edge deployment where the dataset's data is stored and processed. */
54
+ edgeDeployment: string;
55
+ /** Base URL for ingesting and querying data in the dataset's edge deployment. */
56
+ edgeDeploymentUrl: string;
57
+ /** Base URL for Axiom's centralized management API. */
58
+ apiBaseUrl: string;
59
+ /** Root OTLP endpoint. Most exporters auto-append the signal path. */
60
+ otelEndpoint: string;
61
+ /** OTLP/HTTP traces endpoint. */
62
+ otelTracesEndpoint: string;
63
+ /** OTLP/HTTP logs endpoint. */
64
+ otelLogsEndpoint: string;
65
+ /** OTLP/HTTP metrics endpoint. */
66
+ otelMetricsEndpoint: string;
67
+ /**
68
+ * Headers required for OTLP shipping aside from the bearer token:
69
+ * `X-Axiom-Dataset`, or `X-Axiom-Metrics-Dataset` for an
70
+ * `otel:metrics:v1` dataset.
71
+ * Add `Authorization: Bearer <AXIOM_TOKEN>` separately at runtime so the
72
+ * secret is never persisted in resource state.
73
+ */
74
+ otelHeaders: Record<string, string>;
75
+ }, never, Providers>;
76
+ /**
77
+ * An Axiom dataset — the top-level container that stores events, logs,
78
+ * traces, or metrics. Pick a `kind` up-front: it determines schema and how
79
+ * the data is shown in the UI, and **cannot be changed** after creation
80
+ * (changing it triggers a replacement, which deletes the data).
81
+ *
82
+ * Datasets expose their edge deployment and Axiom's OTLP/HTTP endpoints
83
+ * (`otelTracesEndpoint`, `otelLogsEndpoint`, `otelMetricsEndpoint`) as output
84
+ * attributes so you can inject them into a Worker / Lambda's env vars for
85
+ * OpenTelemetry shipping. The bearer token is **not** stored in resource state
86
+ * — supply `Authorization: Bearer <AXIOM_TOKEN>` separately at runtime.
87
+ * @see https://axiom.co/docs/reference/datasets
88
+ * @see https://axiom.co/docs/send-data/opentelemetry — OTLP endpoint reference
89
+ *
90
+ * ### Creating a Dataset
91
+ * **Example:** Logs dataset with 30-day retention
92
+ * ```typescript
93
+ * const logs = yield* Axiom.Dataset("app-logs", {
94
+ * name: "my-app-logs",
95
+ * kind: "otel:logs:v1",
96
+ * description: "Application logs from prod workers",
97
+ * retentionDays: 30,
98
+ * useRetentionPeriod: true,
99
+ * });
100
+ * ```
101
+ *
102
+ * **Example:** Separate datasets per OTEL signal
103
+ * ```typescript
104
+ * const traces = yield* Axiom.Dataset("traces", { name: "app-traces", kind: "otel:traces:v1" });
105
+ * const logs = yield* Axiom.Dataset("logs", { name: "app-logs", kind: "otel:logs:v1" });
106
+ * const metrics = yield* Axiom.Dataset("metrics", { name: "app-metrics", kind: "otel:metrics:v1" });
107
+ * ```
108
+ *
109
+ * ### Shipping OTEL data
110
+ * **Example:** Wire OTEL env vars into a Cloudflare Worker
111
+ * ```typescript
112
+ * yield* Cloudflare.Worker("api", {
113
+ * vars: {
114
+ * OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: traces.otelTracesEndpoint,
115
+ * OTEL_EXPORTER_OTLP_LOGS_ENDPOINT: logs.otelLogsEndpoint,
116
+ * // Bearer token must come from a secret store, not the dataset state.
117
+ * OTEL_EXPORTER_OTLP_HEADERS:
118
+ * `Authorization=Bearer ${env.AXIOM_TOKEN},X-Axiom-Dataset=${traces.name}`,
119
+ * },
120
+ * });
121
+ * ```
122
+ *
123
+ * @resource
124
+ * @product Dataset
125
+ */
126
+ export declare const Dataset: import("alchemy/Resource").ResourceClass<Dataset>;
127
+ export declare const DatasetProvider: () => import("effect/Layer").Layer<Provider.Provider<Dataset>, never, Stack | Stage | Axiom.AxiomOpContext>;
128
+ export {};
129
+ //# 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;AAItC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;;;;AAEhD,qBAAa,yBAA0B,SAAQ,+BAA8C;IAC3F,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;CAAG;AAEL,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,6GA+JzB,CAAC"}
package/lib/Dataset.js ADDED
@@ -0,0 +1,221 @@
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 Data from "effect/Data";
10
+ import * as Effect from "effect/Effect";
11
+ import { otelDatasetHeader } from "./OtelHeaders.js";
12
+ export class DatasetUnexpectedResponse extends Data.TaggedError("DatasetUnexpectedResponse") {
13
+ }
14
+ /**
15
+ * An Axiom dataset — the top-level container that stores events, logs,
16
+ * traces, or metrics. Pick a `kind` up-front: it determines schema and how
17
+ * the data is shown in the UI, and **cannot be changed** after creation
18
+ * (changing it triggers a replacement, which deletes the data).
19
+ *
20
+ * Datasets expose their edge deployment and Axiom's OTLP/HTTP endpoints
21
+ * (`otelTracesEndpoint`, `otelLogsEndpoint`, `otelMetricsEndpoint`) as output
22
+ * attributes so you can inject them into a Worker / Lambda's env vars for
23
+ * OpenTelemetry shipping. The bearer token is **not** stored in resource state
24
+ * — supply `Authorization: Bearer <AXIOM_TOKEN>` separately at runtime.
25
+ * @see https://axiom.co/docs/reference/datasets
26
+ * @see https://axiom.co/docs/send-data/opentelemetry — OTLP endpoint reference
27
+ *
28
+ * ### Creating a Dataset
29
+ * **Example:** Logs dataset with 30-day retention
30
+ * ```typescript
31
+ * const logs = yield* Axiom.Dataset("app-logs", {
32
+ * name: "my-app-logs",
33
+ * kind: "otel:logs:v1",
34
+ * description: "Application logs from prod workers",
35
+ * retentionDays: 30,
36
+ * useRetentionPeriod: true,
37
+ * });
38
+ * ```
39
+ *
40
+ * **Example:** Separate datasets per OTEL signal
41
+ * ```typescript
42
+ * const traces = yield* Axiom.Dataset("traces", { name: "app-traces", kind: "otel:traces:v1" });
43
+ * const logs = yield* Axiom.Dataset("logs", { name: "app-logs", kind: "otel:logs:v1" });
44
+ * const metrics = yield* Axiom.Dataset("metrics", { name: "app-metrics", kind: "otel:metrics:v1" });
45
+ * ```
46
+ *
47
+ * ### Shipping OTEL data
48
+ * **Example:** Wire OTEL env vars into a Cloudflare Worker
49
+ * ```typescript
50
+ * yield* Cloudflare.Worker("api", {
51
+ * vars: {
52
+ * OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: traces.otelTracesEndpoint,
53
+ * OTEL_EXPORTER_OTLP_LOGS_ENDPOINT: logs.otelLogsEndpoint,
54
+ * // Bearer token must come from a secret store, not the dataset state.
55
+ * OTEL_EXPORTER_OTLP_HEADERS:
56
+ * `Authorization=Bearer ${env.AXIOM_TOKEN},X-Axiom-Dataset=${traces.name}`,
57
+ * },
58
+ * });
59
+ * ```
60
+ *
61
+ * @resource
62
+ * @product Dataset
63
+ */
64
+ export const Dataset = Resource("Axiom.Dataset");
65
+ /**
66
+ * Axiom has no tags/labels API. The only writable field we can use to mark
67
+ * ownership is `description`. We append a deterministic marker on create so
68
+ * that on a re-apply (e.g. state was wiped) we can safely identify a dataset
69
+ * that **we** previously created and adopt it idempotently — without
70
+ * accidentally hijacking a dataset created by someone else with the same name.
71
+ */
72
+ const MARKER_RE = /\s*\[alchemy:stack=([^;]+);stage=([^;]+);id=([^\]]+)\]\s*$/;
73
+ const buildMarker = (stack, stage, id) => `[alchemy:stack=${stack};stage=${stage};id=${id}]`;
74
+ const augmentDescription = (description, marker) => {
75
+ const base = stripMarker(description ?? "");
76
+ return base.length > 0 ? `${base}\n${marker}` : marker;
77
+ };
78
+ const stripMarker = (description) => description.replace(MARKER_RE, "").trimEnd();
79
+ const parseMarker = (description) => {
80
+ if (!description)
81
+ return undefined;
82
+ const m = description.match(MARKER_RE);
83
+ if (!m)
84
+ return undefined;
85
+ return { stack: m[1], stage: m[2], id: m[3] };
86
+ };
87
+ export const DatasetProvider = () => Provider.effect(Dataset, Effect.gen(function* () {
88
+ const credentials = yield* Credentials;
89
+ const create = yield* Axiom.createDataset;
90
+ const update = yield* Axiom.updateDataset;
91
+ const get = yield* Axiom.getDataset;
92
+ const listDatasets = yield* Axiom.getDatasets;
93
+ const del = yield* Axiom.deleteDataset;
94
+ const toAttrs = Effect.fn(function* (dataset) {
95
+ if (!dataset.edgeDeployment || !dataset.edgeDeploymentUrl) {
96
+ return yield* new DatasetUnexpectedResponse({
97
+ message: `Axiom dataset "${dataset.name}" is missing its edge deployment metadata`,
98
+ });
99
+ }
100
+ const { apiBaseUrl } = yield* credentials;
101
+ const apiRoot = apiBaseUrl.replace(/\/$/, "");
102
+ const otelRoot = dataset.edgeDeploymentUrl.replace(/\/$/, "");
103
+ return {
104
+ id: dataset.id,
105
+ name: dataset.name,
106
+ kind: dataset.kind,
107
+ description: stripMarker(dataset.description),
108
+ created: dataset.created,
109
+ edgeDeployment: dataset.edgeDeployment,
110
+ edgeDeploymentUrl: dataset.edgeDeploymentUrl,
111
+ apiBaseUrl: apiRoot,
112
+ otelEndpoint: otelRoot,
113
+ otelTracesEndpoint: `${otelRoot}/v1/traces`,
114
+ otelLogsEndpoint: `${otelRoot}/v1/logs`,
115
+ otelMetricsEndpoint: `${otelRoot}/v1/metrics`,
116
+ otelHeaders: {
117
+ [otelDatasetHeader(dataset.kind === "otel:metrics:v1")]: dataset.name,
118
+ },
119
+ };
120
+ });
121
+ return {
122
+ stables: ["id", "name", "kind", "edgeDeployment", "edgeDeploymentUrl"],
123
+ // Enumerate every dataset in the org. Axiom exposes a single
124
+ // account-wide `GET /v2/datasets` collection op (no pagination), so we
125
+ // fetch it once and hydrate each row into the exact `read`/`toAttrs`
126
+ // Attributes shape — directly usable by `delete` with no follow-up get.
127
+ list: () => Effect.gen(function* () {
128
+ const datasets = yield* listDatasets({});
129
+ return yield* Effect.forEach(datasets, toAttrs);
130
+ }),
131
+ diff: Effect.fn(function* ({ olds, news, output }) {
132
+ if (!isResolved(news))
133
+ return undefined;
134
+ if (output && news.name !== output.name) {
135
+ return { action: "replace" };
136
+ }
137
+ if (news.kind && output && news.kind !== output.kind) {
138
+ return { action: "replace" };
139
+ }
140
+ if (news.edgeDeployment && output && news.edgeDeployment !== output.edgeDeployment) {
141
+ return { action: "replace" };
142
+ }
143
+ if (news.description !== olds?.description ||
144
+ news.retentionDays !== olds?.retentionDays ||
145
+ news.useRetentionPeriod !== olds?.useRetentionPeriod) {
146
+ return { action: "update" };
147
+ }
148
+ return undefined;
149
+ }),
150
+ reconcile: Effect.fn(function* ({ id, news, output }) {
151
+ const stack = yield* Stack;
152
+ const stage = yield* Stage;
153
+ const marker = buildMarker(stack.name, stage, id);
154
+ // Observe — Axiom's dataset path identifier is `name`, which is
155
+ // also stable input. Probe for live state by name (preferring the
156
+ // cached `output.id` when present). `read` upstream has already
157
+ // surfaced foreign datasets as `Unowned`, so by the time we land
158
+ // here mutation is safe.
159
+ const datasetId = output?.id ?? news.name;
160
+ const observed = yield* get({ dataset_id: datasetId }).pipe(Effect.catchTag("NotFound", () => Effect.undefined));
161
+ // Ensure — POST creates the dataset. Tolerate Conflict/
162
+ // UnprocessableEntity as a race with a peer reconciler (or with
163
+ // upstream read↔create), falling through to the sync path.
164
+ let current = observed;
165
+ if (current === undefined) {
166
+ current = yield* create({
167
+ name: news.name,
168
+ description: augmentDescription(news.description, marker),
169
+ edgeDeployment: news.edgeDeployment,
170
+ kind: news.kind,
171
+ retentionDays: news.retentionDays,
172
+ useRetentionPeriod: news.useRetentionPeriod,
173
+ }).pipe(Effect.catchIf((e) => e._tag === "Conflict" || e._tag === "UnprocessableEntity", () => update({
174
+ dataset_id: news.name,
175
+ description: augmentDescription(news.description, marker),
176
+ retentionDays: news.retentionDays,
177
+ useRetentionPeriod: news.useRetentionPeriod,
178
+ })));
179
+ return yield* toAttrs(current);
180
+ }
181
+ // Sync — the dataset exists. Apply mutable aspects (description,
182
+ // retentionDays, useRetentionPeriod) via PATCH. `kind` and `name`
183
+ // are stable and replacement-only via diff above.
184
+ const desiredDescription = augmentDescription(news.description, marker);
185
+ const needsSync = current.description !== desiredDescription ||
186
+ current.retentionDays !== news.retentionDays ||
187
+ current.useRetentionPeriod !== news.useRetentionPeriod;
188
+ if (!needsSync) {
189
+ return yield* toAttrs(current);
190
+ }
191
+ const updated = yield* update({
192
+ dataset_id: current.id,
193
+ description: desiredDescription,
194
+ retentionDays: news.retentionDays,
195
+ useRetentionPeriod: news.useRetentionPeriod,
196
+ });
197
+ return yield* toAttrs(updated);
198
+ }),
199
+ delete: Effect.fn(function* ({ output }) {
200
+ yield* del({ dataset_id: output.id }).pipe(Effect.catchTag("NotFound", () => Effect.void));
201
+ }),
202
+ read: Effect.fn(function* ({ id, olds, output }) {
203
+ const stack = yield* Stack;
204
+ const stage = yield* Stage;
205
+ const datasetId = output?.id ?? olds?.name;
206
+ if (!datasetId)
207
+ return undefined;
208
+ const existing = yield* get({ dataset_id: datasetId }).pipe(Effect.catchTag("NotFound", () => Effect.undefined));
209
+ if (!existing)
210
+ return undefined;
211
+ const ownership = parseMarker(existing.description);
212
+ const isOurs = ownership !== undefined &&
213
+ ownership.stack === stack.name &&
214
+ ownership.stage === stage &&
215
+ ownership.id === id;
216
+ const attrs = yield* toAttrs(existing);
217
+ return isOurs ? attrs : Unowned(attrs);
218
+ }),
219
+ };
220
+ }));
221
+ //# 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.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.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"}