alchemy 0.46.1 → 0.48.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 (78) hide show
  1. package/bin/alchemy.mjs +560 -98
  2. package/lib/alchemy.d.ts +9 -3
  3. package/lib/alchemy.d.ts.map +1 -1
  4. package/lib/alchemy.js +19 -39
  5. package/lib/alchemy.js.map +1 -1
  6. package/lib/build-date.d.ts +1 -1
  7. package/lib/build-date.js +1 -1
  8. package/lib/cloudflare/astro.d.ts.map +1 -1
  9. package/lib/cloudflare/astro.js +3 -0
  10. package/lib/cloudflare/astro.js.map +1 -1
  11. package/lib/cloudflare/container.js +1 -1
  12. package/lib/cloudflare/container.js.map +1 -1
  13. package/lib/cloudflare/nuxt.d.ts.map +1 -1
  14. package/lib/cloudflare/nuxt.js +3 -0
  15. package/lib/cloudflare/nuxt.js.map +1 -1
  16. package/lib/cloudflare/oauth.d.ts.map +1 -1
  17. package/lib/cloudflare/oauth.js +4 -3
  18. package/lib/cloudflare/oauth.js.map +1 -1
  19. package/lib/cloudflare/pipeline.d.ts.map +1 -1
  20. package/lib/cloudflare/pipeline.js +1 -3
  21. package/lib/cloudflare/pipeline.js.map +1 -1
  22. package/lib/cloudflare/react-router.d.ts.map +1 -1
  23. package/lib/cloudflare/react-router.js +3 -0
  24. package/lib/cloudflare/react-router.js.map +1 -1
  25. package/lib/cloudflare/vite.d.ts.map +1 -1
  26. package/lib/cloudflare/vite.js +0 -1
  27. package/lib/cloudflare/vite.js.map +1 -1
  28. package/lib/cloudflare/website.d.ts +0 -1
  29. package/lib/cloudflare/website.d.ts.map +1 -1
  30. package/lib/cloudflare/website.js +13 -7
  31. package/lib/cloudflare/website.js.map +1 -1
  32. package/lib/cloudflare/worker-metadata.d.ts +6 -0
  33. package/lib/cloudflare/worker-metadata.d.ts.map +1 -1
  34. package/lib/cloudflare/worker-metadata.js +1 -0
  35. package/lib/cloudflare/worker-metadata.js.map +1 -1
  36. package/lib/cloudflare/worker.d.ts +33 -3
  37. package/lib/cloudflare/worker.d.ts.map +1 -1
  38. package/lib/cloudflare/worker.js +91 -70
  39. package/lib/cloudflare/worker.js.map +1 -1
  40. package/lib/cloudflare/zone.d.ts +1 -1
  41. package/lib/cloudflare/zone.d.ts.map +1 -1
  42. package/lib/cloudflare/zone.js +38 -56
  43. package/lib/cloudflare/zone.js.map +1 -1
  44. package/lib/scope.d.ts +14 -2
  45. package/lib/scope.d.ts.map +1 -1
  46. package/lib/scope.js +8 -3
  47. package/lib/scope.js.map +1 -1
  48. package/lib/util/safe-fetch.d.ts.map +1 -1
  49. package/lib/util/safe-fetch.js +1 -0
  50. package/lib/util/safe-fetch.js.map +1 -1
  51. package/lib/util/telemetry/constants.js +2 -2
  52. package/lib/util/telemetry/constants.js.map +1 -1
  53. package/package.json +4 -2
  54. package/src/alchemy.ts +28 -49
  55. package/src/build-date.ts +1 -1
  56. package/src/cloudflare/astro.ts +3 -0
  57. package/src/cloudflare/container.ts +1 -1
  58. package/src/cloudflare/nuxt.ts +3 -0
  59. package/src/cloudflare/oauth.ts +5 -4
  60. package/src/cloudflare/pipeline.ts +1 -3
  61. package/src/cloudflare/react-router.ts +3 -0
  62. package/src/cloudflare/vite.ts +0 -1
  63. package/src/cloudflare/website.ts +14 -8
  64. package/src/cloudflare/worker-metadata.ts +7 -0
  65. package/src/cloudflare/worker.ts +138 -102
  66. package/src/cloudflare/zone.ts +50 -70
  67. package/src/scope.ts +21 -4
  68. package/src/util/safe-fetch.ts +1 -0
  69. package/src/util/telemetry/constants.ts +2 -2
  70. package/templates/react-router/package.json +1 -1
  71. package/templates/react-router/tsconfig.cloudflare.json +5 -1
  72. package/templates/react-router/tsconfig.json +0 -3
  73. package/templates/react-router/tsconfig.node.json +0 -4
  74. package/lib/util/xdg-paths.d.ts +0 -9
  75. package/lib/util/xdg-paths.d.ts.map +0 -1
  76. package/lib/util/xdg-paths.js +0 -41
  77. package/lib/util/xdg-paths.js.map +0 -1
  78. package/src/util/xdg-paths.ts +0 -48
@@ -74,7 +74,7 @@ export interface ZoneProps extends CloudflareApiOptions {
74
74
  /**
75
75
  * Enable Always Use HTTPS
76
76
  * Redirects all HTTP traffic to HTTPS
77
- * @default "off"
77
+ * @default "on" (unless explicitly set to "off")
78
78
  */
79
79
  alwaysUseHttps?: AlwaysUseHTTPSValue;
80
80
 
@@ -339,6 +339,7 @@ export const Zone = Resource(
339
339
  return this.destroy();
340
340
  }
341
341
 
342
+ let zoneData: CloudflareZone;
342
343
  if (this.phase === "update" && this.output?.id) {
343
344
  // Get zone details to verify it exists
344
345
  const response = await api.get(`/zones/${this.output.id}`);
@@ -349,84 +350,63 @@ export const Zone = Resource(
349
350
  );
350
351
  }
351
352
 
352
- const zoneData = ((await response.json()) as { result: CloudflareZone })
353
- .result;
354
-
355
- // Update zone settings if provided
356
- if (props.settings) {
357
- await updateZoneSettings(api, this.output.id, props.settings);
358
- // Add a small delay to ensure settings are propagated
359
- await new Promise((resolve) => setTimeout(resolve, 2000));
360
- }
361
-
362
- return this({
363
- id: zoneData.id,
364
- name: zoneData.name,
365
- type: zoneData.type,
366
- status: zoneData.status,
367
- paused: zoneData.paused,
368
- accountId: zoneData.account.id,
369
- nameservers: zoneData.name_servers,
370
- originalNameservers: zoneData.original_name_servers,
371
- createdAt: new Date(zoneData.created_on).getTime(),
372
- modifiedAt: new Date(zoneData.modified_on).getTime(),
373
- activatedAt: zoneData.activated_on
374
- ? new Date(zoneData.activated_on).getTime()
375
- : null,
376
- settings: await getZoneSettings(api, zoneData.id),
353
+ zoneData = ((await response.json()) as { result: CloudflareZone }).result;
354
+ } else {
355
+ const response = await api.post("/zones", {
356
+ name: props.name,
357
+ type: props.type || "full",
358
+ jump_start: props.jumpStart !== false,
359
+ account: {
360
+ id: api.accountId,
361
+ },
377
362
  });
378
- }
379
- // Create new zone
380
-
381
- const response = await api.post("/zones", {
382
- name: props.name,
383
- type: props.type || "full",
384
- jump_start: props.jumpStart !== false,
385
- account: {
386
- id: api.accountId,
387
- },
388
- });
389
-
390
- const body = await response.text();
391
- let zoneData;
392
- if (!response.ok) {
393
- if (response.status === 400 && body.includes("already exists")) {
394
- // Zone already exists, fetch it instead
395
- logger.warn(
396
- `Zone '${props.name}' already exists during Zone create, adopting it...`,
397
- );
398
- const getResponse = await api.get(`/zones?name=${props.name}`);
399
363
 
400
- if (!getResponse.ok) {
401
- throw new Error(
402
- `Error fetching existing zone '${props.name}': ${getResponse.statusText}`,
364
+ const body = await response.text();
365
+ if (!response.ok) {
366
+ if (response.status === 400 && body.includes("already exists")) {
367
+ // Zone already exists, fetch it instead
368
+ logger.warn(
369
+ `Zone '${props.name}' already exists during Zone create, adopting it...`,
403
370
  );
404
- }
371
+ const getResponse = await api.get(`/zones?name=${props.name}`);
405
372
 
406
- const zones = (
407
- (await getResponse.json()) as { result: CloudflareZone[] }
408
- ).result;
409
- if (zones.length === 0) {
373
+ if (!getResponse.ok) {
374
+ throw new Error(
375
+ `Error fetching existing zone '${props.name}': ${getResponse.statusText}`,
376
+ );
377
+ }
378
+
379
+ const zones = (
380
+ (await getResponse.json()) as { result: CloudflareZone[] }
381
+ ).result;
382
+ if (zones.length === 0) {
383
+ throw new Error(
384
+ `Zone '${props.name}' does not exist, but the name is reserved for another user.`,
385
+ );
386
+ }
387
+ zoneData = zones[0];
388
+ } else {
410
389
  throw new Error(
411
- `Zone '${props.name}' does not exist, but the name is reserved for another user.`,
390
+ `Error creating zone '${props.name}': ${response.statusText}\n${body}`,
412
391
  );
413
392
  }
414
- zoneData = zones[0];
415
393
  } else {
416
- throw new Error(
417
- `Error creating zone '${props.name}': ${response.statusText}\n${body}`,
418
- );
394
+ zoneData = (JSON.parse(body) as { result: CloudflareZone }).result;
419
395
  }
420
- } else {
421
- zoneData = (JSON.parse(body) as { result: CloudflareZone }).result;
422
396
  }
423
397
 
424
- // Update zone settings if provided
425
- if (props.settings) {
426
- await updateZoneSettings(api, zoneData.id, props.settings);
427
- // Add a small delay to ensure settings are propagated
428
- await new Promise((resolve) => setTimeout(resolve, 2000));
429
- }
398
+ // Apply defaults to settings
399
+ const settingsToApply = {
400
+ ...props.settings,
401
+ alwaysUseHttps: props.settings?.alwaysUseHttps ?? "on",
402
+ };
403
+
404
+ await updateZoneSettings(api, zoneData.id, settingsToApply);
405
+
406
+ // Add a small delay to ensure settings are propagated
407
+ // TODO(michael): do we need this?
408
+ // https://github.com/sam-goodwin/alchemy/issues/681
409
+ await new Promise((resolve) => setTimeout(resolve, 2000));
430
410
 
431
411
  return this({
432
412
  id: zoneData.id,
@@ -451,7 +431,7 @@ export const Zone = Resource(
451
431
  * Helper function to update zone settings
452
432
  */
453
433
  async function updateZoneSettings(
454
- api: any,
434
+ api: CloudflareApi,
455
435
  zoneId: string,
456
436
  settings: ZoneProps["settings"],
457
437
  ): Promise<void> {
@@ -508,7 +488,7 @@ async function updateZoneSettings(
508
488
  * Helper function to get current zone settings
509
489
  */
510
490
  async function getZoneSettings(
511
- api: any,
491
+ api: CloudflareApi,
512
492
  zoneId: string,
513
493
  ): Promise<Zone["settings"]> {
514
494
  const settingsResponse = await api.get(`/zones/${zoneId}/settings`);
package/src/scope.ts CHANGED
@@ -38,7 +38,18 @@ export interface ScopeOptions {
38
38
  stateStore?: StateStoreType;
39
39
  quiet?: boolean;
40
40
  phase?: Phase;
41
- dev?: "prefer-local" | "prefer-remote";
41
+ /**
42
+ * Determines if resources should be simulated locally (where possible)
43
+ *
44
+ * @default - `true` if ran with `alchemy dev` or `bun ./alchemy.run.ts --dev`
45
+ */
46
+ local?: boolean;
47
+ /**
48
+ * Determines if local changes to resources should be reactively pushed to the local or remote environment.
49
+ *
50
+ * @default - `true` if ran with `alchemy dev`, `alchemy watch`, `bun --watch ./alchemy.run.ts`
51
+ */
52
+ watch?: boolean;
42
53
  telemetryClient?: ITelemetryClient;
43
54
  logger?: LoggerApi;
44
55
  }
@@ -107,7 +118,8 @@ export class Scope {
107
118
  public readonly stateStore: StateStoreType;
108
119
  public readonly quiet: boolean;
109
120
  public readonly phase: Phase;
110
- public readonly dev?: "prefer-local" | "prefer-remote";
121
+ public readonly local: boolean;
122
+ public readonly watch: boolean;
111
123
  public readonly logger: LoggerApi;
112
124
  public readonly telemetryClient: ITelemetryClient;
113
125
  public readonly dataMutex: AsyncMutex;
@@ -162,9 +174,10 @@ export class Scope {
162
174
  options.logger,
163
175
  );
164
176
 
165
- this.dev = options.dev ?? this.parent?.dev;
177
+ this.local = options.local ?? this.parent?.local ?? false;
178
+ this.watch = options.watch ?? this.parent?.watch ?? false;
166
179
 
167
- if (this.dev) {
180
+ if (this.local) {
168
181
  this.logger.warnOnce(
169
182
  "Development mode is in beta. Please report any issues to https://github.com/sam-goodwin/alchemy/issues.",
170
183
  );
@@ -415,6 +428,10 @@ export class Scope {
415
428
  await this.rootTelemetryClient?.finalize()?.catch((error) => {
416
429
  this.logger.warn("Telemetry finalization failed:", error);
417
430
  });
431
+
432
+ if (!this.parent && process.env.ALCHEMY_TEST_KILL_ON_FINALIZE) {
433
+ process.exit(0);
434
+ }
418
435
  }
419
436
 
420
437
  public async destroyPendingDeletions() {
@@ -20,6 +20,7 @@ export async function safeFetch(
20
20
  err?.code === "UND_ERR_SOCKET" ||
21
21
  err?.code === "ECONNRESET" ||
22
22
  err?.code === "UND_ERR_CONNECT_TIMEOUT" ||
23
+ err?.code === "EPIPE" ||
23
24
  err?.name === "FetchError";
24
25
 
25
26
  if (!shouldRetry || attempt === retries) {
@@ -1,6 +1,6 @@
1
- import { createXdgAppPaths } from "../xdg-paths.js";
1
+ import xdgAppPaths from "xdg-app-paths";
2
2
 
3
- const xdg = createXdgAppPaths("alchemy");
3
+ const xdg = xdgAppPaths("alchemy");
4
4
 
5
5
  export const CONFIG_DIR = xdg.config();
6
6
 
@@ -3,7 +3,7 @@
3
3
  "name": "@alchemy.run/react-router-template",
4
4
  "private": true,
5
5
  "scripts": {
6
- "build": "vite build",
6
+ "build": "react-router build",
7
7
  "deploy": "alchemy deploy",
8
8
  "destroy": "alchemy destroy",
9
9
  "dev": "alchemy dev",
@@ -11,7 +11,11 @@
11
11
  "composite": true,
12
12
  "strict": true,
13
13
  "lib": ["DOM", "DOM.Iterable", "ES2022"],
14
- "types": ["vite/client"],
14
+ "types": [
15
+ "vite/client",
16
+ "@cloudflare/workers-types",
17
+ "./types/env.d.ts"
18
+ ],
15
19
  "target": "ES2022",
16
20
  "module": "ES2022",
17
21
  "moduleResolution": "bundler",
@@ -10,8 +10,5 @@
10
10
  "skipLibCheck": true,
11
11
  "strict": true,
12
12
  "noEmit": true,
13
- "types": [
14
- "./worker-configuration.d.ts"
15
- ]
16
13
  }
17
14
  }
@@ -8,10 +8,6 @@
8
8
  "compilerOptions": {
9
9
  "composite": true,
10
10
  "strict": true,
11
- "types": [
12
- "@cloudflare/workers-types",
13
- "./types/env.d.ts"
14
- ],
15
11
  "lib": ["ES2022"],
16
12
  "target": "ES2022",
17
13
  "module": "ES2022",
@@ -1,9 +0,0 @@
1
- /**
2
- * Vendored XDG Base Directory Specification implementation
3
- * Provides cross-platform directory paths for config and state
4
- */
5
- export declare function createXdgAppPaths(appName: string): {
6
- config: () => string;
7
- state: () => string;
8
- };
9
- //# sourceMappingURL=xdg-paths.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"xdg-paths.d.ts","sourceRoot":"","sources":["../../src/util/xdg-paths.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM;kBAI5B,MAAM;iBAgBP,MAAM;EAoBzB"}
@@ -1,41 +0,0 @@
1
- import { join } from "node:path";
2
- import { homedir } from "node:os";
3
- /**
4
- * Vendored XDG Base Directory Specification implementation
5
- * Provides cross-platform directory paths for config and state
6
- */
7
- export function createXdgAppPaths(appName) {
8
- const platform = process.platform;
9
- const home = homedir();
10
- function config() {
11
- if (platform === "win32") {
12
- return join(process.env.LOCALAPPDATA || join(home, "AppData", "Local"), appName);
13
- }
14
- else if (platform === "darwin") {
15
- return join(home, "Library", "Preferences", appName);
16
- }
17
- else {
18
- // Linux/Unix - follow XDG Base Directory Specification
19
- const xdgConfigHome = process.env.XDG_CONFIG_HOME || join(home, ".config");
20
- return join(xdgConfigHome, appName);
21
- }
22
- }
23
- function state() {
24
- if (platform === "win32") {
25
- return join(process.env.LOCALAPPDATA || join(home, "AppData", "Local"), appName);
26
- }
27
- else if (platform === "darwin") {
28
- return join(home, "Library", "Application Support", appName);
29
- }
30
- else {
31
- // Linux/Unix - follow XDG Base Directory Specification
32
- const xdgStateHome = process.env.XDG_STATE_HOME || join(home, ".local", "state");
33
- return join(xdgStateHome, appName);
34
- }
35
- }
36
- return {
37
- config,
38
- state,
39
- };
40
- }
41
- //# sourceMappingURL=xdg-paths.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"xdg-paths.js","sourceRoot":"","sources":["../../src/util/xdg-paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,OAAe;IAC/C,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAClC,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IAEvB,SAAS,MAAM;QACb,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;YACzB,OAAO,IAAI,CACT,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAC1D,OAAO,CACR,CAAC;QACJ,CAAC;aAAM,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACjC,OAAO,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,aAAa,EAAE,OAAO,CAAC,CAAC;QACvD,CAAC;aAAM,CAAC;YACN,uDAAuD;YACvD,MAAM,aAAa,GACjB,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;YACvD,OAAO,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;IAED,SAAS,KAAK;QACZ,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;YACzB,OAAO,IAAI,CACT,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAC1D,OAAO,CACR,CAAC;QACJ,CAAC;aAAM,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;YACjC,OAAO,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,qBAAqB,EAAE,OAAO,CAAC,CAAC;QAC/D,CAAC;aAAM,CAAC;YACN,uDAAuD;YACvD,MAAM,YAAY,GAChB,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;YAC9D,OAAO,IAAI,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QACrC,CAAC;IACH,CAAC;IAED,OAAO;QACL,MAAM;QACN,KAAK;KACN,CAAC;AACJ,CAAC"}
@@ -1,48 +0,0 @@
1
- import { join } from "node:path";
2
- import { homedir } from "node:os";
3
-
4
- /**
5
- * Vendored XDG Base Directory Specification implementation
6
- * Provides cross-platform directory paths for config and state
7
- */
8
- export function createXdgAppPaths(appName: string) {
9
- const platform = process.platform;
10
- const home = homedir();
11
-
12
- function config(): string {
13
- if (platform === "win32") {
14
- return join(
15
- process.env.LOCALAPPDATA || join(home, "AppData", "Local"),
16
- appName,
17
- );
18
- } else if (platform === "darwin") {
19
- return join(home, "Library", "Preferences", appName);
20
- } else {
21
- // Linux/Unix - follow XDG Base Directory Specification
22
- const xdgConfigHome =
23
- process.env.XDG_CONFIG_HOME || join(home, ".config");
24
- return join(xdgConfigHome, appName);
25
- }
26
- }
27
-
28
- function state(): string {
29
- if (platform === "win32") {
30
- return join(
31
- process.env.LOCALAPPDATA || join(home, "AppData", "Local"),
32
- appName,
33
- );
34
- } else if (platform === "darwin") {
35
- return join(home, "Library", "Application Support", appName);
36
- } else {
37
- // Linux/Unix - follow XDG Base Directory Specification
38
- const xdgStateHome =
39
- process.env.XDG_STATE_HOME || join(home, ".local", "state");
40
- return join(xdgStateHome, appName);
41
- }
42
- }
43
-
44
- return {
45
- config,
46
- state,
47
- };
48
- }