alchemy 2.0.0-beta.18 → 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/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/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/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/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/bin/alchemy.ts
CHANGED
|
@@ -7,6 +7,7 @@ import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient";
|
|
|
7
7
|
import packageJson from "../package.json" with { type: "json" };
|
|
8
8
|
import { AlchemyContextLive } from "../src/AlchemyContext.ts";
|
|
9
9
|
import { inkCLI } from "../src/Cli/InkCLI.tsx";
|
|
10
|
+
import { TelemetryLive } from "../src/Telemetry/Layer.ts";
|
|
10
11
|
import { PlatformServices, runMain } from "../src/Util/PlatformServices.ts";
|
|
11
12
|
|
|
12
13
|
import { handleCancellation } from "./commands/_shared.ts";
|
|
@@ -45,6 +46,7 @@ const services = Layer.mergeAll(
|
|
|
45
46
|
Layer.provideMerge(AlchemyContextLive, PlatformServices),
|
|
46
47
|
FetchHttpClient.layer,
|
|
47
48
|
ConfigProvider.layer(ConfigProvider.fromEnv()),
|
|
49
|
+
TelemetryLive,
|
|
48
50
|
inkCLI(),
|
|
49
51
|
);
|
|
50
52
|
|
package/bin/commands/_shared.ts
CHANGED
|
@@ -11,6 +11,7 @@ import * as Flag from "effect/unstable/cli/Flag";
|
|
|
11
11
|
|
|
12
12
|
import { AuthError } from "../../src/Auth/AuthProvider.ts";
|
|
13
13
|
import type * as Stack from "../../src/Stack.ts";
|
|
14
|
+
import { recordCli } from "../../src/Telemetry/Metrics.ts";
|
|
14
15
|
import { PromptCancelled } from "../../src/Util/Clank.ts";
|
|
15
16
|
|
|
16
17
|
export const USER = Config.string("USER").pipe(
|
|
@@ -218,6 +219,39 @@ export const parseSince = (value: string): Date => {
|
|
|
218
219
|
return parsed;
|
|
219
220
|
};
|
|
220
221
|
|
|
222
|
+
/**
|
|
223
|
+
* Wraps a CLI command handler with a top-level OpenTelemetry span
|
|
224
|
+
* (`cli.<command>`) and bumps the `alchemy.cli.invocations` counter.
|
|
225
|
+
*
|
|
226
|
+
* `attrs` runs against the parsed command args and contributes
|
|
227
|
+
* additional attributes to the span (e.g. stage, profile, dry-run flag).
|
|
228
|
+
*
|
|
229
|
+
* Usage:
|
|
230
|
+
* ```ts
|
|
231
|
+
* Command.make(
|
|
232
|
+
* "deploy",
|
|
233
|
+
* { ...flags },
|
|
234
|
+
* instrumentCommand("deploy", (a) => ({
|
|
235
|
+
* "alchemy.stage": a.stage,
|
|
236
|
+
* "alchemy.profile": a.profile,
|
|
237
|
+
* }))(execStack),
|
|
238
|
+
* );
|
|
239
|
+
* ```
|
|
240
|
+
*/
|
|
241
|
+
export const instrumentCommand =
|
|
242
|
+
<Args>(
|
|
243
|
+
command: string,
|
|
244
|
+
attrs: (args: Args) => Record<string, unknown> = () => ({}),
|
|
245
|
+
) =>
|
|
246
|
+
<A, E, R>(
|
|
247
|
+
handler: (args: Args) => Effect.Effect<A, E, R>,
|
|
248
|
+
): ((args: Args) => Effect.Effect<A, E, R>) =>
|
|
249
|
+
(args) =>
|
|
250
|
+
handler(args).pipe(
|
|
251
|
+
Effect.withSpan(`cli.${command}`, { attributes: attrs(args) }),
|
|
252
|
+
recordCli(command),
|
|
253
|
+
);
|
|
254
|
+
|
|
221
255
|
export const importStack = Effect.fn(function* (main: string) {
|
|
222
256
|
const path = yield* Path.Path;
|
|
223
257
|
const module = yield* Effect.promise(
|
|
@@ -21,7 +21,7 @@ import * as AWSRegion from "../../src/AWS/Region.ts";
|
|
|
21
21
|
import { loadConfigProvider } from "../../src/Util/ConfigProvider.ts";
|
|
22
22
|
import { fileLogger } from "../../src/Util/FileLogger.ts";
|
|
23
23
|
|
|
24
|
-
import { envFile } from "./_shared.ts";
|
|
24
|
+
import { envFile, instrumentCommand } from "./_shared.ts";
|
|
25
25
|
|
|
26
26
|
const awsProfile = Flag.string("profile").pipe(
|
|
27
27
|
Flag.withDescription("AWS profile to use for credentials"),
|
|
@@ -50,7 +50,14 @@ export const bootstrapCommand = Command.make(
|
|
|
50
50
|
region: awsRegion,
|
|
51
51
|
destroy: bootstrapDestroy,
|
|
52
52
|
},
|
|
53
|
-
|
|
53
|
+
instrumentCommand(
|
|
54
|
+
"bootstrap",
|
|
55
|
+
(a: { profile: string; region: string | undefined; destroy: boolean }) => ({
|
|
56
|
+
"alchemy.profile": a.profile,
|
|
57
|
+
"alchemy.region": a.region ?? "",
|
|
58
|
+
"alchemy.destroy": a.destroy,
|
|
59
|
+
}),
|
|
60
|
+
)(Effect.fnUntraced(function* ({ envFile, profile, region, destroy }) {
|
|
54
61
|
const logger = Logger.layer([fileLogger("bootstrap.txt")]);
|
|
55
62
|
|
|
56
63
|
return yield* Effect.gen(function* () {
|
|
@@ -113,5 +120,5 @@ export const bootstrapCommand = Command.make(
|
|
|
113
120
|
);
|
|
114
121
|
});
|
|
115
122
|
}).pipe(Effect.provide(logger));
|
|
116
|
-
}),
|
|
123
|
+
})),
|
|
117
124
|
);
|
package/bin/commands/deploy.ts
CHANGED
|
@@ -21,12 +21,30 @@ import {
|
|
|
21
21
|
envFile,
|
|
22
22
|
force,
|
|
23
23
|
importStack,
|
|
24
|
+
instrumentCommand,
|
|
24
25
|
main,
|
|
25
26
|
profile,
|
|
26
27
|
stage,
|
|
27
28
|
yes,
|
|
28
29
|
} from "./_shared.ts";
|
|
29
30
|
|
|
31
|
+
const stackSpanAttrs = (args: {
|
|
32
|
+
main: string;
|
|
33
|
+
stage: string;
|
|
34
|
+
profile: string;
|
|
35
|
+
dryRun?: boolean;
|
|
36
|
+
force?: boolean;
|
|
37
|
+
yes?: boolean;
|
|
38
|
+
destroy?: boolean;
|
|
39
|
+
}) => ({
|
|
40
|
+
"alchemy.stage": args.stage,
|
|
41
|
+
"alchemy.profile": args.profile,
|
|
42
|
+
"alchemy.main": args.main,
|
|
43
|
+
"alchemy.dry_run": !!args.dryRun,
|
|
44
|
+
"alchemy.force": !!args.force,
|
|
45
|
+
"alchemy.destroy": !!args.destroy,
|
|
46
|
+
});
|
|
47
|
+
|
|
30
48
|
const execStack = Effect.fn(function* ({
|
|
31
49
|
main,
|
|
32
50
|
stage,
|
|
@@ -118,7 +136,7 @@ export const deployCommand = Command.make(
|
|
|
118
136
|
yes,
|
|
119
137
|
profile,
|
|
120
138
|
},
|
|
121
|
-
execStack,
|
|
139
|
+
instrumentCommand("deploy", stackSpanAttrs)(execStack),
|
|
122
140
|
);
|
|
123
141
|
|
|
124
142
|
export const destroyCommand = Command.make(
|
|
@@ -131,11 +149,12 @@ export const destroyCommand = Command.make(
|
|
|
131
149
|
yes,
|
|
132
150
|
profile,
|
|
133
151
|
},
|
|
134
|
-
(args) =>
|
|
152
|
+
instrumentCommand("destroy", stackSpanAttrs)((args) =>
|
|
135
153
|
execStack({
|
|
136
154
|
...args,
|
|
137
155
|
destroy: true,
|
|
138
156
|
}),
|
|
157
|
+
),
|
|
139
158
|
);
|
|
140
159
|
|
|
141
160
|
export const planCommand = Command.make(
|
|
@@ -146,10 +165,11 @@ export const planCommand = Command.make(
|
|
|
146
165
|
stage,
|
|
147
166
|
profile,
|
|
148
167
|
},
|
|
149
|
-
(args) =>
|
|
168
|
+
instrumentCommand("plan", stackSpanAttrs)((args) =>
|
|
150
169
|
execStack({
|
|
151
170
|
...args,
|
|
152
171
|
// plan is the same as deploy with dryRun always set to true
|
|
153
172
|
dryRun: true,
|
|
154
173
|
}),
|
|
174
|
+
),
|
|
155
175
|
);
|
package/bin/commands/login.ts
CHANGED
|
@@ -18,7 +18,14 @@ import * as State from "../../src/State/index.ts";
|
|
|
18
18
|
import { loadConfigProvider } from "../../src/Util/ConfigProvider.ts";
|
|
19
19
|
import { fileLogger } from "../../src/Util/FileLogger.ts";
|
|
20
20
|
|
|
21
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
envFile,
|
|
23
|
+
importStack,
|
|
24
|
+
instrumentCommand,
|
|
25
|
+
main,
|
|
26
|
+
profile,
|
|
27
|
+
stage,
|
|
28
|
+
} from "./_shared.ts";
|
|
22
29
|
|
|
23
30
|
const loginConfigure = Flag.boolean("configure").pipe(
|
|
24
31
|
Flag.withDescription(
|
|
@@ -36,7 +43,15 @@ export const loginCommand = Command.make(
|
|
|
36
43
|
profile,
|
|
37
44
|
configure: loginConfigure,
|
|
38
45
|
},
|
|
39
|
-
|
|
46
|
+
instrumentCommand(
|
|
47
|
+
"login",
|
|
48
|
+
(a: { main: string; stage: string; profile: string; configure: boolean }) => ({
|
|
49
|
+
"alchemy.stage": a.stage,
|
|
50
|
+
"alchemy.profile": a.profile,
|
|
51
|
+
"alchemy.main": a.main,
|
|
52
|
+
"alchemy.configure": a.configure,
|
|
53
|
+
}),
|
|
54
|
+
)(Effect.fnUntraced(function* ({ main, stage, envFile, profile, configure }) {
|
|
40
55
|
const stackEffect = yield* importStack(main);
|
|
41
56
|
|
|
42
57
|
const authProviders: AuthProviders["Service"] = {};
|
|
@@ -92,5 +107,5 @@ export const loginCommand = Command.make(
|
|
|
92
107
|
{ discard: true },
|
|
93
108
|
);
|
|
94
109
|
}).pipe(Effect.provide(services));
|
|
95
|
-
}),
|
|
110
|
+
})),
|
|
96
111
|
);
|
package/bin/commands/logs.ts
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
envFile,
|
|
19
19
|
formatLocalTimestamp,
|
|
20
20
|
importStack,
|
|
21
|
+
instrumentCommand,
|
|
21
22
|
main,
|
|
22
23
|
parseResourceFilter,
|
|
23
24
|
parseSince,
|
|
@@ -52,7 +53,15 @@ export const logsCommand = Command.make(
|
|
|
52
53
|
limit: logsLimit,
|
|
53
54
|
since: logsSince,
|
|
54
55
|
},
|
|
55
|
-
|
|
56
|
+
instrumentCommand(
|
|
57
|
+
"logs",
|
|
58
|
+
(a: { main: string; stage: string; profile: string; limit: number }) => ({
|
|
59
|
+
"alchemy.stage": a.stage,
|
|
60
|
+
"alchemy.profile": a.profile,
|
|
61
|
+
"alchemy.main": a.main,
|
|
62
|
+
"alchemy.limit": a.limit,
|
|
63
|
+
}),
|
|
64
|
+
)(Effect.fnUntraced(function* ({
|
|
56
65
|
main,
|
|
57
66
|
stage,
|
|
58
67
|
envFile,
|
|
@@ -153,5 +162,5 @@ export const logsCommand = Command.make(
|
|
|
153
162
|
}
|
|
154
163
|
}).pipe(Effect.provide(stack.services));
|
|
155
164
|
}).pipe(Effect.provide(services));
|
|
156
|
-
}),
|
|
165
|
+
})),
|
|
157
166
|
);
|
package/bin/commands/profile.ts
CHANGED
|
@@ -18,12 +18,15 @@ import { CloudflareAuth } from "../../src/Cloudflare/Auth/AuthProvider.ts";
|
|
|
18
18
|
import { loadConfigProvider } from "../../src/Util/ConfigProvider.ts";
|
|
19
19
|
import { fileLogger } from "../../src/Util/FileLogger.ts";
|
|
20
20
|
|
|
21
|
-
import { envFile, profile } from "./_shared.ts";
|
|
21
|
+
import { envFile, instrumentCommand, profile } from "./_shared.ts";
|
|
22
22
|
|
|
23
23
|
const showCommand = Command.make(
|
|
24
24
|
"show",
|
|
25
25
|
{ profile, envFile },
|
|
26
|
-
|
|
26
|
+
instrumentCommand(
|
|
27
|
+
"profile.show",
|
|
28
|
+
(a: { profile: string }) => ({ "alchemy.profile": a.profile }),
|
|
29
|
+
)(Effect.fnUntraced(function* ({ profile, envFile }) {
|
|
27
30
|
const stored = yield* getProfile(profile);
|
|
28
31
|
if (stored == null) {
|
|
29
32
|
const config = yield* readConfig;
|
|
@@ -80,13 +83,16 @@ const showCommand = Command.make(
|
|
|
80
83
|
yield* provider.prettyPrint(profile, cfg);
|
|
81
84
|
}
|
|
82
85
|
}).pipe(Effect.provide(services));
|
|
83
|
-
}),
|
|
86
|
+
})),
|
|
84
87
|
);
|
|
85
88
|
|
|
86
89
|
const clearCommand = Command.make(
|
|
87
90
|
"clear",
|
|
88
91
|
{ profile },
|
|
89
|
-
|
|
92
|
+
instrumentCommand(
|
|
93
|
+
"profile.clear",
|
|
94
|
+
(a: { profile: string }) => ({ "alchemy.profile": a.profile }),
|
|
95
|
+
)(Effect.fnUntraced(function* ({ profile }) {
|
|
90
96
|
const removed = yield* deleteProfile(profile);
|
|
91
97
|
yield* deleteProfileCredentials(profile);
|
|
92
98
|
if (removed) {
|
|
@@ -98,7 +104,7 @@ const clearCommand = Command.make(
|
|
|
98
104
|
`Profile '${profile}' not found in profiles.json; removed any stray credentials directory.`,
|
|
99
105
|
);
|
|
100
106
|
}
|
|
101
|
-
}),
|
|
107
|
+
})),
|
|
102
108
|
);
|
|
103
109
|
|
|
104
110
|
export const profileCommand = Command.make("profile", {}).pipe(
|
package/bin/commands/state.ts
CHANGED
|
@@ -15,7 +15,15 @@ import * as Clank from "../../src/Util/Clank.ts";
|
|
|
15
15
|
import { loadConfigProvider } from "../../src/Util/ConfigProvider.ts";
|
|
16
16
|
import { fileLogger } from "../../src/Util/FileLogger.ts";
|
|
17
17
|
|
|
18
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
envFile,
|
|
20
|
+
importStack,
|
|
21
|
+
instrumentCommand,
|
|
22
|
+
main,
|
|
23
|
+
profile,
|
|
24
|
+
stage,
|
|
25
|
+
yes,
|
|
26
|
+
} from "./_shared.ts";
|
|
19
27
|
|
|
20
28
|
/**
|
|
21
29
|
* When set, the State service is replaced with the on-disk
|
|
@@ -91,7 +99,7 @@ const withStateService = <A, E>(
|
|
|
91
99
|
const stacksCommand = Command.make(
|
|
92
100
|
"stacks",
|
|
93
101
|
{ main, envFile, stage, profile, local: localFlag },
|
|
94
|
-
Effect.fnUntraced(function* (args) {
|
|
102
|
+
instrumentCommand("state.stacks")(Effect.fnUntraced(function* (args) {
|
|
95
103
|
yield* withStateService(args, (state) =>
|
|
96
104
|
Effect.gen(function* () {
|
|
97
105
|
const stacks = yield* state.listStacks();
|
|
@@ -104,13 +112,13 @@ const stacksCommand = Command.make(
|
|
|
104
112
|
}
|
|
105
113
|
}),
|
|
106
114
|
);
|
|
107
|
-
}),
|
|
115
|
+
})),
|
|
108
116
|
);
|
|
109
117
|
|
|
110
118
|
const stagesCommand = Command.make(
|
|
111
119
|
"stages",
|
|
112
120
|
{ stack: stackArg, main, envFile, stage, profile, local: localFlag },
|
|
113
|
-
Effect.fnUntraced(function* ({ stack: stackName, ...rest }) {
|
|
121
|
+
instrumentCommand("state.stages")(Effect.fnUntraced(function* ({ stack: stackName, ...rest }) {
|
|
114
122
|
yield* withStateService(rest, (state) =>
|
|
115
123
|
Effect.gen(function* () {
|
|
116
124
|
const stages = yield* state.listStages(stackName);
|
|
@@ -123,7 +131,7 @@ const stagesCommand = Command.make(
|
|
|
123
131
|
}
|
|
124
132
|
}),
|
|
125
133
|
);
|
|
126
|
-
}),
|
|
134
|
+
})),
|
|
127
135
|
);
|
|
128
136
|
|
|
129
137
|
const resourcesCommand = Command.make(
|
|
@@ -139,7 +147,7 @@ const resourcesCommand = Command.make(
|
|
|
139
147
|
profile,
|
|
140
148
|
local: localFlag,
|
|
141
149
|
},
|
|
142
|
-
Effect.fnUntraced(function* ({ stack: stackName, stageName, ...rest }) {
|
|
150
|
+
instrumentCommand("state.resources")(Effect.fnUntraced(function* ({ stack: stackName, stageName, ...rest }) {
|
|
143
151
|
yield* withStateService(rest, (state) =>
|
|
144
152
|
Effect.gen(function* () {
|
|
145
153
|
const fqns = yield* state.list({ stack: stackName, stage: stageName });
|
|
@@ -152,7 +160,7 @@ const resourcesCommand = Command.make(
|
|
|
152
160
|
}
|
|
153
161
|
}),
|
|
154
162
|
);
|
|
155
|
-
}),
|
|
163
|
+
})),
|
|
156
164
|
);
|
|
157
165
|
|
|
158
166
|
const getCommand = Command.make(
|
|
@@ -169,7 +177,7 @@ const getCommand = Command.make(
|
|
|
169
177
|
profile,
|
|
170
178
|
local: localFlag,
|
|
171
179
|
},
|
|
172
|
-
Effect.fnUntraced(function* ({ stack: stackName, stageName, fqn, ...rest }) {
|
|
180
|
+
instrumentCommand("state.get")(Effect.fnUntraced(function* ({ stack: stackName, stageName, fqn, ...rest }) {
|
|
173
181
|
yield* withStateService(rest, (state) =>
|
|
174
182
|
Effect.gen(function* () {
|
|
175
183
|
const value = yield* state.get({
|
|
@@ -187,13 +195,13 @@ const getCommand = Command.make(
|
|
|
187
195
|
yield* Console.log(JSON.stringify(encodeState(value), null, 2));
|
|
188
196
|
}),
|
|
189
197
|
);
|
|
190
|
-
}),
|
|
198
|
+
})),
|
|
191
199
|
);
|
|
192
200
|
|
|
193
201
|
const treeCommand = Command.make(
|
|
194
202
|
"tree",
|
|
195
203
|
{ main, envFile, stage, profile, local: localFlag },
|
|
196
|
-
Effect.fnUntraced(function* (args) {
|
|
204
|
+
instrumentCommand("state.tree")(Effect.fnUntraced(function* (args) {
|
|
197
205
|
yield* withStateService(args, (state) =>
|
|
198
206
|
Effect.gen(function* () {
|
|
199
207
|
const stacks = [...(yield* state.listStacks())].sort();
|
|
@@ -221,7 +229,7 @@ const treeCommand = Command.make(
|
|
|
221
229
|
}
|
|
222
230
|
}),
|
|
223
231
|
);
|
|
224
|
-
}),
|
|
232
|
+
})),
|
|
225
233
|
);
|
|
226
234
|
|
|
227
235
|
const clearStackArg = Argument.string("stack").pipe(
|
|
@@ -255,7 +263,7 @@ const clearCommand = Command.make(
|
|
|
255
263
|
local: localFlag,
|
|
256
264
|
yes,
|
|
257
265
|
},
|
|
258
|
-
Effect.fnUntraced(function* ({ stack: stackOpt, stageName: stageOpt, yes: yesFlag, ...rest }) {
|
|
266
|
+
instrumentCommand("state.clear")(Effect.fnUntraced(function* ({ stack: stackOpt, stageName: stageOpt, yes: yesFlag, ...rest }) {
|
|
259
267
|
const stackName = Option.getOrUndefined(stackOpt);
|
|
260
268
|
const stageName = Option.getOrUndefined(stageOpt);
|
|
261
269
|
|
|
@@ -308,7 +316,7 @@ const clearCommand = Command.make(
|
|
|
308
316
|
}
|
|
309
317
|
}),
|
|
310
318
|
);
|
|
311
|
-
}),
|
|
319
|
+
})),
|
|
312
320
|
);
|
|
313
321
|
|
|
314
322
|
export const stateCommand = Command.make("state", {}).pipe(
|
package/bin/commands/tail.ts
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
envFile,
|
|
19
19
|
formatLocalTimestamp,
|
|
20
20
|
importStack,
|
|
21
|
+
instrumentCommand,
|
|
21
22
|
main,
|
|
22
23
|
parseResourceFilter,
|
|
23
24
|
profile,
|
|
@@ -36,7 +37,14 @@ export const tailCommand = Command.make(
|
|
|
36
37
|
profile,
|
|
37
38
|
filter: resourceFilter,
|
|
38
39
|
},
|
|
39
|
-
|
|
40
|
+
instrumentCommand(
|
|
41
|
+
"tail",
|
|
42
|
+
(a: { main: string; stage: string; profile: string }) => ({
|
|
43
|
+
"alchemy.stage": a.stage,
|
|
44
|
+
"alchemy.profile": a.profile,
|
|
45
|
+
"alchemy.main": a.main,
|
|
46
|
+
}),
|
|
47
|
+
)(Effect.fnUntraced(function* ({ main, stage, envFile, profile, filter }) {
|
|
40
48
|
const stackEffect = yield* importStack(main);
|
|
41
49
|
|
|
42
50
|
const services = Layer.mergeAll(
|
|
@@ -134,5 +142,5 @@ export const tailCommand = Command.make(
|
|
|
134
142
|
}).pipe(Stream.runForEach((line) => Console.log(line)));
|
|
135
143
|
}).pipe(Effect.provide(stack.services));
|
|
136
144
|
}).pipe(Effect.provide(services));
|
|
137
|
-
}),
|
|
145
|
+
})),
|
|
138
146
|
);
|
|
@@ -96,9 +96,9 @@ export declare const makeLSPClient: (id: string, proc: Subprocess, root: string)
|
|
|
96
96
|
readonly character: number;
|
|
97
97
|
};
|
|
98
98
|
};
|
|
99
|
+
readonly source?: string | undefined;
|
|
99
100
|
readonly code?: string | number | undefined;
|
|
100
101
|
readonly severity?: number | undefined;
|
|
101
|
-
readonly source?: string | undefined;
|
|
102
102
|
}[], never, never>;
|
|
103
103
|
getDiagnostics: (path: string) => Effect.Effect<never[] | {
|
|
104
104
|
readonly message: string;
|
|
@@ -112,9 +112,9 @@ export declare const makeLSPClient: (id: string, proc: Subprocess, root: string)
|
|
|
112
112
|
readonly character: number;
|
|
113
113
|
};
|
|
114
114
|
};
|
|
115
|
+
readonly source?: string | undefined;
|
|
115
116
|
readonly code?: string | number | undefined;
|
|
116
117
|
readonly severity?: number | undefined;
|
|
117
|
-
readonly source?: string | undefined;
|
|
118
118
|
}[], never, never>;
|
|
119
119
|
onDiagnostics: (callback: (uri: string, diagnostics: Diagnostic[]) => Effect.Effect<void>) => Effect.Effect<void, never, never>;
|
|
120
120
|
shutdown: Effect.Effect<void, never, never>;
|
package/lib/Apply.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Apply.d.ts","sourceRoot":"","sources":["../src/Apply.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAQ7C,OAAO,EAGL,GAAG,EACJ,MAAM,cAAc,CAAC;AAItB,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,EAAc,KAAK,MAAM,EAAE,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAG/D,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"Apply.d.ts","sourceRoot":"","sources":["../src/Apply.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAQ7C,OAAO,EAGL,GAAG,EACJ,MAAM,cAAc,CAAC;AAItB,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AACtC,OAAO,EAAc,KAAK,MAAM,EAAE,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAG/D,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EASL,KAAK,EACL,eAAe,EAChB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,MAAM,WAAW,CACrB,CAAC,SAAS,IAAI,EACd,GAAG,GAAG,KAAK,EACX,GAAG,GAAG,KAAK,IACT,MAAM,CAAC,MAAM,CACf;KACG,CAAC,IAAI,MAAM,WAAW,CAAC,CAAC,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAC/C,EACD,GAAG,EACH,GAAG,CACJ,CAAC;AAEF,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,IAAI,IAAI;KACvC,EAAE,IAAI,MAAM,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,SAC5C,MAAM,GACN,SAAS,GACT,KAAK,GACL,KAAK,GACL,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC;CACrD,CAAC;AA2DF,eAAO,MAAM,KAAK,GAAI,CAAC,SAAS,IAAI,EAClC,MAAM,CAAC,KACN,MAAM,CAAC,MAAM,CACd,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAC1B,MAAM,CAAC,qBAAqB,GAAG,MAAM,CAAC,kBAAkB,GAAG,eAAe,EAC1E,GAAG,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAuE1B,CAAC"}
|
package/lib/Apply.js
CHANGED
|
@@ -11,8 +11,28 @@ import {} from "./Plan.js";
|
|
|
11
11
|
import { findProviderByType } from "./Provider.js";
|
|
12
12
|
import { Stack } from "./Stack.js";
|
|
13
13
|
import { Stage } from "./Stage.js";
|
|
14
|
+
import { recordResourceOp } from "./Telemetry/Metrics.js";
|
|
14
15
|
import { State, StateStoreError, } from "./State/index.js";
|
|
15
16
|
const provideLifecycleScope = (fqn, instanceId) => (effect) => Effect.serviceOption(ArtifactStore).pipe(Effect.map(Option.getOrElse(createArtifactStore)), Effect.flatMap((store) => effect.pipe(Effect.provideService(Artifacts, makeScopedArtifacts(store, fqn)), Effect.provideService(InstanceId, instanceId))));
|
|
17
|
+
/**
|
|
18
|
+
* Instruments a single provider lifecycle call with an OTel span
|
|
19
|
+
* (`provider.<op>`), the resource counter / duration histogram, and the
|
|
20
|
+
* scoped artifacts/instance services normally supplied by
|
|
21
|
+
* {@link provideLifecycleScope}.
|
|
22
|
+
*
|
|
23
|
+
* This is the only call site through which provider lifecycle methods
|
|
24
|
+
* are dispatched, so wrapping it here gives us a fully-instrumented
|
|
25
|
+
* toolchain without touching any individual provider implementation.
|
|
26
|
+
*/
|
|
27
|
+
const instrumentLifecycle = (op, fqn, resourceType, logicalId, instanceId) => (effect) => effect.pipe(provideLifecycleScope(fqn, instanceId), recordResourceOp(resourceType, op), Effect.withSpan(`provider.${op}`, {
|
|
28
|
+
attributes: {
|
|
29
|
+
"alchemy.resource.fqn": fqn,
|
|
30
|
+
"alchemy.resource.type": resourceType,
|
|
31
|
+
"alchemy.resource.logical_id": logicalId,
|
|
32
|
+
"alchemy.resource.instance_id": instanceId,
|
|
33
|
+
"alchemy.resource.op": op,
|
|
34
|
+
},
|
|
35
|
+
}));
|
|
16
36
|
export const apply = (plan) => Effect.gen(function* () {
|
|
17
37
|
const cli = yield* Cli;
|
|
18
38
|
const session = yield* cli.startApplySession(plan);
|
|
@@ -35,7 +55,12 @@ export const apply = (plan) => Effect.gen(function* () {
|
|
|
35
55
|
}
|
|
36
56
|
const outputs = Object.fromEntries(Object.entries(tracker).map(([fqn, t]) => [fqn, t.output]));
|
|
37
57
|
return yield* Output.evaluate(plan.output, outputs);
|
|
38
|
-
}).pipe(ensureArtifactStore
|
|
58
|
+
}).pipe(ensureArtifactStore, Effect.withSpan("apply", {
|
|
59
|
+
attributes: {
|
|
60
|
+
"alchemy.resources.count": Object.keys(plan.resources).length,
|
|
61
|
+
"alchemy.deletions.count": Object.keys(plan.deletions).length,
|
|
62
|
+
},
|
|
63
|
+
}));
|
|
39
64
|
// ── Phase 1: concurrent initial execution ──────────────────────────────────
|
|
40
65
|
//
|
|
41
66
|
// Each resource gets a Deferred<void> that signals "I have some output
|
|
@@ -189,7 +214,7 @@ const executeNode = (fqn, node, tracker, ready, terminalStatuses, session, state
|
|
|
189
214
|
instanceId,
|
|
190
215
|
bindings: excludeDeletedBindings(node.bindings),
|
|
191
216
|
})
|
|
192
|
-
.pipe(
|
|
217
|
+
.pipe(instrumentLifecycle("precreate", fqn, node.resource.Type, logicalId, instanceId));
|
|
193
218
|
yield* commit({
|
|
194
219
|
status: "creating",
|
|
195
220
|
fqn,
|
|
@@ -226,7 +251,7 @@ const executeNode = (fqn, node, tracker, ready, terminalStatuses, session, state
|
|
|
226
251
|
session: scopedSession,
|
|
227
252
|
output: attr,
|
|
228
253
|
})
|
|
229
|
-
.pipe(
|
|
254
|
+
.pipe(instrumentLifecycle("create", fqn, node.resource.Type, logicalId, instanceId));
|
|
230
255
|
yield* commit({
|
|
231
256
|
status: "created",
|
|
232
257
|
fqn,
|
|
@@ -307,7 +332,7 @@ const executeNode = (fqn, node, tracker, ready, terminalStatuses, session, state
|
|
|
307
332
|
// @ts-expect-error - type system says this can be undefined, can it be?
|
|
308
333
|
output: node.state.attr,
|
|
309
334
|
})
|
|
310
|
-
.pipe(
|
|
335
|
+
.pipe(instrumentLifecycle("update", fqn, node.resource.Type, logicalId, instanceId));
|
|
311
336
|
if (node.state.status === "replaced") {
|
|
312
337
|
yield* commit({
|
|
313
338
|
// The live replacement changed, but cleanup of older generations still
|
|
@@ -401,7 +426,7 @@ const executeNode = (fqn, node, tracker, ready, terminalStatuses, session, state
|
|
|
401
426
|
instanceId,
|
|
402
427
|
bindings: excludeDeletedBindings(node.bindings),
|
|
403
428
|
})
|
|
404
|
-
.pipe(
|
|
429
|
+
.pipe(instrumentLifecycle("precreate", fqn, node.resource.Type, logicalId, instanceId));
|
|
405
430
|
yield* commit({
|
|
406
431
|
status: "replacing",
|
|
407
432
|
fqn,
|
|
@@ -440,7 +465,7 @@ const executeNode = (fqn, node, tracker, ready, terminalStatuses, session, state
|
|
|
440
465
|
session: scopedSession,
|
|
441
466
|
output: attr,
|
|
442
467
|
})
|
|
443
|
-
.pipe(
|
|
468
|
+
.pipe(instrumentLifecycle("create", fqn, node.resource.Type, logicalId, instanceId));
|
|
444
469
|
yield* commit({
|
|
445
470
|
// Creation of the new generation succeeded; from here on the only remaining
|
|
446
471
|
// work is draining the old chain via garbage collection.
|
|
@@ -475,7 +500,14 @@ const executeNode = (fqn, node, tracker, ready, terminalStatuses, session, state
|
|
|
475
500
|
// @ts-expect-error - node is never, this should be unreachable
|
|
476
501
|
return yield* Effect.die(`Unknown action: ${node.action}`);
|
|
477
502
|
});
|
|
478
|
-
})
|
|
503
|
+
}).pipe(Effect.withSpan("apply.resource", {
|
|
504
|
+
attributes: {
|
|
505
|
+
"alchemy.resource.fqn": fqn,
|
|
506
|
+
"alchemy.resource.type": node.resource.Type,
|
|
507
|
+
"alchemy.resource.logical_id": node.resource.LogicalId,
|
|
508
|
+
"alchemy.resource.action": node.action,
|
|
509
|
+
},
|
|
510
|
+
}));
|
|
479
511
|
// ── Phase 3: imperative convergence loop ───────────────────────────────────
|
|
480
512
|
//
|
|
481
513
|
// After the initial concurrent pass, some resources may have been created
|
|
@@ -519,7 +551,7 @@ const converge = Effect.fnUntraced(function* (plan, tracker, terminalStatuses, s
|
|
|
519
551
|
olds: oldProps,
|
|
520
552
|
output: tracker[fqn].output,
|
|
521
553
|
})
|
|
522
|
-
.pipe(
|
|
554
|
+
.pipe(instrumentLifecycle("update", fqn, node.resource.Type, logicalId, instanceId));
|
|
523
555
|
tracker[fqn] = {
|
|
524
556
|
output: attr,
|
|
525
557
|
props: newProps,
|
|
@@ -647,7 +679,7 @@ const collectGarbage = Effect.fnUntraced(function* (plan, session) {
|
|
|
647
679
|
session: scopedSession,
|
|
648
680
|
bindings: [],
|
|
649
681
|
})
|
|
650
|
-
.pipe(
|
|
682
|
+
.pipe(instrumentLifecycle("delete", fqn, resourceType, logicalId, instanceId));
|
|
651
683
|
}
|
|
652
684
|
if (isDeleteNode(node)) {
|
|
653
685
|
yield* state.delete({
|