alchemy 0.75.1 → 0.76.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 (33) hide show
  1. package/bin/alchemy.js +296 -281
  2. package/bin/commands/util.ts +5 -3
  3. package/lib/cloudflare/compatibility-date.gen.d.ts +1 -1
  4. package/lib/cloudflare/compatibility-date.gen.js +1 -1
  5. package/lib/cloudflare/d1-database.d.ts.map +1 -1
  6. package/lib/cloudflare/d1-database.js +0 -4
  7. package/lib/cloudflare/d1-database.js.map +1 -1
  8. package/lib/cloudflare/hyperdrive.d.ts +0 -1
  9. package/lib/cloudflare/hyperdrive.d.ts.map +1 -1
  10. package/lib/cloudflare/hyperdrive.js.map +1 -1
  11. package/lib/cloudflare/queue-consumer.d.ts.map +1 -1
  12. package/lib/cloudflare/queue-consumer.js +0 -2
  13. package/lib/cloudflare/queue-consumer.js.map +1 -1
  14. package/lib/cloudflare/worker.d.ts.map +1 -1
  15. package/lib/cloudflare/worker.js +3 -1
  16. package/lib/cloudflare/worker.js.map +1 -1
  17. package/lib/cloudflare/zone.d.ts +1 -1
  18. package/lib/cloudflare/zone.d.ts.map +1 -1
  19. package/lib/cloudflare/zone.js +3 -28
  20. package/lib/cloudflare/zone.js.map +1 -1
  21. package/lib/util/find-workspace-root.d.ts +2 -2
  22. package/lib/util/find-workspace-root.d.ts.map +1 -1
  23. package/lib/util/find-workspace-root.js +78 -48
  24. package/lib/util/find-workspace-root.js.map +1 -1
  25. package/package.json +1 -1
  26. package/src/cloudflare/compatibility-date.gen.ts +1 -1
  27. package/src/cloudflare/d1-database.ts +0 -9
  28. package/src/cloudflare/hyperdrive.ts +0 -1
  29. package/src/cloudflare/queue-consumer.ts +0 -2
  30. package/src/cloudflare/worker.ts +3 -1
  31. package/src/cloudflare/zone.ts +6 -38
  32. package/src/util/find-workspace-root.ts +93 -51
  33. package/workers/tunnel-proxy.js +1 -1
@@ -1,64 +1,94 @@
1
1
  import fs from "node:fs";
2
2
  import fsp from "node:fs/promises";
3
3
  import path from "pathe";
4
- export function findWorkspaceRootSync(dir = process.cwd()) {
5
- if (fs.statSync(dir).isDirectory()) {
6
- if (fs.existsSync(path.join(dir, ".git"))) {
7
- return dir;
8
- }
9
- else if (readSync(dir, "package.json")?.workspaces) {
10
- return dir;
11
- }
12
- else if (rootFiles.some((file) => fs.existsSync(path.join(dir, file)))) {
13
- return dir;
14
- }
4
+ import { exists } from "./exists.js";
5
+ export async function findWorkspaceRoot(directory = process.cwd()) {
6
+ const checks = await Promise.all(Object.entries(predicates).map(async ([file, predicate]) => {
7
+ return await check(path.join(directory, file), predicate);
8
+ }));
9
+ if (checks.includes(true)) {
10
+ return directory;
15
11
  }
16
- return findWorkspaceRootSync(path.resolve(dir, ".."));
12
+ const parent = path.resolve(directory, "..");
13
+ if (parent === directory) {
14
+ // Bail if we've reached the filesystem root to avoid infinite recursion
15
+ return directory;
16
+ }
17
+ return await findWorkspaceRoot(parent);
17
18
  }
18
- export async function findWorkspaceRoot(dir = process.cwd()) {
19
- if ((await fsp.stat(dir)).isDirectory()) {
20
- if (await exists(dir, ".git")) {
21
- // the root of the git repo is usually the workspace root and we should always stop here
22
- return dir;
19
+ export function findWorkspaceRootSync(directory = process.cwd()) {
20
+ for (const [file, predicate] of Object.entries(predicates)) {
21
+ const filePath = path.join(directory, file);
22
+ if (checkSync(filePath, predicate)) {
23
+ return directory;
23
24
  }
24
- else if ((await read(dir, "package.json"))?.workspaces) {
25
- // package.json with workspaces (bun, npm, etc.)
26
- return dir;
25
+ }
26
+ const parent = path.resolve(directory, "..");
27
+ if (parent === directory) {
28
+ // Bail if we've reached the filesystem root to avoid infinite recursion
29
+ return directory;
30
+ }
31
+ return findWorkspaceRootSync(parent);
32
+ }
33
+ async function check(filePath, predicate) {
34
+ if (!(await exists(filePath))) {
35
+ return false;
36
+ }
37
+ if (typeof predicate === "function") {
38
+ try {
39
+ const value = await fsp.readFile(filePath, "utf-8");
40
+ const json = JSON.parse(value);
41
+ return predicate(json);
27
42
  }
28
- else if (await anyExists(dir, ...rootFiles)) {
29
- return dir;
43
+ catch {
44
+ return false;
30
45
  }
31
46
  }
32
- return findWorkspaceRoot(path.resolve(dir, ".."));
47
+ else {
48
+ return true;
49
+ }
33
50
  }
34
- const read = (...p) => fsp
35
- .readFile(path.join(...p), "utf8")
36
- .then(JSON.parse)
37
- .catch(() => undefined);
38
- const readSync = (...p) => {
39
- try {
40
- return JSON.parse(fs.readFileSync(path.join(...p), "utf8"));
51
+ function checkSync(filePath, predicate) {
52
+ if (!fs.existsSync(filePath)) {
53
+ return false;
41
54
  }
42
- catch {
43
- return undefined;
55
+ if (typeof predicate === "function") {
56
+ try {
57
+ const value = fs.readFileSync(filePath, "utf-8");
58
+ const json = JSON.parse(value);
59
+ return predicate(json);
60
+ }
61
+ catch {
62
+ return false;
63
+ }
44
64
  }
45
- };
46
- const exists = (...p) => fsp
47
- .access(path.join(...p))
48
- .then(() => true)
49
- .catch(() => false);
50
- const anyExists = (base, ...files) => Promise.all(files.map((file) => exists(base, file))).then((results) => results.some(Boolean));
51
- const rootFiles = [
52
- // pnpm
53
- "pnpm-workspace.yaml",
54
- "pnpm-workspace.yml",
65
+ else {
66
+ return predicate;
67
+ }
68
+ }
69
+ const predicates = {
70
+ // bun
71
+ "bun.lock": true,
72
+ "bun.lockb": true,
73
+ // git
74
+ ".git": true,
55
75
  // lerna
56
- "lerna.json",
76
+ "lerna.json": true,
77
+ // npm
78
+ "package.json": (value) => "workspaces" in value,
79
+ "package-lock.json": true,
57
80
  // nx
58
- "nx.json",
59
- // turbo
60
- // "turbo.json",
81
+ "nx.json": true,
82
+ // pnpm
83
+ "pnpm-lock.yaml": true,
84
+ "pnpm-workspace.yaml": true,
85
+ "pnpm-workspace.yml": true,
61
86
  // rush
62
- "rush.json",
63
- ];
87
+ "rush.json": true,
88
+ // turbo
89
+ // (a monorepo can contain more than one turbo.json, but unless it's the root, it must contain the `extends` property)
90
+ "turbo.json": (value) => !("extends" in value),
91
+ // yarn
92
+ "yarn.lock": true,
93
+ };
64
94
  //# sourceMappingURL=find-workspace-root.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"find-workspace-root.js","sourceRoot":"","sources":["../../src/util/find-workspace-root.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"find-workspace-root.js","sourceRoot":"","sources":["../../src/util/find-workspace-root.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "alchemy",
3
- "version": "0.75.1",
3
+ "version": "0.76.0",
4
4
  "license": "Apache-2.0",
5
5
  "author": "Sam Goodwin <sam@alchemy.run>",
6
6
  "homepage": "https://alchemy.run",
@@ -4,4 +4,4 @@
4
4
  /**
5
5
  * The default Cloudflare Workers compatibility date, set based on the latest workerd release at the time of build.
6
6
  */
7
- export const DEFAULT_COMPATIBILITY_DATE = "2025-10-11";
7
+ export const DEFAULT_COMPATIBILITY_DATE = "2025-10-14";
@@ -318,7 +318,6 @@ const _D1Database = Resource(
318
318
  // after that, the ID will remain the UUID for the lifetime of the database
319
319
  !this.output?.id
320
320
  ) {
321
- logger.log("Creating D1 database:", databaseName);
322
321
  try {
323
322
  dbData = await createDatabase(api, databaseName, props);
324
323
 
@@ -354,9 +353,6 @@ const _D1Database = Resource(
354
353
 
355
354
  // Update the database with the provided properties
356
355
  if (props.readReplication) {
357
- logger.log(
358
- `Updating adopted database ${databaseName} with new properties`,
359
- );
360
356
  dbData = await updateDatabase(api, existingDb.id, props);
361
357
  }
362
358
  } else {
@@ -374,15 +370,10 @@ const _D1Database = Resource(
374
370
  `Cannot update primaryLocationHint from '${this.output.primaryLocationHint}' to '${props.primaryLocationHint}' after database creation.`,
375
371
  );
376
372
  }
377
- logger.log("Updating D1 database:", databaseName);
378
373
  // Update the database with new properties
379
374
  dbData = await updateDatabase(api, this.output.id, props);
380
375
  } else {
381
376
  // If no ID exists, fall back to creating a new database
382
- logger.log(
383
- "No existing database ID found, creating new D1 database:",
384
- databaseName,
385
- );
386
377
  dbData = await createDatabase(api, databaseName, props);
387
378
  }
388
379
 
@@ -167,7 +167,6 @@ export interface HyperdriveProps extends CloudflareApiOptions {
167
167
  /**
168
168
  * UUID of the hyperdrive (only used for update/delete operations)
169
169
  * This is provided by Cloudflare and is different from the resource ID
170
- * @internal
171
170
  */
172
171
  hyperdriveId?: string;
173
172
 
@@ -1,6 +1,5 @@
1
1
  import type { Context } from "../context.ts";
2
2
  import { Resource } from "../resource.ts";
3
- import { logger } from "../util/logger.ts";
4
3
  import { CloudflareApiError, handleApiError } from "./api-error.ts";
5
4
  import {
6
5
  createCloudflareApi,
@@ -174,7 +173,6 @@ export const QueueConsumer = Resource(
174
173
  const api = await createCloudflareApi(props);
175
174
 
176
175
  if (this.phase === "delete") {
177
- logger.log(`Deleting Queue Consumer for queue ${queueId}`);
178
176
  if (props.delete !== false && this.output?.id) {
179
177
  // Delete the consumer
180
178
  await deleteQueueConsumer(api, queueId, this.output.id);
@@ -831,7 +831,9 @@ const _Worker = Resource(
831
831
  ) {
832
832
  let adopt = props.adopt ?? this.scope.adopt;
833
833
  const workerName =
834
- props.name ?? this.output?.name ?? this.scope.createPhysicalName(id);
834
+ props.name ??
835
+ this.output?.name ??
836
+ this.scope.createPhysicalName(id).toLowerCase();
835
837
  if (this.phase === "create" && !props.adopt) {
836
838
  // it is possible that this worker already exists and was created by the old Website wrapper with a nested scope
837
839
  // we need to detect this and set adopt=true so that the previous version will be adopted seamlessly
@@ -2,6 +2,7 @@ import type { Context } from "../context.ts";
2
2
  import { Resource } from "../resource.ts";
3
3
  import { logger } from "../util/logger.ts";
4
4
  import { handleApiError } from "./api-error.ts";
5
+ import { extractCloudflareResult } from "./api-response.ts";
5
6
  import {
6
7
  createCloudflareApi,
7
8
  type CloudflareApi,
@@ -568,45 +569,12 @@ async function getZoneSettings(
568
569
  export async function getZoneByDomain(
569
570
  api: CloudflareApi,
570
571
  domainName: string,
571
- ): Promise<ZoneData | null> {
572
- const response = await api.get(
573
- `/zones?name=${encodeURIComponent(domainName)}`,
572
+ ): Promise<CloudflareZone | undefined> {
573
+ const [zone] = await extractCloudflareResult<CloudflareZone[]>(
574
+ `get zone for ${domainName}`,
575
+ api.get(`/zones?name=${encodeURIComponent(domainName)}`),
574
576
  );
575
-
576
- if (!response.ok) {
577
- throw new Error(
578
- `Error fetching zone for '${domainName}': ${response.statusText}`,
579
- );
580
- }
581
-
582
- const zones = ((await response.json()) as { result: CloudflareZone[] })
583
- .result;
584
-
585
- if (zones.length === 0) {
586
- return null;
587
- }
588
-
589
- const zoneData = zones[0];
590
-
591
- // Get zone settings
592
- const settings = await getZoneSettings(api, zoneData.id);
593
-
594
- return {
595
- id: zoneData.id,
596
- name: zoneData.name,
597
- type: zoneData.type,
598
- status: zoneData.status,
599
- paused: zoneData.paused,
600
- accountId: zoneData.account.id,
601
- nameservers: zoneData.name_servers,
602
- originalNameservers: zoneData.original_name_servers,
603
- createdAt: new Date(zoneData.created_on).getTime(),
604
- modifiedAt: new Date(zoneData.modified_on).getTime(),
605
- activatedAt: zoneData.activated_on
606
- ? new Date(zoneData.activated_on).getTime()
607
- : null,
608
- settings,
609
- };
577
+ return zone;
610
578
  }
611
579
 
612
580
  /**
@@ -1,70 +1,112 @@
1
1
  import fs from "node:fs";
2
2
  import fsp from "node:fs/promises";
3
3
  import path from "pathe";
4
+ import { exists } from "./exists.ts";
4
5
 
5
- export function findWorkspaceRootSync(dir: string = process.cwd()) {
6
- if (fs.statSync(dir).isDirectory()) {
7
- if (fs.existsSync(path.join(dir, ".git"))) {
8
- return dir;
9
- } else if (readSync(dir, "package.json")?.workspaces) {
10
- return dir;
11
- } else if (rootFiles.some((file) => fs.existsSync(path.join(dir, file)))) {
12
- return dir;
6
+ export async function findWorkspaceRoot(
7
+ directory: string = process.cwd(),
8
+ ): Promise<string> {
9
+ const checks = await Promise.all(
10
+ Object.entries(predicates).map(async ([file, predicate]) => {
11
+ return await check(path.join(directory, file), predicate);
12
+ }),
13
+ );
14
+ if (checks.includes(true)) {
15
+ return directory;
16
+ }
17
+ const parent = path.resolve(directory, "..");
18
+ if (parent === directory) {
19
+ // Bail if we've reached the filesystem root to avoid infinite recursion
20
+ return directory;
21
+ }
22
+ return await findWorkspaceRoot(parent);
23
+ }
24
+
25
+ export function findWorkspaceRootSync(
26
+ directory: string = process.cwd(),
27
+ ): string {
28
+ for (const [file, predicate] of Object.entries(predicates)) {
29
+ const filePath = path.join(directory, file);
30
+ if (checkSync(filePath, predicate)) {
31
+ return directory;
13
32
  }
14
33
  }
15
- return findWorkspaceRootSync(path.resolve(dir, ".."));
34
+ const parent = path.resolve(directory, "..");
35
+ if (parent === directory) {
36
+ // Bail if we've reached the filesystem root to avoid infinite recursion
37
+ return directory;
38
+ }
39
+ return findWorkspaceRootSync(parent);
16
40
  }
17
41
 
18
- export async function findWorkspaceRoot(dir: string = process.cwd()) {
19
- if ((await fsp.stat(dir)).isDirectory()) {
20
- if (await exists(dir, ".git")) {
21
- // the root of the git repo is usually the workspace root and we should always stop here
22
- return dir;
23
- } else if ((await read(dir, "package.json"))?.workspaces) {
24
- // package.json with workspaces (bun, npm, etc.)
25
- return dir;
26
- } else if (await anyExists(dir, ...rootFiles)) {
27
- return dir;
42
+ async function check(filePath: string, predicate: Predicate) {
43
+ if (!(await exists(filePath))) {
44
+ return false;
45
+ }
46
+
47
+ if (typeof predicate === "function") {
48
+ try {
49
+ const value = await fsp.readFile(filePath, "utf-8");
50
+ const json = JSON.parse(value);
51
+ return predicate(json);
52
+ } catch {
53
+ return false;
28
54
  }
55
+ } else {
56
+ return true;
29
57
  }
30
- return findWorkspaceRoot(path.resolve(dir, ".."));
31
58
  }
32
59
 
33
- const read = (...p: string[]): Promise<any> =>
34
- fsp
35
- .readFile(path.join(...p), "utf8")
36
- .then(JSON.parse)
37
- .catch(() => undefined);
38
-
39
- const readSync = (...p: string[]): any => {
40
- try {
41
- return JSON.parse(fs.readFileSync(path.join(...p), "utf8"));
42
- } catch {
43
- return undefined;
60
+ function checkSync(filePath: string, predicate: Predicate) {
61
+ if (!fs.existsSync(filePath)) {
62
+ return false;
44
63
  }
45
- };
46
64
 
47
- const exists = (...p: string[]) =>
48
- fsp
49
- .access(path.join(...p))
50
- .then(() => true)
51
- .catch(() => false);
65
+ if (typeof predicate === "function") {
66
+ try {
67
+ const value = fs.readFileSync(filePath, "utf-8");
68
+ const json = JSON.parse(value);
69
+ return predicate(json);
70
+ } catch {
71
+ return false;
72
+ }
73
+ } else {
74
+ return predicate;
75
+ }
76
+ }
52
77
 
53
- const anyExists = (base: string, ...files: string[]) =>
54
- Promise.all(files.map((file) => exists(base, file))).then((results) =>
55
- results.some(Boolean),
56
- );
78
+ type Predicate = true | ((value: Record<string, unknown>) => boolean);
79
+
80
+ const predicates: Record<string, Predicate> = {
81
+ // bun
82
+ "bun.lock": true,
83
+ "bun.lockb": true,
84
+
85
+ // git
86
+ ".git": true,
57
87
 
58
- const rootFiles = [
59
- // pnpm
60
- "pnpm-workspace.yaml",
61
- "pnpm-workspace.yml",
62
88
  // lerna
63
- "lerna.json",
89
+ "lerna.json": true,
90
+
91
+ // npm
92
+ "package.json": (value) => "workspaces" in value,
93
+ "package-lock.json": true,
94
+
64
95
  // nx
65
- "nx.json",
66
- // turbo
67
- // "turbo.json",
96
+ "nx.json": true,
97
+
98
+ // pnpm
99
+ "pnpm-lock.yaml": true,
100
+ "pnpm-workspace.yaml": true,
101
+ "pnpm-workspace.yml": true,
102
+
68
103
  // rush
69
- "rush.json",
70
- ];
104
+ "rush.json": true,
105
+
106
+ // turbo
107
+ // (a monorepo can contain more than one turbo.json, but unless it's the root, it must contain the `extends` property)
108
+ "turbo.json": (value) => !("extends" in value),
109
+
110
+ // yarn
111
+ "yarn.lock": true,
112
+ };
@@ -34,7 +34,7 @@ var h={async fetch(e,r){let n=new URL(e.url);n.host=r.TUNNEL_HOST;let l=new Head
34
34
  Alchemy</a>.</p>
35
35
  </div>
36
36
  <div class="bg-slate-200 px-5 py-3 flex flex-col w-full max-w-lg">
37
- <p class="text-sm text-slate-500">Alchemy 0.75.1</p>
37
+ <p class="text-sm text-slate-500">Alchemy 0.76.0</p>
38
38
  </div>
39
39
  </div>
40
40
  </body>