alchemy 0.1.11 → 0.1.13

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 (312) hide show
  1. package/README.md +324 -0
  2. package/lib/$.d.ts +8 -3
  3. package/lib/$.d.ts.map +1 -1
  4. package/lib/$.js +38 -27
  5. package/lib/$.js.map +1 -1
  6. package/lib/alchemize.d.ts.map +1 -1
  7. package/lib/alchemize.js +7 -0
  8. package/lib/alchemize.js.map +1 -1
  9. package/lib/apply.d.ts +2 -2
  10. package/lib/apply.d.ts.map +1 -1
  11. package/lib/apply.js +3 -3
  12. package/lib/apply.js.map +1 -1
  13. package/lib/aws/index.d.ts +1 -0
  14. package/lib/aws/index.d.ts.map +1 -1
  15. package/lib/aws/index.js +1 -0
  16. package/lib/aws/index.js.map +1 -1
  17. package/lib/aws/ses.d.ts +75 -0
  18. package/lib/aws/ses.d.ts.map +1 -0
  19. package/lib/aws/ses.js +197 -0
  20. package/lib/aws/ses.js.map +1 -0
  21. package/lib/cloudflare/api.d.ts +88 -0
  22. package/lib/cloudflare/api.d.ts.map +1 -0
  23. package/lib/cloudflare/api.js +184 -0
  24. package/lib/cloudflare/api.js.map +1 -0
  25. package/lib/cloudflare/asset-manifest.d.ts +15 -0
  26. package/lib/cloudflare/asset-manifest.d.ts.map +1 -0
  27. package/lib/cloudflare/asset-manifest.js +2 -0
  28. package/lib/cloudflare/asset-manifest.js.map +1 -0
  29. package/lib/cloudflare/auth.d.ts +52 -0
  30. package/lib/cloudflare/auth.d.ts.map +1 -0
  31. package/lib/cloudflare/auth.js +114 -0
  32. package/lib/cloudflare/auth.js.map +1 -0
  33. package/lib/cloudflare/bindings.d.ts +254 -0
  34. package/lib/cloudflare/bindings.d.ts.map +1 -0
  35. package/lib/cloudflare/bindings.js +2 -0
  36. package/lib/cloudflare/bindings.js.map +1 -0
  37. package/lib/cloudflare/durable-object-namespace.d.ts +23 -0
  38. package/lib/cloudflare/durable-object-namespace.d.ts.map +1 -0
  39. package/lib/cloudflare/durable-object-namespace.js +26 -0
  40. package/lib/cloudflare/durable-object-namespace.js.map +1 -0
  41. package/lib/cloudflare/generate-asset-manifest.d.ts +3 -0
  42. package/lib/cloudflare/generate-asset-manifest.d.ts.map +1 -0
  43. package/lib/cloudflare/generate-asset-manifest.js +69 -0
  44. package/lib/cloudflare/generate-asset-manifest.js.map +1 -0
  45. package/lib/cloudflare/index.d.ts +7 -0
  46. package/lib/cloudflare/index.d.ts.map +1 -0
  47. package/lib/cloudflare/index.js +7 -0
  48. package/lib/cloudflare/index.js.map +1 -0
  49. package/lib/cloudflare/kv-namespace.d.ts +61 -0
  50. package/lib/cloudflare/kv-namespace.d.ts.map +1 -0
  51. package/lib/cloudflare/kv-namespace.js +93 -0
  52. package/lib/cloudflare/kv-namespace.js.map +1 -0
  53. package/lib/cloudflare/state.d.ts +83 -0
  54. package/lib/cloudflare/state.d.ts.map +1 -0
  55. package/lib/cloudflare/state.js +109 -0
  56. package/lib/cloudflare/state.js.map +1 -0
  57. package/lib/cloudflare/static-site-router.d.ts +19 -0
  58. package/lib/cloudflare/static-site-router.d.ts.map +1 -0
  59. package/lib/cloudflare/static-site-router.js +110 -0
  60. package/lib/cloudflare/static-site-router.js.map +1 -0
  61. package/lib/cloudflare/static-site.d.ts +166 -0
  62. package/lib/cloudflare/static-site.d.ts.map +1 -0
  63. package/lib/cloudflare/static-site.js +168 -0
  64. package/lib/cloudflare/static-site.js.map +1 -0
  65. package/lib/cloudflare/types.d.ts +52 -0
  66. package/lib/cloudflare/types.d.ts.map +1 -0
  67. package/lib/cloudflare/types.js +21 -0
  68. package/lib/cloudflare/types.js.map +1 -0
  69. package/lib/cloudflare/upload-asset-manifest.d.ts +12 -0
  70. package/lib/cloudflare/upload-asset-manifest.d.ts.map +1 -0
  71. package/lib/cloudflare/upload-asset-manifest.js +55 -0
  72. package/lib/cloudflare/upload-asset-manifest.js.map +1 -0
  73. package/lib/cloudflare/worker-metadata.d.ts +130 -0
  74. package/lib/cloudflare/worker-metadata.d.ts.map +1 -0
  75. package/lib/cloudflare/worker-metadata.js +2 -0
  76. package/lib/cloudflare/worker-metadata.js.map +1 -0
  77. package/lib/cloudflare/worker-migration.d.ts +110 -0
  78. package/lib/cloudflare/worker-migration.d.ts.map +1 -0
  79. package/lib/cloudflare/worker-migration.js +11 -0
  80. package/lib/cloudflare/worker-migration.js.map +1 -0
  81. package/lib/cloudflare/worker.d.ts +105 -0
  82. package/lib/cloudflare/worker.d.ts.map +1 -0
  83. package/lib/cloudflare/worker.js +358 -0
  84. package/lib/cloudflare/worker.js.map +1 -0
  85. package/lib/destroy.d.ts +1 -1
  86. package/lib/destroy.d.ts.map +1 -1
  87. package/lib/destroy.js +2 -1
  88. package/lib/destroy.js.map +1 -1
  89. package/lib/esbuild.d.ts +83 -1
  90. package/lib/esbuild.d.ts.map +1 -1
  91. package/lib/esbuild.js +27 -18
  92. package/lib/esbuild.js.map +1 -1
  93. package/lib/fs.d.ts +1 -1
  94. package/lib/global.d.ts +0 -2
  95. package/lib/global.d.ts.map +1 -1
  96. package/lib/global.js +3 -3
  97. package/lib/global.js.map +1 -1
  98. package/lib/index.d.ts +1 -0
  99. package/lib/index.d.ts.map +1 -1
  100. package/lib/index.js +1 -0
  101. package/lib/index.js.map +1 -1
  102. package/lib/main.js +1 -1
  103. package/lib/main.js.map +1 -1
  104. package/lib/output.d.ts +18 -13
  105. package/lib/output.d.ts.map +1 -1
  106. package/lib/output.js +12 -5
  107. package/lib/output.js.map +1 -1
  108. package/lib/print.d.ts +7 -0
  109. package/lib/print.d.ts.map +1 -0
  110. package/lib/print.js +10 -0
  111. package/lib/print.js.map +1 -0
  112. package/lib/resource.d.ts +9 -4
  113. package/lib/resource.d.ts.map +1 -1
  114. package/lib/resource.js +127 -93
  115. package/lib/resource.js.map +1 -1
  116. package/lib/scope.d.ts +3 -0
  117. package/lib/scope.d.ts.map +1 -1
  118. package/lib/scope.js +4 -12
  119. package/lib/scope.js.map +1 -1
  120. package/lib/utils/content-type.d.ts +8 -0
  121. package/lib/utils/content-type.d.ts.map +1 -0
  122. package/lib/utils/content-type.js +41 -0
  123. package/lib/utils/content-type.js.map +1 -0
  124. package/package.json +27 -8
  125. package/src/$.ts +63 -30
  126. package/src/alchemize.ts +12 -0
  127. package/src/apply.ts +6 -6
  128. package/src/aws/index.ts +1 -0
  129. package/src/aws/ses.ts +346 -0
  130. package/src/cloudflare/api.ts +282 -0
  131. package/src/cloudflare/asset-manifest.ts +16 -0
  132. package/src/cloudflare/auth.ts +187 -0
  133. package/src/cloudflare/bindings.ts +297 -0
  134. package/src/cloudflare/durable-object-namespace.ts +43 -0
  135. package/src/cloudflare/generate-asset-manifest.ts +93 -0
  136. package/src/cloudflare/index.ts +6 -0
  137. package/src/cloudflare/kv-namespace.ts +195 -0
  138. package/src/cloudflare/state.ts +138 -0
  139. package/src/cloudflare/static-site-router.ts +150 -0
  140. package/src/cloudflare/static-site.ts +395 -0
  141. package/src/cloudflare/types.ts +71 -0
  142. package/src/cloudflare/upload-asset-manifest.ts +83 -0
  143. package/src/cloudflare/worker-metadata.ts +154 -0
  144. package/src/cloudflare/worker-migration.ts +145 -0
  145. package/src/cloudflare/worker.ts +666 -0
  146. package/src/destroy.ts +2 -2
  147. package/src/esbuild.ts +34 -22
  148. package/src/global.ts +1 -3
  149. package/src/index.ts +1 -1
  150. package/src/main.ts +1 -1
  151. package/src/output.ts +53 -37
  152. package/src/print.ts +18 -0
  153. package/src/resource.ts +163 -116
  154. package/src/scope.ts +7 -12
  155. package/src/utils/content-type.ts +42 -0
  156. package/lib/agent/ai.d.ts +0 -40
  157. package/lib/agent/ai.d.ts.map +0 -1
  158. package/lib/agent/ai.js +0 -132
  159. package/lib/agent/ai.js.map +0 -1
  160. package/lib/agent/anthropic.d.ts +0 -9
  161. package/lib/agent/anthropic.d.ts.map +0 -1
  162. package/lib/agent/anthropic.js +0 -25
  163. package/lib/agent/anthropic.js.map +0 -1
  164. package/lib/agent/dependencies.d.ts +0 -4
  165. package/lib/agent/dependencies.d.ts.map +0 -1
  166. package/lib/agent/dependencies.js +0 -48
  167. package/lib/agent/dependencies.js.map +0 -1
  168. package/lib/agent/file-context.d.ts +0 -13
  169. package/lib/agent/file-context.d.ts.map +0 -1
  170. package/lib/agent/file-context.js +0 -6
  171. package/lib/agent/file-context.js.map +0 -1
  172. package/lib/agent/index.d.ts +0 -10
  173. package/lib/agent/index.d.ts.map +0 -1
  174. package/lib/agent/index.js +0 -10
  175. package/lib/agent/index.js.map +0 -1
  176. package/lib/agent/model.d.ts +0 -8
  177. package/lib/agent/model.d.ts.map +0 -1
  178. package/lib/agent/model.js +0 -29
  179. package/lib/agent/model.js.map +0 -1
  180. package/lib/agent/module.d.ts +0 -81
  181. package/lib/agent/module.d.ts.map +0 -1
  182. package/lib/agent/module.js +0 -309
  183. package/lib/agent/module.js.map +0 -1
  184. package/lib/agent/openai.d.ts +0 -6
  185. package/lib/agent/openai.d.ts.map +0 -1
  186. package/lib/agent/openai.js +0 -41
  187. package/lib/agent/openai.js.map +0 -1
  188. package/lib/agent/program.d.ts +0 -12
  189. package/lib/agent/program.d.ts.map +0 -1
  190. package/lib/agent/program.js +0 -21
  191. package/lib/agent/program.js.map +0 -1
  192. package/lib/agent/prompts.d.ts +0 -4
  193. package/lib/agent/prompts.d.ts.map +0 -1
  194. package/lib/agent/prompts.js +0 -11
  195. package/lib/agent/prompts.js.map +0 -1
  196. package/lib/agent/scrape.d.ts +0 -71
  197. package/lib/agent/scrape.d.ts.map +0 -1
  198. package/lib/agent/scrape.js +0 -45
  199. package/lib/agent/scrape.js.map +0 -1
  200. package/lib/aws/auto/docs.d.ts +0 -11
  201. package/lib/aws/auto/docs.d.ts.map +0 -1
  202. package/lib/aws/auto/docs.js +0 -56
  203. package/lib/aws/auto/docs.js.map +0 -1
  204. package/lib/aws/auto/index.d.ts +0 -5
  205. package/lib/aws/auto/index.d.ts.map +0 -1
  206. package/lib/aws/auto/index.js +0 -5
  207. package/lib/aws/auto/index.js.map +0 -1
  208. package/lib/aws/auto/resource.d.ts +0 -24
  209. package/lib/aws/auto/resource.d.ts.map +0 -1
  210. package/lib/aws/auto/resource.js +0 -194
  211. package/lib/aws/auto/resource.js.map +0 -1
  212. package/lib/aws/auto/service.d.ts +0 -10
  213. package/lib/aws/auto/service.d.ts.map +0 -1
  214. package/lib/aws/auto/service.js +0 -34
  215. package/lib/aws/auto/service.js.map +0 -1
  216. package/lib/aws/auto/spec.d.ts +0 -83
  217. package/lib/aws/auto/spec.d.ts.map +0 -1
  218. package/lib/aws/auto/spec.js +0 -58
  219. package/lib/aws/auto/spec.js.map +0 -1
  220. package/lib/markdown/design.d.ts +0 -17
  221. package/lib/markdown/design.d.ts.map +0 -1
  222. package/lib/markdown/design.js +0 -92
  223. package/lib/markdown/design.js.map +0 -1
  224. package/lib/markdown/extract.d.ts +0 -7
  225. package/lib/markdown/extract.d.ts.map +0 -1
  226. package/lib/markdown/extract.js +0 -10
  227. package/lib/markdown/extract.js.map +0 -1
  228. package/lib/markdown/index.d.ts +0 -4
  229. package/lib/markdown/index.d.ts.map +0 -1
  230. package/lib/markdown/index.js +0 -4
  231. package/lib/markdown/index.js.map +0 -1
  232. package/lib/markdown/requirements.d.ts +0 -79
  233. package/lib/markdown/requirements.d.ts.map +0 -1
  234. package/lib/markdown/requirements.js +0 -105
  235. package/lib/markdown/requirements.js.map +0 -1
  236. package/lib/typescript/check-omission.d.ts +0 -2
  237. package/lib/typescript/check-omission.d.ts.map +0 -1
  238. package/lib/typescript/check-omission.js +0 -43
  239. package/lib/typescript/check-omission.js.map +0 -1
  240. package/lib/typescript/debug-type-errors.d.ts +0 -3
  241. package/lib/typescript/debug-type-errors.d.ts.map +0 -1
  242. package/lib/typescript/debug-type-errors.js +0 -35
  243. package/lib/typescript/debug-type-errors.js.map +0 -1
  244. package/lib/typescript/extract.d.ts +0 -7
  245. package/lib/typescript/extract.d.ts.map +0 -1
  246. package/lib/typescript/extract.js +0 -10
  247. package/lib/typescript/extract.js.map +0 -1
  248. package/lib/typescript/index.d.ts +0 -7
  249. package/lib/typescript/index.d.ts.map +0 -1
  250. package/lib/typescript/index.js +0 -7
  251. package/lib/typescript/index.js.map +0 -1
  252. package/lib/typescript/install-dependencies.d.ts +0 -29
  253. package/lib/typescript/install-dependencies.d.ts.map +0 -1
  254. package/lib/typescript/install-dependencies.js +0 -52
  255. package/lib/typescript/install-dependencies.js.map +0 -1
  256. package/lib/typescript/package.d.ts +0 -157
  257. package/lib/typescript/package.d.ts.map +0 -1
  258. package/lib/typescript/package.js +0 -115
  259. package/lib/typescript/package.js.map +0 -1
  260. package/lib/typescript/repair-omissions.d.ts +0 -3
  261. package/lib/typescript/repair-omissions.d.ts.map +0 -1
  262. package/lib/typescript/repair-omissions.js +0 -23
  263. package/lib/typescript/repair-omissions.js.map +0 -1
  264. package/lib/typescript/repair.d.ts +0 -3
  265. package/lib/typescript/repair.d.ts.map +0 -1
  266. package/lib/typescript/repair.js +0 -34
  267. package/lib/typescript/repair.js.map +0 -1
  268. package/lib/typescript/tsconfig.d.ts +0 -469
  269. package/lib/typescript/tsconfig.d.ts.map +0 -1
  270. package/lib/typescript/tsconfig.js +0 -143
  271. package/lib/typescript/tsconfig.js.map +0 -1
  272. package/lib/typescript/typescript.d.ts +0 -107
  273. package/lib/typescript/typescript.d.ts.map +0 -1
  274. package/lib/typescript/typescript.js +0 -180
  275. package/lib/typescript/typescript.js.map +0 -1
  276. package/lib/typescript/validate.d.ts +0 -10
  277. package/lib/typescript/validate.d.ts.map +0 -1
  278. package/lib/typescript/validate.js +0 -49
  279. package/lib/typescript/validate.js.map +0 -1
  280. package/src/agent/ai.ts +0 -215
  281. package/src/agent/anthropic.ts +0 -31
  282. package/src/agent/dependencies.ts +0 -62
  283. package/src/agent/file-context.ts +0 -8
  284. package/src/agent/index.ts +0 -9
  285. package/src/agent/model.ts +0 -39
  286. package/src/agent/module.ts +0 -418
  287. package/src/agent/openai.ts +0 -50
  288. package/src/agent/program.md +0 -38
  289. package/src/agent/program.ts +0 -37
  290. package/src/agent/prompts.ts +0 -20
  291. package/src/agent/scrape.ts +0 -47
  292. package/src/aws/auto/cfn.json +0 -225310
  293. package/src/aws/auto/docs.ts +0 -73
  294. package/src/aws/auto/index.ts +0 -4
  295. package/src/aws/auto/resource.ts +0 -269
  296. package/src/aws/auto/service.ts +0 -51
  297. package/src/aws/auto/spec.ts +0 -160
  298. package/src/markdown/design.ts +0 -125
  299. package/src/markdown/extract.ts +0 -9
  300. package/src/markdown/index.ts +0 -3
  301. package/src/markdown/requirements.ts +0 -135
  302. package/src/typescript/check-omission.ts +0 -47
  303. package/src/typescript/debug-type-errors.ts +0 -42
  304. package/src/typescript/extract.ts +0 -9
  305. package/src/typescript/index.ts +0 -6
  306. package/src/typescript/install-dependencies.ts +0 -61
  307. package/src/typescript/package.ts +0 -143
  308. package/src/typescript/repair-omissions.ts +0 -31
  309. package/src/typescript/repair.ts +0 -45
  310. package/src/typescript/tsconfig.ts +0 -170
  311. package/src/typescript/typescript.ts +0 -217
  312. package/src/typescript/validate.ts +0 -73
package/src/alchemize.ts CHANGED
@@ -51,6 +51,7 @@ export async function alchemize(options?: AlchemizeOptions) {
51
51
  const stateStore = new (options?.stateStore ?? defaultStateStore)(
52
52
  scope.getScopePath(stage),
53
53
  );
54
+ const callbacks = scope.callbacks;
54
55
 
55
56
  if (nodes.size > 0) {
56
57
  await stateStore.init?.();
@@ -81,6 +82,17 @@ export async function alchemize(options?: AlchemizeOptions) {
81
82
  if (options?.destroyOrphans === false) {
82
83
  return;
83
84
  }
85
+
86
+ await Promise.all(
87
+ callbacks.map((callback) =>
88
+ evaluate(callback, {
89
+ stage,
90
+ scope,
91
+ stateStore,
92
+ quiet: options?.quiet,
93
+ }),
94
+ ),
95
+ );
84
96
  }
85
97
  const aliveIDs = new Set(
86
98
  mode === "up"
package/src/apply.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { defaultStage, defaultStateStore } from "./global";
2
- import { Output } from "./output";
2
+ import { type Output, OutputChain, type Resolved } from "./output";
3
3
  import {
4
4
  Input,
5
5
  Provider,
@@ -23,9 +23,9 @@ export interface ApplyOptions {
23
23
  * @returns The resource properties.
24
24
  */
25
25
  export async function apply<T>(
26
- output: T | Output<T>,
26
+ output: T,
27
27
  options?: Partial<ApplyOptions>,
28
- ): Promise<T> {
28
+ ): Promise<Resolved<T>> {
29
29
  const stage = options?.stage ?? defaultStage;
30
30
  const scope = options?.scope ?? getScope();
31
31
  const statePath = scope.getScopePath(stage);
@@ -33,7 +33,7 @@ export async function apply<T>(
33
33
 
34
34
  return (
35
35
  await evaluate(output, { stage, scope, stateStore, quiet: options?.quiet })
36
- ).value;
36
+ ).value as Resolved<T>;
37
37
  }
38
38
 
39
39
  class Evaluated<T> {
@@ -83,11 +83,11 @@ export async function evaluate<T>(
83
83
  );
84
84
  resolve!(new Evaluated(result, [resourceID, ...deps]));
85
85
  } catch (error) {
86
- console.error(error);
86
+ // console.error(error);
87
87
  reject!(error);
88
88
  }
89
89
  return promise;
90
- } else if (output instanceof Output) {
90
+ } else if (output instanceof OutputChain) {
91
91
  const inside = output as unknown as {
92
92
  parent: Output<any>;
93
93
  fn: (value: any) => T;
package/src/aws/index.ts CHANGED
@@ -3,4 +3,5 @@ export * from "./function";
3
3
  export * from "./policy";
4
4
  export * from "./queue";
5
5
  export * from "./role";
6
+ export * from "./ses";
6
7
  export * from "./table";
package/src/aws/ses.ts ADDED
@@ -0,0 +1,346 @@
1
+ import {
2
+ AlreadyExistsException,
3
+ CreateConfigurationSetCommand,
4
+ CreateEmailIdentityCommand,
5
+ DeleteConfigurationSetCommand,
6
+ DeleteEmailIdentityCommand,
7
+ type DeliveryOptions,
8
+ GetConfigurationSetCommand,
9
+ GetEmailIdentityCommand,
10
+ NotFoundException,
11
+ PutEmailIdentityDkimAttributesCommand,
12
+ type ReputationOptions,
13
+ SESv2Client,
14
+ type SendingOptions,
15
+ type SuppressionOptions,
16
+ type TrackingOptions,
17
+ } from "@aws-sdk/client-sesv2";
18
+ import { ignore } from "../error";
19
+ import { type Context, Resource } from "../resource";
20
+
21
+ /**
22
+ * Properties for configuring AWS SES resources
23
+ */
24
+ export interface SESProps {
25
+ /**
26
+ * Name of the configuration set
27
+ */
28
+ configurationSetName?: string;
29
+
30
+ /**
31
+ * Email identity to verify (email address or domain)
32
+ */
33
+ emailIdentity?: string;
34
+
35
+ /**
36
+ * Whether to enable DKIM signing for the email identity
37
+ */
38
+ enableDkim?: boolean;
39
+
40
+ /**
41
+ * Sending options for the configuration set
42
+ */
43
+ sendingOptions?: SendingOptions;
44
+
45
+ /**
46
+ * Reputation options for the configuration set
47
+ */
48
+ reputationOptions?: ReputationOptions;
49
+
50
+ /**
51
+ * Tracking options for the configuration set
52
+ */
53
+ trackingOptions?: TrackingOptions;
54
+
55
+ /**
56
+ * Suppression options for the configuration set
57
+ */
58
+ suppressionOptions?: SuppressionOptions;
59
+
60
+ /**
61
+ * Delivery options for the configuration set
62
+ */
63
+ deliveryOptions?: DeliveryOptions;
64
+
65
+ /**
66
+ * Tags to apply to the SES resources
67
+ */
68
+ tags?: Record<string, string>;
69
+ }
70
+
71
+ /**
72
+ * Output returned after SES resource creation/update
73
+ */
74
+ export interface SESOutput extends SESProps {
75
+ /**
76
+ * Resource ID
77
+ */
78
+ id: string;
79
+
80
+ /**
81
+ * ARN of the configuration set if created
82
+ */
83
+ configurationSetArn?: string;
84
+
85
+ /**
86
+ * Email identity verification status if an identity was created
87
+ */
88
+ emailIdentityVerificationStatus?: string;
89
+
90
+ /**
91
+ * DKIM verification status if DKIM was enabled
92
+ */
93
+ dkimVerificationStatus?: string;
94
+
95
+ /**
96
+ * Email identity ARN if an identity was created
97
+ */
98
+ emailIdentityArn?: string;
99
+ }
100
+
101
+ /**
102
+ * AWS SES resource for creating and configuring Simple Email Service
103
+ */
104
+ export class SES extends Resource(
105
+ "aws::SES",
106
+ async (ctx: Context<SESOutput>, props: SESProps): Promise<SESOutput> => {
107
+ // Create SES client
108
+ const client = new SESv2Client({});
109
+
110
+ // Resource ID is either based on the configuration set name or email identity
111
+ const id =
112
+ props.configurationSetName || props.emailIdentity || ctx.resourceID;
113
+
114
+ // Handle deletion
115
+ if (ctx.event === "delete") {
116
+ const output = ctx.output;
117
+
118
+ // Delete configuration set if it exists
119
+ if (output?.configurationSetName) {
120
+ await ignore(NotFoundException.name, () =>
121
+ client.send(
122
+ new DeleteConfigurationSetCommand({
123
+ ConfigurationSetName: output.configurationSetName,
124
+ }),
125
+ ),
126
+ );
127
+ }
128
+
129
+ // Delete email identity if it exists
130
+ if (output?.emailIdentity) {
131
+ await ignore(NotFoundException.name, () =>
132
+ client.send(
133
+ new DeleteEmailIdentityCommand({
134
+ EmailIdentity: output.emailIdentity,
135
+ }),
136
+ ),
137
+ );
138
+ }
139
+
140
+ // Return empty output for delete
141
+ return {
142
+ ...props,
143
+ id,
144
+ };
145
+ }
146
+
147
+ // Created resources
148
+ let configurationSetArn: string | undefined;
149
+ let emailIdentityArn: string | undefined;
150
+ let emailIdentityVerificationStatus: string | undefined;
151
+ let dkimVerificationStatus: string | undefined;
152
+
153
+ // Create or update configuration set if specified
154
+ if (props.configurationSetName) {
155
+ try {
156
+ // Check if configuration set exists
157
+ let getConfigResult;
158
+ try {
159
+ getConfigResult = await client.send(
160
+ new GetConfigurationSetCommand({
161
+ ConfigurationSetName: props.configurationSetName,
162
+ }),
163
+ );
164
+ } catch (error) {
165
+ if (error instanceof NotFoundException) {
166
+ getConfigResult = null;
167
+ } else {
168
+ throw error;
169
+ }
170
+ }
171
+
172
+ if (getConfigResult) {
173
+ // For updating configuration set, we need to use individual commands
174
+ // since there's no unified UpdateConfigurationSetCommand
175
+
176
+ if (props.sendingOptions) {
177
+ await client.send(
178
+ new CreateConfigurationSetCommand({
179
+ ConfigurationSetName: props.configurationSetName,
180
+ SendingOptions: props.sendingOptions,
181
+ }),
182
+ );
183
+ }
184
+
185
+ if (props.reputationOptions) {
186
+ await client.send(
187
+ new CreateConfigurationSetCommand({
188
+ ConfigurationSetName: props.configurationSetName,
189
+ ReputationOptions: props.reputationOptions,
190
+ }),
191
+ );
192
+ }
193
+
194
+ if (props.trackingOptions) {
195
+ await client.send(
196
+ new CreateConfigurationSetCommand({
197
+ ConfigurationSetName: props.configurationSetName,
198
+ TrackingOptions: props.trackingOptions,
199
+ }),
200
+ );
201
+ }
202
+
203
+ if (props.suppressionOptions) {
204
+ await client.send(
205
+ new CreateConfigurationSetCommand({
206
+ ConfigurationSetName: props.configurationSetName,
207
+ SuppressionOptions: props.suppressionOptions,
208
+ }),
209
+ );
210
+ }
211
+
212
+ if (props.deliveryOptions) {
213
+ await client.send(
214
+ new CreateConfigurationSetCommand({
215
+ ConfigurationSetName: props.configurationSetName,
216
+ DeliveryOptions: props.deliveryOptions,
217
+ }),
218
+ );
219
+ }
220
+
221
+ // In SESv2, the ARN isn't directly returned in the response
222
+ configurationSetArn = `arn:aws:ses:${process.env.AWS_REGION}:${process.env.AWS_ACCOUNT_ID}:configuration-set/${props.configurationSetName}`;
223
+ } else {
224
+ // Create new configuration set
225
+ await client.send(
226
+ new CreateConfigurationSetCommand({
227
+ ConfigurationSetName: props.configurationSetName,
228
+ SendingOptions: props.sendingOptions,
229
+ ReputationOptions: props.reputationOptions,
230
+ TrackingOptions: props.trackingOptions,
231
+ SuppressionOptions: props.suppressionOptions,
232
+ DeliveryOptions: props.deliveryOptions,
233
+ Tags: Object.entries(props.tags || {}).map(([Key, Value]) => ({
234
+ Key,
235
+ Value,
236
+ })),
237
+ }),
238
+ );
239
+
240
+ // In SESv2, the ARN isn't directly returned in the response
241
+ configurationSetArn = `arn:aws:ses:${process.env.AWS_REGION}:${process.env.AWS_ACCOUNT_ID}:configuration-set/${props.configurationSetName}`;
242
+ }
243
+ } catch (error: any) {
244
+ if (error instanceof AlreadyExistsException) {
245
+ // If it already exists but we couldn't get it (permissions issue), continue
246
+ console.warn(
247
+ `Configuration set ${props.configurationSetName} already exists but cannot be retrieved`,
248
+ );
249
+ } else {
250
+ throw error;
251
+ }
252
+ }
253
+ }
254
+
255
+ // Create or verify email identity if specified
256
+ if (props.emailIdentity) {
257
+ try {
258
+ // Check if identity exists
259
+ let getIdentityResult;
260
+ try {
261
+ getIdentityResult = await client.send(
262
+ new GetEmailIdentityCommand({
263
+ EmailIdentity: props.emailIdentity,
264
+ }),
265
+ );
266
+ } catch (error) {
267
+ if (error instanceof NotFoundException) {
268
+ getIdentityResult = null;
269
+ } else {
270
+ throw error;
271
+ }
272
+ }
273
+
274
+ if (!getIdentityResult) {
275
+ // Create new email identity
276
+ const createIdentityResult = await client.send(
277
+ new CreateEmailIdentityCommand({
278
+ EmailIdentity: props.emailIdentity,
279
+ Tags: Object.entries(props.tags || {}).map(([Key, Value]) => ({
280
+ Key,
281
+ Value,
282
+ })),
283
+ }),
284
+ );
285
+
286
+ // If it's an email address, we don't need to explicitly verify in v2
287
+ // The verification email is automatically sent by SES in v2
288
+
289
+ // Store the identity information
290
+ emailIdentityArn = `arn:aws:ses:${process.env.AWS_REGION}:${process.env.AWS_ACCOUNT_ID}:identity/${props.emailIdentity}`;
291
+ emailIdentityVerificationStatus =
292
+ createIdentityResult.VerifiedForSendingStatus
293
+ ? "VERIFIED"
294
+ : "PENDING";
295
+ } else {
296
+ // Store the identity information
297
+ emailIdentityArn = `arn:aws:ses:${process.env.AWS_REGION}:${process.env.AWS_ACCOUNT_ID}:identity/${props.emailIdentity}`;
298
+ emailIdentityVerificationStatus =
299
+ getIdentityResult.VerifiedForSendingStatus ? "VERIFIED" : "PENDING";
300
+
301
+ // Update DKIM settings if requested
302
+ if (props.enableDkim !== undefined) {
303
+ if (props.enableDkim) {
304
+ await client.send(
305
+ new PutEmailIdentityDkimAttributesCommand({
306
+ EmailIdentity: props.emailIdentity,
307
+ SigningEnabled: true,
308
+ }),
309
+ );
310
+
311
+ // We can check the current status of DKIM
312
+ dkimVerificationStatus = "PENDING"; // Default to pending
313
+
314
+ // Get the updated identity to check DKIM status
315
+ const updatedIdentity = await client.send(
316
+ new GetEmailIdentityCommand({
317
+ EmailIdentity: props.emailIdentity,
318
+ }),
319
+ );
320
+
321
+ if (updatedIdentity.DkimAttributes?.Status) {
322
+ dkimVerificationStatus = updatedIdentity.DkimAttributes.Status;
323
+ }
324
+ }
325
+ }
326
+ }
327
+ } catch (error) {
328
+ console.error(
329
+ `Error configuring email identity ${props.emailIdentity}:`,
330
+ error,
331
+ );
332
+ throw error;
333
+ }
334
+ }
335
+
336
+ // Return the resource output
337
+ return {
338
+ ...props,
339
+ id,
340
+ configurationSetArn,
341
+ emailIdentityArn,
342
+ emailIdentityVerificationStatus,
343
+ dkimVerificationStatus,
344
+ };
345
+ },
346
+ ) {}
@@ -0,0 +1,282 @@
1
+ import { type CloudflareAuthOptions, getCloudflareHeaders } from "./auth";
2
+
3
+ /**
4
+ * Options for Cloudflare API requests
5
+ */
6
+ export interface CloudflareApiOptions {
7
+ /**
8
+ * API Key to use (overrides CLOUDFLARE_API_KEY env var)
9
+ */
10
+ apiKey?: string;
11
+
12
+ /**
13
+ * Account ID to use (overrides CLOUDFLARE_ACCOUNT_ID env var)
14
+ * If not provided, will be automatically retrieved from the Cloudflare API
15
+ */
16
+ accountId?: string;
17
+
18
+ /**
19
+ * Zone ID to use (overrides CLOUDFLARE_ZONE_ID env var)
20
+ */
21
+ zoneId?: string;
22
+
23
+ /**
24
+ * Email to use with API Key authentication
25
+ * If not provided, will attempt to discover from Cloudflare API
26
+ */
27
+ email?: string;
28
+ }
29
+
30
+ /**
31
+ * Account information returned from Cloudflare API
32
+ */
33
+ interface CloudflareAccount {
34
+ id: string;
35
+ name: string;
36
+ }
37
+
38
+ /**
39
+ * Creates a CloudflareApi instance with automatic account ID discovery if not provided
40
+ *
41
+ * @param options API options
42
+ * @returns Promise resolving to a CloudflareApi instance
43
+ */
44
+ export async function createCloudflareApi(
45
+ options: CloudflareApiOptions = {},
46
+ ): Promise<CloudflareApi> {
47
+ try {
48
+ return new CloudflareApi({
49
+ ...options,
50
+ accountId:
51
+ options.accountId ||
52
+ process.env.CLOUDFLARE_ACCOUNT_ID ||
53
+ (await fetchAccountId()),
54
+ });
55
+ } catch (error) {
56
+ console.error("Error during Cloudflare account ID discovery:", error);
57
+ throw new Error(
58
+ "Failed to automatically discover Cloudflare account ID. Please provide an account ID explicitly or ensure your API token/key has sufficient permissions.",
59
+ );
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Cloudflare API client using raw fetch
65
+ */
66
+ export class CloudflareApi {
67
+ /** Base URL for Cloudflare API */
68
+ readonly baseUrl: string;
69
+
70
+ /** Cloudflare API Key */
71
+ readonly apiKey: string;
72
+
73
+ /** Cloudflare account ID */
74
+ readonly accountId: string;
75
+
76
+ /** Cloudflare zone ID (if provided) */
77
+ readonly zoneId: string | null;
78
+
79
+ /** User email (when using API Key auth) */
80
+ readonly email?: string;
81
+
82
+ /** Auth options for making requests */
83
+ private readonly authOptions: CloudflareAuthOptions;
84
+
85
+ /**
86
+ * Create a new Cloudflare API client
87
+ * Use createCloudflareApi factory function instead of direct constructor
88
+ * for automatic account ID discovery.
89
+ *
90
+ * @param options API options
91
+ */
92
+ constructor(options: CloudflareApiOptions = {}) {
93
+ this.baseUrl = "https://api.cloudflare.com/client/v4";
94
+
95
+ const apiKey =
96
+ options.apiKey || process.env.CLOUDFLARE_API_KEY || undefined;
97
+ if (!apiKey) {
98
+ throw new Error(
99
+ "No API key provided. Use createCloudflareApi() instead for automatic account discovery.",
100
+ );
101
+ }
102
+ this.apiKey = apiKey;
103
+
104
+ this.email = options.email || process.env.CLOUDFLARE_EMAIL;
105
+ if (!this.email) {
106
+ throw new Error(
107
+ "No email provided. Use createCloudflareApi() instead for automatic account discovery.",
108
+ );
109
+ }
110
+
111
+ // Get account ID from options or environment
112
+ this.accountId =
113
+ options.accountId || process.env.CLOUDFLARE_ACCOUNT_ID || "";
114
+ if (!this.accountId) {
115
+ throw new Error(
116
+ "No account ID provided. Use createCloudflareApi() instead for automatic account discovery.",
117
+ );
118
+ }
119
+
120
+ // Zone ID is optional for some operations
121
+ this.zoneId = options.zoneId || process.env.CLOUDFLARE_ZONE_ID || null;
122
+
123
+ // Store auth options for later use
124
+ this.authOptions = {
125
+ email: this.email,
126
+ };
127
+ }
128
+
129
+ /**
130
+ * Make a fetch request to the Cloudflare API
131
+ *
132
+ * @param path API path (without base URL)
133
+ * @param init Fetch init options
134
+ * @returns Raw Response object from fetch
135
+ */
136
+ async fetch(path: string, init: RequestInit = {}): Promise<Response> {
137
+ // Get content type from init headers or default to application/json
138
+ let contentType = "application/json";
139
+ if (init.headers) {
140
+ const initHeaders = init.headers as Record<string, string>;
141
+ if (initHeaders["Content-Type"]) {
142
+ contentType = initHeaders["Content-Type"];
143
+ }
144
+ }
145
+
146
+ // Get auth headers (async now)
147
+ const authHeaders = await getCloudflareHeaders(
148
+ contentType,
149
+ this.authOptions,
150
+ );
151
+
152
+ // Combine all headers
153
+ const combinedHeaders: Record<string, string> = {
154
+ ...authHeaders,
155
+ };
156
+
157
+ // Add headers from init if provided
158
+ if (init.headers) {
159
+ const initHeadersObj = init.headers as Record<string, string>;
160
+ Object.keys(initHeadersObj).forEach((key) => {
161
+ combinedHeaders[key] = initHeadersObj[key];
162
+ });
163
+ }
164
+
165
+ // If using FormData, remove Content-Type to let browser set it with boundary
166
+ if (init.body instanceof FormData) {
167
+ delete combinedHeaders["Content-Type"];
168
+ }
169
+
170
+ const url = `${this.baseUrl}${path}`;
171
+
172
+ // Make the request
173
+ return fetch(url, {
174
+ ...init,
175
+ headers: combinedHeaders,
176
+ });
177
+ }
178
+
179
+ /**
180
+ * Helper for GET requests
181
+ */
182
+ async get(path: string, init: RequestInit = {}): Promise<Response> {
183
+ return this.fetch(path, { ...init, method: "GET" });
184
+ }
185
+
186
+ /**
187
+ * Helper for HEAD requests
188
+ */
189
+ async head(path: string, init: RequestInit = {}): Promise<Response> {
190
+ return this.fetch(path, { ...init, method: "HEAD" });
191
+ }
192
+ /**
193
+ * Helper for POST requests
194
+ */
195
+ async post(
196
+ path: string,
197
+ body: any,
198
+ init: RequestInit = {},
199
+ ): Promise<Response> {
200
+ const requestBody =
201
+ body instanceof FormData
202
+ ? body
203
+ : typeof body === "string"
204
+ ? body
205
+ : JSON.stringify(body);
206
+ return this.fetch(path, { ...init, method: "POST", body: requestBody });
207
+ }
208
+
209
+ /**
210
+ * Helper for PUT requests
211
+ */
212
+ async put(
213
+ path: string,
214
+ body: any,
215
+ init: RequestInit = {},
216
+ ): Promise<Response> {
217
+ const requestBody = body instanceof FormData ? body : JSON.stringify(body);
218
+ return this.fetch(path, { ...init, method: "PUT", body: requestBody });
219
+ }
220
+
221
+ /**
222
+ * Helper for PATCH requests
223
+ */
224
+ async patch(
225
+ path: string,
226
+ body: any,
227
+ init: RequestInit = {},
228
+ ): Promise<Response> {
229
+ return this.fetch(path, {
230
+ ...init,
231
+ method: "PATCH",
232
+ body: JSON.stringify(body),
233
+ });
234
+ }
235
+
236
+ /**
237
+ * Helper for DELETE requests
238
+ */
239
+ async delete(path: string, init: RequestInit = {}): Promise<Response> {
240
+ return this.fetch(path, { ...init, method: "DELETE" });
241
+ }
242
+ }
243
+
244
+ /**
245
+ * Create a temporary API client for bootstrapping
246
+ * This client can only be used to fetch the account ID
247
+ */
248
+ async function fetchAccountId(): Promise<string> {
249
+ // Create a minimal API client for bootstrapping
250
+ const baseUrl = "https://api.cloudflare.com/client/v4";
251
+
252
+ // Get content type and auth headers
253
+ const authHeaders = await getCloudflareHeaders("application/json");
254
+
255
+ // Make the request to get accounts
256
+ const response = await fetch(`${baseUrl}/accounts`, {
257
+ method: "GET",
258
+ headers: authHeaders,
259
+ });
260
+
261
+ if (!response.ok) {
262
+ const errorData: any = await response.json().catch(() => ({
263
+ errors: [{ message: response.statusText }],
264
+ }));
265
+
266
+ throw new Error(
267
+ `Error fetching Cloudflare accounts: ${errorData.errors?.[0]?.message || response.statusText}`,
268
+ );
269
+ }
270
+
271
+ const data: any = await response.json();
272
+ const accounts = data.result as CloudflareAccount[];
273
+
274
+ if (!accounts || accounts.length === 0) {
275
+ throw new Error(
276
+ "No Cloudflare accounts found. Check your API token/key permissions.",
277
+ );
278
+ }
279
+
280
+ // Return the first account ID
281
+ return accounts[0].id;
282
+ }