alchemy 0.70.2 → 0.71.1

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 (131) hide show
  1. package/README.md +1 -1
  2. package/bin/alchemy.js +131 -58
  3. package/bin/commands/init.ts +0 -10
  4. package/bin/services/execute-alchemy.ts +12 -3
  5. package/bin/trpc.ts +12 -12
  6. package/lib/alchemy.d.ts.map +1 -1
  7. package/lib/alchemy.js +21 -6
  8. package/lib/alchemy.js.map +1 -1
  9. package/lib/apply.d.ts.map +1 -1
  10. package/lib/apply.js +3 -1
  11. package/lib/apply.js.map +1 -1
  12. package/lib/cloudflare/bindings.d.ts +12 -2
  13. package/lib/cloudflare/bindings.d.ts.map +1 -1
  14. package/lib/cloudflare/bindings.js.map +1 -1
  15. package/lib/cloudflare/bound.d.ts +3 -2
  16. package/lib/cloudflare/bound.d.ts.map +1 -1
  17. package/lib/cloudflare/bucket.d.ts +6 -0
  18. package/lib/cloudflare/bucket.d.ts.map +1 -1
  19. package/lib/cloudflare/bucket.js +5 -3
  20. package/lib/cloudflare/bucket.js.map +1 -1
  21. package/lib/cloudflare/bun-spa/bun-spa.d.ts +6 -1
  22. package/lib/cloudflare/bun-spa/bun-spa.d.ts.map +1 -1
  23. package/lib/cloudflare/bun-spa/bun-spa.js +29 -9
  24. package/lib/cloudflare/bun-spa/bun-spa.js.map +1 -1
  25. package/lib/cloudflare/compatibility-date.gen.d.ts +1 -1
  26. package/lib/cloudflare/compatibility-date.gen.js +1 -1
  27. package/lib/cloudflare/d1-database.d.ts +1 -1
  28. package/lib/cloudflare/d1-database.d.ts.map +1 -1
  29. package/lib/cloudflare/d1-database.js +1 -1
  30. package/lib/cloudflare/d1-database.js.map +1 -1
  31. package/lib/cloudflare/health-check.d.ts +261 -0
  32. package/lib/cloudflare/health-check.d.ts.map +1 -0
  33. package/lib/cloudflare/health-check.js +277 -0
  34. package/lib/cloudflare/health-check.js.map +1 -0
  35. package/lib/cloudflare/index.d.ts +3 -0
  36. package/lib/cloudflare/index.d.ts.map +1 -1
  37. package/lib/cloudflare/index.js +3 -0
  38. package/lib/cloudflare/index.js.map +1 -1
  39. package/lib/cloudflare/logpush-job.d.ts +349 -0
  40. package/lib/cloudflare/logpush-job.d.ts.map +1 -0
  41. package/lib/cloudflare/logpush-job.js +277 -0
  42. package/lib/cloudflare/logpush-job.js.map +1 -0
  43. package/lib/cloudflare/miniflare/build-worker-options.d.ts +1 -0
  44. package/lib/cloudflare/miniflare/build-worker-options.d.ts.map +1 -1
  45. package/lib/cloudflare/miniflare/build-worker-options.js +16 -6
  46. package/lib/cloudflare/miniflare/build-worker-options.js.map +1 -1
  47. package/lib/cloudflare/miniflare/remote-binding-proxy.d.ts.map +1 -1
  48. package/lib/cloudflare/miniflare/remote-binding-proxy.js +2 -0
  49. package/lib/cloudflare/miniflare/remote-binding-proxy.js.map +1 -1
  50. package/lib/cloudflare/queue-consumer.js +2 -2
  51. package/lib/cloudflare/queue-consumer.js.map +1 -1
  52. package/lib/cloudflare/worker-loader.d.ts +9 -0
  53. package/lib/cloudflare/worker-loader.d.ts.map +1 -0
  54. package/lib/cloudflare/worker-loader.js +6 -0
  55. package/lib/cloudflare/worker-loader.js.map +1 -0
  56. package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
  57. package/lib/cloudflare/worker-metadata.js +6 -0
  58. package/lib/cloudflare/worker-metadata.js.map +1 -1
  59. package/lib/cloudflare/worker-subdomain.js +1 -1
  60. package/lib/cloudflare/worker.d.ts.map +1 -1
  61. package/lib/cloudflare/worker.js +7 -0
  62. package/lib/cloudflare/worker.js.map +1 -1
  63. package/lib/cloudflare/wrangler.json.d.ts +11 -8
  64. package/lib/cloudflare/wrangler.json.d.ts.map +1 -1
  65. package/lib/cloudflare/wrangler.json.js +18 -11
  66. package/lib/cloudflare/wrangler.json.js.map +1 -1
  67. package/lib/cloudflare/zone.js +1 -1
  68. package/lib/destroy.d.ts.map +1 -1
  69. package/lib/destroy.js +27 -0
  70. package/lib/destroy.js.map +1 -1
  71. package/lib/docker/image.js +1 -1
  72. package/lib/docker/image.js.map +1 -1
  73. package/lib/github/comment.d.ts.map +1 -1
  74. package/lib/github/comment.js +14 -7
  75. package/lib/github/comment.js.map +1 -1
  76. package/lib/resource.js +1 -1
  77. package/lib/resource.js.map +1 -1
  78. package/lib/scope.d.ts +6 -1
  79. package/lib/scope.d.ts.map +1 -1
  80. package/lib/scope.js +22 -22
  81. package/lib/scope.js.map +1 -1
  82. package/lib/state/instrumented-state-store.js +1 -1
  83. package/lib/state/instrumented-state-store.js.map +1 -1
  84. package/lib/util/cli-args.d.ts +3 -0
  85. package/lib/util/cli-args.d.ts.map +1 -0
  86. package/lib/util/cli-args.js +6 -0
  87. package/lib/util/cli-args.js.map +1 -0
  88. package/lib/util/retry.d.ts +1 -1
  89. package/lib/util/retry.d.ts.map +1 -1
  90. package/lib/util/retry.js +2 -2
  91. package/lib/util/retry.js.map +1 -1
  92. package/lib/util/telemetry.d.ts +1 -0
  93. package/lib/util/telemetry.d.ts.map +1 -1
  94. package/lib/util/telemetry.js +47 -2
  95. package/lib/util/telemetry.js.map +1 -1
  96. package/lib/util/validate-resource-id.d.ts +2 -0
  97. package/lib/util/validate-resource-id.d.ts.map +1 -0
  98. package/lib/util/validate-resource-id.js +11 -0
  99. package/lib/util/validate-resource-id.js.map +1 -0
  100. package/package.json +11 -9
  101. package/src/alchemy.ts +25 -11
  102. package/src/apply.ts +3 -1
  103. package/src/cloudflare/bindings.ts +13 -0
  104. package/src/cloudflare/bound.ts +36 -32
  105. package/src/cloudflare/bucket.ts +13 -4
  106. package/src/cloudflare/bun-spa/bun-spa.ts +42 -10
  107. package/src/cloudflare/compatibility-date.gen.ts +1 -1
  108. package/src/cloudflare/d1-database.ts +2 -2
  109. package/src/cloudflare/health-check.ts +617 -0
  110. package/src/cloudflare/index.ts +3 -0
  111. package/src/cloudflare/logpush-job.ts +768 -0
  112. package/src/cloudflare/miniflare/build-worker-options.ts +18 -6
  113. package/src/cloudflare/miniflare/remote-binding-proxy.ts +2 -0
  114. package/src/cloudflare/queue-consumer.ts +2 -2
  115. package/src/cloudflare/worker-loader.ts +12 -0
  116. package/src/cloudflare/worker-metadata.ts +5 -0
  117. package/src/cloudflare/worker-subdomain.ts +1 -1
  118. package/src/cloudflare/worker.ts +8 -0
  119. package/src/cloudflare/wrangler.json.ts +36 -23
  120. package/src/cloudflare/zone.ts +1 -1
  121. package/src/destroy.ts +32 -0
  122. package/src/docker/image.ts +1 -1
  123. package/src/github/comment.ts +17 -8
  124. package/src/resource.ts +1 -1
  125. package/src/scope.ts +32 -25
  126. package/src/state/instrumented-state-store.ts +1 -1
  127. package/src/util/cli-args.ts +11 -0
  128. package/src/util/retry.ts +2 -1
  129. package/src/util/telemetry.ts +68 -2
  130. package/src/util/validate-resource-id.ts +10 -0
  131. package/workers/tunnel-proxy.js +1 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Alchemy
2
2
 
3
- [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/sam-goodwin/alchemy)
3
+ [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/alchemy-run/alchemy)
4
4
 
5
5
  Alchemy is an embeddable, TypeScript-native Infrastructure-as-Code (IaC) library for modeling Resources that are Created, Updated and Deleted automatically.
6
6
 
package/bin/alchemy.js CHANGED
@@ -17,12 +17,13 @@ import lockfile from "proper-lockfile";
17
17
  import crypto$1, { createHash } from "node:crypto";
18
18
  import { onExit } from "signal-exit";
19
19
  import { AsyncLocalStorage } from "node:async_hooks";
20
+ import http, { createServer } from "node:http";
21
+ import https from "node:https";
22
+ import { URL as URL$1, fileURLToPath } from "node:url";
20
23
  import path$1 from "pathe";
21
24
  import assert from "node:assert";
22
- import http, { createServer } from "node:http";
23
25
  import { WebSocketServer } from "ws";
24
26
  import { Buffer as Buffer$1 } from "node:buffer";
25
- import { fileURLToPath } from "node:url";
26
27
  import { StringDecoder } from "node:string_decoder";
27
28
  import tty from "node:tty";
28
29
  import { scheduler, setImmediate as setImmediate$1, setTimeout as setTimeout$1 } from "node:timers/promises";
@@ -36733,13 +36734,13 @@ function envPaths(name$1, { suffix = "nodejs" } = {}) {
36733
36734
  //#endregion
36734
36735
  //#region package.json
36735
36736
  var name = "alchemy";
36736
- var version = "0.70.2";
36737
+ var version = "0.71.1";
36737
36738
  var license = "Apache-2.0";
36738
36739
  var author = "Sam Goodwin <sam@alchemy.run>";
36739
36740
  var homepage = "https://alchemy.run";
36740
36741
  var repository = {
36741
36742
  "type": "git",
36742
- "url": "git+https://github.com/sam-goodwin/alchemy.git",
36743
+ "url": "git+https://github.com/alchemy-run/alchemy.git",
36743
36744
  "directory": "alchemy"
36744
36745
  };
36745
36746
  var type = "module";
@@ -36875,6 +36876,10 @@ var exports$1 = {
36875
36876
  "bun": "./src/stripe/index.ts",
36876
36877
  "import": "./lib/stripe/index.js"
36877
36878
  },
36879
+ "./test/vite": {
36880
+ "bun": "./src/test/vite.ts",
36881
+ "import": "./lib/test/vite.js"
36882
+ },
36878
36883
  "./upstash": {
36879
36884
  "bun": "./src/upstash/index.ts",
36880
36885
  "import": "./lib/upstash/index.js"
@@ -36892,6 +36897,7 @@ var dependencies = {
36892
36897
  "@aws-sdk/credential-providers": "^3.0.0",
36893
36898
  "@cloudflare/unenv-preset": "^2.6.3",
36894
36899
  "@iarna/toml": "^2.2.5",
36900
+ "@octokit/rest": "^21.1.1",
36895
36901
  "@smithy/node-config-provider": "^4.0.0",
36896
36902
  "@smithy/types": "^4.6.0",
36897
36903
  "aws4fetch": "^1.0.20",
@@ -36930,12 +36936,12 @@ var peerDependencies = {
36930
36936
  "@cloudflare/vite-plugin": "^1.10.1",
36931
36937
  "@cloudflare/workers-types": "catalog:",
36932
36938
  "@libsql/client": "^0.15.12",
36933
- "@octokit/rest": "^21.1.1",
36934
36939
  "@opennextjs/cloudflare": "^1.6.5",
36935
36940
  "astro": "^5.13.2",
36936
36941
  "drizzle-orm": "^0.44.2",
36937
36942
  "rwsdk": "^0.1.36",
36938
- "stripe": "^18.5.0"
36943
+ "stripe": "^18.5.0",
36944
+ "wrangler": "catalog:"
36939
36945
  };
36940
36946
  var peerDependenciesMeta = {
36941
36947
  "@astrojs/cloudflare": { "optional": true },
@@ -36951,7 +36957,6 @@ var peerDependenciesMeta = {
36951
36957
  "@cloudflare/vite-plugin": { "optional": true },
36952
36958
  "@cloudflare/workers-types": { "optional": true },
36953
36959
  "@opennextjs/cloudflare": { "optional": true },
36954
- "@octokit/rest": { "optional": true },
36955
36960
  "@libsql/client": { "optional": true },
36956
36961
  "astro": { "optional": true },
36957
36962
  "drizzle-orm": { "optional": true },
@@ -37010,7 +37015,7 @@ var devDependencies = {
37010
37015
  "tsdown": "^0.14.2",
37011
37016
  "typescript": "catalog:",
37012
37017
  "undici": "^7.14.0",
37013
- "wrangler": "^4.27.0",
37018
+ "wrangler": "^4.42.2",
37014
37019
  "zod": "^4.0.10"
37015
37020
  };
37016
37021
  var package_default = {
@@ -37032,6 +37037,14 @@ var package_default = {
37032
37037
  devDependencies
37033
37038
  };
37034
37039
 
37040
+ //#endregion
37041
+ //#region src/util/cli-args.ts
37042
+ const cliArgs = process.argv.slice(2);
37043
+ function parseOption(option, defaultValue) {
37044
+ const i$2 = cliArgs.indexOf(option);
37045
+ return i$2 !== -1 && i$2 + 1 < cliArgs.length ? cliArgs[i$2 + 1] : defaultValue;
37046
+ }
37047
+
37035
37048
  //#endregion
37036
37049
  //#region src/util/logger.ts
37037
37050
  const logger = new Proxy({}, { get: (_$2, prop) => {
@@ -37198,9 +37211,12 @@ const collectData = memoize(async () => {
37198
37211
  getRuntime$1(),
37199
37212
  getEnvironment()
37200
37213
  ]);
37214
+ const sessionId = parseOption("--telemetry-session-id") ?? process.env.ALCHEMY_TELEMETRY_SESSION_ID ?? globalThis.ALCHEMY_TELEMETRY_SESSION_ID ?? crypto.randomUUID();
37215
+ const referrer = parseOption("--telemetry-ref") ?? process.env.ALCHEMY_TELEMETRY_REF ?? globalThis.ALCHEMY_TELEMETRY_REF ?? "";
37201
37216
  return {
37202
37217
  userId: userId ?? "",
37203
- sessionId: crypto.randomUUID(),
37218
+ sessionId,
37219
+ referrer,
37204
37220
  platform: os.platform(),
37205
37221
  osVersion: os.release(),
37206
37222
  arch: os.arch(),
@@ -37228,7 +37244,7 @@ async function createAndSendEvent(data, error$83) {
37228
37244
  ...await collectData(),
37229
37245
  ...serializeError(error$83)
37230
37246
  };
37231
- await fetch(TELEMETRY_API_URL, {
37247
+ await fetchNoResponse(TELEMETRY_API_URL, {
37232
37248
  method: "POST",
37233
37249
  headers: { "Content-Type": "application/json" },
37234
37250
  body: JSON.stringify(eventData)
@@ -37255,6 +37271,34 @@ function serializeError(error$83) {
37255
37271
  errorStack: ""
37256
37272
  };
37257
37273
  }
37274
+ async function fetchNoResponse(url$2, options = {}) {
37275
+ const parsedUrl = new URL$1(url$2);
37276
+ const isHttps = parsedUrl.protocol === "https:";
37277
+ const client = isHttps ? https : http;
37278
+ const body = options.body || "";
37279
+ const headers = {
37280
+ ...options.headers,
37281
+ "Content-Length": Buffer.byteLength(body)
37282
+ };
37283
+ const requestOptions = {
37284
+ hostname: parsedUrl.hostname,
37285
+ port: parsedUrl.port || (isHttps ? 443 : 80),
37286
+ path: parsedUrl.pathname + parsedUrl.search,
37287
+ method: options.method || "GET",
37288
+ headers
37289
+ };
37290
+ await new Promise((resolve$1, reject) => {
37291
+ const req = client.request(requestOptions);
37292
+ req.on("error", (error$83) => {
37293
+ reject(error$83);
37294
+ });
37295
+ if (body) req.write(body);
37296
+ req.once("finish", () => {
37297
+ resolve$1(void 0);
37298
+ });
37299
+ req.end();
37300
+ });
37301
+ }
37258
37302
 
37259
37303
  //#endregion
37260
37304
  //#region src/state/instrumented-state-store.ts
@@ -37276,7 +37320,7 @@ var InstrumentedStateStore = class {
37276
37320
  error$83 = err;
37277
37321
  throw err;
37278
37322
  } finally {
37279
- createAndSendEvent({
37323
+ await createAndSendEvent({
37280
37324
  event,
37281
37325
  stateStore: this.stateStoreClass,
37282
37326
  duration: performance.now() - start
@@ -37646,6 +37690,13 @@ const find = async (...args) => {
37646
37690
  //#region src/util/root-dir.ts
37647
37691
  const ALCHEMY_ROOT = process.env.ALCHEMY_ROOT ?? process.cwd();
37648
37692
 
37693
+ //#endregion
37694
+ //#region src/util/validate-resource-id.ts
37695
+ function validateResourceID(id, qualifier = "Resource") {
37696
+ if (!id) throw new Error(`${qualifier} ID cannot be an empty string: "${id}"`);
37697
+ if (id.includes(":")) throw new Error(`${qualifier} ID cannot include colons: "${id}"`);
37698
+ }
37699
+
37649
37700
  //#endregion
37650
37701
  //#region src/scope.ts
37651
37702
  var RootScopeStateAttemptError = class extends Error {
@@ -37697,11 +37748,11 @@ var Scope = class Scope {
37697
37748
  dotAlchemy;
37698
37749
  isSelected;
37699
37750
  profile;
37751
+ startedAt;
37700
37752
  providerCredentials;
37701
37753
  isErrored = false;
37702
37754
  isSkipped = false;
37703
37755
  finalized = false;
37704
- startedAt = performance.now();
37705
37756
  deferred = [];
37706
37757
  cleanups = [];
37707
37758
  get appName() {
@@ -37709,21 +37760,23 @@ var Scope = class Scope {
37709
37760
  return this.scopeName;
37710
37761
  }
37711
37762
  constructor(options) {
37712
- const { scopeName, parent, stage, password, stateStore, quiet, phase, local, watch: watch$1, tunnel, force: force$1, destroyStrategy, logger: logger$1, adopt: adopt$1, dotAlchemy, rootDir, isSelected, noTrack, profile,...providerCredentials } = options;
37763
+ const { scopeName, parent, stage, password, stateStore, quiet, phase, local, watch: watch$1, tunnel, force: force$1, destroyStrategy, logger: logger$1, adopt: adopt$1, dotAlchemy, rootDir, isSelected, noTrack, profile, startedAt,...providerCredentials } = options;
37713
37764
  this.scopeName = scopeName;
37714
37765
  this.name = this.scopeName;
37715
37766
  this.parent = parent ?? Scope.getScope();
37716
37767
  this.rootDir = rootDir ?? this.parent?.rootDir ?? ALCHEMY_ROOT;
37717
37768
  this.isSelected = isSelected ?? this.parent?.isSelected;
37769
+ this.startedAt = startedAt ?? this.parent?.startedAt ?? performance.now();
37718
37770
  this.dotAlchemy = dotAlchemy ?? this.parent?.dotAlchemy ?? path.resolve(this.rootDir, ".alchemy");
37719
37771
  this.providerCredentials = providerCredentials;
37720
- const isChild = this.parent !== void 0;
37721
- if (this.scopeName?.includes(":") && isChild) throw new Error(`Scope name "${this.scopeName}" cannot contain double colons`);
37722
37772
  this.stage = stage ?? this.parent?.stage ?? DEFAULT_STAGE;
37723
37773
  this.profile = profile ?? this.parent?.profile;
37724
37774
  this.parent?.children.set(this.scopeName, this);
37725
37775
  this.quiet = quiet ?? this.parent?.quiet ?? false;
37726
- if (this.parent && !this.scopeName) throw new Error("Scope name is required when creating a child scope");
37776
+ if (this.parent) {
37777
+ if (!this.scopeName) throw new Error("Scope name is required when creating a child scope");
37778
+ validateResourceID(this.scopeName, "Scope");
37779
+ }
37727
37780
  this.password = password ?? this.parent?.password;
37728
37781
  this.noTrack = noTrack ?? this.parent?.noTrack ?? false;
37729
37782
  const resolvedPhase = phase ?? this.parent?.phase;
@@ -37740,7 +37793,7 @@ var Scope = class Scope {
37740
37793
  this.force = force$1 ?? this.parent?.force ?? false;
37741
37794
  this.adopt = adopt$1 ?? this.parent?.adopt ?? false;
37742
37795
  this.destroyStrategy = destroyStrategy ?? this.parent?.destroyStrategy ?? "sequential";
37743
- if (this.local) this.logger.warnOnce("Development mode is in beta. Please report any issues to https://github.com/sam-goodwin/alchemy/issues.");
37796
+ if (this.local) this.logger.warnOnce("Development mode is in beta. Please report any issues to https://github.com/alchemy-run/alchemy/issues.");
37744
37797
  this.stateStore = stateStore ?? this.parent?.stateStore ?? ((scope) => new FileSystemStateStore(scope));
37745
37798
  this.state = new InstrumentedStateStore(this.stateStore(this));
37746
37799
  this.dataMutex = new AsyncMutex();
@@ -37874,10 +37927,10 @@ var Scope = class Scope {
37874
37927
  async finalize(options) {
37875
37928
  const shouldForce = options?.force || this.parent === void 0 || this?.parent?.scopeName === this.root.scopeName;
37876
37929
  if (this.phase === "read") {
37877
- createAndSendEvent({
37878
- event: "alchemy.success",
37930
+ if (this.parent == null) await createAndSendEvent({
37931
+ event: this.isErrored ? "alchemy.error" : "alchemy.success",
37879
37932
  duration: performance.now() - this.startedAt
37880
- });
37933
+ }, this.isErrored ? /* @__PURE__ */ new Error("Scope failed") : void 0);
37881
37934
  return;
37882
37935
  }
37883
37936
  if (this.finalized && !shouldForce) return;
@@ -37901,17 +37954,11 @@ var Scope = class Scope {
37901
37954
  force: shouldForce,
37902
37955
  noop: options?.noop
37903
37956
  });
37904
- createAndSendEvent({
37905
- event: "alchemy.success",
37906
- duration: performance.now() - this.startedAt
37907
- });
37908
- } else if (this.isErrored) {
37909
- this.logger.warn("Scope is in error, skipping finalize");
37910
- createAndSendEvent({
37911
- event: "alchemy.error",
37912
- duration: performance.now() - this.startedAt
37913
- }, /* @__PURE__ */ new Error("Scope failed"));
37914
- }
37957
+ } else if (this.isErrored) this.logger.warn("Scope is in error, skipping finalize");
37958
+ if (this.parent == null) await createAndSendEvent({
37959
+ event: this.isErrored ? "alchemy.error" : "alchemy.success",
37960
+ duration: performance.now() - this.startedAt
37961
+ }, this.isErrored ? /* @__PURE__ */ new Error("Scope failed") : void 0);
37915
37962
  if (!this.parent && process.env.ALCHEMY_TEST_KILL_ON_FINALIZE) {
37916
37963
  await this.cleanup();
37917
37964
  process.exit(0);
@@ -38064,6 +38111,7 @@ async function destroy$1(...args) {
38064
38111
  const scope = instance[ResourceScope];
38065
38112
  if (!scope) logger.warn(`Resource "${instance[ResourceFQN]}" has no scope`);
38066
38113
  const quiet = options?.quiet ?? scope.quiet;
38114
+ const start = performance.now();
38067
38115
  try {
38068
38116
  if (!quiet && !options?.noop) logger.task(instance[ResourceFQN], {
38069
38117
  prefix: options?.replace ? "cleanup" : "deleting",
@@ -38071,6 +38119,14 @@ async function destroy$1(...args) {
38071
38119
  resource: formatFQN(instance[ResourceFQN]),
38072
38120
  message: options?.replace ? "Cleaning Up Old Resource..." : "Deleting Resource..."
38073
38121
  });
38122
+ await createAndSendEvent({
38123
+ event: "resource.start",
38124
+ resource: instance[ResourceKind],
38125
+ status: "deleting",
38126
+ phase: "destroy",
38127
+ duration: performance.now() - start,
38128
+ replaced: !!options?.replace
38129
+ });
38074
38130
  let state;
38075
38131
  let props;
38076
38132
  if (options?.replace) {
@@ -38141,7 +38197,24 @@ async function destroy$1(...args) {
38141
38197
  message: options?.replace ? "Old Resource Cleanup Complete" : "Deleted Resource",
38142
38198
  status: "success"
38143
38199
  });
38200
+ await createAndSendEvent({
38201
+ event: "resource.success",
38202
+ resource: instance[ResourceKind],
38203
+ status: "deleted",
38204
+ phase: "destroy",
38205
+ duration: performance.now() - start,
38206
+ replaced: !!options?.replace
38207
+ });
38144
38208
  } catch (error$83) {
38209
+ let errorToSend = error$83 instanceof Error ? error$83 : new Error(String(error$83));
38210
+ await createAndSendEvent({
38211
+ event: "resource.error",
38212
+ resource: instance[ResourceKind],
38213
+ duration: performance.now() - start,
38214
+ phase: "destroy",
38215
+ status: "deleting",
38216
+ replaced: !!options?.replace
38217
+ }, errorToSend);
38145
38218
  logger.error(error$83);
38146
38219
  throw error$83;
38147
38220
  }
@@ -38242,12 +38315,13 @@ _alchemy.env = env;
38242
38315
  * Implementation of the alchemy function.
38243
38316
  */
38244
38317
  async function _alchemy(appName, options) {
38245
- const cliArgs = process.argv.slice(2);
38318
+ const startedAt = performance.now();
38319
+ let firstAnalyticsPromise;
38320
+ if (!options?.noTrack) firstAnalyticsPromise = createAndSendEvent({
38321
+ event: "alchemy.start",
38322
+ duration: performance.now() - startedAt
38323
+ });
38246
38324
  const app = parseOption("--app");
38247
- function parseOption(option, defaultValue) {
38248
- const i$2 = cliArgs.indexOf(option);
38249
- return i$2 !== -1 && i$2 + 1 < cliArgs.length ? cliArgs[i$2 + 1] : defaultValue;
38250
- }
38251
38325
  const mergedOptions = {
38252
38326
  phase: app && app !== appName ? "read" : cliArgs.includes("--destroy") ? "destroy" : cliArgs.includes("--read") ? "read" : "up",
38253
38327
  local: cliArgs.includes("--local") || cliArgs.includes("--dev"),
@@ -38284,7 +38358,8 @@ If this is a mistake, you can disable this check by setting the ALCHEMY_CI_STATE
38284
38358
  phase,
38285
38359
  password: mergedOptions?.password ?? process.env.ALCHEMY_PASSWORD,
38286
38360
  noTrack: mergedOptions?.noTrack ?? false,
38287
- isSelected: app === void 0 ? void 0 : app === appName
38361
+ isSelected: app === void 0 ? void 0 : app === appName,
38362
+ startedAt
38288
38363
  });
38289
38364
  onExit((code) => {
38290
38365
  root.cleanup().then(() => {
@@ -38303,9 +38378,14 @@ If this is a mistake, you can disable this check by setting the ALCHEMY_CI_STATE
38303
38378
  Scope.storage.enterWith(root);
38304
38379
  Scope.storage.enterWith(stage);
38305
38380
  if (mergedOptions?.phase === "destroy") {
38306
- await destroy$1(stage);
38381
+ const err = await destroy$1(stage).catch((e) => e);
38382
+ if (!options?.noTrack) await createAndSendEvent({
38383
+ event: err instanceof Error ? "alchemy.error" : "alchemy.success",
38384
+ duration: performance.now() - root.startedAt
38385
+ }, err instanceof Error ? err : void 0);
38307
38386
  return process.exit(0);
38308
38387
  }
38388
+ if (firstAnalyticsPromise) await firstAnalyticsPromise;
38309
38389
  return root;
38310
38390
  }
38311
38391
  /**
@@ -39170,23 +39250,23 @@ var ExitSignal = class extends Error {
39170
39250
  };
39171
39251
  var CancelSignal = class extends Error {};
39172
39252
  const loggingMiddleware = t.middleware(async ({ path: path$33, next }) => {
39173
- createAndSendEvent({
39253
+ await createAndSendEvent({
39174
39254
  event: "cli.start",
39175
39255
  command: path$33
39176
39256
  });
39177
39257
  let exitCode = 0;
39178
39258
  try {
39179
- const result = await next();
39180
- createAndSendEvent({
39259
+ await next();
39260
+ await createAndSendEvent({
39181
39261
  event: "cli.success",
39182
39262
  command: path$33
39183
39263
  });
39184
- return result;
39185
39264
  } catch (error$83) {
39186
- createAndSendEvent({
39187
- event: error$83 instanceof ExitSignal && error$83.code === 0 ? "cli.success" : "cli.error",
39265
+ const isSafeExit = error$83 instanceof ExitSignal && error$83.code === 0;
39266
+ await createAndSendEvent({
39267
+ event: isSafeExit ? "cli.success" : "cli.error",
39188
39268
  command: path$33
39189
- });
39269
+ }, isSafeExit ? void 0 : error$83);
39190
39270
  if (error$83 instanceof ExitSignal) exitCode = error$83.code;
39191
39271
  else throw error$83;
39192
39272
  } finally {
@@ -55276,12 +55356,15 @@ const execArgs = {
55276
55356
  envFile: zod_default.string().optional().describe("Path to environment file to load"),
55277
55357
  app: zod_default.string().optional().describe("Select a specific application to target"),
55278
55358
  rootDir: zod_default.string().optional().describe("Path to the root directory of the project"),
55279
- profile: zod_default.string().optional().describe("Alchemy profile to use for authoriziing requests")
55359
+ profile: zod_default.string().optional().describe("Alchemy profile to use for authorizing requests")
55280
55360
  };
55281
55361
  async function execAlchemy(main, { cwd: cwd$1 = process.cwd(), quiet, force: force$1, stage, destroy: destroy$2, watch: watch$1, envFile, read: read$5, dev: dev$1, inspect: inspect$1, inspectBrk, inspectWait, adopt: adopt$1, app, rootDir, profile }) {
55282
55362
  const args = [];
55283
55363
  const execArgs$1 = [];
55284
55364
  const shouldInspect = (inspect$1 || inspectBrk || inspectWait) ?? false;
55365
+ const telemetryData = await collectData();
55366
+ args.push(`--telemetry-session-id ${telemetryData.sessionId}`);
55367
+ args.push(`--telemetry-ref ${telemetryData.referrer ? `${telemetryData.referrer}+cli` : "cli"}`);
55285
55368
  if (quiet) args.push("--quiet");
55286
55369
  if (read$5) args.push("--read");
55287
55370
  if (force$1) args.push("--force");
@@ -55366,7 +55449,7 @@ async function execAlchemy(main, { cwd: cwd$1 = process.cwd(), quiet, force: for
55366
55449
  const { promise: inspectorUrlPromise, resolve: resolveInspectorUrl } = promiseWithResolvers();
55367
55450
  process.on("SIGINT", async () => {
55368
55451
  await exitPromise;
55369
- process.exit(sanitizeExitCode(child.exitCode));
55452
+ throw new ExitSignal(sanitizeExitCode(child.exitCode));
55370
55453
  });
55371
55454
  const child = spawn(command, {
55372
55455
  cwd: cwd$1,
@@ -55421,7 +55504,7 @@ async function execAlchemy(main, { cwd: cwd$1 = process.cwd(), quiet, force: for
55421
55504
  }
55422
55505
  const exitPromise = once(child, "exit");
55423
55506
  await exitPromise.catch(() => {});
55424
- process.exit(sanitizeExitCode(child.exitCode));
55507
+ throw new ExitSignal(sanitizeExitCode(child.exitCode));
55425
55508
  }
55426
55509
  /**
55427
55510
  * If exit code is 130 (SIGINT) or null, return 0.
@@ -231134,7 +231217,6 @@ import { Worker } from "alchemy/cloudflare";
231134
231217
  const app = await alchemy("${context$1.projectName}");
231135
231218
 
231136
231219
  export const worker = await Worker("worker", {
231137
- name: \`\${app.name}-\${app.stage}\`,
231138
231220
  entrypoint: "src/worker.ts",
231139
231221
  });
231140
231222
 
@@ -231147,7 +231229,6 @@ import { Vite } from "alchemy/cloudflare";
231147
231229
  const app = await alchemy("${context$1.projectName}");
231148
231230
 
231149
231231
  export const worker = await Vite("${context$1.projectName}", {
231150
- name: \`\${app.name}-\${app.stage}-website\`,
231151
231232
  // replace if different from default:
231152
231233
  //
231153
231234
  // main: "./.output/server/index.mjs",
@@ -231170,7 +231251,6 @@ import { Astro } from "alchemy/cloudflare";
231170
231251
  const app = await alchemy("${context$1.projectName}");
231171
231252
 
231172
231253
  export const worker = await Astro("website", {
231173
- name: \`\${app.name}-\${app.stage}-website\`,
231174
231254
  // replace if different from default:
231175
231255
  //
231176
231256
  // main: "./.output/server/index.mjs",
@@ -231192,7 +231272,6 @@ import { ReactRouter } from "alchemy/cloudflare";
231192
231272
  const app = await alchemy("${context$1.projectName}");
231193
231273
 
231194
231274
  export const worker = await ReactRouter("website", {
231195
- name: \`\${app.name}-\${app.stage}-website\`,
231196
231275
  // replace if different from default:
231197
231276
  //
231198
231277
  // main: "build/server/index.js",
@@ -231215,7 +231294,6 @@ import { SvelteKit } from "alchemy/cloudflare";
231215
231294
  const app = await alchemy("${context$1.projectName}");
231216
231295
 
231217
231296
  export const worker = await SvelteKit("website", {
231218
- name: \`\${app.name}-\${app.stage}-website\`,
231219
231297
  // replace if different from default:
231220
231298
  //
231221
231299
  // main: "./.output/server/index.mjs",
@@ -231237,7 +231315,6 @@ import { TanStackStart } from "alchemy/cloudflare";
231237
231315
  const app = await alchemy("${context$1.projectName}");
231238
231316
 
231239
231317
  export const worker = await TanStackStart("website", {
231240
- name: \`\${app.name}-\${app.stage}-website\`,
231241
231318
  // replace if different from default:
231242
231319
  //
231243
231320
  // main: "./.output/server/index.mjs",
@@ -231259,12 +231336,10 @@ import { D1Database, DurableObjectNamespace, Redwood } from "alchemy/cloudflare"
231259
231336
  const app = await alchemy("${context$1.projectName}");
231260
231337
 
231261
231338
  const database = await D1Database("database", {
231262
- name: \`\${app.name}-\${app.stage}-db\`,
231263
231339
  migrationsDir: "migrations",
231264
231340
  });
231265
231341
 
231266
231342
  export const worker = await Redwood("website", {
231267
- name: \`\${app.name}-\${app.stage}-website\`,
231268
231343
  bindings: {
231269
231344
  AUTH_SECRET_KEY: alchemy.secret(process.env.AUTH_SECRET_KEY),
231270
231345
  DB: database,
@@ -231293,7 +231368,6 @@ import { Nextjs } from "alchemy/cloudflare";
231293
231368
  const app = await alchemy("${context$1.projectName}");
231294
231369
 
231295
231370
  export const website = await Nextjs("website", {
231296
- name: \`\${app.name}-\${app.stage}-website\`,
231297
231371
  });
231298
231372
 
231299
231373
  console.log({
@@ -231310,7 +231384,6 @@ import { Nuxt } from "alchemy/cloudflare";
231310
231384
  const app = await alchemy("${context$1.projectName}");
231311
231385
 
231312
231386
  export const website = await Nuxt("website", {
231313
- name: \`\${app.name}-\${app.stage}-website\`,
231314
231387
  // replace if different from default:
231315
231388
  //
231316
231389
  // main: "./.output/server/index.mjs",
@@ -255,7 +255,6 @@ import { Worker } from "alchemy/cloudflare";
255
255
  const app = await alchemy("${context.projectName}");
256
256
 
257
257
  export const worker = await Worker("worker", {
258
- name: \`\${app.name}-\${app.stage}\`,
259
258
  entrypoint: "src/worker.ts",
260
259
  });
261
260
 
@@ -269,7 +268,6 @@ import { Vite } from "alchemy/cloudflare";
269
268
  const app = await alchemy("${context.projectName}");
270
269
 
271
270
  export const worker = await Vite("${context.projectName}", {
272
- name: \`\${app.name}-\${app.stage}-website\`,
273
271
  // replace if different from default:
274
272
  //
275
273
  // main: "./.output/server/index.mjs",
@@ -293,7 +291,6 @@ import { Astro } from "alchemy/cloudflare";
293
291
  const app = await alchemy("${context.projectName}");
294
292
 
295
293
  export const worker = await Astro("website", {
296
- name: \`\${app.name}-\${app.stage}-website\`,
297
294
  // replace if different from default:
298
295
  //
299
296
  // main: "./.output/server/index.mjs",
@@ -316,7 +313,6 @@ import { ReactRouter } from "alchemy/cloudflare";
316
313
  const app = await alchemy("${context.projectName}");
317
314
 
318
315
  export const worker = await ReactRouter("website", {
319
- name: \`\${app.name}-\${app.stage}-website\`,
320
316
  // replace if different from default:
321
317
  //
322
318
  // main: "build/server/index.js",
@@ -340,7 +336,6 @@ import { SvelteKit } from "alchemy/cloudflare";
340
336
  const app = await alchemy("${context.projectName}");
341
337
 
342
338
  export const worker = await SvelteKit("website", {
343
- name: \`\${app.name}-\${app.stage}-website\`,
344
339
  // replace if different from default:
345
340
  //
346
341
  // main: "./.output/server/index.mjs",
@@ -363,7 +358,6 @@ import { TanStackStart } from "alchemy/cloudflare";
363
358
  const app = await alchemy("${context.projectName}");
364
359
 
365
360
  export const worker = await TanStackStart("website", {
366
- name: \`\${app.name}-\${app.stage}-website\`,
367
361
  // replace if different from default:
368
362
  //
369
363
  // main: "./.output/server/index.mjs",
@@ -386,12 +380,10 @@ import { D1Database, DurableObjectNamespace, Redwood } from "alchemy/cloudflare"
386
380
  const app = await alchemy("${context.projectName}");
387
381
 
388
382
  const database = await D1Database("database", {
389
- name: \`\${app.name}-\${app.stage}-db\`,
390
383
  migrationsDir: "migrations",
391
384
  });
392
385
 
393
386
  export const worker = await Redwood("website", {
394
- name: \`\${app.name}-\${app.stage}-website\`,
395
387
  bindings: {
396
388
  AUTH_SECRET_KEY: alchemy.secret(process.env.AUTH_SECRET_KEY),
397
389
  DB: database,
@@ -421,7 +413,6 @@ import { Nextjs } from "alchemy/cloudflare";
421
413
  const app = await alchemy("${context.projectName}");
422
414
 
423
415
  export const website = await Nextjs("website", {
424
- name: \`\${app.name}-\${app.stage}-website\`,
425
416
  });
426
417
 
427
418
  console.log({
@@ -439,7 +430,6 @@ import { Nuxt } from "alchemy/cloudflare";
439
430
  const app = await alchemy("${context.projectName}");
440
431
 
441
432
  export const website = await Nuxt("website", {
442
- name: \`\${app.name}-\${app.stage}-website\`,
443
433
  // replace if different from default:
444
434
  //
445
435
  // main: "./.output/server/index.mjs",
@@ -8,6 +8,7 @@ import { detectRuntime } from "../../src/util/detect-node-runtime.ts";
8
8
  import { detectPackageManager } from "../../src/util/detect-package-manager.ts";
9
9
  import { exists } from "../../src/util/exists.ts";
10
10
  import { promiseWithResolvers } from "../../src/util/promise-with-resolvers.ts";
11
+ import { collectData } from "../../src/util/telemetry.ts";
11
12
  import { ExitSignal } from "../trpc.ts";
12
13
  import { CDPProxy } from "./cdp-manager/cdp-proxy.ts";
13
14
  import { CDPManager } from "./cdp-manager/server.ts";
@@ -75,7 +76,7 @@ export const execArgs = {
75
76
  profile: z
76
77
  .string()
77
78
  .optional()
78
- .describe("Alchemy profile to use for authoriziing requests"),
79
+ .describe("Alchemy profile to use for authorizing requests"),
79
80
  } as const;
80
81
 
81
82
  export async function execAlchemy(
@@ -121,6 +122,13 @@ export async function execAlchemy(
121
122
 
122
123
  const shouldInspect = (inspect || inspectBrk || inspectWait) ?? false;
123
124
 
125
+ const telemetryData = await collectData();
126
+
127
+ args.push(`--telemetry-session-id ${telemetryData.sessionId}`);
128
+ args.push(
129
+ `--telemetry-ref ${telemetryData.referrer ? `${telemetryData.referrer}+cli` : "cli"}`,
130
+ );
131
+
124
132
  if (quiet) args.push("--quiet");
125
133
  if (read) args.push("--read");
126
134
  if (force) args.push("--force");
@@ -238,7 +246,7 @@ export async function execAlchemy(
238
246
 
239
247
  process.on("SIGINT", async () => {
240
248
  await exitPromise;
241
- process.exit(sanitizeExitCode(child.exitCode));
249
+ throw new ExitSignal(sanitizeExitCode(child.exitCode));
242
250
  });
243
251
 
244
252
  const child = spawn(command, {
@@ -298,7 +306,8 @@ export async function execAlchemy(
298
306
 
299
307
  const exitPromise = once(child, "exit");
300
308
  await exitPromise.catch(() => {});
301
- process.exit(sanitizeExitCode(child.exitCode));
309
+
310
+ throw new ExitSignal(sanitizeExitCode(child.exitCode));
302
311
  }
303
312
 
304
313
  /**
package/bin/trpc.ts CHANGED
@@ -6,7 +6,7 @@ import { createAndSendEvent } from "../src/util/telemetry.ts";
6
6
  export const t = trpcServer.initTRPC.meta<TrpcCliMeta>().create();
7
7
 
8
8
  export class ExitSignal extends Error {
9
- constructor(public code: 0 | 1 = 0) {
9
+ constructor(public code = 0) {
10
10
  super(`Process exit with code ${code}`);
11
11
  this.name = "ExitSignal";
12
12
  }
@@ -15,27 +15,27 @@ export class ExitSignal extends Error {
15
15
  export class CancelSignal extends Error {}
16
16
 
17
17
  const loggingMiddleware = t.middleware(async ({ path, next }) => {
18
- createAndSendEvent({
18
+ await createAndSendEvent({
19
19
  event: "cli.start",
20
20
  command: path,
21
21
  });
22
22
  let exitCode = 0;
23
23
 
24
24
  try {
25
- const result = await next();
26
- createAndSendEvent({
25
+ await next();
26
+ await createAndSendEvent({
27
27
  event: "cli.success",
28
28
  command: path,
29
29
  });
30
- return result;
31
30
  } catch (error) {
32
- createAndSendEvent({
33
- event:
34
- error instanceof ExitSignal && error.code === 0
35
- ? "cli.success"
36
- : "cli.error",
37
- command: path,
38
- });
31
+ const isSafeExit = error instanceof ExitSignal && error.code === 0;
32
+ await createAndSendEvent(
33
+ {
34
+ event: isSafeExit ? "cli.success" : "cli.error",
35
+ command: path,
36
+ },
37
+ isSafeExit ? undefined : error,
38
+ );
39
39
  if (error instanceof ExitSignal) {
40
40
  exitCode = error.code;
41
41
  } else {