alchemy 2.0.0-beta.17 → 2.0.0-beta.19
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/bin/alchemy.js +428 -80
- package/bin/alchemy.js.map +1 -1
- package/bin/alchemy.ts +2 -0
- package/bin/commands/_shared.ts +34 -0
- package/bin/commands/bootstrap.ts +10 -3
- package/bin/commands/deploy.ts +23 -3
- package/bin/commands/login.ts +18 -3
- package/bin/commands/logs.ts +11 -2
- package/bin/commands/profile.ts +11 -5
- package/bin/commands/state.ts +21 -13
- package/bin/commands/tail.ts +10 -2
- package/lib/AWS/CloudFront/CachePolicy.d.ts +106 -0
- package/lib/AWS/CloudFront/CachePolicy.d.ts.map +1 -0
- package/lib/AWS/CloudFront/CachePolicy.js +147 -0
- package/lib/AWS/CloudFront/CachePolicy.js.map +1 -0
- package/lib/AWS/CloudFront/KeyGroup.d.ts +72 -0
- package/lib/AWS/CloudFront/KeyGroup.d.ts.map +1 -0
- package/lib/AWS/CloudFront/KeyGroup.js +136 -0
- package/lib/AWS/CloudFront/KeyGroup.js.map +1 -0
- package/lib/AWS/CloudFront/OriginRequestPolicy.d.ts +84 -0
- package/lib/AWS/CloudFront/OriginRequestPolicy.d.ts.map +1 -0
- package/lib/AWS/CloudFront/OriginRequestPolicy.js +139 -0
- package/lib/AWS/CloudFront/OriginRequestPolicy.js.map +1 -0
- package/lib/AWS/CloudFront/PublicKey.d.ts +72 -0
- package/lib/AWS/CloudFront/PublicKey.d.ts.map +1 -0
- package/lib/AWS/CloudFront/PublicKey.js +137 -0
- package/lib/AWS/CloudFront/PublicKey.js.map +1 -0
- package/lib/AWS/CloudFront/ResponseHeadersPolicy.d.ts +114 -0
- package/lib/AWS/CloudFront/ResponseHeadersPolicy.d.ts.map +1 -0
- package/lib/AWS/CloudFront/ResponseHeadersPolicy.js +162 -0
- package/lib/AWS/CloudFront/ResponseHeadersPolicy.js.map +1 -0
- package/lib/AWS/CloudFront/index.d.ts +5 -0
- package/lib/AWS/CloudFront/index.d.ts.map +1 -1
- package/lib/AWS/CloudFront/index.js +5 -0
- package/lib/AWS/CloudFront/index.js.map +1 -1
- package/lib/AWS/ECS/CapacityProvider.d.ts +102 -0
- package/lib/AWS/ECS/CapacityProvider.d.ts.map +1 -0
- package/lib/AWS/ECS/CapacityProvider.js +217 -0
- package/lib/AWS/ECS/CapacityProvider.js.map +1 -0
- package/lib/AWS/ECS/index.d.ts +1 -0
- package/lib/AWS/ECS/index.d.ts.map +1 -1
- package/lib/AWS/ECS/index.js +1 -0
- package/lib/AWS/ECS/index.js.map +1 -1
- package/lib/AWS/Providers.d.ts.map +1 -1
- package/lib/AWS/Providers.js +7 -1
- package/lib/AWS/Providers.js.map +1 -1
- package/lib/Agent/lsp/client.d.ts +2 -2
- package/lib/Apply.d.ts.map +1 -1
- package/lib/Apply.js +41 -9
- package/lib/Apply.js.map +1 -1
- package/lib/Axiom/Annotation.d.ts +49 -0
- package/lib/Axiom/Annotation.d.ts.map +1 -0
- package/lib/Axiom/Annotation.js +74 -0
- package/lib/Axiom/Annotation.js.map +1 -0
- package/lib/Axiom/ApiToken.d.ts +62 -0
- package/lib/Axiom/ApiToken.d.ts.map +1 -0
- package/lib/Axiom/ApiToken.js +98 -0
- package/lib/Axiom/ApiToken.js.map +1 -0
- package/lib/Axiom/AuthProvider.d.ts +48 -0
- package/lib/Axiom/AuthProvider.d.ts.map +1 -0
- package/lib/Axiom/AuthProvider.js +177 -0
- package/lib/Axiom/AuthProvider.js.map +1 -0
- package/lib/Axiom/Chart.d.ts +72 -0
- package/lib/Axiom/Chart.d.ts.map +1 -0
- package/lib/Axiom/Chart.js +14 -0
- package/lib/Axiom/Chart.js.map +1 -0
- package/lib/Axiom/Credentials.d.ts +12 -0
- package/lib/Axiom/Credentials.d.ts.map +1 -0
- package/lib/Axiom/Credentials.js +34 -0
- package/lib/Axiom/Credentials.js.map +1 -0
- package/lib/Axiom/Dashboard.d.ts +124 -0
- package/lib/Axiom/Dashboard.d.ts.map +1 -0
- package/lib/Axiom/Dashboard.js +140 -0
- package/lib/Axiom/Dashboard.js.map +1 -0
- package/lib/Axiom/Dataset.d.ts +101 -0
- package/lib/Axiom/Dataset.d.ts.map +1 -0
- package/lib/Axiom/Dataset.js +183 -0
- package/lib/Axiom/Dataset.js.map +1 -0
- package/lib/Axiom/Monitor.d.ts +74 -0
- package/lib/Axiom/Monitor.d.ts.map +1 -0
- package/lib/Axiom/Monitor.js +103 -0
- package/lib/Axiom/Monitor.js.map +1 -0
- package/lib/Axiom/Notifier.d.ts +63 -0
- package/lib/Axiom/Notifier.d.ts.map +1 -0
- package/lib/Axiom/Notifier.js +83 -0
- package/lib/Axiom/Notifier.js.map +1 -0
- package/lib/Axiom/Providers.d.ts +15 -0
- package/lib/Axiom/Providers.d.ts.map +1 -0
- package/lib/Axiom/Providers.js +32 -0
- package/lib/Axiom/Providers.js.map +1 -0
- package/lib/Axiom/View.d.ts +55 -0
- package/lib/Axiom/View.d.ts.map +1 -0
- package/lib/Axiom/View.js +79 -0
- package/lib/Axiom/View.js.map +1 -0
- package/lib/Axiom/VirtualField.d.ts +42 -0
- package/lib/Axiom/VirtualField.d.ts.map +1 -0
- package/lib/Axiom/VirtualField.js +71 -0
- package/lib/Axiom/VirtualField.js.map +1 -0
- package/lib/Axiom/index.d.ts +13 -0
- package/lib/Axiom/index.d.ts.map +1 -0
- package/lib/Axiom/index.js +13 -0
- package/lib/Axiom/index.js.map +1 -0
- package/lib/Bundle/Bundle.d.ts +20 -2
- package/lib/Bundle/Bundle.d.ts.map +1 -1
- package/lib/Bundle/Bundle.js +18 -3
- package/lib/Bundle/Bundle.js.map +1 -1
- package/lib/Bundle/PurePlugin.d.ts +109 -0
- package/lib/Bundle/PurePlugin.d.ts.map +1 -0
- package/lib/Bundle/PurePlugin.js +469 -0
- package/lib/Bundle/PurePlugin.js.map +1 -0
- package/lib/Bundle/index.d.ts +1 -0
- package/lib/Bundle/index.d.ts.map +1 -1
- package/lib/Bundle/index.js +1 -0
- package/lib/Bundle/index.js.map +1 -1
- package/lib/Cloudflare/Workers/Worker.d.ts +12 -0
- package/lib/Cloudflare/Workers/Worker.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/Worker.js +19 -4
- package/lib/Cloudflare/Workers/Worker.js.map +1 -1
- package/lib/GitHub/Secrets.d.ts +45 -0
- package/lib/GitHub/Secrets.d.ts.map +1 -0
- package/lib/GitHub/Secrets.js +30 -0
- package/lib/GitHub/Secrets.js.map +1 -0
- package/lib/GitHub/index.d.ts +1 -0
- package/lib/GitHub/index.d.ts.map +1 -1
- package/lib/GitHub/index.js +1 -0
- package/lib/GitHub/index.js.map +1 -1
- package/lib/Output.d.ts.map +1 -1
- package/lib/Output.js +1 -1
- package/lib/Output.js.map +1 -1
- package/lib/Plan.d.ts.map +1 -1
- package/lib/Plan.js +10 -4
- package/lib/Plan.js.map +1 -1
- package/lib/Resource.d.ts +2 -2
- package/lib/Resource.d.ts.map +1 -1
- package/lib/Telemetry/Attributes.d.ts +43 -0
- package/lib/Telemetry/Attributes.d.ts.map +1 -0
- package/lib/Telemetry/Attributes.js +144 -0
- package/lib/Telemetry/Attributes.js.map +1 -0
- package/lib/Telemetry/Layer.d.ts +14 -0
- package/lib/Telemetry/Layer.d.ts.map +1 -0
- package/lib/Telemetry/Layer.js +50 -0
- package/lib/Telemetry/Layer.js.map +1 -0
- package/lib/Telemetry/Metrics.d.ts +34 -0
- package/lib/Telemetry/Metrics.d.ts.map +1 -0
- package/lib/Telemetry/Metrics.js +57 -0
- package/lib/Telemetry/Metrics.js.map +1 -0
- package/package.json +12 -1
- package/src/AWS/CloudFront/CachePolicy.ts +271 -0
- package/src/AWS/CloudFront/KeyGroup.ts +233 -0
- package/src/AWS/CloudFront/OriginRequestPolicy.ts +256 -0
- package/src/AWS/CloudFront/PublicKey.ts +245 -0
- package/src/AWS/CloudFront/ResponseHeadersPolicy.ts +308 -0
- package/src/AWS/CloudFront/index.ts +11 -0
- package/src/AWS/ECS/CapacityProvider.ts +336 -0
- package/src/AWS/ECS/index.ts +4 -0
- package/src/AWS/Providers.ts +12 -0
- package/src/Apply.ts +118 -9
- package/src/Axiom/Annotation.ts +95 -0
- package/src/Axiom/ApiToken.ts +125 -0
- package/src/Axiom/AuthProvider.ts +341 -0
- package/src/Axiom/Chart.ts +94 -0
- package/src/Axiom/Credentials.ts +70 -0
- package/src/Axiom/Dashboard.ts +192 -0
- package/src/Axiom/Dataset.ts +291 -0
- package/src/Axiom/Monitor.ts +122 -0
- package/src/Axiom/Notifier.ts +104 -0
- package/src/Axiom/Providers.ts +58 -0
- package/src/Axiom/View.ts +106 -0
- package/src/Axiom/VirtualField.ts +90 -0
- package/src/Axiom/index.ts +12 -0
- package/src/Bundle/Bundle.ts +39 -1
- package/src/Bundle/PurePlugin.ts +556 -0
- package/src/Bundle/index.ts +1 -0
- package/src/Cloudflare/Workers/Worker.ts +37 -6
- package/src/GitHub/Secrets.ts +66 -0
- package/src/GitHub/index.ts +1 -0
- package/src/Output.ts +2 -1
- package/src/Plan.ts +13 -4
- package/src/Resource.ts +2 -2
- package/src/Telemetry/Attributes.ts +216 -0
- package/src/Telemetry/Layer.ts +65 -0
- package/src/Telemetry/Metrics.ts +101 -0
package/src/AWS/Providers.ts
CHANGED
|
@@ -49,13 +49,18 @@ export const providers = () =>
|
|
|
49
49
|
AutoScaling.AutoScalingGroup,
|
|
50
50
|
AutoScaling.LaunchTemplate,
|
|
51
51
|
AutoScaling.ScalingPolicy,
|
|
52
|
+
CloudFront.CachePolicy,
|
|
52
53
|
CloudFront.Distribution,
|
|
53
54
|
CloudFront.Function,
|
|
54
55
|
CloudFront.Invalidation,
|
|
56
|
+
CloudFront.KeyGroup,
|
|
55
57
|
CloudFront.KeyValueStore,
|
|
56
58
|
CloudFront.KvEntries,
|
|
57
59
|
CloudFront.KvRoutesUpdate,
|
|
58
60
|
CloudFront.OriginAccessControl,
|
|
61
|
+
CloudFront.OriginRequestPolicy,
|
|
62
|
+
CloudFront.PublicKey,
|
|
63
|
+
CloudFront.ResponseHeadersPolicy,
|
|
59
64
|
CloudWatch.Alarm,
|
|
60
65
|
CloudWatch.AlarmMuteRule,
|
|
61
66
|
CloudWatch.AnomalyDetector,
|
|
@@ -124,6 +129,7 @@ export const providers = () =>
|
|
|
124
129
|
EC2.Vpc,
|
|
125
130
|
EC2.VpcEndpoint,
|
|
126
131
|
ECR.Repository,
|
|
132
|
+
ECS.CapacityProvider,
|
|
127
133
|
ECS.Cluster,
|
|
128
134
|
ECS.DescribeTasksPolicy,
|
|
129
135
|
ECS.ListTasksPolicy,
|
|
@@ -282,13 +288,18 @@ export const providers = () =>
|
|
|
282
288
|
AutoScaling.AutoScalingGroupProvider(),
|
|
283
289
|
AutoScaling.LaunchTemplateProvider(),
|
|
284
290
|
AutoScaling.ScalingPolicyProvider(),
|
|
291
|
+
CloudFront.CachePolicyProvider(),
|
|
285
292
|
CloudFront.DistributionProvider(),
|
|
286
293
|
CloudFront.FunctionProvider(),
|
|
287
294
|
CloudFront.InvalidationProvider(),
|
|
295
|
+
CloudFront.KeyGroupProvider(),
|
|
288
296
|
CloudFront.KeyValueStoreProvider(),
|
|
289
297
|
CloudFront.KvEntriesProvider(),
|
|
290
298
|
CloudFront.KvRoutesUpdateProvider(),
|
|
291
299
|
CloudFront.OriginAccessControlProvider(),
|
|
300
|
+
CloudFront.OriginRequestPolicyProvider(),
|
|
301
|
+
CloudFront.PublicKeyProvider(),
|
|
302
|
+
CloudFront.ResponseHeadersPolicyProvider(),
|
|
292
303
|
CloudWatch.AlarmMuteRuleProvider(),
|
|
293
304
|
CloudWatch.AlarmProvider(),
|
|
294
305
|
CloudWatch.AnomalyDetectorProvider(),
|
|
@@ -357,6 +368,7 @@ export const providers = () =>
|
|
|
357
368
|
EC2.VpcEndpointProvider(),
|
|
358
369
|
EC2.VpcProvider(),
|
|
359
370
|
ECR.RepositoryProvider(),
|
|
371
|
+
ECS.CapacityProviderProvider(),
|
|
360
372
|
ECS.ClusterProvider(),
|
|
361
373
|
ECS.DescribeTasksPolicyLive,
|
|
362
374
|
ECS.ListTasksPolicyLive,
|
package/src/Apply.ts
CHANGED
|
@@ -25,6 +25,7 @@ import { findProviderByType } from "./Provider.ts";
|
|
|
25
25
|
import type { ResourceBinding } from "./Resource.ts";
|
|
26
26
|
import { Stack } from "./Stack.ts";
|
|
27
27
|
import { Stage } from "./Stage.ts";
|
|
28
|
+
import { recordResourceOp, type ResourceOp } from "./Telemetry/Metrics.ts";
|
|
28
29
|
import {
|
|
29
30
|
type CreatedResourceState,
|
|
30
31
|
type CreatingResourceState,
|
|
@@ -81,6 +82,41 @@ const provideLifecycleScope =
|
|
|
81
82
|
),
|
|
82
83
|
) as Effect.Effect<A, E, Exclude<R, InstanceId | Artifacts>>;
|
|
83
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Instruments a single provider lifecycle call with an OTel span
|
|
87
|
+
* (`provider.<op>`), the resource counter / duration histogram, and the
|
|
88
|
+
* scoped artifacts/instance services normally supplied by
|
|
89
|
+
* {@link provideLifecycleScope}.
|
|
90
|
+
*
|
|
91
|
+
* This is the only call site through which provider lifecycle methods
|
|
92
|
+
* are dispatched, so wrapping it here gives us a fully-instrumented
|
|
93
|
+
* toolchain without touching any individual provider implementation.
|
|
94
|
+
*/
|
|
95
|
+
const instrumentLifecycle =
|
|
96
|
+
(
|
|
97
|
+
op: ResourceOp,
|
|
98
|
+
fqn: string,
|
|
99
|
+
resourceType: string,
|
|
100
|
+
logicalId: string,
|
|
101
|
+
instanceId: string,
|
|
102
|
+
) =>
|
|
103
|
+
<A, E, R>(
|
|
104
|
+
effect: Effect.Effect<A, E, R>,
|
|
105
|
+
): Effect.Effect<A, E, Exclude<R, InstanceId | Artifacts>> =>
|
|
106
|
+
effect.pipe(
|
|
107
|
+
provideLifecycleScope(fqn, instanceId),
|
|
108
|
+
recordResourceOp(resourceType, op),
|
|
109
|
+
Effect.withSpan(`provider.${op}`, {
|
|
110
|
+
attributes: {
|
|
111
|
+
"alchemy.resource.fqn": fqn,
|
|
112
|
+
"alchemy.resource.type": resourceType,
|
|
113
|
+
"alchemy.resource.logical_id": logicalId,
|
|
114
|
+
"alchemy.resource.instance_id": instanceId,
|
|
115
|
+
"alchemy.resource.op": op,
|
|
116
|
+
},
|
|
117
|
+
}),
|
|
118
|
+
);
|
|
119
|
+
|
|
84
120
|
export const apply = <P extends Plan>(
|
|
85
121
|
plan: P,
|
|
86
122
|
): Effect.Effect<
|
|
@@ -149,7 +185,15 @@ export const apply = <P extends Plan>(
|
|
|
149
185
|
Object.entries(tracker).map(([fqn, t]) => [fqn, t.output]),
|
|
150
186
|
);
|
|
151
187
|
return yield* Output.evaluate(plan.output, outputs);
|
|
152
|
-
}).pipe(
|
|
188
|
+
}).pipe(
|
|
189
|
+
ensureArtifactStore,
|
|
190
|
+
Effect.withSpan("apply", {
|
|
191
|
+
attributes: {
|
|
192
|
+
"alchemy.resources.count": Object.keys(plan.resources).length,
|
|
193
|
+
"alchemy.deletions.count": Object.keys(plan.deletions).length,
|
|
194
|
+
},
|
|
195
|
+
}),
|
|
196
|
+
);
|
|
153
197
|
|
|
154
198
|
// ── Phase 1: concurrent initial execution ──────────────────────────────────
|
|
155
199
|
//
|
|
@@ -411,7 +455,15 @@ const executeNode = (
|
|
|
411
455
|
instanceId,
|
|
412
456
|
bindings: excludeDeletedBindings(node.bindings),
|
|
413
457
|
})
|
|
414
|
-
.pipe(
|
|
458
|
+
.pipe(
|
|
459
|
+
instrumentLifecycle(
|
|
460
|
+
"precreate",
|
|
461
|
+
fqn,
|
|
462
|
+
node.resource.Type,
|
|
463
|
+
logicalId,
|
|
464
|
+
instanceId,
|
|
465
|
+
),
|
|
466
|
+
);
|
|
415
467
|
yield* commit<CreatingResourceState>({
|
|
416
468
|
status: "creating",
|
|
417
469
|
fqn,
|
|
@@ -458,7 +510,15 @@ const executeNode = (
|
|
|
458
510
|
session: scopedSession,
|
|
459
511
|
output: attr,
|
|
460
512
|
})
|
|
461
|
-
.pipe(
|
|
513
|
+
.pipe(
|
|
514
|
+
instrumentLifecycle(
|
|
515
|
+
"create",
|
|
516
|
+
fqn,
|
|
517
|
+
node.resource.Type,
|
|
518
|
+
logicalId,
|
|
519
|
+
instanceId,
|
|
520
|
+
),
|
|
521
|
+
);
|
|
462
522
|
|
|
463
523
|
yield* commit<CreatedResourceState>({
|
|
464
524
|
status: "created",
|
|
@@ -557,7 +617,15 @@ const executeNode = (
|
|
|
557
617
|
// @ts-expect-error - type system says this can be undefined, can it be?
|
|
558
618
|
output: node.state.attr,
|
|
559
619
|
})
|
|
560
|
-
.pipe(
|
|
620
|
+
.pipe(
|
|
621
|
+
instrumentLifecycle(
|
|
622
|
+
"update",
|
|
623
|
+
fqn,
|
|
624
|
+
node.resource.Type,
|
|
625
|
+
logicalId,
|
|
626
|
+
instanceId,
|
|
627
|
+
),
|
|
628
|
+
);
|
|
561
629
|
|
|
562
630
|
if (node.state.status === "replaced") {
|
|
563
631
|
yield* commit<ReplacedResourceState>({
|
|
@@ -657,7 +725,15 @@ const executeNode = (
|
|
|
657
725
|
instanceId,
|
|
658
726
|
bindings: excludeDeletedBindings(node.bindings),
|
|
659
727
|
})
|
|
660
|
-
.pipe(
|
|
728
|
+
.pipe(
|
|
729
|
+
instrumentLifecycle(
|
|
730
|
+
"precreate",
|
|
731
|
+
fqn,
|
|
732
|
+
node.resource.Type,
|
|
733
|
+
logicalId,
|
|
734
|
+
instanceId,
|
|
735
|
+
),
|
|
736
|
+
);
|
|
661
737
|
yield* commit<ReplacingResourceState>({
|
|
662
738
|
status: "replacing",
|
|
663
739
|
fqn,
|
|
@@ -706,7 +782,15 @@ const executeNode = (
|
|
|
706
782
|
session: scopedSession,
|
|
707
783
|
output: attr,
|
|
708
784
|
})
|
|
709
|
-
.pipe(
|
|
785
|
+
.pipe(
|
|
786
|
+
instrumentLifecycle(
|
|
787
|
+
"create",
|
|
788
|
+
fqn,
|
|
789
|
+
node.resource.Type,
|
|
790
|
+
logicalId,
|
|
791
|
+
instanceId,
|
|
792
|
+
),
|
|
793
|
+
);
|
|
710
794
|
|
|
711
795
|
yield* commit<ReplacedResourceState>({
|
|
712
796
|
// Creation of the new generation succeeded; from here on the only remaining
|
|
@@ -745,7 +829,16 @@ const executeNode = (
|
|
|
745
829
|
// @ts-expect-error - node is never, this should be unreachable
|
|
746
830
|
return yield* Effect.die(`Unknown action: ${node.action}`);
|
|
747
831
|
});
|
|
748
|
-
})
|
|
832
|
+
}).pipe(
|
|
833
|
+
Effect.withSpan("apply.resource", {
|
|
834
|
+
attributes: {
|
|
835
|
+
"alchemy.resource.fqn": fqn,
|
|
836
|
+
"alchemy.resource.type": node.resource.Type,
|
|
837
|
+
"alchemy.resource.logical_id": node.resource.LogicalId,
|
|
838
|
+
"alchemy.resource.action": node.action,
|
|
839
|
+
},
|
|
840
|
+
}),
|
|
841
|
+
) as Effect.Effect<void, never, never>;
|
|
749
842
|
|
|
750
843
|
// ── Phase 3: imperative convergence loop ───────────────────────────────────
|
|
751
844
|
//
|
|
@@ -830,7 +923,15 @@ const converge = Effect.fnUntraced(function* (
|
|
|
830
923
|
olds: oldProps,
|
|
831
924
|
output: tracker[fqn].output,
|
|
832
925
|
})
|
|
833
|
-
.pipe(
|
|
926
|
+
.pipe(
|
|
927
|
+
instrumentLifecycle(
|
|
928
|
+
"update",
|
|
929
|
+
fqn,
|
|
930
|
+
node.resource.Type,
|
|
931
|
+
logicalId,
|
|
932
|
+
instanceId,
|
|
933
|
+
),
|
|
934
|
+
);
|
|
834
935
|
|
|
835
936
|
tracker[fqn] = {
|
|
836
937
|
output: attr,
|
|
@@ -1007,7 +1108,15 @@ const collectGarbage = Effect.fnUntraced(function* (
|
|
|
1007
1108
|
session: scopedSession,
|
|
1008
1109
|
bindings: [],
|
|
1009
1110
|
})
|
|
1010
|
-
.pipe(
|
|
1111
|
+
.pipe(
|
|
1112
|
+
instrumentLifecycle(
|
|
1113
|
+
"delete",
|
|
1114
|
+
fqn,
|
|
1115
|
+
resourceType,
|
|
1116
|
+
logicalId,
|
|
1117
|
+
instanceId,
|
|
1118
|
+
),
|
|
1119
|
+
);
|
|
1011
1120
|
}
|
|
1012
1121
|
|
|
1013
1122
|
if (isDeleteNode(node)) {
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import * as Operations from "@distilled.cloud/axiom/Operations";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Provider from "../Provider.ts";
|
|
4
|
+
import { Resource } from "../Resource.ts";
|
|
5
|
+
import type { Providers } from "./Providers.ts";
|
|
6
|
+
|
|
7
|
+
export type AnnotationProps = Operations.CreateAnnotationInput;
|
|
8
|
+
|
|
9
|
+
export type Annotation = Resource<
|
|
10
|
+
"Axiom.Annotation",
|
|
11
|
+
AnnotationProps,
|
|
12
|
+
Operations.CreateAnnotationOutput,
|
|
13
|
+
never,
|
|
14
|
+
Providers
|
|
15
|
+
>;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* An Axiom annotation — a vertical marker overlaid on charts to flag a
|
|
19
|
+
* deploy, incident, feature flag flip, or any other point/range event you
|
|
20
|
+
* want correlated with telemetry.
|
|
21
|
+
*
|
|
22
|
+
* Annotations are scoped to one or more `datasets`. Use a single `time` for
|
|
23
|
+
* a point marker or `time` + `endTime` for a range. The `type` (e.g.
|
|
24
|
+
* `"deploy"`, `"incident"`) groups markers visually in the UI.
|
|
25
|
+
*
|
|
26
|
+
* Although typically created at deploy/release time (out-of-band of
|
|
27
|
+
* regular IaC), modelling them as resources makes per-environment
|
|
28
|
+
* annotation history reproducible.
|
|
29
|
+
*
|
|
30
|
+
* @see https://axiom.co/docs/query-data/annotate-charts
|
|
31
|
+
*
|
|
32
|
+
* @section Creating an Annotation
|
|
33
|
+
* @example Point-in-time deploy marker
|
|
34
|
+
* ```typescript
|
|
35
|
+
* yield* Axiom.Annotation("deploy-1.2.3", {
|
|
36
|
+
* type: "deploy",
|
|
37
|
+
* title: "Release 1.2.3",
|
|
38
|
+
* description: "https://github.com/acme/app/releases/tag/v1.2.3",
|
|
39
|
+
* datasets: ["my-app-traces", "my-app-logs"],
|
|
40
|
+
* time: new Date().toISOString(),
|
|
41
|
+
* url: "https://github.com/acme/app/releases/tag/v1.2.3",
|
|
42
|
+
* });
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @example Incident time-range
|
|
46
|
+
* ```typescript
|
|
47
|
+
* yield* Axiom.Annotation("inc-2026-04-27", {
|
|
48
|
+
* type: "incident",
|
|
49
|
+
* title: "Database failover",
|
|
50
|
+
* datasets: ["my-app-traces"],
|
|
51
|
+
* time: "2026-04-27T18:05:00Z",
|
|
52
|
+
* endTime: "2026-04-27T18:32:00Z",
|
|
53
|
+
* url: "https://incident.io/incidents/abc123",
|
|
54
|
+
* });
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export const Annotation = Resource<Annotation>("Axiom.Annotation");
|
|
58
|
+
|
|
59
|
+
export const AnnotationProvider = () =>
|
|
60
|
+
Provider.effect(
|
|
61
|
+
Annotation,
|
|
62
|
+
Effect.gen(function* () {
|
|
63
|
+
const create = yield* Operations.createAnnotation;
|
|
64
|
+
const update = yield* Operations.updateAnnotation;
|
|
65
|
+
const get = yield* Operations.getAnnotation;
|
|
66
|
+
const del = yield* Operations.deleteAnnotation;
|
|
67
|
+
|
|
68
|
+
return {
|
|
69
|
+
stables: ["id"],
|
|
70
|
+
create: Effect.fn(function* ({ news }) {
|
|
71
|
+
return yield* create(news);
|
|
72
|
+
}),
|
|
73
|
+
update: Effect.fn(function* ({ news, output }) {
|
|
74
|
+
const result = yield* update({ ...news, id: output.id });
|
|
75
|
+
return { ...result, id: output.id, time: result.time ?? output.time };
|
|
76
|
+
}),
|
|
77
|
+
delete: Effect.fn(function* ({ output }) {
|
|
78
|
+
yield* del({ id: output.id }).pipe(
|
|
79
|
+
Effect.catchTag("NotFound", () => Effect.void),
|
|
80
|
+
);
|
|
81
|
+
}),
|
|
82
|
+
read: Effect.fn(function* ({ output }) {
|
|
83
|
+
if (!output?.id) return undefined;
|
|
84
|
+
return yield* get({ id: output.id }).pipe(
|
|
85
|
+
Effect.map((current) => ({
|
|
86
|
+
...current,
|
|
87
|
+
id: current.id ?? output.id,
|
|
88
|
+
time: current.time ?? output.time,
|
|
89
|
+
})),
|
|
90
|
+
Effect.catchTag("NotFound", () => Effect.succeed(undefined)),
|
|
91
|
+
);
|
|
92
|
+
}),
|
|
93
|
+
};
|
|
94
|
+
}),
|
|
95
|
+
);
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import * as Operations from "@distilled.cloud/axiom/Operations";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Redacted from "effect/Redacted";
|
|
4
|
+
import { deepEqual, isResolved } from "../Diff.ts";
|
|
5
|
+
import * as Provider from "../Provider.ts";
|
|
6
|
+
import { Resource } from "../Resource.ts";
|
|
7
|
+
import type { Providers } from "./Providers.ts";
|
|
8
|
+
|
|
9
|
+
export type ApiTokenProps = Omit<Operations.CreateAPITokenInput, never>;
|
|
10
|
+
|
|
11
|
+
export type ApiToken = Resource<
|
|
12
|
+
"Axiom.ApiToken",
|
|
13
|
+
ApiTokenProps,
|
|
14
|
+
Omit<Operations.CreateAPITokenOutput, "token"> & {
|
|
15
|
+
/**
|
|
16
|
+
* The bearer token. Returned only by `create` (and `regenerate`); Axiom
|
|
17
|
+
* does not return it on subsequent reads. Persisted in resource state via
|
|
18
|
+
* `Redacted` — handle with care.
|
|
19
|
+
*/
|
|
20
|
+
token: Redacted.Redacted<string>;
|
|
21
|
+
},
|
|
22
|
+
never,
|
|
23
|
+
Providers
|
|
24
|
+
>;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* An Axiom API token — a scoped bearer token used to authenticate API
|
|
28
|
+
* requests (ingest, query, admin). Capabilities are pinned at creation time;
|
|
29
|
+
* changing any field triggers a **replacement** because Axiom does not
|
|
30
|
+
* expose an update endpoint.
|
|
31
|
+
*
|
|
32
|
+
* The raw token value is returned only by `create`. After that, Axiom
|
|
33
|
+
* never echoes it back, so it is captured into `output.token` (as a
|
|
34
|
+
* {@link Redacted}) on initial create and persisted in resource state.
|
|
35
|
+
* Treat resource state as sensitive — anyone with read access can recover
|
|
36
|
+
* the token. Pair with a secret store for downstream consumption.
|
|
37
|
+
*
|
|
38
|
+
* @see https://axiom.co/docs/reference/tokens
|
|
39
|
+
*
|
|
40
|
+
* @section Creating an API Token
|
|
41
|
+
* @example Ingest-only token scoped to one dataset
|
|
42
|
+
* ```typescript
|
|
43
|
+
* const ingest = yield* Axiom.ApiToken("ingest", {
|
|
44
|
+
* name: "prod-ingest",
|
|
45
|
+
* description: "OTEL collector ingest",
|
|
46
|
+
* datasetCapabilities: {
|
|
47
|
+
* "my-app-traces": { ingest: ["create"] },
|
|
48
|
+
* },
|
|
49
|
+
* });
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* @example Read-only query token
|
|
53
|
+
* ```typescript
|
|
54
|
+
* yield* Axiom.ApiToken("query", {
|
|
55
|
+
* name: "grafana-reader",
|
|
56
|
+
* datasetCapabilities: {
|
|
57
|
+
* "my-app-traces": { query: ["read"] },
|
|
58
|
+
* "my-app-logs": { query: ["read"] },
|
|
59
|
+
* },
|
|
60
|
+
* });
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* @section Consuming the Token
|
|
64
|
+
* @example Forward the token via Cloudflare Secrets
|
|
65
|
+
* ```typescript
|
|
66
|
+
* const secret = yield* Cloudflare.Secret("axiom-token", {
|
|
67
|
+
* value: ingest.token,
|
|
68
|
+
* });
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export const ApiToken = Resource<ApiToken>("Axiom.ApiToken");
|
|
72
|
+
|
|
73
|
+
export const ApiTokenProvider = () =>
|
|
74
|
+
Provider.effect(
|
|
75
|
+
ApiToken,
|
|
76
|
+
Effect.gen(function* () {
|
|
77
|
+
const create = yield* Operations.createAPIToken;
|
|
78
|
+
const get = yield* Operations.getAPIToken;
|
|
79
|
+
const del = yield* Operations.deleteAPIToken;
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
stables: ["id", "token"],
|
|
83
|
+
diff: Effect.fn(function* ({ olds, news, output }) {
|
|
84
|
+
if (!isResolved(news)) return undefined;
|
|
85
|
+
// First create — let the engine create normally.
|
|
86
|
+
if (output == null) return undefined;
|
|
87
|
+
// Axiom has no update endpoint for tokens, so any actual change
|
|
88
|
+
// to the inputs forces a replacement (which mints a new bearer
|
|
89
|
+
// value). If nothing changed, fall through to `noop` so we don't
|
|
90
|
+
// needlessly rotate the token on every deploy.
|
|
91
|
+
if (deepEqual(olds, news)) return undefined;
|
|
92
|
+
return { action: "replace" } as const;
|
|
93
|
+
}),
|
|
94
|
+
create: Effect.fn(function* ({ news }) {
|
|
95
|
+
const result = yield* create(news);
|
|
96
|
+
if (!result.token) {
|
|
97
|
+
return yield* Effect.die(
|
|
98
|
+
new Error("Axiom did not return a token on create"),
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
return {
|
|
102
|
+
...result,
|
|
103
|
+
token: Redacted.make(result.token),
|
|
104
|
+
};
|
|
105
|
+
}),
|
|
106
|
+
update: Effect.fn(function* ({ output }) {
|
|
107
|
+
// Diff returns "replace" for every change, so update should never
|
|
108
|
+
// run. If invoked, just return current state unchanged.
|
|
109
|
+
return output;
|
|
110
|
+
}),
|
|
111
|
+
delete: Effect.fn(function* ({ output }) {
|
|
112
|
+
yield* del({ id: output.id }).pipe(
|
|
113
|
+
Effect.catchTag("NotFound", () => Effect.void),
|
|
114
|
+
);
|
|
115
|
+
}),
|
|
116
|
+
read: Effect.fn(function* ({ output }) {
|
|
117
|
+
if (!output?.id) return undefined;
|
|
118
|
+
return yield* get({ id: output.id }).pipe(
|
|
119
|
+
Effect.map((current) => ({ ...current, token: output.token })),
|
|
120
|
+
Effect.catchTag("NotFound", () => Effect.succeed(undefined)),
|
|
121
|
+
);
|
|
122
|
+
}),
|
|
123
|
+
};
|
|
124
|
+
}),
|
|
125
|
+
);
|