alchemy 0.1.11 → 0.1.12

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 (277) hide show
  1. package/README.md +324 -0
  2. package/lib/apply.d.ts +2 -2
  3. package/lib/apply.d.ts.map +1 -1
  4. package/lib/apply.js +2 -2
  5. package/lib/apply.js.map +1 -1
  6. package/lib/aws/index.d.ts +1 -0
  7. package/lib/aws/index.d.ts.map +1 -1
  8. package/lib/aws/index.js +1 -0
  9. package/lib/aws/index.js.map +1 -1
  10. package/lib/aws/ses.d.ts +75 -0
  11. package/lib/aws/ses.d.ts.map +1 -0
  12. package/lib/aws/ses.js +197 -0
  13. package/lib/aws/ses.js.map +1 -0
  14. package/lib/cloudflare/api.d.ts +84 -0
  15. package/lib/cloudflare/api.d.ts.map +1 -0
  16. package/lib/cloudflare/api.js +178 -0
  17. package/lib/cloudflare/api.js.map +1 -0
  18. package/lib/cloudflare/asset-manifest.d.ts +15 -0
  19. package/lib/cloudflare/asset-manifest.d.ts.map +1 -0
  20. package/lib/cloudflare/asset-manifest.js +2 -0
  21. package/lib/cloudflare/asset-manifest.js.map +1 -0
  22. package/lib/cloudflare/auth.d.ts +52 -0
  23. package/lib/cloudflare/auth.d.ts.map +1 -0
  24. package/lib/cloudflare/auth.js +114 -0
  25. package/lib/cloudflare/auth.js.map +1 -0
  26. package/lib/cloudflare/bindings.d.ts +244 -0
  27. package/lib/cloudflare/bindings.d.ts.map +1 -0
  28. package/lib/cloudflare/bindings.js +7 -0
  29. package/lib/cloudflare/bindings.js.map +1 -0
  30. package/lib/cloudflare/generate-asset-manifest.d.ts +3 -0
  31. package/lib/cloudflare/generate-asset-manifest.d.ts.map +1 -0
  32. package/lib/cloudflare/generate-asset-manifest.js +69 -0
  33. package/lib/cloudflare/generate-asset-manifest.js.map +1 -0
  34. package/lib/cloudflare/index.d.ts +6 -0
  35. package/lib/cloudflare/index.d.ts.map +1 -0
  36. package/lib/cloudflare/index.js +6 -0
  37. package/lib/cloudflare/index.js.map +1 -0
  38. package/lib/cloudflare/kv-namespace.d.ts +61 -0
  39. package/lib/cloudflare/kv-namespace.d.ts.map +1 -0
  40. package/lib/cloudflare/kv-namespace.js +93 -0
  41. package/lib/cloudflare/kv-namespace.js.map +1 -0
  42. package/lib/cloudflare/state.d.ts +83 -0
  43. package/lib/cloudflare/state.d.ts.map +1 -0
  44. package/lib/cloudflare/state.js +109 -0
  45. package/lib/cloudflare/state.js.map +1 -0
  46. package/lib/cloudflare/static-site-router.d.ts +10 -0
  47. package/lib/cloudflare/static-site-router.d.ts.map +1 -0
  48. package/lib/cloudflare/static-site-router.js +88 -0
  49. package/lib/cloudflare/static-site-router.js.map +1 -0
  50. package/lib/cloudflare/static-site.d.ts +166 -0
  51. package/lib/cloudflare/static-site.d.ts.map +1 -0
  52. package/lib/cloudflare/static-site.js +152 -0
  53. package/lib/cloudflare/static-site.js.map +1 -0
  54. package/lib/cloudflare/types.d.ts +52 -0
  55. package/lib/cloudflare/types.d.ts.map +1 -0
  56. package/lib/cloudflare/types.js +21 -0
  57. package/lib/cloudflare/types.js.map +1 -0
  58. package/lib/cloudflare/upload-asset-manifest.d.ts +12 -0
  59. package/lib/cloudflare/upload-asset-manifest.d.ts.map +1 -0
  60. package/lib/cloudflare/upload-asset-manifest.js +55 -0
  61. package/lib/cloudflare/upload-asset-manifest.js.map +1 -0
  62. package/lib/cloudflare/worker.d.ts +87 -0
  63. package/lib/cloudflare/worker.d.ts.map +1 -0
  64. package/lib/cloudflare/worker.js +364 -0
  65. package/lib/cloudflare/worker.js.map +1 -0
  66. package/lib/esbuild.d.ts +82 -0
  67. package/lib/esbuild.d.ts.map +1 -1
  68. package/lib/esbuild.js +24 -17
  69. package/lib/esbuild.js.map +1 -1
  70. package/lib/fs.d.ts +1 -1
  71. package/lib/global.d.ts.map +1 -1
  72. package/lib/global.js +3 -1
  73. package/lib/global.js.map +1 -1
  74. package/lib/index.d.ts +0 -1
  75. package/lib/index.d.ts.map +1 -1
  76. package/lib/index.js +0 -1
  77. package/lib/index.js.map +1 -1
  78. package/lib/main.js +1 -1
  79. package/lib/main.js.map +1 -1
  80. package/lib/output.d.ts +17 -13
  81. package/lib/output.d.ts.map +1 -1
  82. package/lib/output.js +2 -2
  83. package/lib/output.js.map +1 -1
  84. package/lib/resource.d.ts +3 -2
  85. package/lib/resource.d.ts.map +1 -1
  86. package/lib/resource.js +2 -2
  87. package/lib/resource.js.map +1 -1
  88. package/lib/utils/content-type.d.ts +8 -0
  89. package/lib/utils/content-type.d.ts.map +1 -0
  90. package/lib/utils/content-type.js +41 -0
  91. package/lib/utils/content-type.js.map +1 -0
  92. package/package.json +26 -8
  93. package/src/apply.ts +5 -5
  94. package/src/aws/index.ts +1 -0
  95. package/src/aws/ses.ts +346 -0
  96. package/src/cloudflare/api.ts +276 -0
  97. package/src/cloudflare/asset-manifest.ts +16 -0
  98. package/src/cloudflare/auth.ts +187 -0
  99. package/src/cloudflare/bindings.ts +286 -0
  100. package/src/cloudflare/generate-asset-manifest.ts +93 -0
  101. package/src/cloudflare/index.ts +5 -0
  102. package/src/cloudflare/kv-namespace.ts +195 -0
  103. package/src/cloudflare/state.ts +138 -0
  104. package/src/cloudflare/static-site-router.ts +108 -0
  105. package/src/cloudflare/static-site.ts +379 -0
  106. package/src/cloudflare/types.ts +71 -0
  107. package/src/cloudflare/upload-asset-manifest.ts +83 -0
  108. package/src/cloudflare/worker.ts +568 -0
  109. package/src/esbuild.ts +31 -22
  110. package/src/global.ts +1 -0
  111. package/src/index.ts +0 -1
  112. package/src/main.ts +1 -1
  113. package/src/output.ts +32 -32
  114. package/src/resource.ts +11 -5
  115. package/src/utils/content-type.ts +42 -0
  116. package/lib/$.d.ts +0 -7
  117. package/lib/$.d.ts.map +0 -1
  118. package/lib/$.js +0 -31
  119. package/lib/$.js.map +0 -1
  120. package/lib/agent/ai.d.ts +0 -40
  121. package/lib/agent/ai.d.ts.map +0 -1
  122. package/lib/agent/ai.js +0 -132
  123. package/lib/agent/ai.js.map +0 -1
  124. package/lib/agent/anthropic.d.ts +0 -9
  125. package/lib/agent/anthropic.d.ts.map +0 -1
  126. package/lib/agent/anthropic.js +0 -25
  127. package/lib/agent/anthropic.js.map +0 -1
  128. package/lib/agent/dependencies.d.ts +0 -4
  129. package/lib/agent/dependencies.d.ts.map +0 -1
  130. package/lib/agent/dependencies.js +0 -48
  131. package/lib/agent/dependencies.js.map +0 -1
  132. package/lib/agent/file-context.d.ts +0 -13
  133. package/lib/agent/file-context.d.ts.map +0 -1
  134. package/lib/agent/file-context.js +0 -6
  135. package/lib/agent/file-context.js.map +0 -1
  136. package/lib/agent/index.d.ts +0 -10
  137. package/lib/agent/index.d.ts.map +0 -1
  138. package/lib/agent/index.js +0 -10
  139. package/lib/agent/index.js.map +0 -1
  140. package/lib/agent/model.d.ts +0 -8
  141. package/lib/agent/model.d.ts.map +0 -1
  142. package/lib/agent/model.js +0 -29
  143. package/lib/agent/model.js.map +0 -1
  144. package/lib/agent/module.d.ts +0 -81
  145. package/lib/agent/module.d.ts.map +0 -1
  146. package/lib/agent/module.js +0 -309
  147. package/lib/agent/module.js.map +0 -1
  148. package/lib/agent/openai.d.ts +0 -6
  149. package/lib/agent/openai.d.ts.map +0 -1
  150. package/lib/agent/openai.js +0 -41
  151. package/lib/agent/openai.js.map +0 -1
  152. package/lib/agent/program.d.ts +0 -12
  153. package/lib/agent/program.d.ts.map +0 -1
  154. package/lib/agent/program.js +0 -21
  155. package/lib/agent/program.js.map +0 -1
  156. package/lib/agent/prompts.d.ts +0 -4
  157. package/lib/agent/prompts.d.ts.map +0 -1
  158. package/lib/agent/prompts.js +0 -11
  159. package/lib/agent/prompts.js.map +0 -1
  160. package/lib/agent/scrape.d.ts +0 -71
  161. package/lib/agent/scrape.d.ts.map +0 -1
  162. package/lib/agent/scrape.js +0 -45
  163. package/lib/agent/scrape.js.map +0 -1
  164. package/lib/aws/auto/docs.d.ts +0 -11
  165. package/lib/aws/auto/docs.d.ts.map +0 -1
  166. package/lib/aws/auto/docs.js +0 -56
  167. package/lib/aws/auto/docs.js.map +0 -1
  168. package/lib/aws/auto/index.d.ts +0 -5
  169. package/lib/aws/auto/index.d.ts.map +0 -1
  170. package/lib/aws/auto/index.js +0 -5
  171. package/lib/aws/auto/index.js.map +0 -1
  172. package/lib/aws/auto/resource.d.ts +0 -24
  173. package/lib/aws/auto/resource.d.ts.map +0 -1
  174. package/lib/aws/auto/resource.js +0 -194
  175. package/lib/aws/auto/resource.js.map +0 -1
  176. package/lib/aws/auto/service.d.ts +0 -10
  177. package/lib/aws/auto/service.d.ts.map +0 -1
  178. package/lib/aws/auto/service.js +0 -34
  179. package/lib/aws/auto/service.js.map +0 -1
  180. package/lib/aws/auto/spec.d.ts +0 -83
  181. package/lib/aws/auto/spec.d.ts.map +0 -1
  182. package/lib/aws/auto/spec.js +0 -58
  183. package/lib/aws/auto/spec.js.map +0 -1
  184. package/lib/markdown/design.d.ts +0 -17
  185. package/lib/markdown/design.d.ts.map +0 -1
  186. package/lib/markdown/design.js +0 -92
  187. package/lib/markdown/design.js.map +0 -1
  188. package/lib/markdown/extract.d.ts +0 -7
  189. package/lib/markdown/extract.d.ts.map +0 -1
  190. package/lib/markdown/extract.js +0 -10
  191. package/lib/markdown/extract.js.map +0 -1
  192. package/lib/markdown/index.d.ts +0 -4
  193. package/lib/markdown/index.d.ts.map +0 -1
  194. package/lib/markdown/index.js +0 -4
  195. package/lib/markdown/index.js.map +0 -1
  196. package/lib/markdown/requirements.d.ts +0 -79
  197. package/lib/markdown/requirements.d.ts.map +0 -1
  198. package/lib/markdown/requirements.js +0 -105
  199. package/lib/markdown/requirements.js.map +0 -1
  200. package/lib/typescript/check-omission.d.ts +0 -2
  201. package/lib/typescript/check-omission.d.ts.map +0 -1
  202. package/lib/typescript/check-omission.js +0 -43
  203. package/lib/typescript/check-omission.js.map +0 -1
  204. package/lib/typescript/debug-type-errors.d.ts +0 -3
  205. package/lib/typescript/debug-type-errors.d.ts.map +0 -1
  206. package/lib/typescript/debug-type-errors.js +0 -35
  207. package/lib/typescript/debug-type-errors.js.map +0 -1
  208. package/lib/typescript/extract.d.ts +0 -7
  209. package/lib/typescript/extract.d.ts.map +0 -1
  210. package/lib/typescript/extract.js +0 -10
  211. package/lib/typescript/extract.js.map +0 -1
  212. package/lib/typescript/index.d.ts +0 -7
  213. package/lib/typescript/index.d.ts.map +0 -1
  214. package/lib/typescript/index.js +0 -7
  215. package/lib/typescript/index.js.map +0 -1
  216. package/lib/typescript/install-dependencies.d.ts +0 -29
  217. package/lib/typescript/install-dependencies.d.ts.map +0 -1
  218. package/lib/typescript/install-dependencies.js +0 -52
  219. package/lib/typescript/install-dependencies.js.map +0 -1
  220. package/lib/typescript/package.d.ts +0 -157
  221. package/lib/typescript/package.d.ts.map +0 -1
  222. package/lib/typescript/package.js +0 -115
  223. package/lib/typescript/package.js.map +0 -1
  224. package/lib/typescript/repair-omissions.d.ts +0 -3
  225. package/lib/typescript/repair-omissions.d.ts.map +0 -1
  226. package/lib/typescript/repair-omissions.js +0 -23
  227. package/lib/typescript/repair-omissions.js.map +0 -1
  228. package/lib/typescript/repair.d.ts +0 -3
  229. package/lib/typescript/repair.d.ts.map +0 -1
  230. package/lib/typescript/repair.js +0 -34
  231. package/lib/typescript/repair.js.map +0 -1
  232. package/lib/typescript/tsconfig.d.ts +0 -469
  233. package/lib/typescript/tsconfig.d.ts.map +0 -1
  234. package/lib/typescript/tsconfig.js +0 -143
  235. package/lib/typescript/tsconfig.js.map +0 -1
  236. package/lib/typescript/typescript.d.ts +0 -107
  237. package/lib/typescript/typescript.d.ts.map +0 -1
  238. package/lib/typescript/typescript.js +0 -180
  239. package/lib/typescript/typescript.js.map +0 -1
  240. package/lib/typescript/validate.d.ts +0 -10
  241. package/lib/typescript/validate.d.ts.map +0 -1
  242. package/lib/typescript/validate.js +0 -49
  243. package/lib/typescript/validate.js.map +0 -1
  244. package/src/$.ts +0 -36
  245. package/src/agent/ai.ts +0 -215
  246. package/src/agent/anthropic.ts +0 -31
  247. package/src/agent/dependencies.ts +0 -62
  248. package/src/agent/file-context.ts +0 -8
  249. package/src/agent/index.ts +0 -9
  250. package/src/agent/model.ts +0 -39
  251. package/src/agent/module.ts +0 -418
  252. package/src/agent/openai.ts +0 -50
  253. package/src/agent/program.md +0 -38
  254. package/src/agent/program.ts +0 -37
  255. package/src/agent/prompts.ts +0 -20
  256. package/src/agent/scrape.ts +0 -47
  257. package/src/aws/auto/cfn.json +0 -225310
  258. package/src/aws/auto/docs.ts +0 -73
  259. package/src/aws/auto/index.ts +0 -4
  260. package/src/aws/auto/resource.ts +0 -269
  261. package/src/aws/auto/service.ts +0 -51
  262. package/src/aws/auto/spec.ts +0 -160
  263. package/src/markdown/design.ts +0 -125
  264. package/src/markdown/extract.ts +0 -9
  265. package/src/markdown/index.ts +0 -3
  266. package/src/markdown/requirements.ts +0 -135
  267. package/src/typescript/check-omission.ts +0 -47
  268. package/src/typescript/debug-type-errors.ts +0 -42
  269. package/src/typescript/extract.ts +0 -9
  270. package/src/typescript/index.ts +0 -6
  271. package/src/typescript/install-dependencies.ts +0 -61
  272. package/src/typescript/package.ts +0 -143
  273. package/src/typescript/repair-omissions.ts +0 -31
  274. package/src/typescript/repair.ts +0 -45
  275. package/src/typescript/tsconfig.ts +0 -170
  276. package/src/typescript/typescript.ts +0 -217
  277. package/src/typescript/validate.ts +0 -73
@@ -0,0 +1,195 @@
1
+ import { type Context, Resource } from "../resource";
2
+ import { createCloudflareApi } from "./api";
3
+
4
+ /**
5
+ * Properties for creating or updating a KV Namespace
6
+ */
7
+ export interface KVNamespaceProps {
8
+ /**
9
+ * Title of the namespace
10
+ */
11
+ title: string;
12
+
13
+ /**
14
+ * KV pairs to store in the namespace
15
+ * Only used for initial setup or updates
16
+ */
17
+ values?: KVPair[];
18
+ }
19
+
20
+ /**
21
+ * Key-value pair to store in a KV Namespace
22
+ */
23
+ export interface KVPair {
24
+ /**
25
+ * Key name
26
+ */
27
+ key: string;
28
+
29
+ /**
30
+ * Value to store (string or JSON object)
31
+ */
32
+ value: string | object;
33
+
34
+ /**
35
+ * Optional expiration in seconds from now
36
+ */
37
+ expiration?: number;
38
+
39
+ /**
40
+ * Optional expiration timestamp in seconds since epoch
41
+ */
42
+ expirationTtl?: number;
43
+
44
+ /**
45
+ * Optional metadata for the key
46
+ */
47
+ metadata?: any;
48
+ }
49
+
50
+ /**
51
+ * Output returned after KV Namespace creation/update
52
+ */
53
+ export interface KVNamespaceOutput extends KVNamespaceProps {
54
+ /**
55
+ * The ID of the namespace
56
+ */
57
+ id: string;
58
+
59
+ /**
60
+ * Time at which the namespace was created
61
+ */
62
+ createdAt: number;
63
+
64
+ /**
65
+ * Time at which the namespace was last modified
66
+ */
67
+ modifiedAt: number;
68
+ }
69
+
70
+ export class KVNamespace extends Resource(
71
+ "cloudflare::KVNamespace",
72
+ async (ctx: Context<KVNamespaceOutput>, props: KVNamespaceProps) => {
73
+ // Create Cloudflare API client with automatic account discovery
74
+ const api = await createCloudflareApi();
75
+
76
+ if (ctx.event === "delete") {
77
+ // For delete operations, we need to check if the namespace ID exists in the output
78
+ const namespaceId = ctx.output?.id;
79
+
80
+ if (namespaceId) {
81
+ // Delete KV namespace
82
+ const deleteResponse = await api.delete(
83
+ `/accounts/${api.accountId}/storage/kv/namespaces/${namespaceId}`,
84
+ );
85
+
86
+ if (!deleteResponse.ok && deleteResponse.status !== 404) {
87
+ const errorData: any = await deleteResponse.json().catch(() => ({
88
+ errors: [{ message: deleteResponse.statusText }],
89
+ }));
90
+ console.error(
91
+ "Error deleting KV namespace:",
92
+ errorData.errors?.[0]?.message || deleteResponse.statusText,
93
+ );
94
+ }
95
+ }
96
+
97
+ // Return minimal output for deleted state
98
+ return;
99
+ } else {
100
+ // For create or update operations
101
+ // If ctx.event is "update", we expect ctx.output to exist
102
+ let namespaceId = ctx.event === "update" ? ctx.output?.id || "" : "";
103
+ let createdAt =
104
+ ctx.event === "update"
105
+ ? ctx.output?.createdAt || Date.now()
106
+ : Date.now();
107
+
108
+ if (ctx.event === "update" && namespaceId) {
109
+ // Can't update a KV namespace title directly, just work with existing ID
110
+ } else {
111
+ // Create new KV namespace
112
+ const createResponse = await api.post(
113
+ `/accounts/${api.accountId}/storage/kv/namespaces`,
114
+ {
115
+ title: props.title,
116
+ },
117
+ );
118
+
119
+ if (!createResponse.ok) {
120
+ const errorData: any = await createResponse.json().catch(() => ({
121
+ errors: [{ message: createResponse.statusText }],
122
+ }));
123
+ throw new Error(
124
+ `Error creating KV namespace: ${errorData.errors?.[0]?.message || createResponse.statusText}`,
125
+ );
126
+ }
127
+
128
+ const createResult: any = await createResponse.json();
129
+ namespaceId = createResult.result.id;
130
+ createdAt = Date.now();
131
+ }
132
+
133
+ // Handle KV pairs if provided
134
+ if (props.values && props.values.length > 0 && namespaceId) {
135
+ // Process KV pairs in batches of 10000 (API limit)
136
+ const BATCH_SIZE = 10000;
137
+
138
+ for (let i = 0; i < props.values.length; i += BATCH_SIZE) {
139
+ const batch = props.values.slice(i, i + BATCH_SIZE);
140
+
141
+ const bulkPayload = batch.map((entry) => {
142
+ const item: any = {
143
+ key: entry.key,
144
+ value:
145
+ typeof entry.value === "string"
146
+ ? entry.value
147
+ : JSON.stringify(entry.value),
148
+ };
149
+
150
+ if (entry.expiration) {
151
+ item.expiration = entry.expiration;
152
+ }
153
+
154
+ if (entry.expirationTtl) {
155
+ item.expiration_ttl = entry.expirationTtl;
156
+ }
157
+
158
+ if (entry.metadata) {
159
+ item.metadata = entry.metadata;
160
+ }
161
+
162
+ return item;
163
+ });
164
+
165
+ const bulkResponse = await api.put(
166
+ `/accounts/${api.accountId}/storage/kv/namespaces/${namespaceId}/bulk`,
167
+ bulkPayload,
168
+ );
169
+
170
+ if (!bulkResponse.ok) {
171
+ const errorData: any = await bulkResponse.json().catch(() => ({
172
+ errors: [{ message: bulkResponse.statusText }],
173
+ }));
174
+ console.warn(
175
+ `Error writing KV batch: ${errorData.errors?.[0]?.message || bulkResponse.statusText}`,
176
+ );
177
+ }
178
+ }
179
+ }
180
+
181
+ const now = Date.now();
182
+
183
+ // Construct the output
184
+ const output: KVNamespaceOutput = {
185
+ id: namespaceId,
186
+ title: props.title,
187
+ values: props.values,
188
+ createdAt: createdAt,
189
+ modifiedAt: now,
190
+ };
191
+
192
+ return output;
193
+ }
194
+ },
195
+ ) {}
@@ -0,0 +1,138 @@
1
+ import type { KVNamespace } from "@cloudflare/workers-types";
2
+ import type { State, StateStore } from "../state";
3
+
4
+ /**
5
+ * Options for CloudflareStateStore
6
+ */
7
+ export interface CloudflareStateStoreOptions {
8
+ /**
9
+ * The prefix to use for keys in the KV namespace
10
+ * This allows multiple state stores to use the same KV namespace
11
+ */
12
+ prefix?: string;
13
+ }
14
+
15
+ /**
16
+ * State store implementation using Cloudflare KV Namespace
17
+ */
18
+ export class CloudflareStateStore implements StateStore {
19
+ private namespace: KVNamespace;
20
+ private prefix: string;
21
+
22
+ /**
23
+ * Create a new CloudflareStateStore
24
+ *
25
+ * @param namespace The KV namespace to use for storing state
26
+ * @param options Options for the state store
27
+ */
28
+ constructor(
29
+ namespace: KVNamespace,
30
+ options: CloudflareStateStoreOptions = {},
31
+ ) {
32
+ this.namespace = namespace;
33
+ this.prefix = options.prefix || "alchemy-state:";
34
+ }
35
+
36
+ /**
37
+ * No initialization needed for KV namespaces as they are pre-provisioned
38
+ */
39
+ async init(): Promise<void> {
40
+ // KV namespaces are pre-provisioned in Cloudflare, so no initialization is needed
41
+ }
42
+
43
+ /**
44
+ * KV namespaces cannot be deleted programmatically
45
+ */
46
+ async deinit(): Promise<void> {
47
+ // KV namespaces can only be deleted through the Cloudflare dashboard or API
48
+ // We cannot delete the namespace itself here
49
+ }
50
+
51
+ /**
52
+ * List all resources in the state store
53
+ */
54
+ async list(): Promise<string[]> {
55
+ const list = await this.namespace.list({ prefix: this.prefix });
56
+ return list.keys.map((key) => key.name.slice(this.prefix.length));
57
+ }
58
+
59
+ /**
60
+ * Count the number of items in the state store
61
+ */
62
+ async count(): Promise<number> {
63
+ const list = await this.namespace.list({ prefix: this.prefix });
64
+ return list.keys.length;
65
+ }
66
+
67
+ /**
68
+ * Get a state by key
69
+ *
70
+ * @param key The key to look up
71
+ * @returns The state or undefined if not found
72
+ */
73
+ async get(key: string): Promise<State | undefined> {
74
+ const data = await this.namespace.get(this.getNamespacedKey(key), "json");
75
+ return data as State | undefined;
76
+ }
77
+
78
+ /**
79
+ * Get multiple states by their keys
80
+ *
81
+ * @param ids Array of keys to fetch
82
+ * @returns Record mapping keys to their states
83
+ */
84
+ async getBatch(ids: string[]): Promise<Record<string, State>> {
85
+ const result: Record<string, State> = {};
86
+
87
+ // KV doesn't have a native batch get operation, so we need to make multiple requests
88
+ // We could optimize this with Promise.all for parallel fetching
89
+ const promises = ids.map(async (id) => {
90
+ const state = await this.get(id);
91
+ if (state) {
92
+ result[id] = state;
93
+ }
94
+ });
95
+
96
+ await Promise.all(promises);
97
+ return result;
98
+ }
99
+
100
+ /**
101
+ * Get all states in the store
102
+ *
103
+ * @returns Record mapping all keys to their states
104
+ */
105
+ async all(): Promise<Record<string, State>> {
106
+ const keys = await this.list();
107
+ return this.getBatch(keys);
108
+ }
109
+
110
+ /**
111
+ * Set a state for a key
112
+ *
113
+ * @param key The key to set
114
+ * @param value The state to store
115
+ */
116
+ async set(key: string, value: State): Promise<void> {
117
+ await this.namespace.put(this.getNamespacedKey(key), JSON.stringify(value));
118
+ }
119
+
120
+ /**
121
+ * Delete a state by key
122
+ *
123
+ * @param key The key to delete
124
+ */
125
+ async delete(key: string): Promise<void> {
126
+ await this.namespace.delete(this.getNamespacedKey(key));
127
+ }
128
+
129
+ /**
130
+ * Get the namespaced key for storage
131
+ *
132
+ * @param key The original key
133
+ * @returns The key with prefix for use in the KV namespace
134
+ */
135
+ private getNamespacedKey(key: string): string {
136
+ return `${this.prefix}${key}`;
137
+ }
138
+ }
@@ -0,0 +1,108 @@
1
+ // see: https://developers.cloudflare.com/workers/runtime-apis/cache/
2
+ declare var caches: any;
3
+
4
+ // injected by src/cloudflare/static-site.ts
5
+ declare var __ASSET_MANIFEST__: Record<string, string>;
6
+
7
+ export interface Env {
8
+ ASSETS: KVNamespace;
9
+ INDEX_PAGE: string;
10
+ ERROR_PAGE?: string;
11
+ }
12
+
13
+ export default {
14
+ async fetch(request: Request, env: Env): Promise<Response> {
15
+ const url = new URL(request.url);
16
+ console.log("url", request.url);
17
+ const pathname = url.pathname.replace(/^\//, "");
18
+ const filePath = pathname === "" ? env.INDEX_PAGE : pathname;
19
+
20
+ // Return from cache if available
21
+ const cachedResponse = await lookupCache();
22
+ if (cachedResponse) return cachedResponse;
23
+
24
+ // Fetch from KV
25
+ {
26
+ const object = await env.ASSETS.getWithMetadata(filePath);
27
+ if (object.value) return await respond(200, filePath, object);
28
+ }
29
+ {
30
+ const guess =
31
+ filePath + (filePath.endsWith("/") ? "" : "/") + "index.html";
32
+ const object = await env.ASSETS.getWithMetadata(guess);
33
+ if (object.value) return await respond(200, guess, object);
34
+ }
35
+ {
36
+ const guess = filePath + ".html";
37
+ const object = await env.ASSETS.getWithMetadata(guess);
38
+ if (object.value) return await respond(200, guess, object);
39
+ }
40
+
41
+ // Handle error page
42
+ if (env.ERROR_PAGE) {
43
+ const object = await env.ASSETS.getWithMetadata(env.ERROR_PAGE);
44
+ if (object.value) return await respond(404, env.ERROR_PAGE, object);
45
+ } else {
46
+ const object = await env.ASSETS.getWithMetadata(env.INDEX_PAGE);
47
+ if (object.value) return await respond(200, env.INDEX_PAGE, object);
48
+ }
49
+
50
+ // Handle failed to render error page
51
+ return new Response("Page Not Found", { status: 404 });
52
+
53
+ async function lookupCache() {
54
+ const cache = caches.default;
55
+ const r = await cache.match(request);
56
+
57
+ // cache does not exist
58
+ if (!r) return;
59
+
60
+ // cache exists but etag does not match
61
+ if (r.headers.get("etag") !== __ASSET_MANIFEST__[filePath]) return;
62
+
63
+ // cache exists
64
+ return r;
65
+ }
66
+
67
+ async function saveCache(response: Response) {
68
+ const cache = caches.default;
69
+ await cache.put(request, response.clone());
70
+ }
71
+
72
+ async function respond(
73
+ status: number,
74
+ filePath: string,
75
+ object: KVNamespaceGetWithMetadataResult<any, any>,
76
+ ) {
77
+ // build response
78
+ const headers = new Headers();
79
+ if (__ASSET_MANIFEST__[filePath]) {
80
+ headers.set("etag", __ASSET_MANIFEST__[filePath]);
81
+ headers.set("content-type", object.metadata.contentType);
82
+ headers.set("cache-control", object.metadata.cacheControl);
83
+ }
84
+ // TODO: do we need base64 encoded here?
85
+ // const response = new Response(base64ToArrayBuffer(object.value), {
86
+ const response = new Response(object.value, {
87
+ status,
88
+ headers,
89
+ });
90
+
91
+ if (request.method === "GET") {
92
+ await saveCache(response);
93
+ }
94
+
95
+ return response;
96
+ }
97
+ },
98
+ };
99
+
100
+ function base64ToArrayBuffer(base64: any) {
101
+ const binaryString = atob(base64);
102
+ const len = binaryString.length;
103
+ const bytes = new Uint8Array(len);
104
+ for (let i = 0; i < len; i++) {
105
+ bytes[i] = binaryString.charCodeAt(i);
106
+ }
107
+ return bytes.buffer;
108
+ }