alchemy 0.2.5 → 0.3.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 (197) hide show
  1. package/README.md +3 -3
  2. package/lib/ai/approve.d.ts +99 -0
  3. package/lib/ai/approve.js +76 -0
  4. package/lib/ai/astro-file.d.ts +137 -0
  5. package/lib/ai/astro-file.js +156 -0
  6. package/lib/ai/client.d.ts +7 -8
  7. package/lib/ai/client.js +48 -19
  8. package/lib/ai/css-file.d.ts +131 -0
  9. package/lib/ai/css-file.js +141 -0
  10. package/lib/ai/data.d.ts +51 -6
  11. package/lib/ai/data.js +58 -12
  12. package/lib/ai/document.d.ts +108 -24
  13. package/lib/ai/document.js +166 -52
  14. package/lib/ai/html-file.d.ts +128 -0
  15. package/lib/ai/html-file.js +138 -0
  16. package/lib/ai/index.d.ts +9 -0
  17. package/lib/ai/index.js +9 -0
  18. package/lib/ai/json-file.d.ts +160 -0
  19. package/lib/ai/json-file.js +187 -0
  20. package/lib/ai/review.d.ts +122 -0
  21. package/lib/ai/review.js +101 -0
  22. package/lib/ai/typescript-file.d.ts +139 -0
  23. package/lib/ai/typescript-file.js +145 -0
  24. package/lib/ai/vue-file.d.ts +126 -0
  25. package/lib/ai/vue-file.js +136 -0
  26. package/lib/ai/yaml-file.d.ts +169 -0
  27. package/lib/ai/yaml-file.js +200 -0
  28. package/lib/alchemy.js +17 -2
  29. package/lib/apply.js +1 -1
  30. package/lib/cloudflare/dns.d.ts +88 -0
  31. package/lib/cloudflare/dns.js +192 -0
  32. package/lib/cloudflare/generate-asset-manifest.js +1 -1
  33. package/lib/cloudflare/index.d.ts +2 -1
  34. package/lib/cloudflare/index.js +2 -1
  35. package/lib/cloudflare/r2-rest-state-store.d.ts +125 -0
  36. package/lib/cloudflare/r2-rest-state-store.js +254 -0
  37. package/lib/cloudflare/response.d.ts +12 -0
  38. package/lib/cloudflare/response.js +0 -0
  39. package/lib/cloudflare/static-site-router.js +16 -7
  40. package/lib/cloudflare/static-site.js +3 -0
  41. package/lib/cloudflare/upload-asset-manifest.js +4 -2
  42. package/lib/cloudflare/zone.d.ts +18 -0
  43. package/lib/cloudflare/zone.js +17 -4
  44. package/lib/dns/godaddy.d.ts +7 -0
  45. package/lib/dns/godaddy.js +21 -0
  46. package/lib/dns/import-dns.d.ts +82 -0
  47. package/lib/dns/import-dns.js +114 -0
  48. package/lib/dns/index.d.ts +2 -0
  49. package/lib/dns/index.js +2 -0
  50. package/lib/dns/record.d.ts +88 -0
  51. package/lib/dns/record.js +13 -0
  52. package/lib/fs/copy-file.d.ts +54 -0
  53. package/lib/fs/copy-file.js +63 -0
  54. package/lib/fs/file-system-state-store.d.ts +17 -0
  55. package/lib/fs/file-system-state-store.js +87 -0
  56. package/lib/fs/file.d.ts +13 -0
  57. package/lib/fs/file.js +24 -5
  58. package/lib/fs/folder.d.ts +10 -0
  59. package/lib/fs/folder.js +2 -2
  60. package/lib/fs/index.d.ts +10 -4
  61. package/lib/fs/index.js +10 -4
  62. package/lib/fs/static-astro-file.d.ts +34 -0
  63. package/lib/fs/static-astro-file.js +39 -0
  64. package/lib/fs/static-css-file.d.ts +24 -0
  65. package/lib/fs/static-css-file.js +29 -0
  66. package/lib/fs/static-html-file.d.ts +31 -0
  67. package/lib/fs/static-html-file.js +36 -0
  68. package/lib/fs/{json-file.d.ts → static-json-file.d.ts} +3 -3
  69. package/lib/fs/static-json-file.js +15 -0
  70. package/lib/fs/{text-file.d.ts → static-text-file.d.ts} +2 -2
  71. package/lib/fs/static-text-file.js +8 -0
  72. package/lib/fs/{typescript-file.d.ts → static-typescript-file.d.ts} +3 -3
  73. package/lib/fs/{typescript-file.js → static-typescript-file.js} +3 -2
  74. package/lib/fs/static-vue-file.d.ts +28 -0
  75. package/lib/fs/static-vue-file.js +33 -0
  76. package/lib/fs/static-yaml-file.d.ts +22 -0
  77. package/lib/fs/{yaml-file.js → static-yaml-file.js} +3 -2
  78. package/lib/internal/getting-started.d.ts +21 -0
  79. package/lib/internal/getting-started.js +87 -0
  80. package/lib/internal/index.d.ts +3 -0
  81. package/lib/internal/index.js +3 -0
  82. package/lib/internal/providers.d.ts +27 -0
  83. package/lib/internal/providers.js +172 -0
  84. package/lib/internal/tutorial.d.ts +104 -0
  85. package/lib/internal/tutorial.js +251 -0
  86. package/lib/resource.js +8 -1
  87. package/lib/scope.d.ts +1 -1
  88. package/lib/scope.js +4 -2
  89. package/lib/state.d.ts +1 -16
  90. package/lib/state.js +0 -87
  91. package/lib/test/bun.d.ts +11 -0
  92. package/lib/test/bun.js +36 -8
  93. package/lib/web/astro.d.ts +147 -0
  94. package/lib/web/astro.js +414 -0
  95. package/lib/{shadcn/component.js → web/shadcn-component.js} +2 -9
  96. package/lib/web/shadcn.d.ts +97 -0
  97. package/lib/web/shadcn.js +99 -0
  98. package/lib/web/tailwind.d.ts +64 -0
  99. package/lib/web/tailwind.js +97 -0
  100. package/lib/{vite → web}/vite.d.ts +5 -0
  101. package/lib/{vite → web}/vite.js +44 -44
  102. package/lib/web/vitepress/config.d.ts +10 -0
  103. package/lib/web/vitepress/config.js +31 -0
  104. package/lib/web/vitepress/custom-theme.d.ts +173 -0
  105. package/lib/web/vitepress/custom-theme.js +336 -0
  106. package/lib/{vitepress → web/vitepress}/dependencies.d.ts +2 -2
  107. package/lib/{vitepress → web/vitepress}/dependencies.js +1 -1
  108. package/lib/web/vitepress/home-page.d.ts +246 -0
  109. package/lib/web/vitepress/home-page.js +147 -0
  110. package/lib/{vitepress → web/vitepress}/index.d.ts +2 -0
  111. package/lib/{vitepress → web/vitepress}/index.js +2 -0
  112. package/lib/{vitepress → web/vitepress}/vitepress.d.ts +15 -12
  113. package/lib/{vitepress → web/vitepress}/vitepress.js +26 -44
  114. package/package.json +6 -2
  115. package/src/ai/approve.ts +163 -0
  116. package/src/ai/astro-file.ts +269 -0
  117. package/src/ai/client.ts +60 -21
  118. package/src/ai/css-file.ts +247 -0
  119. package/src/ai/data.ts +81 -19
  120. package/src/ai/document.ts +240 -62
  121. package/src/ai/html-file.ts +244 -0
  122. package/src/ai/index.ts +9 -0
  123. package/src/ai/json-file.ts +325 -0
  124. package/src/ai/review.ts +213 -0
  125. package/src/ai/typescript-file.ts +259 -0
  126. package/src/ai/vue-file.ts +242 -0
  127. package/src/ai/yaml-file.ts +328 -0
  128. package/src/alchemy.ts +18 -7
  129. package/src/apply.ts +8 -9
  130. package/src/cloudflare/dns.ts +344 -0
  131. package/src/cloudflare/generate-asset-manifest.ts +4 -4
  132. package/src/cloudflare/index.ts +2 -1
  133. package/src/cloudflare/r2-rest-state-store.ts +353 -0
  134. package/src/cloudflare/response.ts +9 -0
  135. package/src/cloudflare/static-site-router.ts +18 -8
  136. package/src/cloudflare/static-site.ts +11 -7
  137. package/src/cloudflare/upload-asset-manifest.ts +6 -4
  138. package/src/cloudflare/worker.ts +44 -43
  139. package/src/cloudflare/zone.ts +39 -23
  140. package/src/dns/godaddy.ts +29 -0
  141. package/src/dns/import-dns.ts +213 -0
  142. package/src/dns/index.ts +2 -0
  143. package/src/dns/record.ts +121 -0
  144. package/src/fs/copy-file.ts +115 -0
  145. package/src/fs/file-system-state-store.ts +109 -0
  146. package/src/fs/file.ts +35 -9
  147. package/src/fs/folder.ts +14 -2
  148. package/src/fs/index.ts +10 -4
  149. package/src/fs/static-astro-file.ts +45 -0
  150. package/src/fs/static-css-file.ts +35 -0
  151. package/src/fs/static-html-file.ts +42 -0
  152. package/src/fs/static-json-file.ts +34 -0
  153. package/src/fs/{text-file.ts → static-text-file.ts} +9 -3
  154. package/src/fs/{typescript-file.ts → static-typescript-file.ts} +7 -6
  155. package/src/fs/static-vue-file.ts +39 -0
  156. package/src/fs/static-yaml-file.ts +33 -0
  157. package/src/internal/getting-started.ts +115 -0
  158. package/src/internal/index.ts +3 -0
  159. package/src/internal/providers.ts +241 -0
  160. package/src/internal/tutorial.ts +392 -0
  161. package/src/resource.ts +10 -1
  162. package/src/scope.ts +5 -6
  163. package/src/state.ts +1 -111
  164. package/src/test/bun.ts +60 -10
  165. package/src/web/astro.ts +644 -0
  166. package/src/{shadcn/component.ts → web/shadcn-component.ts} +5 -13
  167. package/src/web/shadcn.ts +219 -0
  168. package/src/web/tailwind.ts +167 -0
  169. package/src/{vite → web}/vite.ts +54 -56
  170. package/src/web/vitepress/config.ts +48 -0
  171. package/src/web/vitepress/custom-theme.ts +514 -0
  172. package/src/{vitepress → web/vitepress}/dependencies.ts +2 -2
  173. package/src/web/vitepress/home-page.ts +375 -0
  174. package/src/{vitepress → web/vitepress}/index.ts +2 -0
  175. package/src/{vitepress → web/vitepress}/vitepress.ts +62 -72
  176. package/lib/cloudflare/state.d.ts +0 -82
  177. package/lib/cloudflare/state.js +0 -108
  178. package/lib/fs/json-file.js +0 -7
  179. package/lib/fs/text-file.js +0 -7
  180. package/lib/fs/yaml-file.d.ts +0 -19
  181. package/lib/internal/docs.d.ts +0 -5
  182. package/lib/internal/docs.js +0 -287
  183. package/lib/shadcn/index.d.ts +0 -1
  184. package/lib/shadcn/index.js +0 -1
  185. package/lib/vite/index.d.ts +0 -1
  186. package/lib/vite/index.js +0 -1
  187. package/lib/vitepress/home-page.d.ts +0 -133
  188. package/lib/vitepress/home-page.js +0 -11
  189. package/src/cloudflare/state.ts +0 -138
  190. package/src/fs/json-file.ts +0 -23
  191. package/src/fs/yaml-file.ts +0 -26
  192. package/src/internal/docs.ts +0 -338
  193. package/src/shadcn/index.ts +0 -1
  194. package/src/vite/index.ts +0 -1
  195. package/src/vitepress/home-page.ts +0 -166
  196. /package/lib/{shadcn/component.d.ts → web/shadcn-component.d.ts} +0 -0
  197. /package/src/{vitepress → web/vitepress}/index.md +0 -0
@@ -16,6 +16,7 @@ import type { DurableObjectNamespace } from "./durable-object-namespace";
16
16
  import { isKVNamespace } from "./kv-namespace";
17
17
  import type { WorkerScriptMetadata } from "./worker-metadata";
18
18
  import type { SingleStepMigration } from "./worker-migration";
19
+
19
20
  /**
20
21
  * Properties for creating or updating a Worker
21
22
  */
@@ -209,7 +210,7 @@ export const Worker = Resource(
209
210
  async function <const B extends Bindings>(
210
211
  this: Context<Worker<NoInfer<B>>>,
211
212
  id: string,
212
- props: WorkerProps<B>,
213
+ props: WorkerProps<B>
213
214
  ): Promise<Worker<B>> {
214
215
  // Create Cloudflare API client with automatic account discovery
215
216
  const api = await createCloudflareApi();
@@ -234,7 +235,7 @@ export const Worker = Resource(
234
235
  const scriptMetadata = await prepareWorkerMetadata(
235
236
  this,
236
237
  oldBindings,
237
- props,
238
+ props
238
239
  );
239
240
 
240
241
  // Get the script content - either from props.script, or by bundling
@@ -254,7 +255,7 @@ export const Worker = Resource(
254
255
  this,
255
256
  api,
256
257
  workerName,
257
- props.url ?? false,
258
+ props.url ?? false
258
259
  );
259
260
 
260
261
  // Get current timestamp
@@ -277,17 +278,17 @@ export const Worker = Resource(
277
278
  // phantom property
278
279
  Env: undefined!,
279
280
  });
280
- },
281
+ }
281
282
  );
282
283
 
283
284
  async function deleteWorker<B extends Bindings>(
284
285
  ctx: Context<Worker<B>>,
285
286
  api: CloudflareApi,
286
- workerName: string,
287
+ workerName: string
287
288
  ) {
288
289
  // Delete worker
289
290
  const deleteResponse = await api.delete(
290
- `/accounts/${api.accountId}/workers/scripts/${workerName}`,
291
+ `/accounts/${api.accountId}/workers/scripts/${workerName}`
291
292
  );
292
293
 
293
294
  // Check for success (2xx status code)
@@ -297,7 +298,7 @@ async function deleteWorker<B extends Bindings>(
297
298
  .catch(() => ({ errors: [{ message: deleteResponse.statusText }] }));
298
299
  console.error(
299
300
  "Error deleting worker:",
300
- errorData.errors?.[0]?.message || deleteResponse.statusText,
301
+ errorData.errors?.[0]?.message || deleteResponse.statusText
301
302
  );
302
303
  }
303
304
 
@@ -308,7 +309,7 @@ async function deleteWorker<B extends Bindings>(
308
309
 
309
310
  if (!routesResponse.ok) {
310
311
  throw new Error(
311
- `Could not fetch routes for cleanup: ${routesResponse.status} ${routesResponse.statusText}`,
312
+ `Could not fetch routes for cleanup: ${routesResponse.status} ${routesResponse.statusText}`
312
313
  );
313
314
  }
314
315
  const routesData: any = await routesResponse.json();
@@ -318,12 +319,12 @@ async function deleteWorker<B extends Bindings>(
318
319
  if (ctx.output.routes.includes(route.pattern)) {
319
320
  // Delete the route
320
321
  const routeDeleteResponse = await api.delete(
321
- `/zones/${api.zoneId}/workers/routes/${route.id}`,
322
+ `/zones/${api.zoneId}/workers/routes/${route.id}`
322
323
  );
323
324
 
324
325
  if (!routeDeleteResponse.ok) {
325
326
  console.warn(
326
- `Failed to delete route ${route.pattern}: ${routeDeleteResponse.status} ${routeDeleteResponse.statusText}`,
327
+ `Failed to delete route ${route.pattern}: ${routeDeleteResponse.status} ${routeDeleteResponse.statusText}`
327
328
  );
328
329
  }
329
330
  }
@@ -338,7 +339,7 @@ async function deleteWorker<B extends Bindings>(
338
339
  JSON.stringify({ enabled: false }),
339
340
  {
340
341
  headers: { "Content-Type": "application/json" },
341
- },
342
+ }
342
343
  );
343
344
  } catch (error) {
344
345
  console.warn("Failed to disable worker URL during deletion:", error);
@@ -353,7 +354,7 @@ async function putWorker(
353
354
  api: CloudflareApi,
354
355
  workerName: string,
355
356
  scriptContent: string,
356
- scriptMetadata: WorkerMetadata,
357
+ scriptMetadata: WorkerMetadata
357
358
  ) {
358
359
  return withExponentialBackoff(
359
360
  async () => {
@@ -371,7 +372,7 @@ async function putWorker(
371
372
  ? "application/javascript+module"
372
373
  : "application/javascript",
373
374
  }),
374
- scriptName,
375
+ scriptName
375
376
  );
376
377
 
377
378
  // Add metadata as JSON
@@ -379,7 +380,7 @@ async function putWorker(
379
380
  "metadata",
380
381
  new Blob([JSON.stringify(scriptMetadata)], {
381
382
  type: "application/json",
382
- }),
383
+ })
383
384
  );
384
385
 
385
386
  await new Promise((resolve) => setTimeout(resolve, 1000));
@@ -392,7 +393,7 @@ async function putWorker(
392
393
  headers: {
393
394
  "Content-Type": "multipart/form-data",
394
395
  },
395
- },
396
+ }
396
397
  );
397
398
 
398
399
  // Check if the upload was successful
@@ -416,7 +417,7 @@ async function putWorker(
416
417
  },
417
418
  (err) => err instanceof NotFoundError,
418
419
  10,
419
- 100,
420
+ 100
420
421
  );
421
422
  }
422
423
 
@@ -441,7 +442,7 @@ interface WorkerMetadata {
441
442
  async function prepareWorkerMetadata<B extends Bindings>(
442
443
  ctx: Context<Worker<B>>,
443
444
  oldBindings: Bindings | undefined,
444
- props: WorkerProps,
445
+ props: WorkerProps
445
446
  ): Promise<WorkerMetadata> {
446
447
  // Prepare metadata with bindings
447
448
  const meta: WorkerMetadata = {
@@ -498,7 +499,7 @@ async function prepareWorkerMetadata<B extends Bindings>(
498
499
  });
499
500
 
500
501
  const oldBinding: DurableObjectNamespace | undefined = Object.values(
501
- oldBindings ?? {},
502
+ oldBindings ?? {}
502
503
  )
503
504
  ?.filter(isDurableObjectNamespace)
504
505
  ?.find((b) => b.id === stableId);
@@ -561,10 +562,10 @@ async function prepareWorkerMetadata<B extends Bindings>(
561
562
  async function assertWorkerDoesNotExist<B extends Bindings>(
562
563
  ctx: Context<Worker<B>>,
563
564
  api: CloudflareApi,
564
- workerName: string,
565
+ workerName: string
565
566
  ) {
566
567
  const response = await api.get(
567
- `/accounts/${api.accountId}/workers/scripts/${workerName}`,
568
+ `/accounts/${api.accountId}/workers/scripts/${workerName}`
568
569
  );
569
570
  if (response.status === 404) {
570
571
  return true;
@@ -574,24 +575,24 @@ async function assertWorkerDoesNotExist<B extends Bindings>(
574
575
 
575
576
  if (!metadata) {
576
577
  throw new Error(
577
- `Worker exists but failed to fetch metadata: ${response.status} ${response.statusText}`,
578
+ `Worker exists but failed to fetch metadata: ${response.status} ${response.statusText}`
578
579
  );
579
580
  }
580
581
 
581
582
  if (
582
583
  metadata.default_environment?.script.tags.includes(
583
- `alchemy:id:${slugify(ctx.fqn)}`,
584
+ `alchemy:id:${slugify(ctx.fqn)}`
584
585
  )
585
586
  ) {
586
587
  return true;
587
588
  }
588
589
 
589
590
  throw new Error(
590
- `Worker with name '${workerName}' already exists. Please use a unique name.`,
591
+ `Worker with name '${workerName}' already exists. Please use a unique name.`
591
592
  );
592
593
  } else {
593
594
  throw new Error(
594
- `Error checking if worker exists: ${response.status} ${response.statusText} ${await response.text()}`,
595
+ `Error checking if worker exists: ${response.status} ${response.statusText} ${await response.text()}`
595
596
  );
596
597
  }
597
598
  }
@@ -634,7 +635,7 @@ async function bundleWorkerScript<B extends Bindings>(props: WorkerProps) {
634
635
  async function setupRoutes(
635
636
  api: CloudflareApi,
636
637
  workerName: string,
637
- routes: string[],
638
+ routes: string[]
638
639
  ) {
639
640
  // Set up routes if provided
640
641
  if (routes && routes.length > 0 && api.zoneId) {
@@ -643,7 +644,7 @@ async function setupRoutes(
643
644
 
644
645
  if (!routesResponse.ok) {
645
646
  throw new Error(
646
- `Could not fetch routes: ${routesResponse.status} ${routesResponse.statusText}`,
647
+ `Could not fetch routes: ${routesResponse.status} ${routesResponse.statusText}`
647
648
  );
648
649
  }
649
650
  const routesData: any = await routesResponse.json();
@@ -652,7 +653,7 @@ async function setupRoutes(
652
653
  // For each desired route
653
654
  for (const pattern of routes) {
654
655
  const existingRoute = existingRoutes.find(
655
- (r: any) => r.pattern === pattern,
656
+ (r: any) => r.pattern === pattern
656
657
  );
657
658
 
658
659
  if (existingRoute) {
@@ -663,12 +664,12 @@ async function setupRoutes(
663
664
  {
664
665
  pattern,
665
666
  script: workerName,
666
- },
667
+ }
667
668
  );
668
669
 
669
670
  if (!updateRouteResponse.ok) {
670
671
  console.warn(
671
- `Failed to update route ${pattern}: ${updateRouteResponse.status} ${updateRouteResponse.statusText}`,
672
+ `Failed to update route ${pattern}: ${updateRouteResponse.status} ${updateRouteResponse.statusText}`
672
673
  );
673
674
  }
674
675
  }
@@ -679,12 +680,12 @@ async function setupRoutes(
679
680
  {
680
681
  pattern,
681
682
  script: workerName,
682
- },
683
+ }
683
684
  );
684
685
 
685
686
  if (!createRouteResponse.ok) {
686
687
  throw new Error(
687
- `Failed to create route ${pattern}: ${createRouteResponse.status} ${createRouteResponse.statusText}`,
688
+ `Failed to create route ${pattern}: ${createRouteResponse.status} ${createRouteResponse.statusText}`
688
689
  );
689
690
  }
690
691
  }
@@ -696,7 +697,7 @@ async function configureURL<B extends Bindings>(
696
697
  ctx: Context<Worker<B>>,
697
698
  api: CloudflareApi,
698
699
  workerName: string,
699
- url: boolean,
700
+ url: boolean
700
701
  ) {
701
702
  let workerUrl;
702
703
  if (url) {
@@ -706,17 +707,17 @@ async function configureURL<B extends Bindings>(
706
707
  { enabled: true, previews_enabled: true },
707
708
  {
708
709
  headers: { "Content-Type": "application/json" },
709
- },
710
+ }
710
711
  );
711
712
 
712
713
  // Get the account's workers.dev subdomain
713
714
  const subdomainResponse = await api.get(
714
- `/accounts/${api.accountId}/workers/subdomain`,
715
+ `/accounts/${api.accountId}/workers/subdomain`
715
716
  );
716
717
 
717
718
  if (!subdomainResponse.ok) {
718
719
  throw new Error(
719
- `Could not fetch workers.dev subdomain: ${subdomainResponse.status} ${subdomainResponse.statusText}`,
720
+ `Could not fetch workers.dev subdomain: ${subdomainResponse.status} ${subdomainResponse.statusText}`
720
721
  );
721
722
  }
722
723
  const subdomainData: {
@@ -743,11 +744,11 @@ async function configureURL<B extends Bindings>(
743
744
  JSON.stringify({ enabled: false }),
744
745
  {
745
746
  headers: { "Content-Type": "application/json" },
746
- },
747
+ }
747
748
  );
748
749
  if (!response.ok) {
749
750
  throw new Error(
750
- `Failed to disable worker URL: ${response.status} ${response.statusText}`,
751
+ `Failed to disable worker URL: ${response.status} ${response.statusText}`
751
752
  );
752
753
  }
753
754
  }
@@ -756,17 +757,17 @@ async function configureURL<B extends Bindings>(
756
757
 
757
758
  async function getWorkerScriptMetadata(
758
759
  api: CloudflareApi,
759
- workerName: string,
760
+ workerName: string
760
761
  ): Promise<WorkerScriptMetadata | undefined> {
761
762
  const response = await api.get(
762
- `/accounts/${api.accountId}/workers/services/${workerName}`,
763
+ `/accounts/${api.accountId}/workers/services/${workerName}`
763
764
  );
764
765
  if (response.status === 404) {
765
766
  return undefined;
766
767
  }
767
768
  if (!response.ok) {
768
769
  throw new Error(
769
- `Error getting worker script metadata: ${response.status} ${response.statusText}`,
770
+ `Error getting worker script metadata: ${response.status} ${response.statusText}`
770
771
  );
771
772
  }
772
773
  return ((await response.json()) as any).result as WorkerScriptMetadata;
@@ -775,7 +776,7 @@ async function getWorkerScriptMetadata(
775
776
  async function getWorkerBindings(
776
777
  api: CloudflareApi,
777
778
  workerName: string,
778
- environment = "production",
779
+ environment = "production"
779
780
  ) {
780
781
  const response = await api.get(
781
782
  `/accounts/${api.accountId}/workers/services/${workerName}/environments/${environment}/bindings`,
@@ -784,7 +785,7 @@ async function getWorkerBindings(
784
785
  Authorization: `Bearer ${process.env.CLOUDFLARE_API_TOKEN}`,
785
786
  "Content-Type": "application/json",
786
787
  },
787
- },
788
+ }
788
789
  );
789
790
 
790
791
  if (response.status === 404) {
@@ -793,7 +794,7 @@ async function getWorkerBindings(
793
794
 
794
795
  if (!response.ok) {
795
796
  throw new Error(
796
- `Failed to fetch bindings: ${response.status} ${response.statusText}`,
797
+ `Failed to fetch bindings: ${response.status} ${response.statusText}`
797
798
  );
798
799
  }
799
800
 
@@ -341,8 +341,8 @@ export const Zone = Resource(
341
341
  );
342
342
  }
343
343
 
344
- const result = (await response.json()) as CloudflareZoneResponse;
345
- const zoneData = result.result;
344
+ const zoneData = ((await response.json()) as { result: CloudflareZone })
345
+ .result;
346
346
 
347
347
  // Update zone settings if provided
348
348
  if (props.settings) {
@@ -380,18 +380,39 @@ export const Zone = Resource(
380
380
  });
381
381
 
382
382
  const body = await response.text();
383
+ let zoneData;
383
384
  if (!response.ok) {
384
385
  if (response.status === 400 && body.includes("already exists")) {
385
- // TODO: should we error?
386
+ // Zone already exists, fetch it instead
387
+ console.warn(
388
+ `Zone '${props.name}' already exists during Zone create, adopting it...`,
389
+ );
390
+ const getResponse = await api.get(`/zones?name=${props.name}`);
391
+
392
+ if (!getResponse.ok) {
393
+ throw new Error(
394
+ `Error fetching existing zone '${props.name}': ${getResponse.statusText}`,
395
+ );
396
+ }
397
+
398
+ const zones = (
399
+ (await getResponse.json()) as { result: CloudflareZone[] }
400
+ ).result;
401
+ if (zones.length === 0) {
402
+ throw new Error(
403
+ `Zone '${props.name}' does not exist, but the name is reserved for another user.`,
404
+ );
405
+ }
406
+ zoneData = zones[0];
386
407
  } else {
387
408
  throw new Error(
388
409
  `Error creating zone '${props.name}': ${response.statusText}\n${body}`,
389
410
  );
390
411
  }
412
+ } else {
413
+ zoneData = (JSON.parse(body) as { result: CloudflareZone }).result;
391
414
  }
392
415
 
393
- const zoneData = (JSON.parse(body) as CloudflareZoneResponse).result;
394
-
395
416
  // Update zone settings if provided
396
417
  if (props.settings) {
397
418
  await updateZoneSettings(api, zoneData.id, props.settings);
@@ -522,25 +543,20 @@ async function getZoneSettings(
522
543
  }
523
544
 
524
545
  /**
525
- * Cloudflare Zone API response
546
+ * Cloudflare Zone response format
526
547
  */
527
- interface CloudflareZoneResponse {
528
- result: {
548
+ export interface CloudflareZone {
549
+ id: string;
550
+ name: string;
551
+ type: "full" | "partial" | "secondary";
552
+ status: string;
553
+ paused: boolean;
554
+ account: {
529
555
  id: string;
530
- name: string;
531
- type: "full" | "partial" | "secondary";
532
- status: string;
533
- paused: boolean;
534
- account: {
535
- id: string;
536
- };
537
- name_servers: string[];
538
- original_name_servers: string[] | null;
539
- created_on: string;
540
- modified_on: string;
541
- activated_on: string | null;
542
556
  };
543
- success: boolean;
544
- errors: Array<{ code: number; message: string }>;
545
- messages: string[];
557
+ name_servers: string[];
558
+ original_name_servers: string[] | null;
559
+ created_on: string;
560
+ modified_on: string;
561
+ activated_on: string | null;
546
562
  }
@@ -0,0 +1,29 @@
1
+ export type UpdateNameserversOptions = {
2
+ domain: string;
3
+ apiKey: string;
4
+ apiSecret: string;
5
+ nameservers: string[];
6
+ };
7
+
8
+ export async function updateNameservers(options: UpdateNameserversOptions) {
9
+ const url = `https://api.godaddy.com/v1/domains/${options.domain}/nameservers`;
10
+
11
+ const response = await fetch(url, {
12
+ method: "PUT",
13
+ headers: {
14
+ Authorization: `sso-key ${options.apiKey}:${options.apiSecret}`,
15
+ "Content-Type": "application/json",
16
+ Accept: "application/json",
17
+ },
18
+ body: JSON.stringify({
19
+ nameservers: options.nameservers,
20
+ }),
21
+ });
22
+
23
+ if (response.ok) {
24
+ console.log(`✅ Nameservers updated for ${options.domain}`);
25
+ } else {
26
+ const error = await response.json();
27
+ console.error(`❌ Failed to update nameservers:`, error);
28
+ }
29
+ }
@@ -0,0 +1,213 @@
1
+ import type { Context } from "../context";
2
+ import { Resource } from "../resource";
3
+ import { DEFAULT_RECORD_TYPES, type DnsRecordType } from "./record";
4
+
5
+ /**
6
+ * DNS record response structure from Cloudflare DNS API
7
+ * with both data (original) and content (for compatibility)
8
+ */
9
+ export interface DnsApiRecord {
10
+ name: string;
11
+ type: DnsRecordType;
12
+ TTL: number;
13
+ data: string;
14
+ content: string; // Added for compatibility with DnsRecords
15
+ ttl: number; // Normalized ttl (lowercase)
16
+ priority?: number; // Priority for MX and SRV records
17
+ }
18
+
19
+ /**
20
+ * Cloudflare DNS-over-HTTPS API response structure
21
+ */
22
+ interface CloudflareDNSResponse {
23
+ Status: number;
24
+ TC: boolean;
25
+ RD: boolean;
26
+ RA: boolean;
27
+ AD: boolean;
28
+ CD: boolean;
29
+ Question: Array<{
30
+ name: string;
31
+ type: number;
32
+ }>;
33
+ Answer?: {
34
+ name: string;
35
+ type: number;
36
+ TTL: number;
37
+ data: string;
38
+ }[];
39
+ }
40
+
41
+ /**
42
+ * Properties for importing DNS records
43
+ */
44
+ export interface ImportDnsRecordsProps {
45
+ /**
46
+ * The domain to fetch DNS records for
47
+ */
48
+ domain: string;
49
+
50
+ /**
51
+ * Specific record types to fetch. If not provided, defaults to all supported types.
52
+ */
53
+ recordTypes?: DnsRecordType[];
54
+
55
+ /**
56
+ * Bump the resource to force a new import
57
+ */
58
+ bump?: number;
59
+ }
60
+
61
+ /**
62
+ * Output returned after DNS records import
63
+ */
64
+ export interface ImportDnsRecords
65
+ extends Resource<"dns::ImportDnsRecords">,
66
+ ImportDnsRecordsProps {
67
+ /**
68
+ * The DNS records as a flat array, directly compatible with DnsRecords function
69
+ */
70
+ records: DnsApiRecord[];
71
+
72
+ /**
73
+ * Time at which the records were imported
74
+ */
75
+ importedAt: number;
76
+ }
77
+
78
+ /**
79
+ * Map numeric DNS record type to string type
80
+ */
81
+ function mapDnsRecordType(type: number): DnsRecordType {
82
+ const typeMap: Record<number, DnsRecordType> = {
83
+ 1: "A",
84
+ 2: "NS",
85
+ 5: "CNAME",
86
+ 6: "SOA",
87
+ 15: "MX",
88
+ 16: "TXT",
89
+ 28: "AAAA",
90
+ 33: "SRV",
91
+ 12: "PTR",
92
+ };
93
+
94
+ return typeMap[type] || "A";
95
+ }
96
+
97
+ /**
98
+ * Import DNS records for a domain using Cloudflare's DNS-over-HTTPS API.
99
+ * This resource allows you to fetch DNS records for a domain and store them
100
+ * in a structured format.
101
+ *
102
+ * @example
103
+ * // Import all default record types
104
+ * const allRecords = await ImportDnsRecords("example.com", {
105
+ * domain: "example.com"
106
+ * });
107
+ *
108
+ * @example
109
+ * // Import only specific record types
110
+ * const specificRecords = await ImportDnsRecords("example.com", {
111
+ * domain: "example.com",
112
+ * recordTypes: ["A", "MX"]
113
+ * });
114
+ *
115
+ * @example
116
+ * // Import DNS records and transfer them to a Cloudflare zone
117
+ * const dnsRecords = await ImportDnsRecords("dns-records", {
118
+ * domain: "example.com",
119
+ * });
120
+ *
121
+ * const zone = await Zone("example.com", {
122
+ * name: "example.com",
123
+ * type: "full",
124
+ * });
125
+ *
126
+ * // Records are directly compatible with DnsRecords function
127
+ * await DnsRecords("transfer-dns-records", {
128
+ * zoneId: zone.id,
129
+ * records: dnsRecords.records,
130
+ * });
131
+ */
132
+ export const ImportDnsRecords = Resource(
133
+ "dns::ImportDnsRecords",
134
+ async function (
135
+ this: Context<ImportDnsRecords>,
136
+ id: string,
137
+ props: ImportDnsRecordsProps,
138
+ ): Promise<ImportDnsRecords> {
139
+ // For delete phase, just return destroyed state since this is a read-only resource
140
+ if (this.phase === "delete") {
141
+ return this.destroy();
142
+ }
143
+
144
+ const recordTypes = props.recordTypes || DEFAULT_RECORD_TYPES;
145
+ const allRecords: DnsApiRecord[] = [];
146
+
147
+ for (const type of recordTypes) {
148
+ try {
149
+ const res = await fetch(
150
+ `https://cloudflare-dns.com/dns-query?name=${props.domain}&type=${type}`,
151
+ {
152
+ headers: {
153
+ accept: "application/dns-json",
154
+ },
155
+ },
156
+ );
157
+
158
+ if (!res.ok) {
159
+ throw new Error(`Failed to fetch ${type} records: ${res.statusText}`);
160
+ }
161
+
162
+ const data = (await res.json()) as CloudflareDNSResponse;
163
+
164
+ if (data.Answer) {
165
+ // Transform records to be compatible with DnsRecords function
166
+ const compatRecords = data.Answer.map((record) => {
167
+ const recordType = mapDnsRecordType(record.type);
168
+ const result: DnsApiRecord = {
169
+ name: record.name,
170
+ type: recordType,
171
+ TTL: record.TTL,
172
+ data: record.data,
173
+ content: record.data, // Default mapping
174
+ ttl: record.TTL, // Normalized lowercase ttl
175
+ };
176
+
177
+ // Special handling for MX records to split priority and content
178
+ if (recordType === "MX") {
179
+ const parts = record.data.split(" ");
180
+ if (parts.length >= 2) {
181
+ const priority = Number.parseInt(parts[0], 10);
182
+ // Join the rest as hostname in case there are spaces
183
+ const hostname = parts.slice(1).join(" ");
184
+
185
+ if (!isNaN(priority)) {
186
+ result.priority = priority;
187
+ result.content = hostname;
188
+ }
189
+ }
190
+ }
191
+
192
+ return result;
193
+ });
194
+
195
+ allRecords.push(...compatRecords);
196
+ }
197
+ } catch (error) {
198
+ console.warn(
199
+ `Failed to fetch ${type} records for ${props.domain}:`,
200
+ error,
201
+ );
202
+ }
203
+ }
204
+
205
+ // Return the resource with fetched records as a flat array
206
+ return this({
207
+ domain: props.domain,
208
+ recordTypes: [...recordTypes],
209
+ records: allRecords,
210
+ importedAt: Date.now(),
211
+ });
212
+ },
213
+ );
@@ -0,0 +1,2 @@
1
+ export * from "./import-dns";
2
+ export * from "./record";