alchemy 0.69.0 → 0.70.0

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 (314) hide show
  1. package/bin/alchemy.js +3810 -3889
  2. package/bin/commands/init.ts +41 -55
  3. package/bin/trpc.ts +4 -9
  4. package/lib/alchemy.d.ts +3 -3
  5. package/lib/alchemy.d.ts.map +1 -1
  6. package/lib/alchemy.js +2 -15
  7. package/lib/alchemy.js.map +1 -1
  8. package/lib/apply.js +24 -9
  9. package/lib/apply.js.map +1 -1
  10. package/lib/aws/utils.d.ts +2 -1
  11. package/lib/aws/utils.d.ts.map +1 -1
  12. package/lib/aws/utils.js.map +1 -1
  13. package/lib/clickhouse/api/client.gen.d.ts +13 -0
  14. package/lib/clickhouse/api/client.gen.d.ts.map +1 -0
  15. package/lib/clickhouse/api/client.gen.js +7 -0
  16. package/lib/clickhouse/api/client.gen.js.map +1 -0
  17. package/lib/clickhouse/api/sdk.gen.d.ts +322 -0
  18. package/lib/clickhouse/api/sdk.gen.d.ts.map +1 -0
  19. package/lib/clickhouse/api/sdk.gen.js +683 -0
  20. package/lib/clickhouse/api/sdk.gen.js.map +1 -0
  21. package/lib/clickhouse/api/types.gen.d.ts +5030 -0
  22. package/lib/clickhouse/api/types.gen.d.ts.map +1 -0
  23. package/lib/clickhouse/api/types.gen.js +2 -0
  24. package/lib/clickhouse/api/types.gen.js.map +1 -0
  25. package/lib/clickhouse/api-key.d.ts +29 -0
  26. package/lib/clickhouse/api-key.d.ts.map +1 -0
  27. package/lib/clickhouse/api-key.js +83 -0
  28. package/lib/clickhouse/api-key.js.map +1 -0
  29. package/lib/clickhouse/api.d.ts +36 -0
  30. package/lib/clickhouse/api.d.ts.map +1 -0
  31. package/lib/clickhouse/api.js +48 -0
  32. package/lib/clickhouse/api.js.map +1 -0
  33. package/lib/clickhouse/index.d.ts +4 -0
  34. package/lib/clickhouse/index.d.ts.map +1 -0
  35. package/lib/clickhouse/index.js +4 -0
  36. package/lib/clickhouse/index.js.map +1 -0
  37. package/lib/clickhouse/organization.d.ts +16 -0
  38. package/lib/clickhouse/organization.d.ts.map +1 -0
  39. package/lib/clickhouse/organization.js +21 -0
  40. package/lib/clickhouse/organization.js.map +1 -0
  41. package/lib/clickhouse/service.d.ts +275 -0
  42. package/lib/clickhouse/service.d.ts.map +1 -0
  43. package/lib/clickhouse/service.js +274 -0
  44. package/lib/clickhouse/service.js.map +1 -0
  45. package/lib/cloudflare/account-api-token.d.ts.map +1 -1
  46. package/lib/cloudflare/account-api-token.js +1 -1
  47. package/lib/cloudflare/account-api-token.js.map +1 -1
  48. package/lib/cloudflare/bucket-object.d.ts +5 -0
  49. package/lib/cloudflare/bucket-object.d.ts.map +1 -1
  50. package/lib/cloudflare/bucket-object.js +1 -1
  51. package/lib/cloudflare/bucket-object.js.map +1 -1
  52. package/lib/cloudflare/bucket.d.ts +6 -3
  53. package/lib/cloudflare/bucket.d.ts.map +1 -1
  54. package/lib/cloudflare/bucket.js +60 -8
  55. package/lib/cloudflare/bucket.js.map +1 -1
  56. package/lib/cloudflare/bun-spa/bun-spa.d.ts +15 -0
  57. package/lib/cloudflare/bun-spa/bun-spa.d.ts.map +1 -0
  58. package/lib/cloudflare/bun-spa/bun-spa.js +95 -0
  59. package/lib/cloudflare/bun-spa/bun-spa.js.map +1 -0
  60. package/lib/cloudflare/compatibility-date.gen.d.ts +1 -1
  61. package/lib/cloudflare/compatibility-date.gen.js +1 -1
  62. package/lib/cloudflare/container.d.ts +11 -1
  63. package/lib/cloudflare/container.d.ts.map +1 -1
  64. package/lib/cloudflare/container.js +1 -1
  65. package/lib/cloudflare/container.js.map +1 -1
  66. package/lib/cloudflare/index.d.ts +1 -0
  67. package/lib/cloudflare/index.d.ts.map +1 -1
  68. package/lib/cloudflare/index.js +1 -0
  69. package/lib/cloudflare/index.js.map +1 -1
  70. package/lib/cloudflare/miniflare/build-worker-options.d.ts.map +1 -1
  71. package/lib/cloudflare/miniflare/build-worker-options.js +3 -3
  72. package/lib/cloudflare/miniflare/build-worker-options.js.map +1 -1
  73. package/lib/cloudflare/miniflare/miniflare-controller.d.ts.map +1 -1
  74. package/lib/cloudflare/miniflare/miniflare-controller.js +2 -0
  75. package/lib/cloudflare/miniflare/miniflare-controller.js.map +1 -1
  76. package/lib/cloudflare/miniflare/miniflare-worker-proxy.d.ts +1 -1
  77. package/lib/cloudflare/miniflare/miniflare-worker-proxy.d.ts.map +1 -1
  78. package/lib/cloudflare/miniflare/miniflare-worker-proxy.js +33 -14
  79. package/lib/cloudflare/miniflare/miniflare-worker-proxy.js.map +1 -1
  80. package/lib/cloudflare/permission-groups.d.ts +2 -3
  81. package/lib/cloudflare/permission-groups.d.ts.map +1 -1
  82. package/lib/cloudflare/permission-groups.js +10 -15
  83. package/lib/cloudflare/permission-groups.js.map +1 -1
  84. package/lib/cloudflare/redwood/redwood.d.ts +1 -1
  85. package/lib/cloudflare/redwood/redwood.js +1 -1
  86. package/lib/cloudflare/tanstack-start/plugin.d.ts +2 -15
  87. package/lib/cloudflare/tanstack-start/plugin.d.ts.map +1 -1
  88. package/lib/cloudflare/tanstack-start/plugin.js +5 -25
  89. package/lib/cloudflare/tanstack-start/plugin.js.map +1 -1
  90. package/lib/cloudflare/tanstack-start/tanstack-start.d.ts.map +1 -1
  91. package/lib/cloudflare/tanstack-start/tanstack-start.js +15 -4
  92. package/lib/cloudflare/tanstack-start/tanstack-start.js.map +1 -1
  93. package/lib/cloudflare/util/filter-env-bindings.d.ts +62 -0
  94. package/lib/cloudflare/util/filter-env-bindings.d.ts.map +1 -0
  95. package/lib/cloudflare/util/filter-env-bindings.js +81 -0
  96. package/lib/cloudflare/util/filter-env-bindings.js.map +1 -0
  97. package/lib/cloudflare/website.d.ts +2 -2
  98. package/lib/cloudflare/website.d.ts.map +1 -1
  99. package/lib/cloudflare/website.js +7 -18
  100. package/lib/cloudflare/website.js.map +1 -1
  101. package/lib/cloudflare/worker-metadata.d.ts +5 -1
  102. package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
  103. package/lib/cloudflare/worker-metadata.js +3 -0
  104. package/lib/cloudflare/worker-metadata.js.map +1 -1
  105. package/lib/cloudflare/worker.d.ts +17 -1
  106. package/lib/cloudflare/worker.d.ts.map +1 -1
  107. package/lib/cloudflare/worker.js +1 -0
  108. package/lib/cloudflare/worker.js.map +1 -1
  109. package/lib/cloudflare/wrangler.json.d.ts +15 -1
  110. package/lib/cloudflare/wrangler.json.d.ts.map +1 -1
  111. package/lib/cloudflare/wrangler.json.js +12 -4
  112. package/lib/cloudflare/wrangler.json.js.map +1 -1
  113. package/lib/coinbase/client.d.ts +54 -0
  114. package/lib/coinbase/client.d.ts.map +1 -0
  115. package/lib/coinbase/client.js +89 -0
  116. package/lib/coinbase/client.js.map +1 -0
  117. package/lib/coinbase/evm-account.d.ts +118 -0
  118. package/lib/coinbase/evm-account.d.ts.map +1 -0
  119. package/lib/coinbase/evm-account.js +198 -0
  120. package/lib/coinbase/evm-account.js.map +1 -0
  121. package/lib/coinbase/evm-smart-account.d.ts +122 -0
  122. package/lib/coinbase/evm-smart-account.d.ts.map +1 -0
  123. package/lib/coinbase/evm-smart-account.js +213 -0
  124. package/lib/coinbase/evm-smart-account.js.map +1 -0
  125. package/lib/coinbase/faucet.d.ts +11 -0
  126. package/lib/coinbase/faucet.d.ts.map +1 -0
  127. package/lib/coinbase/faucet.js +146 -0
  128. package/lib/coinbase/faucet.js.map +1 -0
  129. package/lib/coinbase/index.d.ts +4 -0
  130. package/lib/coinbase/index.d.ts.map +1 -0
  131. package/lib/coinbase/index.js +4 -0
  132. package/lib/coinbase/index.js.map +1 -0
  133. package/lib/coinbase/types.d.ts +28 -0
  134. package/lib/coinbase/types.d.ts.map +1 -0
  135. package/lib/coinbase/types.js +4 -0
  136. package/lib/{util/telemetry → coinbase}/types.js.map +1 -1
  137. package/lib/coinbase/utils.d.ts +13 -0
  138. package/lib/coinbase/utils.d.ts.map +1 -0
  139. package/lib/coinbase/utils.js +17 -0
  140. package/lib/coinbase/utils.js.map +1 -0
  141. package/lib/docker/api.d.ts +22 -1
  142. package/lib/docker/api.d.ts.map +1 -1
  143. package/lib/docker/api.js +64 -2
  144. package/lib/docker/api.js.map +1 -1
  145. package/lib/docker/container.d.ts +88 -0
  146. package/lib/docker/container.d.ts.map +1 -1
  147. package/lib/docker/container.js +36 -0
  148. package/lib/docker/container.js.map +1 -1
  149. package/lib/neon/api/types.gen.d.ts +7 -2
  150. package/lib/neon/api/types.gen.d.ts.map +1 -1
  151. package/lib/planetscale/api/types.gen.d.ts +20 -4
  152. package/lib/planetscale/api/types.gen.d.ts.map +1 -1
  153. package/lib/planetscale/api.d.ts.map +1 -1
  154. package/lib/planetscale/api.js +1 -0
  155. package/lib/planetscale/api.js.map +1 -1
  156. package/lib/planetscale/password.d.ts.map +1 -1
  157. package/lib/planetscale/password.js +1 -13
  158. package/lib/planetscale/password.js.map +1 -1
  159. package/lib/resource.d.ts.map +1 -1
  160. package/lib/resource.js +7 -3
  161. package/lib/resource.js.map +1 -1
  162. package/lib/scope.d.ts +4 -9
  163. package/lib/scope.d.ts.map +1 -1
  164. package/lib/scope.js +16 -37
  165. package/lib/scope.js.map +1 -1
  166. package/lib/state/instrumented-state-store.d.ts +1 -3
  167. package/lib/state/instrumented-state-store.d.ts.map +1 -1
  168. package/lib/state/instrumented-state-store.js +19 -21
  169. package/lib/state/instrumented-state-store.js.map +1 -1
  170. package/lib/test/bun.d.ts.map +1 -1
  171. package/lib/test/bun.js +1 -2
  172. package/lib/test/bun.js.map +1 -1
  173. package/lib/test/vitest.d.ts.map +1 -1
  174. package/lib/test/vitest.js +1 -2
  175. package/lib/test/vitest.js.map +1 -1
  176. package/lib/util/diff.d.ts +5 -0
  177. package/lib/util/diff.d.ts.map +1 -0
  178. package/lib/util/diff.js +14 -0
  179. package/lib/util/diff.js.map +1 -0
  180. package/lib/util/telemetry.d.ts +52 -0
  181. package/lib/util/telemetry.d.ts.map +1 -0
  182. package/lib/util/telemetry.js +191 -0
  183. package/lib/util/telemetry.js.map +1 -0
  184. package/package.json +19 -1
  185. package/src/alchemy.ts +5 -20
  186. package/src/apply.ts +29 -12
  187. package/src/aws/utils.ts +2 -1
  188. package/src/clickhouse/api/client.gen.ts +28 -0
  189. package/src/clickhouse/api/sdk.gen.ts +1286 -0
  190. package/src/clickhouse/api/types.gen.ts +5898 -0
  191. package/src/clickhouse/api-key.ts +136 -0
  192. package/src/clickhouse/api.ts +84 -0
  193. package/src/clickhouse/index.ts +3 -0
  194. package/src/clickhouse/organization.ts +31 -0
  195. package/src/clickhouse/service.ts +668 -0
  196. package/src/cloudflare/account-api-token.ts +1 -4
  197. package/src/cloudflare/bucket-object.ts +11 -1
  198. package/src/cloudflare/bucket.ts +72 -6
  199. package/src/cloudflare/bun-spa/bun-spa.ts +139 -0
  200. package/src/cloudflare/compatibility-date.gen.ts +1 -1
  201. package/src/cloudflare/container.ts +22 -3
  202. package/src/cloudflare/index.ts +1 -0
  203. package/src/cloudflare/miniflare/build-worker-options.ts +6 -4
  204. package/src/cloudflare/miniflare/miniflare-controller.ts +2 -0
  205. package/src/cloudflare/miniflare/miniflare-worker-proxy.ts +42 -15
  206. package/src/cloudflare/permission-groups.ts +21 -49
  207. package/src/cloudflare/redwood/redwood.ts +1 -1
  208. package/src/cloudflare/tanstack-start/plugin.ts +7 -28
  209. package/src/cloudflare/tanstack-start/tanstack-start.ts +15 -4
  210. package/src/cloudflare/util/filter-env-bindings.ts +96 -0
  211. package/src/cloudflare/website.ts +12 -24
  212. package/src/cloudflare/worker-metadata.ts +8 -1
  213. package/src/cloudflare/worker.ts +19 -1
  214. package/src/cloudflare/wrangler.json.ts +27 -9
  215. package/src/coinbase/client.ts +146 -0
  216. package/src/coinbase/evm-account.ts +317 -0
  217. package/src/coinbase/evm-smart-account.ts +339 -0
  218. package/src/coinbase/faucet.ts +173 -0
  219. package/src/coinbase/index.ts +3 -0
  220. package/src/coinbase/types.ts +33 -0
  221. package/src/coinbase/utils.ts +19 -0
  222. package/src/docker/api.ts +86 -2
  223. package/src/docker/container.ts +97 -0
  224. package/src/neon/api/types.gen.ts +13 -1
  225. package/src/planetscale/api/types.gen.ts +22 -2
  226. package/src/planetscale/api.ts +1 -0
  227. package/src/planetscale/password.ts +1 -14
  228. package/src/resource.ts +11 -4
  229. package/src/scope.ts +22 -45
  230. package/src/state/instrumented-state-store.ts +27 -26
  231. package/src/test/bun.ts +1 -2
  232. package/src/test/vitest.ts +1 -2
  233. package/src/util/diff.ts +14 -0
  234. package/src/util/telemetry.ts +297 -0
  235. package/templates/tanstack-start/.cta.json +13 -0
  236. package/templates/tanstack-start/README.md +267 -49
  237. package/templates/tanstack-start/_gitignore +9 -18
  238. package/templates/tanstack-start/alchemy.run.ts +2 -2
  239. package/templates/tanstack-start/package.json +24 -19
  240. package/templates/tanstack-start/public/favicon.ico +0 -0
  241. package/templates/tanstack-start/public/logo192.png +0 -0
  242. package/templates/tanstack-start/public/logo512.png +0 -0
  243. package/templates/tanstack-start/public/manifest.json +25 -0
  244. package/templates/tanstack-start/public/robots.txt +3 -0
  245. package/templates/tanstack-start/src/components/Header.tsx +21 -0
  246. package/templates/tanstack-start/src/logo.svg +12 -0
  247. package/templates/tanstack-start/src/router.tsx +7 -16
  248. package/templates/tanstack-start/src/routes/__root.tsx +13 -114
  249. package/templates/tanstack-start/src/routes/api.demo-names.ts +15 -0
  250. package/templates/tanstack-start/src/routes/demo.start.api-request.tsx +44 -0
  251. package/templates/tanstack-start/src/routes/demo.start.server-funcs.tsx +97 -0
  252. package/templates/tanstack-start/src/routes/index.tsx +31 -4
  253. package/templates/tanstack-start/src/styles.css +15 -0
  254. package/templates/tanstack-start/tsconfig.json +19 -22
  255. package/templates/tanstack-start/types/env.d.ts +2 -2
  256. package/templates/tanstack-start/vite.config.ts +16 -25
  257. package/workers/tunnel-proxy.js +1 -1
  258. package/lib/util/telemetry/client.d.ts +0 -31
  259. package/lib/util/telemetry/client.d.ts.map +0 -1
  260. package/lib/util/telemetry/client.js +0 -121
  261. package/lib/util/telemetry/client.js.map +0 -1
  262. package/lib/util/telemetry/constants.d.ts +0 -5
  263. package/lib/util/telemetry/constants.d.ts.map +0 -1
  264. package/lib/util/telemetry/constants.js +0 -8
  265. package/lib/util/telemetry/constants.js.map +0 -1
  266. package/lib/util/telemetry/context.d.ts +0 -15
  267. package/lib/util/telemetry/context.d.ts.map +0 -1
  268. package/lib/util/telemetry/context.js +0 -128
  269. package/lib/util/telemetry/context.js.map +0 -1
  270. package/lib/util/telemetry/index.d.ts +0 -3
  271. package/lib/util/telemetry/index.d.ts.map +0 -1
  272. package/lib/util/telemetry/index.js +0 -3
  273. package/lib/util/telemetry/index.js.map +0 -1
  274. package/lib/util/telemetry/types.d.ts +0 -68
  275. package/lib/util/telemetry/types.d.ts.map +0 -1
  276. package/lib/util/telemetry/types.js +0 -1
  277. package/src/util/telemetry/client.ts +0 -156
  278. package/src/util/telemetry/constants.ts +0 -13
  279. package/src/util/telemetry/context.ts +0 -154
  280. package/src/util/telemetry/index.ts +0 -2
  281. package/src/util/telemetry/types.ts +0 -105
  282. package/templates/tanstack-start/_prettierignore +0 -4
  283. package/templates/tanstack-start/public/android-chrome-192x192.png +0 -0
  284. package/templates/tanstack-start/public/android-chrome-512x512.png +0 -0
  285. package/templates/tanstack-start/public/apple-touch-icon.png +0 -0
  286. package/templates/tanstack-start/public/favicon-16x16.png +0 -0
  287. package/templates/tanstack-start/public/favicon-32x32.png +0 -0
  288. package/templates/tanstack-start/public/favicon.png +0 -0
  289. package/templates/tanstack-start/public/site.webmanifest +0 -19
  290. package/templates/tanstack-start/src/components/DefaultCatchBoundary.tsx +0 -53
  291. package/templates/tanstack-start/src/components/NotFound.tsx +0 -25
  292. package/templates/tanstack-start/src/components/PostError.tsx +0 -5
  293. package/templates/tanstack-start/src/components/UserError.tsx +0 -5
  294. package/templates/tanstack-start/src/routes/_pathlessLayout/_nested-layout/route-a.tsx +0 -10
  295. package/templates/tanstack-start/src/routes/_pathlessLayout/_nested-layout/route-b.tsx +0 -10
  296. package/templates/tanstack-start/src/routes/_pathlessLayout/_nested-layout.tsx +0 -34
  297. package/templates/tanstack-start/src/routes/_pathlessLayout.tsx +0 -16
  298. package/templates/tanstack-start/src/routes/api/users.$userId.ts +0 -28
  299. package/templates/tanstack-start/src/routes/api/users.ts +0 -59
  300. package/templates/tanstack-start/src/routes/customScript[.]js.ts +0 -10
  301. package/templates/tanstack-start/src/routes/deferred.tsx +0 -62
  302. package/templates/tanstack-start/src/routes/posts.$postId.tsx +0 -34
  303. package/templates/tanstack-start/src/routes/posts.index.tsx +0 -8
  304. package/templates/tanstack-start/src/routes/posts.tsx +0 -38
  305. package/templates/tanstack-start/src/routes/posts_.$postId.deep.tsx +0 -29
  306. package/templates/tanstack-start/src/routes/redirect.tsx +0 -9
  307. package/templates/tanstack-start/src/routes/users.$userId.tsx +0 -44
  308. package/templates/tanstack-start/src/routes/users.index.tsx +0 -18
  309. package/templates/tanstack-start/src/routes/users.tsx +0 -49
  310. package/templates/tanstack-start/src/styles/app.css +0 -12
  311. package/templates/tanstack-start/src/utils/loggingMiddleware.tsx +0 -41
  312. package/templates/tanstack-start/src/utils/posts.tsx +0 -40
  313. package/templates/tanstack-start/src/utils/seo.ts +0 -33
  314. package/templates/tanstack-start/src/utils/users.tsx +0 -5
@@ -0,0 +1,173 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Request testnet tokens from Coinbase faucet for accounts with faucet metadata
4
+ *
5
+ * Usage:
6
+ * bun node_modules/alchemy/src/coinbase/faucet.ts # Fund all accounts with faucet config
7
+ * bun node_modules/alchemy/src/coinbase/faucet.ts dev # Fund only accounts in dev scope
8
+ * bun node_modules/alchemy/src/coinbase/faucet.ts backend/dev # Fund specific scope
9
+ */
10
+
11
+ import { readFile, access } from "node:fs/promises";
12
+ import { join } from "node:path";
13
+ import { glob } from "glob";
14
+ import { createCdpClient } from "./client.ts";
15
+ import type { EvmAccount } from "./evm-account.ts";
16
+ import type { EvmSmartAccount } from "./evm-smart-account.ts";
17
+ import type { FaucetNetwork, FaucetToken } from "./types.ts";
18
+
19
+ // Track funded combinations to avoid duplicates within this run
20
+ const funded = new Set<string>();
21
+
22
+ // Get optional scope argument
23
+ const scope = process.argv[2];
24
+
25
+ // Initialize CDP client (uses CDP_* env vars automatically)
26
+ const cdp = await createCdpClient();
27
+
28
+ let stateFiles: string[];
29
+
30
+ if (scope) {
31
+ // If scope provided, find matching directories
32
+ const scopePath = join(".alchemy", scope);
33
+
34
+ let scopeExists = false;
35
+ try {
36
+ await access(scopePath);
37
+ scopeExists = true;
38
+ } catch {
39
+ scopeExists = false;
40
+ }
41
+
42
+ if (scopeExists) {
43
+ // Exact match found
44
+ console.log(`Using scope: ${scope}`);
45
+ stateFiles = await glob("*.json", {
46
+ cwd: scopePath,
47
+ absolute: true,
48
+ });
49
+ } else {
50
+ // Try to find directories that end with the provided scope
51
+ // We need to find directories, so we'll look for JSON files and extract the directory paths
52
+ const allFiles = await glob(`**/${scope}/*.json`, {
53
+ cwd: ".alchemy",
54
+ });
55
+
56
+ // Extract unique directory paths
57
+ const possibleScopes = [
58
+ ...new Set(allFiles.map((f) => f.replace(/\/[^/]+\.json$/, ""))),
59
+ ];
60
+
61
+ if (possibleScopes.length === 0) {
62
+ console.error(`No scope found matching: ${scope}`);
63
+ process.exit(1);
64
+ } else if (possibleScopes.length === 1) {
65
+ // Found exactly one match
66
+ const foundScope = possibleScopes[0];
67
+ console.log(`Using scope: ${foundScope}`);
68
+ stateFiles = await glob("*.json", {
69
+ cwd: join(".alchemy", foundScope),
70
+ absolute: true,
71
+ });
72
+ } else {
73
+ // Multiple matches found - use all of them
74
+ console.log(`Found ${possibleScopes.length} scopes matching '${scope}':`);
75
+ for (const s of possibleScopes) {
76
+ console.log(` - ${s}`);
77
+ }
78
+
79
+ // Collect state files from all matching scopes
80
+ stateFiles = [];
81
+ for (const foundScope of possibleScopes) {
82
+ const files = await glob("*.json", {
83
+ cwd: join(".alchemy", foundScope),
84
+ absolute: true,
85
+ });
86
+ stateFiles.push(...files);
87
+ }
88
+ }
89
+ }
90
+ } else {
91
+ // No scope provided, search all .alchemy subdirectories
92
+ console.log("Searching all scopes for accounts with faucet configuration...");
93
+ stateFiles = await glob("**/*.json", {
94
+ cwd: ".alchemy",
95
+ absolute: true,
96
+ });
97
+ }
98
+
99
+ const allStates = (
100
+ await Promise.all(
101
+ stateFiles.map(async (file) => {
102
+ try {
103
+ const content = await readFile(file, "utf-8");
104
+ return JSON.parse(content);
105
+ } catch {
106
+ return null;
107
+ }
108
+ }),
109
+ )
110
+ ).filter(Boolean);
111
+
112
+ const accounts = allStates
113
+ .filter(
114
+ (state) =>
115
+ state?.kind?.startsWith("coinbase::evm") &&
116
+ state.output &&
117
+ (state.output as EvmAccount | EvmSmartAccount).faucet,
118
+ )
119
+ .map((state) => {
120
+ const output = state.output as EvmAccount | EvmSmartAccount;
121
+ return {
122
+ address: output.address,
123
+ name: output.name,
124
+ faucet: output.faucet!,
125
+ };
126
+ });
127
+
128
+ if (accounts.length === 0) {
129
+ console.log("No accounts with faucet configuration found");
130
+ process.exit(0);
131
+ }
132
+
133
+ console.log(`Found ${accounts.length} accounts with faucet configuration\n`);
134
+
135
+ for (const account of accounts) {
136
+ console.log(`Account: ${account.name} (${account.address})`);
137
+
138
+ for (const [network, tokens] of Object.entries(account.faucet)) {
139
+ for (const token of tokens) {
140
+ const key = `${account.address}-${network}-${token}`;
141
+
142
+ // Skip if already processed in this run
143
+ if (funded.has(key)) {
144
+ console.log(` ⏭️ Skipping ${token} on ${network} (already requested)`);
145
+ continue;
146
+ }
147
+
148
+ // Request tokens from faucet
149
+ try {
150
+ console.log(` 💧 Requesting ${token} on ${network}...`);
151
+
152
+ const response = await cdp.evm.requestFaucet({
153
+ address: account.address,
154
+ network: network as FaucetNetwork,
155
+ token: token as FaucetToken,
156
+ });
157
+
158
+ console.log(` ✅ Funded: ${response.transactionHash}`);
159
+ funded.add(key);
160
+
161
+ // Small delay to avoid rate limits
162
+ await new Promise((resolve) => setTimeout(resolve, 1000));
163
+ } catch (error: any) {
164
+ console.log(
165
+ ` ❌ Error funding ${token} on ${network}: ${error.message}`,
166
+ );
167
+ }
168
+ }
169
+ }
170
+ console.log();
171
+ }
172
+
173
+ console.log("✨ Funding complete!");
@@ -0,0 +1,3 @@
1
+ export * from "./types.ts";
2
+ export * from "./evm-account.ts";
3
+ export * from "./evm-smart-account.ts";
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Common types for Coinbase CDP resources
3
+ */
4
+
5
+ /**
6
+ * Ethereum address format (20 bytes, 40 hex chars)
7
+ */
8
+ export type Address = `0x${string}`;
9
+
10
+ /**
11
+ * Private key format (32 bytes, 64 hex chars)
12
+ */
13
+ export type PrivateKey = `0x${string}`;
14
+
15
+ /**
16
+ * Transaction hash format (32 bytes, 64 hex chars)
17
+ */
18
+ export type TransactionHash = `0x${string}`;
19
+
20
+ /**
21
+ * Supported testnet networks for faucet requests
22
+ */
23
+ export type FaucetNetwork = "base-sepolia" | "ethereum-sepolia";
24
+
25
+ /**
26
+ * Supported tokens for faucet requests
27
+ */
28
+ export type FaucetToken = "eth" | "usdc" | "eurc" | "cbbtc";
29
+
30
+ /**
31
+ * Faucet configuration for requesting testnet tokens
32
+ */
33
+ export type FaucetConfig = Partial<Record<FaucetNetwork, FaucetToken[]>>;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Utility functions for Coinbase CDP resources
3
+ */
4
+
5
+ /**
6
+ * Validates that a CDP account name follows the required format.
7
+ * CDP only allows letters, numbers, and hyphens in account names.
8
+ * Names cannot start or end with a hyphen.
9
+ *
10
+ * @param name - The account name to validate
11
+ * @throws Error if the name contains invalid characters or starts/ends with hyphen
12
+ */
13
+ export function validateAccountName(name: string): void {
14
+ if (!/^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/.test(name)) {
15
+ throw new Error(
16
+ `Invalid account name '${name}'. CDP only allows letters, numbers, and hyphens. Names cannot start or end with a hyphen.`,
17
+ );
18
+ }
19
+ }
package/src/docker/api.ts CHANGED
@@ -3,6 +3,41 @@ import { Buffer } from "node:buffer";
3
3
  import { spawn } from "node:child_process";
4
4
  import fs from "node:fs/promises";
5
5
  import path from "node:path";
6
+ import type { Duration, HealthcheckConfig } from "./container.ts";
7
+
8
+ /**
9
+ * Normalize a duration value to Docker CLI format
10
+ * Accepts either a number (seconds) or a string with unit suffix (ms|s|m|h)
11
+ * Returns a string in Docker CLI format
12
+ *
13
+ * The Duration type provides compile-time type safety, ensuring only valid
14
+ * formats are accepted: number or string matching the pattern `${number}${unit}`
15
+ *
16
+ * @param duration Duration value (number in seconds or string with unit)
17
+ * @returns Normalized duration string for Docker CLI
18
+ *
19
+ * @example
20
+ * normalizeDuration(30) // "30s"
21
+ * normalizeDuration("30s") // "30s"
22
+ * normalizeDuration("1m") // "1m"
23
+ * normalizeDuration("500ms") // "500ms"
24
+ * normalizeDuration("1.5s") // "1.5s"
25
+ */
26
+ export function normalizeDuration(duration: Duration): string {
27
+ if (typeof duration === "number") {
28
+ return `${duration}s`;
29
+ }
30
+
31
+ // Validate string format: must be a number followed by unit (ms|s|m|h)
32
+ const match = duration.match(/^(\d+(?:\.\d+)?)(ms|s|m|h)$/);
33
+ if (!match) {
34
+ throw new Error(
35
+ `Invalid duration format: "${duration}". Expected format: number followed by unit (ms|s|m|h), e.g., "30s", "1m", "500ms"`,
36
+ );
37
+ }
38
+
39
+ return duration;
40
+ }
6
41
 
7
42
  /**
8
43
  * Options for Docker API requests
@@ -59,7 +94,10 @@ export class DockerApi {
59
94
  * @param args Command arguments to pass to Docker CLI
60
95
  * @returns Result of the command
61
96
  */
62
- async exec(args: string[]): Promise<{ stdout: string; stderr: string }> {
97
+ async exec(
98
+ args: string[],
99
+ remainingAttempts = 3,
100
+ ): Promise<{ stdout: string; stderr: string }> {
63
101
  // If a custom config directory is provided, ensure all commands use it by
64
102
  // setting the DOCKER_CONFIG env variable for the spawned process.
65
103
  const env = this.configDir
@@ -94,8 +132,16 @@ export class DockerApi {
94
132
  try {
95
133
  await subprocess;
96
134
  } catch (error: any) {
135
+ const message = stderr || error.message || "Command failed";
136
+ if (
137
+ message.includes("unexpected status from HEAD request") &&
138
+ remainingAttempts > 0
139
+ ) {
140
+ await new Promise((resolve) => setTimeout(resolve, 1000));
141
+ return this.exec(args, remainingAttempts - 1);
142
+ }
97
143
  // Process failed, but we still have the output
98
- throw new Error(stderr || error.message || "Command failed");
144
+ throw new Error(message);
99
145
  }
100
146
 
101
147
  return { stdout, stderr };
@@ -177,6 +223,7 @@ export class DockerApi {
177
223
  env?: Record<string, string>;
178
224
  volumes?: Record<string, string>;
179
225
  cmd?: string[];
226
+ healthcheck?: HealthcheckConfig;
180
227
  } = {},
181
228
  ): Promise<string> {
182
229
  const args = ["create", "--name", name];
@@ -202,6 +249,43 @@ export class DockerApi {
202
249
  }
203
250
  }
204
251
 
252
+ // Add healthcheck configuration
253
+ if (options.healthcheck) {
254
+ const hc = options.healthcheck;
255
+
256
+ // Format the cmd
257
+ const cmdStr = Array.isArray(hc.cmd) ? hc.cmd.join(" ") : hc.cmd;
258
+ args.push("--health-cmd", cmdStr);
259
+
260
+ // Add interval
261
+ if (hc.interval !== undefined) {
262
+ args.push("--health-interval", normalizeDuration(hc.interval));
263
+ }
264
+
265
+ // Add timeout
266
+ if (hc.timeout !== undefined) {
267
+ args.push("--health-timeout", normalizeDuration(hc.timeout));
268
+ }
269
+
270
+ // Add retries
271
+ if (hc.retries !== undefined) {
272
+ args.push("--health-retries", String(hc.retries));
273
+ }
274
+
275
+ // Add start period
276
+ if (hc.startPeriod !== undefined) {
277
+ args.push("--health-start-period", normalizeDuration(hc.startPeriod));
278
+ }
279
+
280
+ // Add start interval (API 1.44+)
281
+ if (hc.startInterval !== undefined) {
282
+ args.push(
283
+ "--health-start-interval",
284
+ normalizeDuration(hc.startInterval),
285
+ );
286
+ }
287
+ }
288
+
205
289
  args.push(image);
206
290
 
207
291
  // Add command if specified
@@ -59,6 +59,62 @@ export interface NetworkMapping {
59
59
  aliases?: string[];
60
60
  }
61
61
 
62
+ /**
63
+ * Duration value supporting both number (seconds) and string format (value + unit)
64
+ * Units: ms (milliseconds), s (seconds), m (minutes), h (hours)
65
+ * Examples: 30, "30s", "1m", "500ms", "2h"
66
+ */
67
+ export type Duration = number | `${number}${"ms" | "s" | "m" | "h"}`;
68
+
69
+ /**
70
+ * Healthcheck configuration
71
+ */
72
+ export interface HealthcheckConfig {
73
+ /**
74
+ * Command to run to check health.
75
+ * Can be an array of command arguments or a shell command string.
76
+ * Examples:
77
+ * - ["curl", "-f", "http://localhost/"]
78
+ * - "curl -f http://localhost/ || exit 1"
79
+ */
80
+ cmd: string[] | string;
81
+
82
+ /**
83
+ * Time between running the check
84
+ * Can be a number (in seconds) or string with unit (e.g., "30s", "1m")
85
+ * @default 0
86
+ */
87
+ interval?: Duration;
88
+
89
+ /**
90
+ * Maximum time to allow one check to run
91
+ * Can be a number (in seconds) or string with unit (e.g., "10s", "500ms")
92
+ * @default 0
93
+ */
94
+ timeout?: Duration;
95
+
96
+ /**
97
+ * Consecutive failures needed to report unhealthy
98
+ */
99
+ retries?: number;
100
+
101
+ /**
102
+ * Start period for the container to initialize before starting
103
+ * health-retries countdown
104
+ * Can be a number (in seconds) or string with unit (e.g., "40s", "1m")
105
+ * @default 0
106
+ */
107
+ startPeriod?: Duration;
108
+
109
+ /**
110
+ * Time between running the check during the start period
111
+ * Can be a number (in seconds) or string with unit (e.g., "5s", "500ms")
112
+ * Requires Docker API 1.44+
113
+ * @default 0
114
+ */
115
+ startInterval?: Duration;
116
+ }
117
+
62
118
  /**
63
119
  * Properties for creating a Docker container
64
120
  */
@@ -115,6 +171,11 @@ export interface ContainerProps {
115
171
  * Start the container after creation
116
172
  */
117
173
  start?: boolean;
174
+
175
+ /**
176
+ * Healthcheck configuration
177
+ */
178
+ healthcheck?: HealthcheckConfig;
118
179
  }
119
180
 
120
181
  /**
@@ -172,6 +233,41 @@ export interface Container extends ContainerProps {
172
233
  * restart: "always",
173
234
  * start: true
174
235
  * });
236
+ *
237
+ * @example
238
+ * // Create a container with healthcheck using numeric values (seconds)
239
+ * const healthyContainer = await Container("api", {
240
+ * image: "my-api:latest",
241
+ * ports: [
242
+ * { external: 3000, internal: 3000 }
243
+ * ],
244
+ * healthcheck: {
245
+ * cmd: ["curl", "-f", "http://localhost:3000/health"],
246
+ * interval: 30,
247
+ * timeout: 10,
248
+ * retries: 3,
249
+ * startPeriod: 40
250
+ * },
251
+ * start: true
252
+ * });
253
+ *
254
+ * @example
255
+ * // Create a container with healthcheck using string duration format
256
+ * const healthyContainer2 = await Container("api2", {
257
+ * image: "my-api:latest",
258
+ * ports: [
259
+ * { external: 3001, internal: 3000 }
260
+ * ],
261
+ * healthcheck: {
262
+ * cmd: ["curl", "-f", "http://localhost:3000/health"],
263
+ * interval: "30s",
264
+ * timeout: "10s",
265
+ * retries: 3,
266
+ * startPeriod: "1m",
267
+ * startInterval: "500ms"
268
+ * },
269
+ * start: true
270
+ * });
175
271
  */
176
272
  export const Container = Resource(
177
273
  "docker::Container",
@@ -245,6 +341,7 @@ export const Container = Resource(
245
341
  env: props.environment,
246
342
  volumes: volumeMappings,
247
343
  cmd: props.command,
344
+ healthcheck: props.healthcheck,
248
345
  });
249
346
 
250
347
  // Connect to networks if specified
@@ -1899,6 +1899,7 @@ export type BillingAccountState =
1899
1899
  export type BillingSubscriptionType =
1900
1900
  | "UNKNOWN"
1901
1901
  | "direct_sales"
1902
+ | "direct_sales_v3"
1902
1903
  | "aws_marketplace"
1903
1904
  | "free_v2"
1904
1905
  | "free_v3"
@@ -2527,6 +2528,11 @@ export type NeonAuthCreateIntegrationRequest = {
2527
2528
  role_name?: string;
2528
2529
  };
2529
2530
 
2531
+ export type EnableNeonAuthIntegrationRequest = {
2532
+ auth_provider: NeonAuthSupportedAuthProvider;
2533
+ database_name?: string;
2534
+ };
2535
+
2530
2536
  export type NeonAuthCreateIntegrationResponse = {
2531
2537
  auth_provider: NeonAuthSupportedAuthProvider;
2532
2538
  auth_provider_project_id: string;
@@ -2677,7 +2683,12 @@ export type RoleOperations = RoleResponse & OperationsResponse;
2677
2683
 
2678
2684
  export type JwksCreationOperation = JwksResponse & OperationsResponse;
2679
2685
 
2680
- export type SupportTicketSeverity = "low" | "normal" | "high" | "critical";
2686
+ export type SupportTicketSeverity =
2687
+ | "low"
2688
+ | "normal"
2689
+ | "high"
2690
+ | "critical"
2691
+ | "urgent";
2681
2692
 
2682
2693
  export type AnnotationData = {
2683
2694
  object: AnnotationObjectData;
@@ -2758,6 +2769,7 @@ export type Snapshot = {
2758
2769
  source_branch_id?: string;
2759
2770
  created_at: string;
2760
2771
  expires_at?: string;
2772
+ manual?: boolean;
2761
2773
  };
2762
2774
 
2763
2775
  export type SnapshotUpdateRequest = {
@@ -514,6 +514,10 @@ export type PaginatedPostgresClusterResizeRequest = {
514
514
  * The SKU representing the branch cluster for display
515
515
  */
516
516
  cluster_display_name: string;
517
+ /**
518
+ * Whether or not this is a metal database
519
+ */
520
+ cluster_metal: boolean;
517
521
  /**
518
522
  * The total number of replicas
519
523
  */
@@ -532,6 +536,10 @@ export type PaginatedPostgresClusterResizeRequest = {
532
536
  * The previous SKU representing the branch cluster for display
533
537
  */
534
538
  previous_cluster_display_name: string;
539
+ /**
540
+ * Whether or not the previous SKU was a metal database
541
+ */
542
+ previous_cluster_metal: boolean;
535
543
  /**
536
544
  * The previous total number of replicas
537
545
  */
@@ -652,6 +660,10 @@ export type PostgresClusterResizeRequest = {
652
660
  * The SKU representing the branch cluster for display
653
661
  */
654
662
  cluster_display_name: string;
663
+ /**
664
+ * Whether or not this is a metal database
665
+ */
666
+ cluster_metal: boolean;
655
667
  /**
656
668
  * The total number of replicas
657
669
  */
@@ -670,6 +682,10 @@ export type PostgresClusterResizeRequest = {
670
682
  * The previous SKU representing the branch cluster for display
671
683
  */
672
684
  previous_cluster_display_name: string;
685
+ /**
686
+ * Whether or not the previous SKU was a metal database
687
+ */
688
+ previous_cluster_metal: boolean;
673
689
  /**
674
690
  * The previous total number of replicas
675
691
  */
@@ -1301,7 +1317,8 @@ export type PostgresClusterExtension = {
1301
1317
  | "milliseconds"
1302
1318
  | "seconds"
1303
1319
  | "select"
1304
- | "string";
1320
+ | "string"
1321
+ | "time";
1305
1322
  /**
1306
1323
  * The default value of the parameter
1307
1324
  */
@@ -1408,7 +1425,8 @@ export type PostgresClusterParameter = {
1408
1425
  | "milliseconds"
1409
1426
  | "seconds"
1410
1427
  | "select"
1411
- | "string";
1428
+ | "string"
1429
+ | "time";
1412
1430
  /**
1413
1431
  * The default value of the parameter
1414
1432
  */
@@ -6943,6 +6961,7 @@ export type PaginatedDatabaseWebhook = {
6943
6961
  | "branch.anomaly"
6944
6962
  | "branch.schema_recommendation"
6945
6963
  | "branch.sleeping"
6964
+ | "branch.start_maintenance"
6946
6965
  | "cluster.storage"
6947
6966
  | "database.access_request"
6948
6967
  | "deploy_request.closed"
@@ -7004,6 +7023,7 @@ export type DatabaseWebhook = {
7004
7023
  | "branch.anomaly"
7005
7024
  | "branch.schema_recommendation"
7006
7025
  | "branch.sleeping"
7026
+ | "branch.start_maintenance"
7007
7027
  | "cluster.storage"
7008
7028
  | "database.access_request"
7009
7029
  | "deploy_request.closed"
@@ -67,6 +67,7 @@ export function createPlanetScaleClient(
67
67
  headers: {
68
68
  Authorization: token, // PlanetScale does not use the Bearer prefix
69
69
  },
70
+ throwOnError: true,
70
71
  }),
71
72
  );
72
73
  client.interceptors.error.use((error, response, request) => {
@@ -1,8 +1,8 @@
1
- import { isDeepStrictEqual } from "node:util";
2
1
  import { alchemy } from "../alchemy.ts";
3
2
  import type { Context } from "../context.ts";
4
3
  import { Resource } from "../resource.ts";
5
4
  import type { Secret } from "../secret.ts";
5
+ import { diff } from "../util/diff.ts";
6
6
  import { lowercaseId } from "../util/nanoid.ts";
7
7
  import { createPlanetScaleClient, type PlanetScaleProps } from "./api.ts";
8
8
  import type { Branch } from "./branch.ts";
@@ -357,16 +357,3 @@ export const Password = Resource(
357
357
  };
358
358
  },
359
359
  );
360
-
361
- /**
362
- * Returns an array of keys in `b` that are different from `a`.
363
- */
364
- const diff = <T>(a: T, b: NoInfer<T>) => {
365
- const keys: (keyof T)[] = [];
366
- for (const key in a) {
367
- if (!isDeepStrictEqual(a[key], b[key])) {
368
- keys.push(key);
369
- }
370
- }
371
- return keys;
372
- };
package/src/resource.ts CHANGED
@@ -2,6 +2,7 @@ import { apply } from "./apply.ts";
2
2
  import type { Context } from "./context.ts";
3
3
  import { DestroyStrategy } from "./destroy.ts";
4
4
  import { Scope as _Scope, type Scope } from "./scope.ts";
5
+ import { createAndSendEvent } from "./util/telemetry.ts";
5
6
 
6
7
  declare global {
7
8
  var ALCHEMY_PROVIDERS: Map<ResourceKind, Provider<string, any>>;
@@ -178,11 +179,17 @@ export function Resource<
178
179
  const error = new Error(
179
180
  `Resource ${resourceID} already exists in the stack and is of a different type: '${otherResource?.[ResourceKind]}' !== '${type}'`,
180
181
  );
181
- scope.telemetryClient.record({
182
- event: "resource.error",
183
- resource: type,
182
+ createAndSendEvent(
183
+ {
184
+ event: "resource.error",
185
+ resource: type,
186
+ phase: scope.phase,
187
+ status: "unknown",
188
+ duration: 0,
189
+ replaced: false,
190
+ },
184
191
  error,
185
- });
192
+ );
186
193
  throw error;
187
194
  }
188
195
  }