alchemy 0.1.17 → 0.2.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 (301) hide show
  1. package/lib/alchemy.d.ts +67 -0
  2. package/lib/alchemy.js +40 -0
  3. package/lib/apply.d.ts +3 -20
  4. package/lib/apply.js +86 -78
  5. package/lib/aws/account-id.d.ts +0 -1
  6. package/lib/aws/account-id.js +0 -1
  7. package/lib/aws/bucket.d.ts +4 -7
  8. package/lib/aws/bucket.js +8 -12
  9. package/lib/aws/function.d.ts +4 -7
  10. package/lib/aws/function.js +9 -30
  11. package/lib/aws/index.d.ts +1 -1
  12. package/lib/aws/index.js +1 -1
  13. package/lib/aws/oidc/github-oidc-provider.d.ts +2 -6
  14. package/lib/aws/oidc/github-oidc-provider.js +10 -15
  15. package/lib/aws/oidc/index.d.ts +0 -1
  16. package/lib/aws/oidc/index.js +0 -1
  17. package/lib/aws/oidc/oidc-provider.d.ts +4 -6
  18. package/lib/aws/oidc/oidc-provider.js +9 -11
  19. package/lib/aws/policy-attachment.d.ts +9 -0
  20. package/lib/aws/policy-attachment.js +20 -0
  21. package/lib/aws/policy.d.ts +4 -16
  22. package/lib/aws/policy.js +9 -40
  23. package/lib/aws/queue.d.ts +4 -34
  24. package/lib/aws/queue.js +10 -20
  25. package/lib/aws/role.d.ts +4 -7
  26. package/lib/aws/role.js +17 -26
  27. package/lib/aws/ses.d.ts +4 -10
  28. package/lib/aws/ses.js +118 -131
  29. package/lib/aws/table.d.ts +4 -7
  30. package/lib/aws/table.js +8 -11
  31. package/lib/cloudflare/api.d.ts +0 -1
  32. package/lib/cloudflare/api.js +0 -1
  33. package/lib/cloudflare/asset-manifest.d.ts +0 -1
  34. package/lib/cloudflare/asset-manifest.js +0 -1
  35. package/lib/cloudflare/auth.d.ts +0 -1
  36. package/lib/cloudflare/auth.js +0 -1
  37. package/lib/cloudflare/bindings.d.ts +4 -3
  38. package/lib/cloudflare/bindings.js +3 -2
  39. package/lib/cloudflare/bound.d.ts +7 -0
  40. package/lib/cloudflare/bound.js +1 -0
  41. package/lib/cloudflare/bucket.d.ts +7 -15
  42. package/lib/cloudflare/bucket.js +12 -19
  43. package/lib/cloudflare/durable-object-namespace.d.ts +0 -3
  44. package/lib/cloudflare/durable-object-namespace.js +0 -4
  45. package/lib/cloudflare/generate-asset-manifest.d.ts +0 -1
  46. package/lib/cloudflare/generate-asset-manifest.js +1 -2
  47. package/lib/cloudflare/index.d.ts +0 -1
  48. package/lib/cloudflare/index.js +0 -1
  49. package/lib/cloudflare/kv-namespace.d.ts +6 -9
  50. package/lib/cloudflare/kv-namespace.js +16 -20
  51. package/lib/cloudflare/state.d.ts +0 -1
  52. package/lib/cloudflare/state.js +0 -1
  53. package/lib/cloudflare/static-site-router.d.ts +0 -1
  54. package/lib/cloudflare/static-site-router.js +0 -4
  55. package/lib/cloudflare/static-site.d.ts +5 -11
  56. package/lib/cloudflare/static-site.js +16 -28
  57. package/lib/cloudflare/types.d.ts +0 -1
  58. package/lib/cloudflare/types.js +0 -1
  59. package/lib/cloudflare/upload-asset-manifest.d.ts +0 -1
  60. package/lib/cloudflare/upload-asset-manifest.js +0 -1
  61. package/lib/cloudflare/worker-metadata.d.ts +0 -1
  62. package/lib/cloudflare/worker-metadata.js +0 -1
  63. package/lib/cloudflare/worker-migration.d.ts +0 -1
  64. package/lib/cloudflare/worker-migration.js +0 -1
  65. package/lib/cloudflare/worker.d.ts +13 -13
  66. package/lib/cloudflare/worker.js +43 -43
  67. package/lib/context.d.ts +58 -0
  68. package/lib/context.js +36 -0
  69. package/lib/destroy.d.ts +10 -13
  70. package/lib/destroy.js +104 -43
  71. package/lib/{esbuild.d.ts → esbuild/bundle.d.ts} +4 -18
  72. package/lib/{esbuild.js → esbuild/bundle.js} +10 -24
  73. package/lib/esbuild/index.d.ts +1 -0
  74. package/lib/esbuild/index.js +1 -0
  75. package/lib/fs/file.d.ts +13 -0
  76. package/lib/fs/file.js +20 -0
  77. package/lib/fs/folder.d.ts +10 -0
  78. package/lib/fs/folder.js +16 -0
  79. package/lib/fs/index.d.ts +2 -0
  80. package/lib/fs/index.js +2 -0
  81. package/lib/github/client.d.ts +0 -1
  82. package/lib/github/client.js +0 -1
  83. package/lib/github/index.d.ts +0 -1
  84. package/lib/github/index.js +0 -1
  85. package/lib/github/secret.d.ts +6 -7
  86. package/lib/github/secret.js +30 -36
  87. package/lib/index.d.ts +7 -10
  88. package/lib/index.js +6 -9
  89. package/lib/resource.d.ts +34 -62
  90. package/lib/resource.js +29 -270
  91. package/lib/scope.d.ts +35 -14
  92. package/lib/scope.js +78 -23
  93. package/lib/secret.d.ts +7 -0
  94. package/lib/secret.js +17 -0
  95. package/lib/state.d.ts +14 -10
  96. package/lib/state.js +15 -13
  97. package/lib/stripe/index.d.ts +0 -1
  98. package/lib/stripe/index.js +0 -1
  99. package/lib/stripe/price.d.ts +4 -5
  100. package/lib/stripe/price.js +10 -20
  101. package/lib/stripe/product.d.ts +4 -5
  102. package/lib/stripe/product.js +10 -20
  103. package/lib/stripe/webhook.d.ts +4 -6
  104. package/lib/stripe/webhook.js +12 -25
  105. package/lib/test/bun.d.ts +19 -0
  106. package/lib/test/bun.js +55 -0
  107. package/lib/{utils → util}/content-type.d.ts +0 -1
  108. package/lib/{utils → util}/content-type.js +0 -1
  109. package/lib/util/encrypt.d.ts +16 -0
  110. package/lib/util/encrypt.js +44 -0
  111. package/lib/{error.d.ts → util/ignore.d.ts} +0 -1
  112. package/lib/{error.js → util/ignore.js} +0 -1
  113. package/lib/{utils → util}/retry.d.ts +0 -1
  114. package/lib/{utils → util}/retry.js +0 -1
  115. package/lib/util/serde.d.ts +3 -0
  116. package/lib/util/serde.js +46 -0
  117. package/lib/util/slugify.d.ts +1 -0
  118. package/lib/util/slugify.js +3 -0
  119. package/package.json +5 -7
  120. package/src/alchemy.ts +120 -0
  121. package/src/apply.ts +109 -111
  122. package/src/aws/bucket.ts +11 -14
  123. package/src/aws/function.ts +13 -33
  124. package/src/aws/index.ts +1 -0
  125. package/src/aws/oidc/github-oidc-provider.ts +14 -19
  126. package/src/aws/oidc/oidc-provider.ts +19 -12
  127. package/src/aws/policy-attachment.ts +51 -0
  128. package/src/aws/policy.ts +15 -60
  129. package/src/aws/queue.ts +17 -21
  130. package/src/aws/role.ts +25 -28
  131. package/src/aws/ses.ts +150 -164
  132. package/src/aws/table.ts +15 -15
  133. package/src/cloudflare/bindings.ts +12 -2
  134. package/src/cloudflare/bound.ts +18 -0
  135. package/src/cloudflare/bucket.ts +40 -48
  136. package/src/cloudflare/durable-object-namespace.ts +0 -10
  137. package/src/cloudflare/generate-asset-manifest.ts +1 -1
  138. package/src/cloudflare/kv-namespace.ts +35 -33
  139. package/src/cloudflare/static-site-router.ts +0 -3
  140. package/src/cloudflare/static-site.ts +43 -59
  141. package/src/cloudflare/worker.ts +76 -64
  142. package/src/context.ts +122 -0
  143. package/src/destroy.ts +124 -73
  144. package/src/{esbuild.ts → esbuild/bundle.ts} +16 -28
  145. package/src/esbuild/index.ts +1 -0
  146. package/src/fs/file.ts +39 -0
  147. package/src/fs/folder.ts +32 -0
  148. package/src/fs/index.ts +2 -0
  149. package/src/github/secret.ts +53 -49
  150. package/src/index.ts +8 -15
  151. package/src/resource.ts +101 -462
  152. package/src/scope.ts +101 -36
  153. package/src/secret.ts +18 -0
  154. package/src/state.ts +30 -20
  155. package/src/stripe/price.ts +17 -22
  156. package/src/stripe/product.ts +17 -22
  157. package/src/stripe/webhook.ts +21 -27
  158. package/src/test/bun.ts +105 -0
  159. package/src/util/encrypt.ts +79 -0
  160. package/src/util/serde.ts +53 -0
  161. package/src/util/slugify.ts +3 -0
  162. package/lib/$.d.ts +0 -12
  163. package/lib/$.d.ts.map +0 -1
  164. package/lib/$.js +0 -42
  165. package/lib/$.js.map +0 -1
  166. package/lib/alchemize.d.ts +0 -43
  167. package/lib/alchemize.d.ts.map +0 -1
  168. package/lib/alchemize.js +0 -112
  169. package/lib/alchemize.js.map +0 -1
  170. package/lib/apply.d.ts.map +0 -1
  171. package/lib/apply.js.map +0 -1
  172. package/lib/aws/account-id.d.ts.map +0 -1
  173. package/lib/aws/account-id.js.map +0 -1
  174. package/lib/aws/bucket.d.ts.map +0 -1
  175. package/lib/aws/bucket.js.map +0 -1
  176. package/lib/aws/function.d.ts.map +0 -1
  177. package/lib/aws/function.js.map +0 -1
  178. package/lib/aws/index.d.ts.map +0 -1
  179. package/lib/aws/index.js.map +0 -1
  180. package/lib/aws/oidc/github-oidc-provider.d.ts.map +0 -1
  181. package/lib/aws/oidc/github-oidc-provider.js.map +0 -1
  182. package/lib/aws/oidc/index.d.ts.map +0 -1
  183. package/lib/aws/oidc/index.js.map +0 -1
  184. package/lib/aws/oidc/oidc-provider.d.ts.map +0 -1
  185. package/lib/aws/oidc/oidc-provider.js.map +0 -1
  186. package/lib/aws/policy.d.ts.map +0 -1
  187. package/lib/aws/policy.js.map +0 -1
  188. package/lib/aws/queue.d.ts.map +0 -1
  189. package/lib/aws/queue.js.map +0 -1
  190. package/lib/aws/role.d.ts.map +0 -1
  191. package/lib/aws/role.js.map +0 -1
  192. package/lib/aws/ses.d.ts.map +0 -1
  193. package/lib/aws/ses.js.map +0 -1
  194. package/lib/aws/table.d.ts.map +0 -1
  195. package/lib/aws/table.js.map +0 -1
  196. package/lib/cloudflare/api.d.ts.map +0 -1
  197. package/lib/cloudflare/api.js.map +0 -1
  198. package/lib/cloudflare/asset-manifest.d.ts.map +0 -1
  199. package/lib/cloudflare/asset-manifest.js.map +0 -1
  200. package/lib/cloudflare/auth.d.ts.map +0 -1
  201. package/lib/cloudflare/auth.js.map +0 -1
  202. package/lib/cloudflare/bindings.d.ts.map +0 -1
  203. package/lib/cloudflare/bindings.js.map +0 -1
  204. package/lib/cloudflare/bucket.d.ts.map +0 -1
  205. package/lib/cloudflare/bucket.js.map +0 -1
  206. package/lib/cloudflare/durable-object-namespace.d.ts.map +0 -1
  207. package/lib/cloudflare/durable-object-namespace.js.map +0 -1
  208. package/lib/cloudflare/generate-asset-manifest.d.ts.map +0 -1
  209. package/lib/cloudflare/generate-asset-manifest.js.map +0 -1
  210. package/lib/cloudflare/index.d.ts.map +0 -1
  211. package/lib/cloudflare/index.js.map +0 -1
  212. package/lib/cloudflare/kv-namespace.d.ts.map +0 -1
  213. package/lib/cloudflare/kv-namespace.js.map +0 -1
  214. package/lib/cloudflare/state.d.ts.map +0 -1
  215. package/lib/cloudflare/state.js.map +0 -1
  216. package/lib/cloudflare/static-site-router.d.ts.map +0 -1
  217. package/lib/cloudflare/static-site-router.js.map +0 -1
  218. package/lib/cloudflare/static-site.d.ts.map +0 -1
  219. package/lib/cloudflare/static-site.js.map +0 -1
  220. package/lib/cloudflare/types.d.ts.map +0 -1
  221. package/lib/cloudflare/types.js.map +0 -1
  222. package/lib/cloudflare/upload-asset-manifest.d.ts.map +0 -1
  223. package/lib/cloudflare/upload-asset-manifest.js.map +0 -1
  224. package/lib/cloudflare/worker-metadata.d.ts.map +0 -1
  225. package/lib/cloudflare/worker-metadata.js.map +0 -1
  226. package/lib/cloudflare/worker-migration.d.ts.map +0 -1
  227. package/lib/cloudflare/worker-migration.js.map +0 -1
  228. package/lib/cloudflare/worker.d.ts.map +0 -1
  229. package/lib/cloudflare/worker.js.map +0 -1
  230. package/lib/destroy.d.ts.map +0 -1
  231. package/lib/destroy.js.map +0 -1
  232. package/lib/error.d.ts.map +0 -1
  233. package/lib/error.js.map +0 -1
  234. package/lib/esbuild.d.ts.map +0 -1
  235. package/lib/esbuild.js.map +0 -1
  236. package/lib/fs.d.ts +0 -12
  237. package/lib/fs.d.ts.map +0 -1
  238. package/lib/fs.js +0 -41
  239. package/lib/fs.js.map +0 -1
  240. package/lib/github/client.d.ts.map +0 -1
  241. package/lib/github/client.js.map +0 -1
  242. package/lib/github/index.d.ts.map +0 -1
  243. package/lib/github/index.js.map +0 -1
  244. package/lib/github/secret.d.ts.map +0 -1
  245. package/lib/github/secret.js.map +0 -1
  246. package/lib/global.d.ts +0 -16
  247. package/lib/global.d.ts.map +0 -1
  248. package/lib/global.js +0 -42
  249. package/lib/global.js.map +0 -1
  250. package/lib/index.d.ts.map +0 -1
  251. package/lib/index.js.map +0 -1
  252. package/lib/input.d.ts +0 -6
  253. package/lib/input.d.ts.map +0 -1
  254. package/lib/input.js +0 -2
  255. package/lib/input.js.map +0 -1
  256. package/lib/main.d.ts +0 -3
  257. package/lib/main.d.ts.map +0 -1
  258. package/lib/main.js +0 -32
  259. package/lib/main.js.map +0 -1
  260. package/lib/output.d.ts +0 -24
  261. package/lib/output.d.ts.map +0 -1
  262. package/lib/output.js +0 -23
  263. package/lib/output.js.map +0 -1
  264. package/lib/print.d.ts +0 -7
  265. package/lib/print.d.ts.map +0 -1
  266. package/lib/print.js +0 -10
  267. package/lib/print.js.map +0 -1
  268. package/lib/resource.d.ts.map +0 -1
  269. package/lib/resource.js.map +0 -1
  270. package/lib/scope.d.ts.map +0 -1
  271. package/lib/scope.js.map +0 -1
  272. package/lib/slug.d.ts +0 -2
  273. package/lib/slug.d.ts.map +0 -1
  274. package/lib/slug.js +0 -4
  275. package/lib/slug.js.map +0 -1
  276. package/lib/state.d.ts.map +0 -1
  277. package/lib/state.js.map +0 -1
  278. package/lib/stripe/index.d.ts.map +0 -1
  279. package/lib/stripe/index.js.map +0 -1
  280. package/lib/stripe/price.d.ts.map +0 -1
  281. package/lib/stripe/price.js.map +0 -1
  282. package/lib/stripe/product.d.ts.map +0 -1
  283. package/lib/stripe/product.js.map +0 -1
  284. package/lib/stripe/webhook.d.ts.map +0 -1
  285. package/lib/stripe/webhook.js.map +0 -1
  286. package/lib/utils/content-type.d.ts.map +0 -1
  287. package/lib/utils/content-type.js.map +0 -1
  288. package/lib/utils/retry.d.ts.map +0 -1
  289. package/lib/utils/retry.js.map +0 -1
  290. package/src/$.ts +0 -69
  291. package/src/alchemize.ts +0 -186
  292. package/src/fs.ts +0 -47
  293. package/src/global.ts +0 -63
  294. package/src/input.ts +0 -16
  295. package/src/main.ts +0 -34
  296. package/src/output.ts +0 -71
  297. package/src/print.ts +0 -18
  298. package/src/slug.ts +0 -3
  299. /package/src/{utils → util}/content-type.ts +0 -0
  300. /package/src/{error.ts → util/ignore.ts} +0 -0
  301. /package/src/{utils → util}/retry.ts +0 -0
@@ -1,4 +1,6 @@
1
- import type { Resolved } from "../output";
1
+ import type { Context } from "../context";
2
+ import { Resource } from "../resource";
3
+ export declare function isKVNamespace(resource: any): resource is KVNamespace;
2
4
  /**
3
5
  * Properties for creating or updating a KV Namespace
4
6
  */
@@ -41,12 +43,12 @@ export interface KVPair {
41
43
  /**
42
44
  * Output returned after KV Namespace creation/update
43
45
  */
44
- export interface KVNamespaceOutput extends KVNamespaceProps {
46
+ export interface KVNamespace extends Resource<"cloudflare::KVNamespace">, KVNamespaceProps {
45
47
  type: "kv_namespace";
46
48
  /**
47
49
  * The ID of the namespace
48
50
  */
49
- id: string;
51
+ namespaceId: string;
50
52
  /**
51
53
  * Time at which the namespace was created
52
54
  */
@@ -56,9 +58,4 @@ export interface KVNamespaceOutput extends KVNamespaceProps {
56
58
  */
57
59
  modifiedAt: number;
58
60
  }
59
- export declare function isKVNamespace(resource: any): resource is Resolved<KVNamespace>;
60
- declare const KVNamespace_base: import("../resource").Provider<"cloudflare::KVNamespace", [props: KVNamespaceProps], KVNamespaceOutput>;
61
- export declare class KVNamespace extends KVNamespace_base {
62
- }
63
- export {};
64
- //# sourceMappingURL=kv-namespace.d.ts.map
61
+ export declare const KVNamespace: ((this: Context<KVNamespace>, id: string, props: KVNamespaceProps) => Promise<KVNamespace>) | (((this: any, id: string, props: KVNamespaceProps) => never) & (new (_: never) => never));
@@ -1,15 +1,15 @@
1
1
  import { Resource } from "../resource";
2
- import { withExponentialBackoff } from "../utils/retry";
2
+ import { withExponentialBackoff } from "../util/retry";
3
3
  import { createCloudflareApi } from "./api";
4
4
  export function isKVNamespace(resource) {
5
5
  return (resource && typeof resource === "object" && resource.type === "kv_namespace");
6
6
  }
7
- export class KVNamespace extends Resource("cloudflare::KVNamespace", async (ctx, props) => {
7
+ export const KVNamespace = Resource("cloudflare::KVNamespace", async function (id, props) {
8
8
  // Create Cloudflare API client with automatic account discovery
9
9
  const api = await createCloudflareApi();
10
- if (ctx.event === "delete") {
10
+ if (this.phase === "delete") {
11
11
  // For delete operations, we need to check if the namespace ID exists in the output
12
- const namespaceId = ctx.output?.id;
12
+ const namespaceId = this.output?.namespaceId;
13
13
  if (namespaceId) {
14
14
  // Delete KV namespace
15
15
  const deleteResponse = await api.delete(`/accounts/${api.accountId}/storage/kv/namespaces/${namespaceId}`);
@@ -21,19 +21,20 @@ export class KVNamespace extends Resource("cloudflare::KVNamespace", async (ctx,
21
21
  }
22
22
  }
23
23
  // Return minimal output for deleted state
24
- return;
24
+ return this.destroy();
25
25
  }
26
26
  else {
27
27
  // For create or update operations
28
- // If ctx.event is "update", we expect ctx.output to exist
29
- let namespaceId = ctx.event === "update" ? ctx.output?.id || "" : "";
30
- let createdAt = ctx.event === "update"
31
- ? ctx.output?.createdAt || Date.now()
28
+ // If this.phase is "update", we expect this.output to exist
29
+ let namespaceId = this.phase === "update" ? this.output?.namespaceId || "" : "";
30
+ let createdAt = this.phase === "update"
31
+ ? this.output?.createdAt || Date.now()
32
32
  : Date.now();
33
- if (ctx.event === "update" && namespaceId) {
33
+ if (this.phase === "update" && namespaceId) {
34
34
  // Can't update a KV namespace title directly, just work with existing ID
35
35
  }
36
36
  else {
37
+ // TODO: if it already exists, then check the tags to see if we own it and continue
37
38
  // Create new KV namespace
38
39
  const createResponse = await api.post(`/accounts/${api.accountId}/storage/kv/namespaces`, {
39
40
  title: props.title,
@@ -97,18 +98,13 @@ export class KVNamespace extends Resource("cloudflare::KVNamespace", async (ctx,
97
98
  }
98
99
  }
99
100
  }
100
- const now = Date.now();
101
- // Construct the output
102
- const output = {
101
+ return this({
103
102
  type: "kv_namespace",
104
- id: namespaceId,
103
+ namespaceId: namespaceId,
105
104
  title: props.title,
106
105
  values: props.values,
107
106
  createdAt: createdAt,
108
- modifiedAt: now,
109
- };
110
- return output;
107
+ modifiedAt: Date.now(),
108
+ });
111
109
  }
112
- }) {
113
- }
114
- //# sourceMappingURL=kv-namespace.js.map
110
+ });
@@ -80,4 +80,3 @@ export declare class CloudflareStateStore implements StateStore {
80
80
  */
81
81
  private getNamespacedKey;
82
82
  }
83
- //# sourceMappingURL=state.d.ts.map
@@ -106,4 +106,3 @@ export class CloudflareStateStore {
106
106
  return `${this.prefix}${key}`;
107
107
  }
108
108
  }
109
- //# sourceMappingURL=state.js.map
@@ -16,4 +16,3 @@ declare const _default: {
16
16
  fetch(request: Request, env: Env): Promise<Response>;
17
17
  };
18
18
  export default _default;
19
- //# sourceMappingURL=static-site-router.d.ts.map
@@ -7,14 +7,12 @@ export default {
7
7
  isInit = true;
8
8
  for (const [key, value] of Object.entries(env)) {
9
9
  if (key.startsWith("ROUTE_")) {
10
- console.log(key, value);
11
10
  const service = value;
12
11
  const patternKey = `__${key}`;
13
12
  if (!(patternKey in env)) {
14
13
  throw new Error(`Missing pattern key: ${patternKey}`);
15
14
  }
16
15
  const pattern = env[`__${key}`];
17
- console.log("pattern", pattern);
18
16
  // TODO(sam): we should support narrowing down METHOD
19
17
  app.all(pattern, (ctx) => service.fetch(ctx.req.raw));
20
18
  }
@@ -25,7 +23,6 @@ export default {
25
23
  };
26
24
  // fall back to assuming the request is a static file
27
25
  app.notFound(async (ctx) => {
28
- console.log("not found");
29
26
  const request = ctx.req;
30
27
  const env = ctx.env;
31
28
  const url = new URL(request.url);
@@ -107,4 +104,3 @@ app.notFound(async (ctx) => {
107
104
  return response;
108
105
  }
109
106
  });
110
- //# sourceMappingURL=static-site-router.js.map
@@ -1,4 +1,6 @@
1
- import type { BundleProps } from "../esbuild";
1
+ import type { Context } from "../context";
2
+ import type { BundleProps } from "../esbuild/bundle";
3
+ import { Resource } from "../resource";
2
4
  import { Worker } from "./worker";
3
5
  /**
4
6
  * Idea of using KV with timeout inspired by SST: https://github.com/sst/sst/blob/dev/platform/src/components/cloudflare/static-site.ts
@@ -98,11 +100,7 @@ export interface StaticSiteProps {
98
100
  /**
99
101
  * Output returned after StaticSite creation/update
100
102
  */
101
- export interface StaticSiteOutput {
102
- /**
103
- * The ID of the site
104
- */
105
- id: string;
103
+ export interface StaticSite extends Resource<"cloudflare::StaticSite"> {
106
104
  /**
107
105
  * The ID of the worker
108
106
  */
@@ -159,8 +157,4 @@ export interface StaticSiteOutput {
159
157
  */
160
158
  routes?: Record<string, string>;
161
159
  }
162
- declare const StaticSite_base: import("../resource").Provider<string, [props: StaticSiteProps], StaticSiteOutput>;
163
- export declare class StaticSite extends StaticSite_base {
164
- }
165
- export {};
166
- //# sourceMappingURL=static-site.d.ts.map
160
+ export declare const StaticSite: ((this: Context<StaticSite>, id: string, props: StaticSiteProps) => Promise<StaticSite>) | (((this: any, id: string, props: StaticSiteProps) => never) & (new (_: never) => never));
@@ -2,7 +2,6 @@ import { exec } from "child_process";
2
2
  import { promises as fs } from "fs";
3
3
  import path from "path";
4
4
  import { promisify } from "util";
5
- import { apply } from "../apply";
6
5
  import { Resource } from "../resource";
7
6
  import { createCloudflareApi } from "./api";
8
7
  import { generateAssetManifest } from "./generate-asset-manifest";
@@ -10,13 +9,13 @@ import { KVNamespace } from "./kv-namespace";
10
9
  import { uploadAssetManifest } from "./upload-asset-manifest";
11
10
  import { Worker } from "./worker";
12
11
  const __dirname = import.meta.dirname;
13
- export class StaticSite extends Resource("cloudflare::StaticSite", {
12
+ export const StaticSite = Resource("cloudflare::StaticSite", {
14
13
  alwaysUpdate: true,
15
- }, async (ctx, props) => {
16
- if (ctx.event === "delete") {
14
+ }, async function (id, props) {
15
+ if (this.phase === "delete") {
17
16
  // For delete operations, we'll rely on the Worker delete to clean up
18
17
  // Return empty output for deleted state
19
- return;
18
+ return this.destroy();
20
19
  }
21
20
  // Validate that a name is provided
22
21
  if (!props.name) {
@@ -38,14 +37,14 @@ export class StaticSite extends Resource("cloudflare::StaticSite", {
38
37
  // Run build command if provided
39
38
  if (props.build?.command) {
40
39
  try {
41
- if (!ctx.quiet) {
40
+ if (!this.quiet) {
42
41
  console.log(props.build.command);
43
42
  }
44
43
  const execAsync = promisify(exec);
45
44
  const { stdout, stderr } = await execAsync(props.build.command, {
46
45
  cwd: process.cwd(),
47
46
  });
48
- if (stdout && !ctx.quiet)
47
+ if (stdout && !this.quiet)
49
48
  console.log(stdout);
50
49
  }
51
50
  catch (error) {
@@ -65,17 +64,14 @@ export class StaticSite extends Resource("cloudflare::StaticSite", {
65
64
  // Create Cloudflare API client with automatic account discovery
66
65
  const api = await createCloudflareApi();
67
66
  // Step 1: Create or get the KV namespace for assets
68
- const kv = new KVNamespace("assets", {
69
- title: `${siteName}-assets`,
70
- });
71
- const [{ id: kvNamespaceId }, assetManifest] = await Promise.all([
72
- apply(kv, {
73
- quiet: ctx.quiet,
67
+ const [kv, assetManifest] = await Promise.all([
68
+ KVNamespace("assets", {
69
+ title: `${siteName}-assets`,
74
70
  }),
75
71
  generateAssetManifest(props.dir),
76
72
  ]);
77
73
  // Step 3: Upload assets to KV
78
- await uploadAssetManifest(api, kvNamespaceId, assetManifest);
74
+ await uploadAssetManifest(api, kv.namespaceId, assetManifest);
79
75
  // Prepare the bindings for the worker
80
76
  const bindings = {
81
77
  ASSETS: kv,
@@ -115,7 +111,7 @@ export class StaticSite extends Resource("cloudflare::StaticSite", {
115
111
  catch (error) {
116
112
  // TypeScript file doesn't exist, use JavaScript (default)
117
113
  }
118
- const worker = new Worker("worker", {
114
+ const worker = await Worker("worker", {
119
115
  name: siteName,
120
116
  url: true,
121
117
  format: props.format || "esm",
@@ -133,14 +129,9 @@ export class StaticSite extends Resource("cloudflare::StaticSite", {
133
129
  key,
134
130
  ])),
135
131
  },
136
- },
137
- // place a dependency on the namespace
138
- kv.id);
139
- // Get current timestamp
132
+ });
140
133
  const now = Date.now();
141
- // Construct the output
142
- const output = {
143
- id: siteName,
134
+ return this({
144
135
  workerId: worker.id,
145
136
  name: siteName,
146
137
  directory: props.dir,
@@ -148,14 +139,11 @@ export class StaticSite extends Resource("cloudflare::StaticSite", {
148
139
  errorPage: props.errorPage,
149
140
  indexPage,
150
141
  assets: assetManifest.map((item) => item.key),
151
- createdAt: ctx.output?.createdAt || now,
142
+ createdAt: this.output?.createdAt || now,
152
143
  updatedAt: now,
153
144
  domain: props.domain,
154
145
  production: props.production !== false,
155
146
  url: worker.url,
156
147
  routes,
157
- };
158
- return output;
159
- }) {
160
- }
161
- //# sourceMappingURL=static-site.js.map
148
+ });
149
+ });
@@ -49,4 +49,3 @@ export interface CloudflareApiError {
49
49
  * @returns Formatted error message
50
50
  */
51
51
  export declare function extractCloudflareError(response: Response): Promise<string>;
52
- //# sourceMappingURL=types.d.ts.map
@@ -18,4 +18,3 @@ export async function extractCloudflareError(response) {
18
18
  return `HTTP ${response.status}: ${response.statusText}`;
19
19
  }
20
20
  }
21
- //# sourceMappingURL=types.js.map
@@ -9,4 +9,3 @@ type UploadResult = {
9
9
  };
10
10
  export declare function uploadAssetManifest(api: CloudflareApi, namespaceId: string, manifest: AssetManifest): Promise<UploadResult[]>;
11
11
  export {};
12
- //# sourceMappingURL=upload-asset-manifest.d.ts.map
@@ -52,4 +52,3 @@ export async function uploadAssetManifest(api, namespaceId, manifest) {
52
52
  }
53
53
  }
54
54
  }
55
- //# sourceMappingURL=upload-asset-manifest.js.map
@@ -127,4 +127,3 @@ export interface WorkerDefaultEnvironment extends WorkerEnvironment {
127
127
  */
128
128
  script: WorkerScriptInfo;
129
129
  }
130
- //# sourceMappingURL=worker-metadata.d.ts.map
@@ -1,2 +1 @@
1
1
  export {};
2
- //# sourceMappingURL=worker-metadata.js.map
@@ -107,4 +107,3 @@ export interface MigrationStep {
107
107
  */
108
108
  transferred_classes?: TransferredClass[];
109
109
  }
110
- //# sourceMappingURL=worker-migration.d.ts.map
@@ -8,4 +8,3 @@ export function isSingleStepMigration(migration) {
8
8
  export function isMultiStepMigration(migration) {
9
9
  return "steps" in migration;
10
10
  }
11
- //# sourceMappingURL=worker-migration.js.map
@@ -1,11 +1,13 @@
1
- import { type BundleProps } from "../esbuild";
2
- import type { Resolved } from "../output";
3
- import type { Bindings } from "./bindings";
1
+ import type { Context } from "../context";
2
+ import { type BundleProps } from "../esbuild/bundle";
3
+ import { Resource } from "../resource";
4
+ import { type Bindings } from "./bindings";
5
+ import type { Bound } from "./bound";
4
6
  import type { SingleStepMigration } from "./worker-migration";
5
7
  /**
6
8
  * Properties for creating or updating a Worker
7
9
  */
8
- export interface WorkerProps {
10
+ export interface WorkerProps<B extends Bindings = Bindings> {
9
11
  /**
10
12
  * The worker script content (JavaScript or WASM)
11
13
  * One of script, entryPoint, or bundle must be provided
@@ -42,7 +44,7 @@ export interface WorkerProps {
42
44
  /**
43
45
  * Bindings to attach to the worker
44
46
  */
45
- bindings?: Bindings;
47
+ bindings?: B;
46
48
  /**
47
49
  * Environment variables to attach to the worker
48
50
  * These will be converted to plain_text bindings
@@ -76,7 +78,7 @@ export interface WorkerProps {
76
78
  /**
77
79
  * Output returned after Worker creation/update
78
80
  */
79
- export interface WorkerOutput extends Omit<WorkerProps, "url"> {
81
+ export interface Worker<B extends Bindings = Bindings> extends Resource<"cloudflare::Worker">, Omit<WorkerProps<B>, "url"> {
80
82
  type: "service";
81
83
  /**
82
84
  * The ID of the worker
@@ -98,11 +100,9 @@ export interface WorkerOutput extends Omit<WorkerProps, "url"> {
98
100
  /**
99
101
  * The bindings that were created
100
102
  */
101
- bindings: Bindings;
102
- }
103
- export declare function isWorker(resource: any): resource is Resolved<Worker>;
104
- declare const Worker_base: import("../resource").Provider<string, [props: WorkerProps], WorkerOutput>;
105
- export declare class Worker extends Worker_base {
103
+ bindings: B | undefined;
104
+ Env: {
105
+ [bindingName in keyof B]: Bound<B[bindingName]>;
106
+ };
106
107
  }
107
- export {};
108
- //# sourceMappingURL=worker.d.ts.map
108
+ export declare const Worker: (<const B extends Bindings>(this: Context<Worker<NoInfer<B>>>, id: string, props: WorkerProps<B>) => Promise<Worker<B>>) | (((this: any, id: string, props: WorkerProps<Bindings>) => never) & (new (_: never) => never));
@@ -1,18 +1,15 @@
1
1
  import * as fs from "fs/promises";
2
- import { apply } from "../apply";
3
- import { Bundle } from "../esbuild";
2
+ import { Bundle } from "../esbuild/bundle";
4
3
  import { Resource } from "../resource";
5
- import { withExponentialBackoff } from "../utils/retry";
4
+ import { isSecret } from "../secret";
5
+ import { withExponentialBackoff } from "../util/retry";
6
+ import { slugify } from "../util/slugify";
6
7
  import { createCloudflareApi } from "./api";
7
- import { isBucket } from "./bucket";
8
- import { isDurableObjectNamespace } from "./durable-object-namespace";
8
+ import { isDurableObjectNamespace, } from "./bindings";
9
9
  import { isKVNamespace } from "./kv-namespace";
10
- export function isWorker(resource) {
11
- return resource && typeof resource === "object" && resource.type === "worker";
12
- }
13
- export class Worker extends Resource("cloudflare::Worker", {
10
+ export const Worker = Resource("cloudflare::Worker", {
14
11
  alwaysUpdate: true,
15
- }, async (ctx, props) => {
12
+ }, async function (id, props) {
16
13
  // Create Cloudflare API client with automatic account discovery
17
14
  const api = await createCloudflareApi();
18
15
  // Use the provided name
@@ -21,30 +18,31 @@ export class Worker extends Resource("cloudflare::Worker", {
21
18
  if (!props.script && !props.entrypoint) {
22
19
  throw new Error("One of script or entryPoint must be provided");
23
20
  }
24
- if (ctx.event === "delete") {
25
- return deleteWorker(ctx, api, workerName);
21
+ if (this.phase === "delete") {
22
+ await deleteWorker(this, api, workerName);
23
+ return this.destroy();
26
24
  }
27
- else if (ctx.event === "create") {
28
- await assertWorkerDoesNotExist(ctx, api, workerName);
25
+ else if (this.phase === "create") {
26
+ await assertWorkerDoesNotExist(this, api, workerName);
29
27
  }
30
- const oldBindings = await ctx.get("bindings");
31
- const scriptMetadata = await prepareWorkerMetadata(ctx, oldBindings, props);
28
+ const oldBindings = await this.get("bindings");
29
+ const scriptMetadata = await prepareWorkerMetadata(this, oldBindings, props);
32
30
  // Get the script content - either from props.script, or by bundling
33
- const scriptContent = props.script ?? (await bundleWorkerScript(ctx, props));
31
+ const scriptContent = props.script ?? (await bundleWorkerScript(props));
34
32
  // Upload the worker script
35
- await putWorker(ctx, api, workerName, scriptContent, scriptMetadata);
33
+ await putWorker(api, workerName, scriptContent, scriptMetadata);
36
34
  // TODO: it is less than ideal that this can fail, resulting in state problem
37
- await ctx.set("bindings", props.bindings);
35
+ await this.set("bindings", props.bindings);
38
36
  // Handle routes if requested
39
37
  await setupRoutes(api, workerName, props.routes || []);
40
38
  // Handle worker URL if requested
41
- const workerUrl = await configureURL(ctx, api, workerName, props.url ?? false);
39
+ const workerUrl = await configureURL(this, api, workerName, props.url ?? false);
42
40
  // Get current timestamp
43
41
  const now = Date.now();
44
42
  // Construct the output
45
- const output = {
43
+ return this({
46
44
  type: "service",
47
- id: workerName,
45
+ id,
48
46
  name: workerName,
49
47
  script: scriptContent,
50
48
  format: props.format || "esm", // Include format in the output
@@ -55,10 +53,10 @@ export class Worker extends Resource("cloudflare::Worker", {
55
53
  createdAt: now,
56
54
  updatedAt: now,
57
55
  url: workerUrl,
58
- };
59
- return output;
60
- }) {
61
- }
56
+ // phantom property
57
+ Env: undefined,
58
+ });
59
+ });
62
60
  async function deleteWorker(ctx, api, workerName) {
63
61
  // Delete worker
64
62
  const deleteResponse = await api.delete(`/accounts/${api.accountId}/workers/scripts/${workerName}`);
@@ -102,7 +100,7 @@ async function deleteWorker(ctx, api, workerName) {
102
100
  // Return minimal output for deleted state
103
101
  return;
104
102
  }
105
- async function putWorker(ctx, api, workerName, scriptContent, scriptMetadata) {
103
+ async function putWorker(api, workerName, scriptContent, scriptMetadata) {
106
104
  return withExponentialBackoff(async () => {
107
105
  const scriptName = scriptMetadata.main_module ?? scriptMetadata.body_part;
108
106
  // Create FormData for the upload
@@ -113,7 +111,6 @@ async function putWorker(ctx, api, workerName, scriptContent, scriptMetadata) {
113
111
  ? "application/javascript+module"
114
112
  : "application/javascript",
115
113
  }), scriptName);
116
- // console.log("Script Metadata:", JSON.stringify(scriptMetadata, null, 2));
117
114
  // Add metadata as JSON
118
115
  formData.append("metadata", new Blob([JSON.stringify(scriptMetadata)], {
119
116
  type: "application/json",
@@ -153,7 +150,8 @@ async function prepareWorkerMetadata(ctx, oldBindings, props) {
153
150
  observability: {
154
151
  enabled: props.observability?.enabled !== false,
155
152
  },
156
- tags: [`alchemy:id:${ctx.resourceFQN}`],
153
+ // TODO(sam): base64 encode instead? 0 collision risk vs readability.
154
+ tags: [`alchemy:id:${slugify(ctx.fqn)}`],
157
155
  migrations: {
158
156
  new_classes: props.migrations?.new_classes ?? [],
159
157
  deleted_classes: props.migrations?.deleted_classes ?? [],
@@ -170,7 +168,7 @@ async function prepareWorkerMetadata(ctx, oldBindings, props) {
170
168
  meta.bindings.push({
171
169
  type: "kv_namespace",
172
170
  name: bindingName,
173
- namespace_id: binding.id,
171
+ namespace_id: binding.namespaceId,
174
172
  });
175
173
  }
176
174
  else if (typeof binding === "string") {
@@ -187,7 +185,7 @@ async function prepareWorkerMetadata(ctx, oldBindings, props) {
187
185
  service: binding.id,
188
186
  });
189
187
  }
190
- else if (isDurableObjectNamespace(binding)) {
188
+ else if (binding.type === "durable_object_namespace") {
191
189
  const stableId = binding.id;
192
190
  const className = binding.className;
193
191
  meta.bindings.push({
@@ -216,11 +214,18 @@ async function prepareWorkerMetadata(ctx, oldBindings, props) {
216
214
  });
217
215
  }
218
216
  }
219
- else if (isBucket(binding)) {
217
+ else if (binding.type === "r2_bucket") {
220
218
  meta.bindings.push({
221
219
  type: "r2_bucket",
222
220
  name: bindingName,
223
- bucket_name: binding.name || binding.id,
221
+ bucket_name: binding.name,
222
+ });
223
+ }
224
+ else if (isSecret(binding)) {
225
+ meta.bindings.push({
226
+ type: "secret_text",
227
+ name: bindingName,
228
+ text: binding.unencrypted,
224
229
  });
225
230
  }
226
231
  else {
@@ -261,7 +266,7 @@ async function assertWorkerDoesNotExist(ctx, api, workerName) {
261
266
  if (!metadata) {
262
267
  throw new Error(`Worker exists but failed to fetch metadata: ${response.status} ${response.statusText}`);
263
268
  }
264
- if (metadata.default_environment?.script.tags.includes(`alchemy:id:${ctx.resourceFQN}`)) {
269
+ if (metadata.default_environment?.script.tags.includes(`alchemy:id:${slugify(ctx.fqn)}`)) {
265
270
  return true;
266
271
  }
267
272
  throw new Error(`Worker with name '${workerName}' already exists. Please use a unique name.`);
@@ -270,7 +275,7 @@ async function assertWorkerDoesNotExist(ctx, api, workerName) {
270
275
  throw new Error(`Error checking if worker exists: ${response.status} ${response.statusText}`);
271
276
  }
272
277
  }
273
- async function bundleWorkerScript(ctx, props) {
278
+ async function bundleWorkerScript(props) {
274
279
  // Get the script content - either from props.script, or by bundling
275
280
  // Create and use a Bundle resource with worker-optimized configuration
276
281
  const defaultBundleOptions = {
@@ -288,17 +293,13 @@ async function bundleWorkerScript(ctx, props) {
288
293
  ...(props.bundle || {}),
289
294
  };
290
295
  // Create the bundle
291
- const bundle = new Bundle("bundle", {
296
+ const bundle = await Bundle("bundle", {
292
297
  entryPoint: props.entrypoint,
293
298
  ...bundleOptions,
294
299
  external: [...(bundleOptions.external ?? []), "cloudflare:workers"],
295
300
  });
296
- // Get the bundled content
297
- const bundleOutput = await apply(bundle, {
298
- quiet: ctx.quiet,
299
- });
300
301
  try {
301
- return await fs.readFile(bundleOutput.path, "utf-8");
302
+ return await fs.readFile(bundle.path, "utf-8");
302
303
  }
303
304
  catch (error) {
304
305
  console.error("Error reading bundle:", error);
@@ -362,7 +363,7 @@ async function configureURL(ctx, api, workerName, url) {
362
363
  // Add a delay when the subdomain is first created.
363
364
  // This is to prevent an issue where a negative cache-hit
364
365
  // causes the subdomain to be unavailable for 30 seconds.
365
- if (ctx.event === "create" || !ctx.output?.url) {
366
+ if (ctx.phase === "create" || !ctx.output?.url) {
366
367
  await new Promise((resolve) => setTimeout(resolve, 3000));
367
368
  }
368
369
  }
@@ -404,4 +405,3 @@ async function getWorkerBindings(api, workerName, environment = "production") {
404
405
  const data = await response.json();
405
406
  return data.result;
406
407
  }
407
- //# sourceMappingURL=worker.js.map
@@ -0,0 +1,58 @@
1
+ import type { Resource, ResourceFQN, ResourceID, ResourceKind, ResourceProps } from "./resource";
2
+ import type { Scope } from "./scope";
3
+ import type { State } from "./state";
4
+ export type Context<Out extends Resource> = CreateContext<Out> | UpdateContext<Out> | DeleteContext<Out>;
5
+ export interface CreateContext<Out extends Resource> extends BaseContext<Out> {
6
+ phase: "create";
7
+ output?: undefined;
8
+ }
9
+ export interface UpdateContext<Out extends Resource> extends BaseContext<Out> {
10
+ phase: "update";
11
+ output: Out;
12
+ }
13
+ export interface DeleteContext<Out extends Resource> extends BaseContext<Out> {
14
+ phase: "delete";
15
+ output: Out;
16
+ }
17
+ export interface BaseContext<Out extends Resource> {
18
+ quiet: boolean;
19
+ stage: string;
20
+ id: ResourceID;
21
+ fqn: ResourceFQN;
22
+ scope: Scope;
23
+ get<T>(key: string): Promise<T | undefined>;
24
+ set<T>(key: string, value: T): Promise<void>;
25
+ delete<T>(key: string): Promise<T | undefined>;
26
+ /**
27
+ * Indicate that this resource is being replaced.
28
+ * This will cause the resource to be deleted at the end of the stack's CREATE phase.
29
+ */
30
+ replace(): void;
31
+ /**
32
+ * Terminate the resource lifecycle handler and destroy the resource.
33
+ *
34
+ * This is the final operation performed during a delete operation.
35
+ *
36
+ * It is so that the resource lifecycle handler can "return never" instead of
37
+ * "return undefined" so that `await MyResource()` always returns a value.
38
+ */
39
+ destroy(): never;
40
+ /**
41
+ * Create the Resource envelope (with Alchemy + User properties)
42
+ */
43
+ create(props: Omit<Out, keyof Resource>): Out;
44
+ /**
45
+ * Create the Resource envelope (with Alchemy + User properties)
46
+ */
47
+ (props: Omit<Out, keyof Resource>): Out;
48
+ }
49
+ export declare function context<Kind extends string, Props extends ResourceProps, Out extends Resource>({ scope, phase, kind, id, fqn, seq, state, replace, }: {
50
+ scope: Scope;
51
+ phase: "create" | "update" | "delete";
52
+ kind: ResourceKind;
53
+ id: ResourceID;
54
+ fqn: ResourceFQN;
55
+ seq: number;
56
+ state: State<Kind, Props, Out>;
57
+ replace: () => void;
58
+ }): Context<Out>;