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.
Files changed (128) hide show
  1. package/bin/alchemy.js +428 -80
  2. package/bin/alchemy.js.map +1 -1
  3. package/bin/alchemy.ts +2 -0
  4. package/bin/commands/_shared.ts +34 -0
  5. package/bin/commands/bootstrap.ts +10 -3
  6. package/bin/commands/deploy.ts +23 -3
  7. package/bin/commands/login.ts +18 -3
  8. package/bin/commands/logs.ts +11 -2
  9. package/bin/commands/profile.ts +11 -5
  10. package/bin/commands/state.ts +21 -13
  11. package/bin/commands/tail.ts +10 -2
  12. package/lib/Agent/lsp/client.d.ts +2 -2
  13. package/lib/Apply.d.ts.map +1 -1
  14. package/lib/Apply.js +41 -9
  15. package/lib/Apply.js.map +1 -1
  16. package/lib/Axiom/Annotation.d.ts +49 -0
  17. package/lib/Axiom/Annotation.d.ts.map +1 -0
  18. package/lib/Axiom/Annotation.js +74 -0
  19. package/lib/Axiom/Annotation.js.map +1 -0
  20. package/lib/Axiom/ApiToken.d.ts +62 -0
  21. package/lib/Axiom/ApiToken.d.ts.map +1 -0
  22. package/lib/Axiom/ApiToken.js +98 -0
  23. package/lib/Axiom/ApiToken.js.map +1 -0
  24. package/lib/Axiom/AuthProvider.d.ts +48 -0
  25. package/lib/Axiom/AuthProvider.d.ts.map +1 -0
  26. package/lib/Axiom/AuthProvider.js +177 -0
  27. package/lib/Axiom/AuthProvider.js.map +1 -0
  28. package/lib/Axiom/Chart.d.ts +72 -0
  29. package/lib/Axiom/Chart.d.ts.map +1 -0
  30. package/lib/Axiom/Chart.js +14 -0
  31. package/lib/Axiom/Chart.js.map +1 -0
  32. package/lib/Axiom/Credentials.d.ts +12 -0
  33. package/lib/Axiom/Credentials.d.ts.map +1 -0
  34. package/lib/Axiom/Credentials.js +34 -0
  35. package/lib/Axiom/Credentials.js.map +1 -0
  36. package/lib/Axiom/Dashboard.d.ts +124 -0
  37. package/lib/Axiom/Dashboard.d.ts.map +1 -0
  38. package/lib/Axiom/Dashboard.js +140 -0
  39. package/lib/Axiom/Dashboard.js.map +1 -0
  40. package/lib/Axiom/Dataset.d.ts +101 -0
  41. package/lib/Axiom/Dataset.d.ts.map +1 -0
  42. package/lib/Axiom/Dataset.js +183 -0
  43. package/lib/Axiom/Dataset.js.map +1 -0
  44. package/lib/Axiom/Monitor.d.ts +74 -0
  45. package/lib/Axiom/Monitor.d.ts.map +1 -0
  46. package/lib/Axiom/Monitor.js +103 -0
  47. package/lib/Axiom/Monitor.js.map +1 -0
  48. package/lib/Axiom/Notifier.d.ts +63 -0
  49. package/lib/Axiom/Notifier.d.ts.map +1 -0
  50. package/lib/Axiom/Notifier.js +83 -0
  51. package/lib/Axiom/Notifier.js.map +1 -0
  52. package/lib/Axiom/Providers.d.ts +15 -0
  53. package/lib/Axiom/Providers.d.ts.map +1 -0
  54. package/lib/Axiom/Providers.js +32 -0
  55. package/lib/Axiom/Providers.js.map +1 -0
  56. package/lib/Axiom/View.d.ts +55 -0
  57. package/lib/Axiom/View.d.ts.map +1 -0
  58. package/lib/Axiom/View.js +79 -0
  59. package/lib/Axiom/View.js.map +1 -0
  60. package/lib/Axiom/VirtualField.d.ts +42 -0
  61. package/lib/Axiom/VirtualField.d.ts.map +1 -0
  62. package/lib/Axiom/VirtualField.js +71 -0
  63. package/lib/Axiom/VirtualField.js.map +1 -0
  64. package/lib/Axiom/index.d.ts +13 -0
  65. package/lib/Axiom/index.d.ts.map +1 -0
  66. package/lib/Axiom/index.js +13 -0
  67. package/lib/Axiom/index.js.map +1 -0
  68. package/lib/Bundle/Bundle.d.ts +20 -2
  69. package/lib/Bundle/Bundle.d.ts.map +1 -1
  70. package/lib/Bundle/Bundle.js +18 -3
  71. package/lib/Bundle/Bundle.js.map +1 -1
  72. package/lib/Bundle/PurePlugin.d.ts +109 -0
  73. package/lib/Bundle/PurePlugin.d.ts.map +1 -0
  74. package/lib/Bundle/PurePlugin.js +469 -0
  75. package/lib/Bundle/PurePlugin.js.map +1 -0
  76. package/lib/Bundle/index.d.ts +1 -0
  77. package/lib/Bundle/index.d.ts.map +1 -1
  78. package/lib/Bundle/index.js +1 -0
  79. package/lib/Bundle/index.js.map +1 -1
  80. package/lib/Cloudflare/Workers/Worker.d.ts +12 -0
  81. package/lib/Cloudflare/Workers/Worker.d.ts.map +1 -1
  82. package/lib/Cloudflare/Workers/Worker.js +19 -4
  83. package/lib/Cloudflare/Workers/Worker.js.map +1 -1
  84. package/lib/Output.d.ts.map +1 -1
  85. package/lib/Output.js +1 -1
  86. package/lib/Output.js.map +1 -1
  87. package/lib/Plan.d.ts.map +1 -1
  88. package/lib/Plan.js +10 -4
  89. package/lib/Plan.js.map +1 -1
  90. package/lib/Resource.d.ts +2 -2
  91. package/lib/Resource.d.ts.map +1 -1
  92. package/lib/Telemetry/Attributes.d.ts +43 -0
  93. package/lib/Telemetry/Attributes.d.ts.map +1 -0
  94. package/lib/Telemetry/Attributes.js +144 -0
  95. package/lib/Telemetry/Attributes.js.map +1 -0
  96. package/lib/Telemetry/Layer.d.ts +14 -0
  97. package/lib/Telemetry/Layer.d.ts.map +1 -0
  98. package/lib/Telemetry/Layer.js +50 -0
  99. package/lib/Telemetry/Layer.js.map +1 -0
  100. package/lib/Telemetry/Metrics.d.ts +34 -0
  101. package/lib/Telemetry/Metrics.d.ts.map +1 -0
  102. package/lib/Telemetry/Metrics.js +57 -0
  103. package/lib/Telemetry/Metrics.js.map +1 -0
  104. package/package.json +12 -1
  105. package/src/Apply.ts +118 -9
  106. package/src/Axiom/Annotation.ts +95 -0
  107. package/src/Axiom/ApiToken.ts +125 -0
  108. package/src/Axiom/AuthProvider.ts +341 -0
  109. package/src/Axiom/Chart.ts +94 -0
  110. package/src/Axiom/Credentials.ts +70 -0
  111. package/src/Axiom/Dashboard.ts +192 -0
  112. package/src/Axiom/Dataset.ts +291 -0
  113. package/src/Axiom/Monitor.ts +122 -0
  114. package/src/Axiom/Notifier.ts +104 -0
  115. package/src/Axiom/Providers.ts +58 -0
  116. package/src/Axiom/View.ts +106 -0
  117. package/src/Axiom/VirtualField.ts +90 -0
  118. package/src/Axiom/index.ts +12 -0
  119. package/src/Bundle/Bundle.ts +39 -1
  120. package/src/Bundle/PurePlugin.ts +556 -0
  121. package/src/Bundle/index.ts +1 -0
  122. package/src/Cloudflare/Workers/Worker.ts +37 -6
  123. package/src/Output.ts +2 -1
  124. package/src/Plan.ts +13 -4
  125. package/src/Resource.ts +2 -2
  126. package/src/Telemetry/Attributes.ts +216 -0
  127. package/src/Telemetry/Layer.ts +65 -0
  128. 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.18",
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(ensureArtifactStore);
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(provideLifecycleScope(fqn, instanceId));
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(provideLifecycleScope(fqn, instanceId));
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(provideLifecycleScope(fqn, instanceId));
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(provideLifecycleScope(fqn, instanceId));
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(provideLifecycleScope(fqn, instanceId));
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
- }) as Effect.Effect<void, never, never>;
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(provideLifecycleScope(fqn, instanceId));
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(provideLifecycleScope(fqn, instanceId));
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)) {