alchemy 0.76.0 → 0.77.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 (107) hide show
  1. package/bin/alchemy.js +111 -30
  2. package/bin/services/execute-alchemy.ts +10 -1
  3. package/lib/aws/ec2/index.d.ts +1 -0
  4. package/lib/aws/ec2/index.d.ts.map +1 -1
  5. package/lib/aws/ec2/index.js +1 -0
  6. package/lib/aws/ec2/index.js.map +1 -1
  7. package/lib/aws/index.d.ts +1 -7
  8. package/lib/aws/index.d.ts.map +1 -1
  9. package/lib/aws/index.js +1 -8
  10. package/lib/aws/index.js.map +1 -1
  11. package/lib/cloudflare/auth.d.ts.map +1 -1
  12. package/lib/cloudflare/auth.js +1 -0
  13. package/lib/cloudflare/auth.js.map +1 -1
  14. package/lib/cloudflare/bucket-custom-domain.d.ts +90 -0
  15. package/lib/cloudflare/bucket-custom-domain.d.ts.map +1 -0
  16. package/lib/cloudflare/bucket-custom-domain.js +81 -0
  17. package/lib/cloudflare/bucket-custom-domain.js.map +1 -0
  18. package/lib/cloudflare/bucket.d.ts +28 -4
  19. package/lib/cloudflare/bucket.d.ts.map +1 -1
  20. package/lib/cloudflare/bucket.js +41 -14
  21. package/lib/cloudflare/bucket.js.map +1 -1
  22. package/lib/cloudflare/cloudflare-env-proxy.d.ts.map +1 -1
  23. package/lib/cloudflare/cloudflare-env-proxy.js +0 -3
  24. package/lib/cloudflare/cloudflare-env-proxy.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/index.d.ts +1 -0
  28. package/lib/cloudflare/index.d.ts.map +1 -1
  29. package/lib/cloudflare/index.js +1 -0
  30. package/lib/cloudflare/index.js.map +1 -1
  31. package/lib/cloudflare/miniflare/build-worker-options.d.ts.map +1 -1
  32. package/lib/cloudflare/miniflare/build-worker-options.js +3 -1
  33. package/lib/cloudflare/miniflare/build-worker-options.js.map +1 -1
  34. package/lib/cloudflare/miniflare/remote-binding-proxy.d.ts.map +1 -1
  35. package/lib/cloudflare/miniflare/remote-binding-proxy.js +73 -9
  36. package/lib/cloudflare/miniflare/remote-binding-proxy.js.map +1 -1
  37. package/lib/cloudflare/react-router/plugin.d.ts +2 -1
  38. package/lib/cloudflare/react-router/plugin.d.ts.map +1 -1
  39. package/lib/cloudflare/react-router/plugin.js +6 -14
  40. package/lib/cloudflare/react-router/plugin.js.map +1 -1
  41. package/lib/cloudflare/vite/plugin.d.ts.map +1 -1
  42. package/lib/cloudflare/vite/plugin.js +0 -3
  43. package/lib/cloudflare/vite/plugin.js.map +1 -1
  44. package/lib/cloudflare/worker-bundle.d.ts.map +1 -1
  45. package/lib/cloudflare/worker-bundle.js +17 -0
  46. package/lib/cloudflare/worker-bundle.js.map +1 -1
  47. package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
  48. package/lib/cloudflare/worker-metadata.js +6 -4
  49. package/lib/cloudflare/worker-metadata.js.map +1 -1
  50. package/lib/cloudflare/worker-subdomain.d.ts.map +1 -1
  51. package/lib/cloudflare/worker-subdomain.js +0 -1
  52. package/lib/cloudflare/worker-subdomain.js.map +1 -1
  53. package/lib/cloudflare/worker.d.ts.map +1 -1
  54. package/lib/cloudflare/worker.js +28 -38
  55. package/lib/cloudflare/worker.js.map +1 -1
  56. package/lib/cloudflare/wrangler.json.d.ts +2 -281
  57. package/lib/cloudflare/wrangler.json.d.ts.map +1 -1
  58. package/lib/cloudflare/wrangler.json.js +23 -11
  59. package/lib/cloudflare/wrangler.json.js.map +1 -1
  60. package/lib/encrypt.d.ts +28 -3
  61. package/lib/encrypt.d.ts.map +1 -1
  62. package/lib/encrypt.js +77 -3
  63. package/lib/encrypt.js.map +1 -1
  64. package/lib/planetscale/role.d.ts +9 -0
  65. package/lib/planetscale/role.d.ts.map +1 -1
  66. package/lib/planetscale/role.js +18 -3
  67. package/lib/planetscale/role.js.map +1 -1
  68. package/lib/prisma-postgres/api.d.ts.map +1 -1
  69. package/lib/prisma-postgres/api.js +1 -0
  70. package/lib/prisma-postgres/api.js.map +1 -1
  71. package/lib/scope.d.ts +5 -0
  72. package/lib/scope.d.ts.map +1 -1
  73. package/lib/scope.js +23 -0
  74. package/lib/scope.js.map +1 -1
  75. package/lib/serde.js +1 -1
  76. package/lib/serde.js.map +1 -1
  77. package/lib/util/telemetry.js +4 -0
  78. package/lib/util/telemetry.js.map +1 -1
  79. package/package.json +12 -10
  80. package/src/aws/ec2/index.ts +1 -0
  81. package/src/aws/index.ts +1 -9
  82. package/src/cloudflare/auth.ts +1 -0
  83. package/src/cloudflare/bucket-custom-domain.ts +281 -0
  84. package/src/cloudflare/bucket.ts +85 -18
  85. package/src/cloudflare/cloudflare-env-proxy.ts +0 -3
  86. package/src/cloudflare/compatibility-date.gen.ts +1 -1
  87. package/src/cloudflare/index.ts +1 -0
  88. package/src/cloudflare/miniflare/build-worker-options.ts +3 -1
  89. package/src/cloudflare/miniflare/remote-binding-proxy.ts +80 -9
  90. package/src/cloudflare/react-router/plugin.ts +9 -21
  91. package/src/cloudflare/vite/plugin.ts +0 -3
  92. package/src/cloudflare/worker-bundle.ts +28 -1
  93. package/src/cloudflare/worker-metadata.ts +8 -4
  94. package/src/cloudflare/worker-subdomain.ts +0 -1
  95. package/src/cloudflare/worker.ts +32 -45
  96. package/src/cloudflare/wrangler.json.ts +62 -397
  97. package/src/encrypt.ts +115 -3
  98. package/src/planetscale/role.ts +30 -3
  99. package/src/prisma-postgres/api.ts +1 -0
  100. package/src/scope.ts +27 -0
  101. package/src/serde.ts +1 -1
  102. package/src/util/telemetry.ts +5 -0
  103. package/workers/cloudflare-state-store.js +4802 -8
  104. package/workers/dofs-state-store.js +632 -2
  105. package/workers/remote-binding-proxy.js +1929 -3
  106. package/workers/tunnel-proxy.js +58 -6
  107. package/workers/tunnel-proxy.ts +1 -1
@@ -51,6 +51,12 @@ export interface RoleProps extends PlanetScaleProps {
51
51
  * @default true
52
52
  */
53
53
  delete?: boolean;
54
+
55
+ /**
56
+ * successor role
57
+ * @default postgres
58
+ */
59
+ successor?: string | Role;
54
60
  }
55
61
 
56
62
  /**
@@ -123,6 +129,11 @@ export interface Role extends Omit<RoleProps, "inheritedRoles"> {
123
129
  * The roles that this role inherits from.
124
130
  */
125
131
  inheritedRoles: InheritedRole[];
132
+
133
+ /**
134
+ * The successor role
135
+ */
136
+ successor: string;
126
137
  }
127
138
 
128
139
  /**
@@ -198,10 +209,18 @@ export const Role = Resource(
198
209
  : props.inheritedRoles.inheritedRoles;
199
210
  const shouldDelete = props.delete ?? false;
200
211
 
212
+ const successorRole =
213
+ typeof props.successor === "string"
214
+ ? props.successor
215
+ : (props.successor?.name ?? "postgres");
216
+
201
217
  switch (this.phase) {
202
218
  case "delete": {
203
219
  if (shouldDelete && this.output?.id) {
204
220
  const res = await api.deleteRole({
221
+ body: {
222
+ successor: successorRole,
223
+ },
205
224
  path: {
206
225
  organization,
207
226
  database,
@@ -275,6 +294,7 @@ export const Role = Resource(
275
294
  expiresAt: data.expires_at,
276
295
  databaseName: data.database_name,
277
296
  inheritedRoles,
297
+ successor: successorRole,
278
298
  connectionUrl: alchemy.secret(
279
299
  `postgresql://${data.username}:${data.password}@${data.access_host_url}:5432/${data.database_name}?sslmode=verify-full`,
280
300
  ),
@@ -284,9 +304,16 @@ export const Role = Resource(
284
304
  };
285
305
  }
286
306
  case "update": {
287
- // According to the types, the only property that can be updated is the name.
288
- // However, I was getting 500 errors when trying to update the name, so we'll just replace.
289
- return this.replace();
307
+ if (successorRole !== this.output.successor) {
308
+ // According to the types, the only property that can be updated is the name.
309
+ // However, I was getting 500 errors when trying to update the name, so we'll just replace.
310
+ return this.replace();
311
+ } else {
312
+ return {
313
+ ...this.output,
314
+ successor: successorRole,
315
+ };
316
+ }
290
317
  }
291
318
  }
292
319
  },
@@ -56,6 +56,7 @@ export function createPrismaApi(
56
56
  throwOnError: true,
57
57
  headers: {
58
58
  Authorization: `Bearer ${serviceToken}`,
59
+ "User-Agent": "alchemy/1.0",
59
60
  },
60
61
  }),
61
62
  );
package/src/scope.ts CHANGED
@@ -1,4 +1,7 @@
1
1
  import { AsyncLocalStorage } from "node:async_hooks";
2
+ import { spawn } from "node:child_process";
3
+ import { once } from "node:events";
4
+ import fs from "node:fs/promises";
2
5
  import util from "node:util";
3
6
  import path from "pathe";
4
7
  import pc from "picocolors";
@@ -357,6 +360,30 @@ export class Scope {
357
360
  return result.extracted as any;
358
361
  }
359
362
 
363
+ public async exec(
364
+ id: string,
365
+ command: string,
366
+ ): Promise<{ exitCode: number; stdout: string; stderr: string }> {
367
+ const logFilePath = path.join(this.dotAlchemy, "logs", `${id}.log`);
368
+ const [cmd, ...args] = command.split(/\s+/);
369
+ const child = spawn(cmd, args, { stdio: "pipe" });
370
+ await once(child, "spawn"); // throws if command not found
371
+ await fs.mkdir(path.dirname(logFilePath), { recursive: true });
372
+ const logFile = await fs.open(logFilePath, "w");
373
+ let stdout = "";
374
+ let stderr = "";
375
+ child.stdout.on("data", (chunk) => {
376
+ stdout += chunk.toString();
377
+ logFile.write(chunk);
378
+ });
379
+ child.stderr.on("data", (chunk) => {
380
+ stderr += chunk.toString();
381
+ logFile.write(chunk);
382
+ });
383
+ const [exitCode] = (await once(child, "exit")) as [number];
384
+ return { exitCode, stdout, stderr };
385
+ }
386
+
360
387
  /**
361
388
  * @internal
362
389
  */
package/src/serde.ts CHANGED
@@ -183,7 +183,7 @@ export async function deserialize(
183
183
  "See: https://alchemy.run/concepts/secret/#encryption-password",
184
184
  );
185
185
  }
186
- if (typeof value["@secret"] === "object") {
186
+ if (typeof value["@secret"] === "object" && "data" in value["@secret"]) {
187
187
  return new Secret(
188
188
  JSON.parse(
189
189
  await decryptWithKey(value["@secret"].data, scope.password),
@@ -354,6 +354,7 @@ async function fetchNoResponse(
354
354
  body?: string;
355
355
  headers?: Record<string, string>;
356
356
  method?: string;
357
+ timeout?: number;
357
358
  } = {},
358
359
  ) {
359
360
  const parsedUrl = new URL(url);
@@ -372,12 +373,16 @@ async function fetchNoResponse(
372
373
  path: parsedUrl.pathname + parsedUrl.search,
373
374
  method: options.method || "GET",
374
375
  headers,
376
+ signal: AbortSignal.timeout(options?.timeout ?? 3000),
375
377
  };
376
378
 
377
379
  await new Promise<void>((resolve, reject) => {
378
380
  const req = client.request(requestOptions);
379
381
 
380
382
  req.on("error", (error) => {
383
+ if (error.name === "AbortError" && !SUPPRESS_TELEMETRY_ERRORS) {
384
+ logger.warn("Telemetry request timed out");
385
+ }
381
386
  reject(error);
382
387
  });
383
388