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
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { exec } from "node:child_process";
|
|
2
|
+
import * as crypto from "node:crypto";
|
|
3
|
+
import * as fs from "node:fs/promises";
|
|
4
|
+
import * as os from "node:os";
|
|
5
|
+
import * as nodePath from "node:path";
|
|
6
|
+
import * as Effect from "effect/Effect";
|
|
7
|
+
import packageJson from "../../package.json" with { type: "json" };
|
|
8
|
+
const ALCHEMY_DIR = nodePath.join(os.homedir(), ".alchemy");
|
|
9
|
+
const ID_PATH = nodePath.join(ALCHEMY_DIR, "id");
|
|
10
|
+
const DISABLED_PATH = nodePath.join(ALCHEMY_DIR, "telemetry-disabled");
|
|
11
|
+
const tryRead = (path) => Effect.tryPromise({
|
|
12
|
+
try: () => fs.readFile(path, "utf-8").then((s) => s.trim()),
|
|
13
|
+
catch: () => null,
|
|
14
|
+
}).pipe(Effect.catch(() => Effect.succeed(null)));
|
|
15
|
+
const sha256Hex = (input) => crypto.createHash("sha256").update(input).digest("hex");
|
|
16
|
+
const hashStringOrEmpty = (input) => input == null || input === "" ? "" : sha256Hex(input);
|
|
17
|
+
const execCapture = (cmd) => Effect.callback((resume) => {
|
|
18
|
+
exec(cmd, { timeout: 1500 }, (err, stdout) => {
|
|
19
|
+
if (err) {
|
|
20
|
+
resume(Effect.succeed(null));
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
resume(Effect.succeed(stdout.toString().trim()));
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
const getOrCreateUserId = Effect.gen(function* () {
|
|
27
|
+
const existing = yield* tryRead(ID_PATH);
|
|
28
|
+
if (existing)
|
|
29
|
+
return existing;
|
|
30
|
+
const id = crypto.randomUUID();
|
|
31
|
+
yield* Effect.tryPromise({
|
|
32
|
+
try: async () => {
|
|
33
|
+
await fs.mkdir(ALCHEMY_DIR, { recursive: true });
|
|
34
|
+
await fs.writeFile(ID_PATH, id);
|
|
35
|
+
},
|
|
36
|
+
catch: () => null,
|
|
37
|
+
}).pipe(Effect.catch(() => Effect.void));
|
|
38
|
+
return id;
|
|
39
|
+
});
|
|
40
|
+
const getRootCommitHash = execCapture(process.platform === "win32"
|
|
41
|
+
? `git rev-list --max-parents=0 HEAD | ForEach-Object { if (-not (git cat-file -p $_ | Select-String "^parent ")) { $_ } }`
|
|
42
|
+
: `git rev-list --max-parents=0 HEAD | xargs -r -I{} sh -c 'git cat-file -p {} | grep -q "^parent " || echo {}'`);
|
|
43
|
+
const getGitOriginUrl = execCapture("git config --get remote.origin.url");
|
|
44
|
+
const getBranchName = execCapture("git rev-parse --abbrev-ref HEAD");
|
|
45
|
+
const getRuntime = () => {
|
|
46
|
+
const g = globalThis;
|
|
47
|
+
if (typeof g.Bun !== "undefined") {
|
|
48
|
+
return { name: "bun", version: g.Bun.version };
|
|
49
|
+
}
|
|
50
|
+
if (typeof g.Deno !== "undefined") {
|
|
51
|
+
return { name: "deno", version: g.Deno.version?.deno ?? "" };
|
|
52
|
+
}
|
|
53
|
+
if (globalThis.process?.versions?.node) {
|
|
54
|
+
return { name: "node", version: process.versions.node };
|
|
55
|
+
}
|
|
56
|
+
return { name: "", version: "" };
|
|
57
|
+
};
|
|
58
|
+
const CI_PROVIDERS = [
|
|
59
|
+
{ env: "GITHUB_ACTIONS", provider: "GitHub Actions", isCI: true },
|
|
60
|
+
{ env: "GITLAB_CI", provider: "GitLab CI", isCI: true },
|
|
61
|
+
{ env: "CIRCLECI", provider: "CircleCI", isCI: true },
|
|
62
|
+
{ env: "JENKINS_URL", provider: "Jenkins", isCI: true },
|
|
63
|
+
{ env: "TRAVIS", provider: "Travis CI", isCI: true },
|
|
64
|
+
{ env: "BUILDKITE", provider: "Buildkite", isCI: true },
|
|
65
|
+
{ env: "NOW_BUILDER", provider: "Vercel", isCI: true },
|
|
66
|
+
{ env: "VERCEL", provider: "Vercel", isCI: false },
|
|
67
|
+
];
|
|
68
|
+
const getCIEnvironment = () => {
|
|
69
|
+
for (const p of CI_PROVIDERS) {
|
|
70
|
+
if (process.env[p.env]) {
|
|
71
|
+
return { provider: p.provider, isCI: p.isCI };
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return { provider: "", isCI: !!process.env.CI };
|
|
75
|
+
};
|
|
76
|
+
const TELEMETRY_DISABLED_ENV = () => !!process.env.ALCHEMY_TELEMETRY_DISABLED ||
|
|
77
|
+
!!process.env.DO_NOT_TRACK ||
|
|
78
|
+
!!process.env.NO_TRACK;
|
|
79
|
+
/**
|
|
80
|
+
* `true` if telemetry is disabled via environment variable
|
|
81
|
+
* (`DO_NOT_TRACK`, `NO_TRACK`, `ALCHEMY_TELEMETRY_DISABLED`) or via a
|
|
82
|
+
* persisted opt-out file at `~/.alchemy/telemetry-disabled`.
|
|
83
|
+
*/
|
|
84
|
+
export const isTelemetryDisabled = Effect.gen(function* () {
|
|
85
|
+
if (TELEMETRY_DISABLED_ENV())
|
|
86
|
+
return true;
|
|
87
|
+
const persisted = yield* tryRead(DISABLED_PATH);
|
|
88
|
+
return persisted === "true";
|
|
89
|
+
});
|
|
90
|
+
/**
|
|
91
|
+
* Persists an opt-out so future invocations skip telemetry without needing
|
|
92
|
+
* an env var.
|
|
93
|
+
*/
|
|
94
|
+
export const setTelemetryDisabled = Effect.tryPromise({
|
|
95
|
+
try: async () => {
|
|
96
|
+
await fs.mkdir(ALCHEMY_DIR, { recursive: true });
|
|
97
|
+
await fs.writeFile(DISABLED_PATH, "true");
|
|
98
|
+
},
|
|
99
|
+
catch: () => null,
|
|
100
|
+
}).pipe(Effect.catch(() => Effect.void));
|
|
101
|
+
export const setTelemetryEnabled = Effect.tryPromise({
|
|
102
|
+
try: () => fs.rm(DISABLED_PATH, { force: true }),
|
|
103
|
+
catch: () => null,
|
|
104
|
+
}).pipe(Effect.catch(() => Effect.void));
|
|
105
|
+
const collectAttributesUncached = Effect.gen(function* () {
|
|
106
|
+
const [userId, rootCommit, originUrl, branch] = yield* Effect.all([
|
|
107
|
+
getOrCreateUserId,
|
|
108
|
+
getRootCommitHash,
|
|
109
|
+
getGitOriginUrl,
|
|
110
|
+
getBranchName,
|
|
111
|
+
], { concurrency: "unbounded" });
|
|
112
|
+
const runtime = getRuntime();
|
|
113
|
+
const ci = getCIEnvironment();
|
|
114
|
+
return {
|
|
115
|
+
"alchemy.user.id": userId,
|
|
116
|
+
"alchemy.session.id": process.env.ALCHEMY_TELEMETRY_SESSION_ID ?? crypto.randomUUID(),
|
|
117
|
+
"alchemy.version": packageJson.version,
|
|
118
|
+
"alchemy.git.root_commit": rootCommit ?? "",
|
|
119
|
+
"alchemy.git.origin_hash": hashStringOrEmpty(originUrl),
|
|
120
|
+
"alchemy.git.branch_hash": hashStringOrEmpty(branch),
|
|
121
|
+
"alchemy.runtime.name": runtime.name,
|
|
122
|
+
"alchemy.runtime.version": runtime.version,
|
|
123
|
+
"alchemy.ci.provider": ci.provider,
|
|
124
|
+
"alchemy.ci": ci.isCI,
|
|
125
|
+
"host.arch": os.arch(),
|
|
126
|
+
"os.type": os.platform(),
|
|
127
|
+
"os.version": os.release(),
|
|
128
|
+
"host.cpus": os.cpus().length,
|
|
129
|
+
"host.memory_mb": Math.round(os.totalmem() / 1024 / 1024),
|
|
130
|
+
};
|
|
131
|
+
});
|
|
132
|
+
let cachedAttrs;
|
|
133
|
+
/**
|
|
134
|
+
* Resolves the {@link TelemetryAttributes} for the current process. Cached
|
|
135
|
+
* so file I/O and `git` invocations only run once per CLI invocation; every
|
|
136
|
+
* subsequent caller gets the same record (and therefore the same session
|
|
137
|
+
* id).
|
|
138
|
+
*/
|
|
139
|
+
export const collectAttributes = Effect.suspend(() => cachedAttrs !== undefined
|
|
140
|
+
? Effect.succeed(cachedAttrs)
|
|
141
|
+
: collectAttributesUncached.pipe(Effect.tap((a) => Effect.sync(() => {
|
|
142
|
+
cachedAttrs = a;
|
|
143
|
+
}))));
|
|
144
|
+
//# sourceMappingURL=Attributes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Attributes.js","sourceRoot":"","sources":["../../src/Telemetry/Attributes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as Layer from "effect/Layer";
|
|
2
|
+
/**
|
|
3
|
+
* The CLI's telemetry layer. Builds an OTLP HTTP exporter that ships spans
|
|
4
|
+
* to {@link TRACES_URL} and metrics to {@link METRICS_URL}, attaching
|
|
5
|
+
* {@link collectAttributes} as resource-level attributes so every signal
|
|
6
|
+
* carries user/project/runtime context.
|
|
7
|
+
*
|
|
8
|
+
* If the user has opted out (via `DO_NOT_TRACK`, `NO_TRACK`,
|
|
9
|
+
* `ALCHEMY_TELEMETRY_DISABLED`, or `~/.alchemy/telemetry-disabled`), this
|
|
10
|
+
* resolves to {@link Layer.empty}. Effect's default `Tracer` is a no-op,
|
|
11
|
+
* so all `withSpan`/`Effect.fn` instrumentation in core stays free.
|
|
12
|
+
*/
|
|
13
|
+
export declare const TelemetryLive: Layer.Layer<never, never, never>;
|
|
14
|
+
//# sourceMappingURL=Layer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Layer.d.ts","sourceRoot":"","sources":["../../src/Telemetry/Layer.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AA2CtC;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CASxD,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Layer from "effect/Layer";
|
|
3
|
+
import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient";
|
|
4
|
+
import * as OtlpMetrics from "effect/unstable/observability/OtlpMetrics";
|
|
5
|
+
import * as OtlpSerialization from "effect/unstable/observability/OtlpSerialization";
|
|
6
|
+
import * as OtlpTracer from "effect/unstable/observability/OtlpTracer";
|
|
7
|
+
import packageJson from "../../package.json" with { type: "json" };
|
|
8
|
+
import { collectAttributes, isTelemetryDisabled } from "./Attributes.js";
|
|
9
|
+
const TRACES_URL = "https://otel.alchemy.run/v1/traces";
|
|
10
|
+
const METRICS_URL = "https://otel.alchemy.run/v1/metrics";
|
|
11
|
+
const SERVICE_NAME = "alchemy-cli";
|
|
12
|
+
const buildOtlpLayer = (attrs) => {
|
|
13
|
+
const resource = {
|
|
14
|
+
serviceName: SERVICE_NAME,
|
|
15
|
+
serviceVersion: packageJson.version,
|
|
16
|
+
attributes: attrs,
|
|
17
|
+
};
|
|
18
|
+
// Short export intervals so even sub-second CLI invocations flush at
|
|
19
|
+
// least one batch before the process exits.
|
|
20
|
+
const tracer = OtlpTracer.layer({
|
|
21
|
+
url: TRACES_URL,
|
|
22
|
+
resource,
|
|
23
|
+
exportInterval: "1 second",
|
|
24
|
+
});
|
|
25
|
+
const metrics = OtlpMetrics.layer({
|
|
26
|
+
url: METRICS_URL,
|
|
27
|
+
resource,
|
|
28
|
+
exportInterval: "1 second",
|
|
29
|
+
});
|
|
30
|
+
return Layer.mergeAll(tracer, metrics).pipe(Layer.provide(OtlpSerialization.layerJson), Layer.provide(FetchHttpClient.layer));
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* The CLI's telemetry layer. Builds an OTLP HTTP exporter that ships spans
|
|
34
|
+
* to {@link TRACES_URL} and metrics to {@link METRICS_URL}, attaching
|
|
35
|
+
* {@link collectAttributes} as resource-level attributes so every signal
|
|
36
|
+
* carries user/project/runtime context.
|
|
37
|
+
*
|
|
38
|
+
* If the user has opted out (via `DO_NOT_TRACK`, `NO_TRACK`,
|
|
39
|
+
* `ALCHEMY_TELEMETRY_DISABLED`, or `~/.alchemy/telemetry-disabled`), this
|
|
40
|
+
* resolves to {@link Layer.empty}. Effect's default `Tracer` is a no-op,
|
|
41
|
+
* so all `withSpan`/`Effect.fn` instrumentation in core stays free.
|
|
42
|
+
*/
|
|
43
|
+
export const TelemetryLive = Layer.unwrap(Effect.gen(function* () {
|
|
44
|
+
if (yield* isTelemetryDisabled) {
|
|
45
|
+
return Layer.empty;
|
|
46
|
+
}
|
|
47
|
+
const attrs = yield* collectAttributes;
|
|
48
|
+
return buildOtlpLayer(attrs);
|
|
49
|
+
}));
|
|
50
|
+
//# sourceMappingURL=Layer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Layer.js","sourceRoot":"","sources":["../../src/Telemetry/Layer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,eAAe,MAAM,sCAAsC,CAAC;AACxE,OAAO,KAAK,WAAW,MAAM,2CAA2C,CAAC;AACzE,OAAO,KAAK,iBAAiB,MAAM,iDAAiD,CAAC;AACrF,OAAO,KAAK,UAAU,MAAM,0CAA0C,CAAC;AAEvE,OAAO,WAAW,MAAM,oBAAoB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAEnE,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAEzE,MAAM,UAAU,GAAG,oCAAoC,CAAC;AACxD,MAAM,WAAW,GAAG,qCAAqC,CAAC;AAE1D,MAAM,YAAY,GAAG,aAAa,CAAC;AAEnC,MAAM,cAAc,GAAG,CACrB,KAA8B,EACI,EAAE;IACpC,MAAM,QAAQ,GAAG;QACf,WAAW,EAAE,YAAY;QACzB,cAAc,EAAE,WAAW,CAAC,OAAO;QACnC,UAAU,EAAE,KAAK;KAClB,CAAC;IAEF,qEAAqE;IACrE,4CAA4C;IAC5C,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC;QAC9B,GAAG,EAAE,UAAU;QACf,QAAQ;QACR,cAAc,EAAE,UAAU;KAC3B,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,CAAC;QAChC,GAAG,EAAE,WAAW;QAChB,QAAQ;QACR,cAAc,EAAE,UAAU;KAC3B,CAAC,CAAC;IAEH,OAAO,KAAK,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,IAAI,CACzC,KAAK,CAAC,OAAO,CAAC,iBAAiB,CAAC,SAAS,CAAC,EAC1C,KAAK,CAAC,OAAO,CAAC,eAAe,CAAC,KAAK,CAAC,CACrC,CAAC;AACJ,CAAC,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,aAAa,GACxB,KAAK,CAAC,MAAM,CACV,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,IAAI,KAAK,CAAC,CAAC,mBAAmB,EAAE,CAAC;QAC/B,OAAO,KAAK,CAAC,KAAK,CAAC;IACrB,CAAC;IACD,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,iBAAiB,CAAC;IACvC,OAAO,cAAc,CAAC,KAA2C,CAAC,CAAC;AACrE,CAAC,CAAC,CACH,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as Duration from "effect/Duration";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Metric from "effect/Metric";
|
|
4
|
+
/**
|
|
5
|
+
* Counter for resource lifecycle operations. Tagged per call with
|
|
6
|
+
* `resource_type`, `op` (`precreate`/`create`/`update`/`delete`/`read`),
|
|
7
|
+
* and `status` (`success`/`error`).
|
|
8
|
+
*/
|
|
9
|
+
export declare const resourceCounter: Metric.Counter<number>;
|
|
10
|
+
/**
|
|
11
|
+
* Histogram of how long each lifecycle operation takes.
|
|
12
|
+
*/
|
|
13
|
+
export declare const resourceDuration: Metric.Histogram<Duration.Duration>;
|
|
14
|
+
/**
|
|
15
|
+
* Counter for CLI command invocations.
|
|
16
|
+
*/
|
|
17
|
+
export declare const cliCounter: Metric.Counter<number>;
|
|
18
|
+
/**
|
|
19
|
+
* Wraps a resource lifecycle Effect to record a counter + timer entry,
|
|
20
|
+
* tagged with `resource_type`, `op`, and `status` (`success`/`error`).
|
|
21
|
+
*
|
|
22
|
+
* Usage:
|
|
23
|
+
* ```ts
|
|
24
|
+
* provider.create(input).pipe(recordResourceOp(node.resource.Type, "create"))
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare const recordResourceOp: (resourceType: string, op: ResourceOp) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
|
|
28
|
+
/**
|
|
29
|
+
* Wraps a CLI command Effect to bump {@link cliCounter} with the
|
|
30
|
+
* outcome (`success`/`error`).
|
|
31
|
+
*/
|
|
32
|
+
export declare const recordCli: (command: string) => <A, E, R>(self: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
|
|
33
|
+
export type ResourceOp = "precreate" | "create" | "update" | "delete" | "read";
|
|
34
|
+
//# sourceMappingURL=Metrics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Metrics.d.ts","sourceRoot":"","sources":["../../src/Telemetry/Metrics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC;;;;GAIG;AACH,eAAO,MAAM,eAAe,wBAI1B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,gBAAgB,qCAG3B,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,UAAU,wBAGrB,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,gBAAgB,GAC1B,cAAc,MAAM,EAAE,IAAI,UAAU,MACpC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAAG,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAa1D,CAAC;AAEP;;;GAGG;AACH,eAAO,MAAM,SAAS,GACnB,SAAS,MAAM,MACf,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAAG,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAW3D,CAAC;AAEN,MAAM,MAAM,UAAU,GAClB,WAAW,GACX,QAAQ,GACR,QAAQ,GACR,QAAQ,GACR,MAAM,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import * as Duration from "effect/Duration";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Exit from "effect/Exit";
|
|
4
|
+
import * as Metric from "effect/Metric";
|
|
5
|
+
/**
|
|
6
|
+
* Counter for resource lifecycle operations. Tagged per call with
|
|
7
|
+
* `resource_type`, `op` (`precreate`/`create`/`update`/`delete`/`read`),
|
|
8
|
+
* and `status` (`success`/`error`).
|
|
9
|
+
*/
|
|
10
|
+
export const resourceCounter = Metric.counter("alchemy.resource.operations", {
|
|
11
|
+
description: "Number of resource lifecycle operations dispatched by alchemy apply.",
|
|
12
|
+
incremental: true,
|
|
13
|
+
});
|
|
14
|
+
/**
|
|
15
|
+
* Histogram of how long each lifecycle operation takes.
|
|
16
|
+
*/
|
|
17
|
+
export const resourceDuration = Metric.timer("alchemy.resource.duration", {
|
|
18
|
+
description: "Wall-clock duration of resource lifecycle operations dispatched by alchemy apply.",
|
|
19
|
+
});
|
|
20
|
+
/**
|
|
21
|
+
* Counter for CLI command invocations.
|
|
22
|
+
*/
|
|
23
|
+
export const cliCounter = Metric.counter("alchemy.cli.invocations", {
|
|
24
|
+
description: "Number of alchemy CLI command invocations.",
|
|
25
|
+
incremental: true,
|
|
26
|
+
});
|
|
27
|
+
/**
|
|
28
|
+
* Wraps a resource lifecycle Effect to record a counter + timer entry,
|
|
29
|
+
* tagged with `resource_type`, `op`, and `status` (`success`/`error`).
|
|
30
|
+
*
|
|
31
|
+
* Usage:
|
|
32
|
+
* ```ts
|
|
33
|
+
* provider.create(input).pipe(recordResourceOp(node.resource.Type, "create"))
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export const recordResourceOp = (resourceType, op) => (self) => Effect.suspend(() => {
|
|
37
|
+
const startNs = process.hrtime.bigint();
|
|
38
|
+
const baseAttrs = { resource_type: resourceType, op };
|
|
39
|
+
return self.pipe(Effect.onExit((exit) => recordOutcome(baseAttrs, Exit.isSuccess(exit) ? "success" : "error", elapsed(startNs))));
|
|
40
|
+
});
|
|
41
|
+
/**
|
|
42
|
+
* Wraps a CLI command Effect to bump {@link cliCounter} with the
|
|
43
|
+
* outcome (`success`/`error`).
|
|
44
|
+
*/
|
|
45
|
+
export const recordCli = (command) => (self) => self.pipe(Effect.onExit((exit) => Metric.update(Metric.withAttributes(cliCounter, {
|
|
46
|
+
command,
|
|
47
|
+
status: Exit.isSuccess(exit) ? "success" : "error",
|
|
48
|
+
}), 1)));
|
|
49
|
+
const elapsed = (startNs) => Duration.nanos(process.hrtime.bigint() - startNs);
|
|
50
|
+
const recordOutcome = (baseAttrs, status, duration) => {
|
|
51
|
+
const attrs = { ...baseAttrs, status };
|
|
52
|
+
return Effect.all([
|
|
53
|
+
Metric.update(Metric.withAttributes(resourceCounter, attrs), 1),
|
|
54
|
+
Metric.update(Metric.withAttributes(resourceDuration, attrs), duration),
|
|
55
|
+
], { discard: true });
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=Metrics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Metrics.js","sourceRoot":"","sources":["../../src/Telemetry/Metrics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAC;AAC5C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AACxC,OAAO,KAAK,IAAI,MAAM,aAAa,CAAC;AACpC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAC;AAExC;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC,OAAO,CAAC,6BAA6B,EAAE;IAC3E,WAAW,EACT,sEAAsE;IACxE,WAAW,EAAE,IAAI;CAClB,CAAC,CAAC;AAEH;;GAEG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC,KAAK,CAAC,2BAA2B,EAAE;IACxE,WAAW,EACT,mFAAmF;CACtF,CAAC,CAAC;AAEH;;GAEG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,yBAAyB,EAAE;IAClE,WAAW,EAAE,4CAA4C;IACzD,WAAW,EAAE,IAAI;CAClB,CAAC,CAAC;AAEH;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAC3B,CAAC,YAAoB,EAAE,EAAc,EAAE,EAAE,CACzC,CAAU,IAA4B,EAA0B,EAAE,CAChE,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE;IAClB,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;IACxC,MAAM,SAAS,GAAG,EAAE,aAAa,EAAE,YAAY,EAAE,EAAE,EAAW,CAAC;IAC/D,OAAO,IAAI,CAAC,IAAI,CACd,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CACrB,aAAa,CACX,SAAS,EACT,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,EAC1C,OAAO,CAAC,OAAO,CAAC,CACjB,CACF,CACF,CAAC;AACJ,CAAC,CAAC,CAAC;AAEP;;;GAGG;AACH,MAAM,CAAC,MAAM,SAAS,GACpB,CAAC,OAAe,EAAE,EAAE,CACpB,CAAU,IAA4B,EAA0B,EAAE,CAChE,IAAI,CAAC,IAAI,CACP,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CACrB,MAAM,CAAC,MAAM,CACX,MAAM,CAAC,cAAc,CAAC,UAAU,EAAE;IAChC,OAAO;IACP,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO;CACnD,CAAC,EACF,CAAC,CACF,CACF,CACF,CAAC;AASN,MAAM,OAAO,GAAG,CAAC,OAAe,EAAqB,EAAE,CACrD,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,OAAO,CAAC,CAAC;AAEpD,MAAM,aAAa,GAAG,CACpB,SAAoD,EACpD,MAA2B,EAC3B,QAA2B,EACN,EAAE;IACvB,MAAM,KAAK,GAAG,EAAE,GAAG,SAAS,EAAE,MAAM,EAAE,CAAC;IACvC,OAAO,MAAM,CAAC,GAAG,CACf;QACE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;QAC/D,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,cAAc,CAAC,gBAAgB,EAAE,KAAK,CAAC,EAAE,QAAQ,CAAC;KACxE,EACD,EAAE,OAAO,EAAE,IAAI,EAAE,CAClB,CAAC;AACJ,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "alchemy",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.19",
|
|
4
4
|
"homepage": "https://alchemy.run",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Sam Goodwin <sam@alchemy.run>",
|
|
@@ -136,6 +136,12 @@
|
|
|
136
136
|
"worker": "./src/Cloudflare/*/index.ts",
|
|
137
137
|
"import": "./lib/Cloudflare/*/index.js"
|
|
138
138
|
},
|
|
139
|
+
"./Axiom": {
|
|
140
|
+
"types": "./src/Axiom/index.ts",
|
|
141
|
+
"bun": "./src/Axiom/index.ts",
|
|
142
|
+
"worker": "./src/Axiom/index.ts",
|
|
143
|
+
"import": "./lib/Axiom/index.js"
|
|
144
|
+
},
|
|
139
145
|
"./GitHub": {
|
|
140
146
|
"types": "./src/GitHub/index.ts",
|
|
141
147
|
"bun": "./src/GitHub/index.ts",
|
|
@@ -232,6 +238,7 @@
|
|
|
232
238
|
"@aws-sdk/credential-providers": "^3.0.0",
|
|
233
239
|
"@clack/prompts": "^0.11.0",
|
|
234
240
|
"@distilled.cloud/aws": "^0.12.5",
|
|
241
|
+
"@distilled.cloud/axiom": "^0.12.5",
|
|
235
242
|
"@distilled.cloud/cloudflare": "^0.12.5",
|
|
236
243
|
"@distilled.cloud/cloudflare-rolldown-plugin": "0.3.0",
|
|
237
244
|
"@distilled.cloud/cloudflare-vite-plugin": "0.2.0",
|
|
@@ -254,6 +261,8 @@
|
|
|
254
261
|
"ignore": "^7.0.5",
|
|
255
262
|
"jszip": "^3.10.1",
|
|
256
263
|
"libsodium-wrappers": "^0.8.3",
|
|
264
|
+
"magic-string": "^0.30.21",
|
|
265
|
+
"picomatch": "^4.0.4",
|
|
257
266
|
"rolldown": "1.0.0-rc.13",
|
|
258
267
|
"sonda": "^0.11.1",
|
|
259
268
|
"web-tree-sitter": "0.25.10",
|
|
@@ -266,9 +275,11 @@
|
|
|
266
275
|
"@effect/platform-bun": "4.0.0-beta.48",
|
|
267
276
|
"@effect/platform-node": "4.0.0-beta.48",
|
|
268
277
|
"@effect/platform-node-shared": "4.0.0-beta.48",
|
|
278
|
+
"@oxc-project/types": "^0.127.0",
|
|
269
279
|
"@types/aws-lambda": "^8.10.152",
|
|
270
280
|
"@types/bun": "latest",
|
|
271
281
|
"@types/node": "latest",
|
|
282
|
+
"@types/picomatch": "^4.0.0",
|
|
272
283
|
"@types/react": "^19.2.2",
|
|
273
284
|
"better-auth": "^1.6.2",
|
|
274
285
|
"effect": "4.0.0-beta.48",
|
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)) {
|