alchemy 0.3.0 → 0.4.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 (154) hide show
  1. package/README.md +8 -3
  2. package/lib/ai/astro-file.js +7 -24
  3. package/lib/ai/client.d.ts +7 -0
  4. package/lib/ai/client.js +45 -0
  5. package/lib/ai/css-file.js +7 -24
  6. package/lib/ai/data.d.ts +26 -21
  7. package/lib/ai/data.js +33 -22
  8. package/lib/ai/document.d.ts +76 -5
  9. package/lib/ai/document.js +112 -56
  10. package/lib/ai/html-file.js +7 -24
  11. package/lib/ai/json-file.js +11 -32
  12. package/lib/ai/typescript-file.d.ts +1 -2
  13. package/lib/ai/typescript-file.js +8 -39
  14. package/lib/ai/vue-file.js +7 -24
  15. package/lib/ai/yaml-file.js +12 -48
  16. package/lib/alchemy.d.ts +5 -0
  17. package/lib/alchemy.js +61 -3
  18. package/lib/apply.js +18 -11
  19. package/lib/aws/account-id.d.ts +4 -1
  20. package/lib/aws/account-id.js +1 -1
  21. package/lib/aws/credentials.d.ts +5 -0
  22. package/lib/aws/credentials.js +0 -0
  23. package/lib/aws/oidc/oidc-provider.js +2 -2
  24. package/lib/aws/role.js +35 -23
  25. package/lib/cloudflare/account-api-token.d.ts +168 -0
  26. package/lib/cloudflare/account-api-token.js +179 -0
  27. package/lib/cloudflare/account-id.d.ts +1 -0
  28. package/lib/cloudflare/account-id.js +0 -0
  29. package/lib/cloudflare/api.d.ts +38 -1
  30. package/lib/cloudflare/api.js +55 -4
  31. package/lib/cloudflare/bucket.d.ts +118 -3
  32. package/lib/cloudflare/bucket.js +269 -89
  33. package/lib/cloudflare/custom-domain.d.ts +75 -0
  34. package/lib/cloudflare/custom-domain.js +154 -0
  35. package/lib/cloudflare/{dns.js → dns-records.js} +14 -1
  36. package/lib/cloudflare/generate-asset-manifest.js +1 -1
  37. package/lib/cloudflare/index.d.ts +5 -1
  38. package/lib/cloudflare/index.js +5 -1
  39. package/lib/cloudflare/permission-groups.d.ts +78 -0
  40. package/lib/cloudflare/permission-groups.js +48 -0
  41. package/lib/cloudflare/r2-rest-state-store.d.ts +2 -1
  42. package/lib/cloudflare/r2-rest-state-store.js +3 -2
  43. package/lib/cloudflare/static-site-router.js +16 -7
  44. package/lib/cloudflare/static-site.d.ts +17 -18
  45. package/lib/cloudflare/static-site.js +19 -7
  46. package/lib/cloudflare/upload-asset-manifest.js +4 -2
  47. package/lib/{util/encrypt.d.ts → encrypt.d.ts} +1 -1
  48. package/lib/{util/encrypt.js → encrypt.js} +1 -1
  49. package/lib/fs/copy-file.d.ts +54 -0
  50. package/lib/fs/copy-file.js +63 -0
  51. package/lib/fs/file-system-state-store.js +1 -1
  52. package/lib/fs/file.d.ts +31 -0
  53. package/lib/fs/file.js +30 -5
  54. package/lib/fs/index.d.ts +6 -0
  55. package/lib/fs/index.js +6 -0
  56. package/lib/fs/static-astro-file.d.ts +34 -0
  57. package/lib/fs/static-astro-file.js +39 -0
  58. package/lib/fs/static-css-file.d.ts +24 -0
  59. package/lib/fs/static-css-file.js +29 -0
  60. package/lib/fs/static-html-file.d.ts +31 -0
  61. package/lib/fs/static-html-file.js +36 -0
  62. package/lib/fs/static-json-file.d.ts +2 -2
  63. package/lib/fs/static-json-file.js +3 -2
  64. package/lib/fs/static-text-file.d.ts +1 -1
  65. package/lib/fs/static-text-file.js +3 -2
  66. package/lib/fs/static-typescript-file.d.ts +2 -2
  67. package/lib/fs/static-typescript-file.js +3 -2
  68. package/lib/fs/static-vue-file.d.ts +28 -0
  69. package/lib/fs/static-vue-file.js +33 -0
  70. package/lib/fs/static-yaml-file.d.ts +11 -8
  71. package/lib/fs/static-yaml-file.js +3 -2
  72. package/lib/internal/{providers.d.ts → docs/providers.d.ts} +14 -4
  73. package/lib/internal/docs/providers.js +196 -0
  74. package/lib/secret.d.ts +3 -0
  75. package/lib/secret.js +13 -0
  76. package/lib/{util/serde.d.ts → serde.d.ts} +1 -1
  77. package/lib/{util/serde.js → serde.js} +12 -4
  78. package/lib/test/bun.js +3 -2
  79. package/lib/util/sha256.d.ts +1 -0
  80. package/lib/util/sha256.js +4 -0
  81. package/lib/web/vitepress/config.d.ts +11 -0
  82. package/lib/web/vitepress/config.js +32 -0
  83. package/lib/web/vitepress/home-page.d.ts +5 -3
  84. package/lib/web/vitepress/home-page.js +48 -35
  85. package/lib/web/vitepress/index.d.ts +2 -0
  86. package/lib/web/vitepress/index.js +2 -0
  87. package/lib/web/vitepress/process-front-matter-files.d.ts +18 -0
  88. package/lib/web/vitepress/process-front-matter-files.js +68 -0
  89. package/lib/web/vitepress/vitepress.d.ts +5 -4
  90. package/lib/web/vitepress/vitepress.js +20 -36
  91. package/package.json +8 -4
  92. package/src/ai/astro-file.ts +11 -30
  93. package/src/ai/client.ts +59 -0
  94. package/src/ai/css-file.ts +11 -30
  95. package/src/ai/data.ts +50 -25
  96. package/src/ai/document.ts +171 -66
  97. package/src/ai/html-file.ts +11 -30
  98. package/src/ai/json-file.ts +13 -37
  99. package/src/ai/typescript-file.ts +14 -48
  100. package/src/ai/vue-file.ts +11 -30
  101. package/src/ai/yaml-file.ts +13 -53
  102. package/src/alchemy.ts +74 -9
  103. package/src/apply.ts +27 -26
  104. package/src/aws/account-id.ts +6 -2
  105. package/src/aws/credentials.ts +6 -0
  106. package/src/aws/oidc/oidc-provider.ts +17 -17
  107. package/src/aws/role.ts +68 -54
  108. package/src/cloudflare/account-api-token.ts +365 -0
  109. package/src/cloudflare/account-id.ts +0 -0
  110. package/src/cloudflare/api.ts +88 -17
  111. package/src/cloudflare/bucket.ts +493 -133
  112. package/src/cloudflare/custom-domain.ts +318 -0
  113. package/src/cloudflare/{dns.ts → dns-records.ts} +36 -22
  114. package/src/cloudflare/generate-asset-manifest.ts +4 -4
  115. package/src/cloudflare/index.ts +5 -1
  116. package/src/cloudflare/permission-groups.ts +137 -0
  117. package/src/cloudflare/r2-rest-state-store.ts +14 -13
  118. package/src/cloudflare/static-site-router.ts +18 -8
  119. package/src/cloudflare/static-site.ts +27 -35
  120. package/src/cloudflare/upload-asset-manifest.ts +6 -4
  121. package/src/cloudflare/worker.ts +44 -43
  122. package/src/dns/import-dns.ts +4 -4
  123. package/src/{util/encrypt.ts → encrypt.ts} +7 -10
  124. package/src/fs/copy-file.ts +115 -0
  125. package/src/fs/file-system-state-store.ts +5 -5
  126. package/src/fs/file.ts +64 -9
  127. package/src/fs/index.ts +6 -0
  128. package/src/fs/static-astro-file.ts +45 -0
  129. package/src/fs/static-css-file.ts +35 -0
  130. package/src/fs/static-html-file.ts +42 -0
  131. package/src/fs/static-json-file.ts +4 -3
  132. package/src/fs/static-text-file.ts +5 -2
  133. package/src/fs/static-typescript-file.ts +4 -3
  134. package/src/fs/static-vue-file.ts +39 -0
  135. package/src/fs/static-yaml-file.ts +13 -9
  136. package/src/internal/docs/providers.ts +281 -0
  137. package/src/secret.ts +16 -0
  138. package/src/{util/serde.ts → serde.ts} +16 -10
  139. package/src/test/bun.ts +8 -7
  140. package/src/util/sha256.ts +5 -0
  141. package/src/web/vitepress/config.ts +50 -0
  142. package/src/web/vitepress/home-page.ts +54 -42
  143. package/src/web/vitepress/index.ts +2 -0
  144. package/src/web/vitepress/process-front-matter-files.ts +98 -0
  145. package/src/web/vitepress/vitepress.ts +34 -50
  146. package/lib/internal/getting-started.d.ts +0 -10
  147. package/lib/internal/getting-started.js +0 -94
  148. package/lib/internal/project.d.ts +0 -13
  149. package/lib/internal/project.js +0 -101
  150. package/lib/internal/providers.js +0 -148
  151. package/src/internal/getting-started.ts +0 -107
  152. package/src/internal/project.ts +0 -119
  153. package/src/internal/providers.ts +0 -209
  154. /package/lib/cloudflare/{dns.d.ts → dns-records.d.ts} +0 -0
@@ -1,7 +1,8 @@
1
1
  import type { Scope } from "../scope";
2
+ import type { Secret } from "../secret";
3
+ import { deserialize, serialize } from "../serde";
2
4
  import type { State, StateStore } from "../state";
3
5
  import { withExponentialBackoff } from "../util/retry";
4
- import { deserialize, serialize } from "../util/serde";
5
6
  import { type CloudflareApi, createCloudflareApi } from "./api";
6
7
 
7
8
  /**
@@ -23,7 +24,7 @@ export interface CloudflareR2StateStoreOptions {
23
24
  /**
24
25
  * API key to use (overrides CLOUDFLARE_API_KEY env var)
25
26
  */
26
- apiKey?: string;
27
+ apiKey?: Secret;
27
28
 
28
29
  /**
29
30
  * Account ID to use (overrides CLOUDFLARE_ACCOUNT_ID env var)
@@ -44,7 +45,7 @@ export class R2RestStateStore implements StateStore {
44
45
  private api: CloudflareApi;
45
46
  private prefix: string;
46
47
  private bucketName: string;
47
- private apiKey: string | undefined;
48
+ private apiKey: Secret | undefined;
48
49
  private accountId: string | undefined;
49
50
  private email: string | undefined;
50
51
  private initialized = false;
@@ -57,7 +58,7 @@ export class R2RestStateStore implements StateStore {
57
58
  */
58
59
  constructor(
59
60
  public readonly scope: Scope,
60
- options: CloudflareR2StateStoreOptions,
61
+ options: CloudflareR2StateStoreOptions
61
62
  ) {
62
63
  // Use the scope's chain to build the prefix, similar to how FileSystemStateStore builds its directory
63
64
  const scopePath = scope.chain.join("/");
@@ -129,7 +130,7 @@ export class R2RestStateStore implements StateStore {
129
130
  errors: [{ message: response.statusText }],
130
131
  }));
131
132
  throw new Error(
132
- `Error listing R2 objects: ${errorData.errors?.[0]?.message || response.statusText}`,
133
+ `Error listing R2 objects: ${errorData.errors?.[0]?.message || response.statusText}`
133
134
  );
134
135
  }
135
136
 
@@ -144,7 +145,7 @@ export class R2RestStateStore implements StateStore {
144
145
  objects.map((obj: any) => {
145
146
  const keyName = obj.key || obj.name;
146
147
  return this.convertKeyFromStorage(keyName.slice(this.prefix.length));
147
- }),
148
+ })
148
149
  );
149
150
 
150
151
  // Update cursor for next page if available
@@ -176,7 +177,7 @@ export class R2RestStateStore implements StateStore {
176
177
 
177
178
  try {
178
179
  const response = await this.api.get(
179
- `/accounts/${this.api.accountId}/r2/buckets/${this.bucketName}/objects/${this.getObjectKey(key)}`,
180
+ `/accounts/${this.api.accountId}/r2/buckets/${this.bucketName}/objects/${this.getObjectKey(key)}`
180
181
  );
181
182
 
182
183
  if (!response.ok) {
@@ -188,7 +189,7 @@ export class R2RestStateStore implements StateStore {
188
189
  errors: [{ message: response.statusText }],
189
190
  }));
190
191
  throw new Error(
191
- `Error getting R2 object: ${errorData.errors?.[0]?.message || response.statusText}`,
192
+ `Error getting R2 object: ${errorData.errors?.[0]?.message || response.statusText}`
192
193
  );
193
194
  }
194
195
 
@@ -267,7 +268,7 @@ export class R2RestStateStore implements StateStore {
267
268
  headers: {
268
269
  "Content-Type": "application/json",
269
270
  },
270
- },
271
+ }
271
272
  );
272
273
 
273
274
  if (!response.ok) {
@@ -275,7 +276,7 @@ export class R2RestStateStore implements StateStore {
275
276
  errors: [{ message: response.statusText }],
276
277
  }));
277
278
  throw new Error(
278
- `Error writing to R2: ${errorData.errors?.[0]?.message || response.statusText}`,
279
+ `Error writing to R2: ${errorData.errors?.[0]?.message || response.statusText}`
279
280
  );
280
281
  }
281
282
 
@@ -285,7 +286,7 @@ export class R2RestStateStore implements StateStore {
285
286
  (error) =>
286
287
  error.message?.includes("503") || error.message?.includes("timeout"),
287
288
  5, // 5 retry attempts
288
- 1000, // Start with 1 second delay
289
+ 1000 // Start with 1 second delay
289
290
  );
290
291
  }
291
292
 
@@ -298,7 +299,7 @@ export class R2RestStateStore implements StateStore {
298
299
  await this.ensureInitialized();
299
300
 
300
301
  const response = await this.api.delete(
301
- `/accounts/${this.api.accountId}/r2/buckets/${this.bucketName}/objects/${this.getObjectKey(key)}`,
302
+ `/accounts/${this.api.accountId}/r2/buckets/${this.bucketName}/objects/${this.getObjectKey(key)}`
302
303
  );
303
304
 
304
305
  if (!response.ok && response.status !== 404) {
@@ -306,7 +307,7 @@ export class R2RestStateStore implements StateStore {
306
307
  errors: [{ message: response.statusText }],
307
308
  }));
308
309
  throw new Error(
309
- `Error deleting from R2: ${errorData.errors?.[0]?.message || response.statusText}`,
310
+ `Error deleting from R2: ${errorData.errors?.[0]?.message || response.statusText}`
310
311
  );
311
312
  }
312
313
  }
@@ -65,21 +65,27 @@ app.notFound(async (ctx) => {
65
65
 
66
66
  // Fetch from KV
67
67
  {
68
- const object = await env.ASSETS.getWithMetadata(filePath);
68
+ const object = await env.ASSETS.getWithMetadata(filePath, {
69
+ type: "arrayBuffer",
70
+ });
69
71
  if (object.value) {
70
72
  return await respond(200, filePath, object);
71
73
  }
72
74
  }
73
75
  {
74
76
  const guess = filePath + (filePath.endsWith("/") ? "" : "/") + "index.html";
75
- const object = await env.ASSETS.getWithMetadata(guess);
77
+ const object = await env.ASSETS.getWithMetadata(guess, {
78
+ type: "arrayBuffer",
79
+ });
76
80
  if (object.value) {
77
81
  return await respond(200, guess, object);
78
82
  }
79
83
  }
80
84
  {
81
85
  const guess = filePath + ".html";
82
- const object = await env.ASSETS.getWithMetadata(guess);
86
+ const object = await env.ASSETS.getWithMetadata(guess, {
87
+ type: "arrayBuffer",
88
+ });
83
89
  if (object.value) {
84
90
  return await respond(200, guess, object);
85
91
  }
@@ -87,12 +93,16 @@ app.notFound(async (ctx) => {
87
93
 
88
94
  // Handle error page
89
95
  if (env.ERROR_PAGE) {
90
- const object = await env.ASSETS.getWithMetadata(env.ERROR_PAGE);
96
+ const object = await env.ASSETS.getWithMetadata(env.ERROR_PAGE, {
97
+ type: "arrayBuffer",
98
+ });
91
99
  if (object.value) {
92
100
  return await respond(404, env.ERROR_PAGE, object);
93
101
  }
94
102
  } else {
95
- const object = await env.ASSETS.getWithMetadata(env.INDEX_PAGE);
103
+ const object = await env.ASSETS.getWithMetadata(env.INDEX_PAGE, {
104
+ type: "arrayBuffer",
105
+ });
96
106
  if (object.value) {
97
107
  return await respond(200, env.INDEX_PAGE, object);
98
108
  }
@@ -122,7 +132,7 @@ app.notFound(async (ctx) => {
122
132
  async function respond(
123
133
  status: number,
124
134
  filePath: string,
125
- object: KVNamespaceGetWithMetadataResult<any, any>,
135
+ object: KVNamespaceGetWithMetadataResult<any, any>
126
136
  ) {
127
137
  // build response
128
138
  const headers = new Headers();
@@ -131,8 +141,8 @@ app.notFound(async (ctx) => {
131
141
  headers.set("content-type", object.metadata.contentType);
132
142
  headers.set("cache-control", object.metadata.cacheControl);
133
143
  }
134
- // TODO: do we need base64 encoded here?
135
- // const response = new Response(base64ToArrayBuffer(object.value), {
144
+
145
+ // Create response with the raw data directly
136
146
  const response = new Response(object.value, {
137
147
  status,
138
148
  headers,
@@ -96,16 +96,6 @@ export interface StaticSiteProps {
96
96
  */
97
97
  production?: boolean;
98
98
 
99
- /**
100
- * Custom domain for the site
101
- */
102
- domain?:
103
- | string
104
- | {
105
- name: string;
106
- redirects?: string[];
107
- };
108
-
109
99
  build?: {
110
100
  /**
111
101
  * Command to run before deploying the site
@@ -176,16 +166,6 @@ export interface StaticSite extends Resource<"cloudflare::StaticSite"> {
176
166
  */
177
167
  assets: string[];
178
168
 
179
- /**
180
- * Custom domain for the site
181
- */
182
- domain?:
183
- | string
184
- | {
185
- name: string;
186
- redirects?: string[];
187
- };
188
-
189
169
  /**
190
170
  * Whether the site is deployed to production
191
171
  */
@@ -243,13 +223,26 @@ export interface StaticSite extends Resource<"cloudflare::StaticSite"> {
243
223
  * });
244
224
  *
245
225
  * @example
246
- * // Create a static site with custom error page and index
247
- * const customSite = await StaticSite("custom-site", {
226
+ * // Create a static site with a custom domain using the CustomDomain resource
227
+ * const site = await StaticSite("custom-site", {
248
228
  * name: "custom-site",
249
229
  * dir: "./www",
250
230
  * errorPage: "404.html",
251
- * indexPage: "home.html",
252
- * domain: "www.example.com"
231
+ * indexPage: "home.html"
232
+ * });
233
+ *
234
+ * // Then configure the custom domain separately
235
+ * const domain = await CustomDomain("custom-domain", {
236
+ * name: "www.example.com",
237
+ * zoneId: "abcdef123456789",
238
+ * workerName: site.name
239
+ * });
240
+ *
241
+ * @example
242
+ * // Create a static site with multiple domains (primary + redirects)
243
+ * const site = await StaticSite("multi-domain-site", {
244
+ * name: "multi-domain-site",
245
+ * dir: "./public"
253
246
  * });
254
247
  *
255
248
  * @see https://developers.cloudflare.com/workers/platform/sites
@@ -262,8 +255,11 @@ export const StaticSite = Resource(
262
255
  async function (
263
256
  this: Context<StaticSite>,
264
257
  id: string,
265
- props: StaticSiteProps,
258
+ props: StaticSiteProps
266
259
  ) {
260
+ // Create Cloudflare API client with automatic account discovery
261
+ const api = await createCloudflareApi();
262
+
267
263
  if (this.phase === "delete") {
268
264
  // For delete operations, we'll rely on the Worker delete to clean up
269
265
  // Return empty output for deleted state
@@ -273,7 +269,7 @@ export const StaticSite = Resource(
273
269
  // Validate that a name is provided
274
270
  if (!props.name) {
275
271
  throw new Error(
276
- "StaticSite name is required - must be explicitly specified",
272
+ "StaticSite name is required - must be explicitly specified"
277
273
  );
278
274
  }
279
275
 
@@ -302,7 +298,7 @@ export const StaticSite = Resource(
302
298
 
303
299
  // Throw a more descriptive error that includes the exit code and stderr
304
300
  throw new Error(
305
- `Build command "${props.build.command}" failed with exit code ${error.code}:\n${error.stderr || error.message}`,
301
+ `Build command "${props.build.command}" failed with exit code ${error.code}:\n${error.stderr || error.message}`
306
302
  );
307
303
  }
308
304
  }
@@ -314,7 +310,7 @@ export const StaticSite = Resource(
314
310
  }
315
311
  } catch (error: any) {
316
312
  throw new Error(
317
- `Directory "${props.dir}" does not exist: ${error.message}`,
313
+ `Directory "${props.dir}" does not exist: ${error.message}`
318
314
  );
319
315
  }
320
316
 
@@ -322,9 +318,6 @@ export const StaticSite = Resource(
322
318
  const siteName = props.name;
323
319
  const indexPage = props.indexPage || "index.html";
324
320
 
325
- // Create Cloudflare API client with automatic account discovery
326
- const api = await createCloudflareApi();
327
-
328
321
  // Step 1: Create or get the KV namespace for assets
329
322
  const [kv, assetManifest] = await Promise.all([
330
323
  KVNamespace("assets", {
@@ -356,7 +349,7 @@ export const StaticSite = Resource(
356
349
 
357
350
  // Create asset manifest banner for static site router
358
351
  const assetManifestBanner = `const __ASSET_MANIFEST__ = ${JSON.stringify(
359
- Object.fromEntries(assetManifest.map((item) => [item.key, item.hash])),
352
+ Object.fromEntries(assetManifest.map((item) => [item.key, item.hash]))
360
353
  )};\n`;
361
354
 
362
355
  const bundleOptions = {
@@ -398,7 +391,7 @@ export const StaticSite = Resource(
398
391
  Object.entries(routes).map(([key, value]) => [
399
392
  `__ROUTE_${value}`,
400
393
  key,
401
- ]),
394
+ ])
402
395
  ),
403
396
  },
404
397
  });
@@ -415,10 +408,9 @@ export const StaticSite = Resource(
415
408
  assets: assetManifest.map((item) => item.key),
416
409
  createdAt: this.output?.createdAt || now,
417
410
  updatedAt: now,
418
- domain: props.domain,
419
411
  production: props.production !== false,
420
412
  url: worker.url,
421
413
  routes,
422
414
  });
423
- },
415
+ }
424
416
  );
@@ -17,7 +17,7 @@ type UploadResult = {
17
17
  export async function uploadAssetManifest(
18
18
  api: CloudflareApi,
19
19
  namespaceId: string,
20
- manifest: AssetManifest,
20
+ manifest: AssetManifest
21
21
  ): Promise<UploadResult[]> {
22
22
  const results: UploadResult[] = [];
23
23
 
@@ -59,14 +59,16 @@ export async function uploadAssetManifest(
59
59
  const formData = new FormData();
60
60
  formData.append("metadata", JSON.stringify(metadata));
61
61
 
62
- // Add file content as 'value'
63
- const blob = new Blob([content]);
62
+ // Add file content as 'value' - Fix for binary file corruption
63
+ const blob = new Blob([new Uint8Array(content)], {
64
+ type: item.contentType,
65
+ });
64
66
  formData.append("value", blob);
65
67
 
66
68
  try {
67
69
  const response = await api.put(
68
70
  `/accounts/${api.accountId}/storage/kv/namespaces/${namespaceId}/values/${item.key}`,
69
- formData,
71
+ formData
70
72
  );
71
73
 
72
74
  const result = { item, success: response.ok, status: response.status };