alchemy 0.85.0 → 0.85.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 (66) hide show
  1. package/bin/alchemy.js +32 -23
  2. package/lib/cloudflare/account-api-token.d.ts +5 -0
  3. package/lib/cloudflare/account-api-token.d.ts.map +1 -1
  4. package/lib/cloudflare/account-api-token.js +13 -2
  5. package/lib/cloudflare/account-api-token.js.map +1 -1
  6. package/lib/cloudflare/ai-crawler.d.ts +27 -0
  7. package/lib/cloudflare/ai-crawler.d.ts.map +1 -0
  8. package/lib/cloudflare/ai-crawler.js +75 -0
  9. package/lib/cloudflare/ai-crawler.js.map +1 -0
  10. package/lib/cloudflare/ai-search-token.d.ts +150 -0
  11. package/lib/cloudflare/ai-search-token.d.ts.map +1 -0
  12. package/lib/cloudflare/ai-search-token.js +171 -0
  13. package/lib/cloudflare/ai-search-token.js.map +1 -0
  14. package/lib/cloudflare/ai-search.d.ts +397 -0
  15. package/lib/cloudflare/ai-search.d.ts.map +1 -0
  16. package/lib/cloudflare/ai-search.js +307 -0
  17. package/lib/cloudflare/ai-search.js.map +1 -0
  18. package/lib/cloudflare/auth.d.ts +3 -2
  19. package/lib/cloudflare/auth.d.ts.map +1 -1
  20. package/lib/cloudflare/auth.js +17 -14
  21. package/lib/cloudflare/auth.js.map +1 -1
  22. package/lib/cloudflare/bucket-custom-domain.d.ts.map +1 -1
  23. package/lib/cloudflare/bucket-custom-domain.js +4 -2
  24. package/lib/cloudflare/bucket-custom-domain.js.map +1 -1
  25. package/lib/cloudflare/bucket.d.ts.map +1 -1
  26. package/lib/cloudflare/bucket.js +8 -2
  27. package/lib/cloudflare/bucket.js.map +1 -1
  28. package/lib/cloudflare/index.d.ts +3 -0
  29. package/lib/cloudflare/index.d.ts.map +1 -1
  30. package/lib/cloudflare/index.js +3 -0
  31. package/lib/cloudflare/index.js.map +1 -1
  32. package/lib/cloudflare/permission-groups.d.ts +5 -0
  33. package/lib/cloudflare/permission-groups.d.ts.map +1 -1
  34. package/lib/cloudflare/permission-groups.js +6 -0
  35. package/lib/cloudflare/permission-groups.js.map +1 -1
  36. package/lib/scope.d.ts +1 -1
  37. package/lib/scope.d.ts.map +1 -1
  38. package/lib/scope.js +10 -5
  39. package/lib/scope.js.map +1 -1
  40. package/lib/test/bun.d.ts +17 -0
  41. package/lib/test/bun.d.ts.map +1 -1
  42. package/lib/test/bun.js +1 -0
  43. package/lib/test/bun.js.map +1 -1
  44. package/lib/test/vitest.d.ts +17 -0
  45. package/lib/test/vitest.d.ts.map +1 -1
  46. package/lib/test/vitest.js +3 -0
  47. package/lib/test/vitest.js.map +1 -1
  48. package/lib/util/snake-to-camel.d.ts +21 -0
  49. package/lib/util/snake-to-camel.d.ts.map +1 -0
  50. package/lib/util/snake-to-camel.js +34 -0
  51. package/lib/util/snake-to-camel.js.map +1 -0
  52. package/package.json +1 -1
  53. package/src/cloudflare/account-api-token.ts +23 -6
  54. package/src/cloudflare/ai-crawler.ts +89 -0
  55. package/src/cloudflare/ai-search-token.ts +324 -0
  56. package/src/cloudflare/ai-search.ts +952 -0
  57. package/src/cloudflare/auth.ts +17 -14
  58. package/src/cloudflare/bucket-custom-domain.ts +4 -1
  59. package/src/cloudflare/bucket.ts +9 -2
  60. package/src/cloudflare/index.ts +3 -0
  61. package/src/cloudflare/permission-groups.ts +6 -0
  62. package/src/scope.ts +14 -5
  63. package/src/test/bun.ts +25 -0
  64. package/src/test/vitest.ts +33 -0
  65. package/src/util/snake-to-camel.ts +59 -0
  66. package/workers/tunnel-proxy.js +1 -1
@@ -75,8 +75,9 @@ export namespace CloudflareAuth {
75
75
  "queues:write": "See and change Cloudflare Queues settings and data",
76
76
  "r2_catalog:write": "Grants write level access to R2 Data Catalog",
77
77
  "radar:read": "Grants access to read Cloudflare Radar data",
78
- "rag:read": "Grants read level access to Auto Rag",
79
- "rag:write": "Grants write level access to Auto Rag",
78
+ "ai-search:read": "Grants read level access to AI Search",
79
+ "ai-search:write": "Grants write level access to AI Search",
80
+ "ai-search:run": "Grants run level access to AI Search",
80
81
  "secrets_store:read": "Grants read level access to Secrets Store",
81
82
  "secrets_store:write": "Grants write level access to Secrets Store",
82
83
  "ssl_certs:write":
@@ -120,24 +121,26 @@ export namespace CloudflareAuth {
120
121
  export type Scope = keyof typeof ALL_SCOPES;
121
122
  export const DEFAULT_SCOPES = [
122
123
  "account:read",
124
+ "ai-search:write",
125
+ "ai-search:run",
126
+ "ai:write",
127
+ "cloudchamber:write",
128
+ "connectivity:admin",
129
+ "containers:write",
130
+ "d1:write",
131
+ "pages:write",
132
+ "pipelines:write",
133
+ "queues:write",
134
+ "secrets_store:write",
135
+ "ssl_certs:write",
123
136
  "user:read",
124
- "workers:write",
137
+ "vectorize:write",
125
138
  "workers_kv:write",
126
139
  "workers_routes:write",
127
140
  "workers_scripts:write",
128
141
  "workers_tail:read",
129
- "d1:write",
130
- "pages:write",
142
+ "workers:write",
131
143
  "zone:read",
132
- "ssl_certs:write",
133
- "ai:write",
134
- "queues:write",
135
- "pipelines:write",
136
- "secrets_store:write",
137
- "containers:write",
138
- "cloudchamber:write",
139
- "vectorize:write",
140
- "connectivity:admin",
141
144
  ];
142
145
 
143
146
  /**
@@ -100,6 +100,8 @@ export type R2BucketCustomDomain = {
100
100
  minTLS?: "1.0" | "1.1" | "1.2" | "1.3" | (string & {});
101
101
  };
102
102
 
103
+ const DEV_NOOP_ZONE = "noop-zone";
104
+
103
105
  export const R2BucketCustomDomain = Resource(
104
106
  "cloudflare::R2BucketCustomDomain",
105
107
  async function (
@@ -114,7 +116,7 @@ export const R2BucketCustomDomain = Resource(
114
116
  zoneId:
115
117
  typeof props.zone === "string"
116
118
  ? props.zone
117
- : (props.zone?.id ?? "noop-zone"),
119
+ : (props.zone?.id ?? DEV_NOOP_ZONE),
118
120
  ciphers: props.ciphers,
119
121
  minTLS: props.minTLS,
120
122
  };
@@ -158,6 +160,7 @@ export const R2BucketCustomDomain = Resource(
158
160
  }
159
161
  case "update": {
160
162
  if (
163
+ this.output.zoneId !== DEV_NOOP_ZONE && // dev mode placeholder should not trigger replacement
161
164
  diff(this.output, payload).some(
162
165
  (property) => property === "domain" || property === "zoneId",
163
166
  )
@@ -689,6 +689,13 @@ const _R2Bucket = Resource(
689
689
 
690
690
  if (this.phase === "delete") {
691
691
  if (props.delete !== false) {
692
+ // Use bucket's actual jurisdiction from output, falling back to props
693
+ // This ensures EU/FedRAMP buckets are correctly addressed during cleanup
694
+ const deleteProps: BucketProps = {
695
+ ...props,
696
+ jurisdiction: this.output?.jurisdiction ?? props.jurisdiction,
697
+ };
698
+
692
699
  if (this.output?.catalog) {
693
700
  await disableDataCatalog(api, bucketName);
694
701
  }
@@ -696,9 +703,9 @@ const _R2Bucket = Resource(
696
703
  await deleteMiniflareBinding(this.scope, "r2", this.output.dev.id);
697
704
  }
698
705
  if (props.empty) {
699
- await emptyBucket(api, bucketName, props);
706
+ await emptyBucket(api, bucketName, deleteProps);
700
707
  }
701
- await deleteBucket(api, bucketName, props);
708
+ await deleteBucket(api, bucketName, deleteProps);
702
709
  }
703
710
  return this.destroy();
704
711
  }
@@ -2,7 +2,10 @@
2
2
 
3
3
  export * from "./account-api-token.ts";
4
4
  export * from "./account-id.ts";
5
+ export * from "./ai-crawler.ts";
5
6
  export * from "./ai-gateway.ts";
7
+ export * from "./ai-search-token.ts";
8
+ export * from "./ai-search.ts";
6
9
  export * from "./ai.ts";
7
10
  export * from "./analytics-engine.ts";
8
11
  export * from "./api-error.ts";
@@ -98,6 +98,12 @@ export const PERMISSION_GROUPS = [
98
98
  description: "Grants access to reading AI Gateways",
99
99
  scopes: ["com.cloudflare.api.account"],
100
100
  },
101
+ {
102
+ id: "9e9b428a0bcd46fd80e580b46a69963c",
103
+ name: "AI Search Index Engine",
104
+ description: "Grants access to run AI Search Index Engine",
105
+ scopes: ["com.cloudflare.api.account"],
106
+ },
101
107
  {
102
108
  id: "644535f4ed854494a59cb289d634b257",
103
109
  name: "AI Gateway Run",
package/src/scope.ts CHANGED
@@ -342,12 +342,21 @@ export class Scope {
342
342
  return state !== undefined && (type === undefined || state.kind === type);
343
343
  }
344
344
 
345
- public createPhysicalName(id: string, delimiter = "-"): string {
346
- const app = this.appName;
347
- const stage = this.stage;
348
- return [app, ...this.chain.slice(2), id, stage]
349
- .map((s) => s.replaceAll(/[^a-z0-9_-]/gi, delimiter))
345
+ public createPhysicalName(
346
+ id: string,
347
+ delimiter = "-",
348
+ maxLength?: number,
349
+ ): string {
350
+ let name = [this.appName, ...this.chain.slice(2), id, this.stage]
351
+ .map((part) => part.replaceAll(/[^a-z0-9_-]/gi, delimiter))
350
352
  .join(delimiter);
353
+ while (maxLength && name.length > maxLength) {
354
+ name = name
355
+ .split(delimiter)
356
+ .map((part) => part.slice(0, -1))
357
+ .join(delimiter);
358
+ }
359
+ return name;
351
360
  }
352
361
 
353
362
  public async spawn<
package/src/test/bun.ts CHANGED
@@ -51,6 +51,29 @@ type test = {
51
51
  * @param condition If true, test will be skipped
52
52
  */
53
53
  skipIf(condition: boolean): test;
54
+ skip: {
55
+ /**
56
+ * Create a test with default options
57
+ * @param name Test name
58
+ * @param fn Test function
59
+ * @param timeout Optional timeout in milliseconds
60
+ */
61
+ (name: string, fn: (scope: Scope) => Promise<any>, timeout?: number): void;
62
+
63
+ /**
64
+ * Create a test with custom options
65
+ * @param name Test name
66
+ * @param options Test configuration options
67
+ * @param fn Test function
68
+ * @param timeout Optional timeout in milliseconds
69
+ */
70
+ (
71
+ name: string,
72
+ options: TestOptions,
73
+ fn: (scope: Scope) => Promise<any>,
74
+ timeout?: number,
75
+ ): void;
76
+ };
54
77
 
55
78
  beforeAll(fn: (scope: Scope) => Promise<void>, timeout?: number): void;
56
79
 
@@ -95,6 +118,8 @@ export function test(meta: ImportMeta, defaultOptions?: TestOptions): test {
95
118
  // Add skipIf functionality
96
119
  test.skipIf = it.skipIf.bind(it);
97
120
 
121
+ test.skip = it.skip.bind(it);
122
+
98
123
  // Create local test scope based on filename
99
124
  const scope = new Scope({
100
125
  parent: undefined,
@@ -55,6 +55,29 @@ type test = {
55
55
  * @param condition If true, test will be skipped
56
56
  */
57
57
  skipIf(condition: boolean): test;
58
+ skip: {
59
+ /**
60
+ * Create a test with default options
61
+ * @param name Test name
62
+ * @param fn Test function
63
+ * @param timeout Optional timeout in milliseconds
64
+ */
65
+ (name: string, fn: (scope: Scope) => Promise<any>, timeout?: number): void;
66
+
67
+ /**
68
+ * Create a test with custom options
69
+ * @param name Test name
70
+ * @param options Test configuration options
71
+ * @param fn Test function
72
+ * @param timeout Optional timeout in milliseconds
73
+ */
74
+ (
75
+ name: string,
76
+ options: TestOptions,
77
+ fn: (scope: Scope) => Promise<any>,
78
+ timeout?: number,
79
+ ): void;
80
+ };
58
81
 
59
82
  beforeAll(fn: (scope: Scope) => Promise<void>, timeout?: number): void;
60
83
 
@@ -125,6 +148,16 @@ export function test(
125
148
  }
126
149
  return test;
127
150
  };
151
+ test.skip = (
152
+ ...args:
153
+ | [
154
+ name: string,
155
+ options: TestOptions,
156
+ fn: (scope: Scope) => Promise<void>,
157
+ ]
158
+ | [name: string, fn: (scope: Scope) => Promise<void>]
159
+ // @ts-expect-error
160
+ ) => it.skip(...args);
128
161
 
129
162
  const scope = new Scope({
130
163
  parent: undefined,
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Convert a snake_case string to camelCase.
3
+ * @example "account_id" -> "accountId", "created_at" -> "createdAt"
4
+ */
5
+ export function snakeToCamelString(s: string): string {
6
+ return s.replace(/_([a-z])/g, (_, c) => c.toUpperCase());
7
+ }
8
+
9
+ /**
10
+ * Recursively convert an object's keys from snake_case to camelCase.
11
+ * Arrays and nested objects are traversed; primitives, Date, and RegExp are returned as-is.
12
+ */
13
+ export function snakeToCamelObjectDeep<T>(obj: T): SnakeToCamel<T> {
14
+ if (obj === undefined || obj === null) {
15
+ return obj as SnakeToCamel<T>;
16
+ }
17
+ if (typeof obj !== "object") {
18
+ return obj as SnakeToCamel<T>;
19
+ }
20
+ if (obj instanceof RegExp || obj instanceof Date) {
21
+ return obj as SnakeToCamel<T>;
22
+ }
23
+ if (Array.isArray(obj)) {
24
+ return obj.map(snakeToCamelObjectDeep) as SnakeToCamel<T>;
25
+ }
26
+ return Object.fromEntries(
27
+ Object.entries(obj).map(([key, value]) => [
28
+ snakeToCamelString(key),
29
+ typeof value === "object" && value !== null && !Array.isArray(value)
30
+ ? snakeToCamelObjectDeep(value)
31
+ : Array.isArray(value)
32
+ ? value.map(snakeToCamelObjectDeep)
33
+ : value,
34
+ ]),
35
+ ) as SnakeToCamel<T>;
36
+ }
37
+
38
+ /**
39
+ * Convert a snake_case string to camelCase at the type level.
40
+ */
41
+ export type SnakeToCamelString<S extends string> =
42
+ S extends `${infer A}_${infer B}`
43
+ ? `${A}${Capitalize<SnakeToCamelString<B>>}`
44
+ : S;
45
+
46
+ /**
47
+ * Recursively convert an object type's keys from snake_case to camelCase.
48
+ */
49
+ export type SnakeToCamel<T> = T extends object
50
+ ? T extends Array<infer U>
51
+ ? Array<SnakeToCamel<U>>
52
+ : T extends Date | RegExp | Function
53
+ ? T
54
+ : {
55
+ [K in keyof T as K extends string
56
+ ? SnakeToCamelString<K>
57
+ : K]: SnakeToCamel<T[K]>;
58
+ }
59
+ : T;
@@ -83,7 +83,7 @@ const renderErrorHtml = (props) => `
83
83
  Alchemy</a>.</p>
84
84
  </div>
85
85
  <div class="bg-slate-200 px-5 py-3 flex flex-col w-full max-w-lg">
86
- <p class="text-sm text-slate-500">Alchemy 0.85.0</p>
86
+ <p class="text-sm text-slate-500">Alchemy 0.85.1</p>
87
87
  </div>
88
88
  </div>
89
89
  </body>