alchemy 2.0.0-beta.2 → 2.0.0-beta.test-export-fix-3

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 (65) hide show
  1. package/bin/{alchemy-effect.js → alchemy.js} +173 -173
  2. package/bin/alchemy.js.map +1 -0
  3. package/bin/{alchemy-effect.sh → alchemy.sh} +2 -2
  4. package/lib/cli/index.js +169 -169
  5. package/lib/cli/index.js.map +1 -1
  6. package/package.json +75 -67
  7. package/src/AWS/AGENTS.md +10 -10
  8. package/src/AWS/Assets.ts +1 -1
  9. package/src/AWS/Credentials.ts +0 -1
  10. package/src/AWS/DynamoDB/Table.ts +63 -10
  11. package/src/AWS/EC2/hosted.ts +1 -1
  12. package/src/AWS/ECS/Task.ts +1 -1
  13. package/src/AWS/Kinesis/Stream.ts +44 -8
  14. package/src/AWS/Lambda/Function.ts +218 -10
  15. package/src/AWS/S3/Bucket.ts +26 -19
  16. package/src/AWS/S3/BucketNotifications.ts +1 -1
  17. package/src/AWS/SNS/Topic.ts +47 -10
  18. package/src/AWS/SQS/Queue.ts +66 -0
  19. package/src/Cloudflare/Container/Container.ts +122 -0
  20. package/src/Cloudflare/Container/ContainerApplication.ts +6 -3
  21. package/src/Cloudflare/Container/ContainerBinding.ts +2 -4
  22. package/src/Cloudflare/Container/StartContainer.ts +1 -1
  23. package/src/Cloudflare/D1/D1Database.ts +23 -4
  24. package/src/Cloudflare/KV/KVNamespace.ts +25 -0
  25. package/src/Cloudflare/Providers.ts +1 -6
  26. package/src/Cloudflare/R2/R2Bucket.ts +45 -2
  27. package/src/Cloudflare/Website/StaticSite.ts +79 -6
  28. package/src/Cloudflare/Website/Vite.ts +67 -13
  29. package/src/Cloudflare/Workers/Assets.ts +170 -207
  30. package/src/Cloudflare/Workers/DurableObjectNamespace.ts +629 -0
  31. package/src/Cloudflare/Workers/DurableObjectState.ts +63 -0
  32. package/src/Cloudflare/Workers/DurableObjectStorage.ts +256 -0
  33. package/src/Cloudflare/Workers/DynamicWorkerLoader.ts +66 -7
  34. package/src/Cloudflare/Workers/InferEnv.ts +11 -7
  35. package/src/Cloudflare/Workers/Rpc.ts +13 -2
  36. package/src/Cloudflare/Workers/ScheduledEvents.ts +185 -0
  37. package/src/Cloudflare/Workers/WebSocket.ts +1 -1
  38. package/src/Cloudflare/Workers/Worker.ts +406 -49
  39. package/src/Cloudflare/Workers/Workflow.ts +53 -11
  40. package/src/Cloudflare/Workers/index.ts +4 -1
  41. package/src/Construct.ts +2 -2
  42. package/src/GitHub/Comment.ts +224 -0
  43. package/src/GitHub/Secret.ts +257 -0
  44. package/src/GitHub/Variable.ts +166 -0
  45. package/src/GitHub/index.ts +3 -0
  46. package/src/Kubernetes/client.ts +2 -2
  47. package/src/Output.ts +1 -1
  48. package/src/Platform.ts +1 -1
  49. package/src/Provider.ts +1 -1
  50. package/src/Test/Vitest.ts +4 -3
  51. package/src/Util/PlatformServices.ts +21 -0
  52. package/src/Util/dedent.ts +59 -0
  53. package/src/Util/index.ts +1 -0
  54. package/bin/alchemy-effect.js.map +0 -1
  55. package/src/Cloudflare/Workers/DurableObject.ts +0 -527
  56. package/src/Daemon/Client.ts +0 -116
  57. package/src/Daemon/Config.ts +0 -28
  58. package/src/Daemon/Errors.ts +0 -48
  59. package/src/Daemon/Lock.ts +0 -162
  60. package/src/Daemon/ProcessRegistry.ts +0 -284
  61. package/src/Daemon/RpcSchema.ts +0 -43
  62. package/src/Daemon/RpcServer.ts +0 -231
  63. package/src/Daemon/index.ts +0 -27
  64. package/src/Spawn.ts +0 -23
  65. /package/bin/{alchemy-effect.ts → alchemy.ts} +0 -0
@@ -1,9 +1,7 @@
1
1
  import * as workers from "@distilled.cloud/cloudflare/workers";
2
- import * as Context from "effect/Context";
3
2
  import * as Data from "effect/Data";
4
3
  import * as Effect from "effect/Effect";
5
4
  import * as FileSystem from "effect/FileSystem";
6
- import * as Layer from "effect/Layer";
7
5
  import * as Path from "effect/Path";
8
6
  import type { PlatformError } from "effect/PlatformError";
9
7
  import type { ScopedPlanStatusSession } from "../../Cli/Cli.ts";
@@ -12,6 +10,13 @@ import { sha256, sha256Object } from "../../Util/index.ts";
12
10
  const MAX_ASSET_SIZE = 1024 * 1024 * 25; // 25MB
13
11
  const MAX_ASSET_COUNT = 20_000;
14
12
 
13
+ export interface Assets {
14
+ kind: "Cloudflare.Workers.Assets";
15
+ }
16
+
17
+ export const isAssets = (value: any): value is Assets =>
18
+ value?.kind === "Cloudflare.Workers.Assets";
19
+
15
20
  export interface AssetsConfig extends Exclude<
16
21
  Exclude<workers.PutScriptRequest["metadata"]["assets"], undefined>["config"],
17
22
  undefined
@@ -31,27 +36,6 @@ export interface AssetsProps {
31
36
  config?: AssetsConfig;
32
37
  }
33
38
 
34
- export class Assets extends Context.Service<
35
- Assets,
36
- {
37
- read(
38
- directory: AssetsProps,
39
- ): Effect.Effect<AssetReadResult, PlatformError | ValidationError>;
40
- upload(
41
- accountId: string,
42
- workerName: string,
43
- assets: AssetReadResult,
44
- session: ScopedPlanStatusSession,
45
- ): Effect.Effect<
46
- { jwt: string | undefined },
47
- | PlatformError
48
- | ValidationError
49
- | workers.CreateScriptAssetUploadError
50
- | workers.CreateAssetUploadError
51
- >;
52
- }
53
- >()("Cloudflare.Assets") {}
54
-
55
39
  export type ValidationError =
56
40
  | AssetTooLargeError
57
41
  | TooManyAssetsError
@@ -102,196 +86,175 @@ const getContentType = (name: string) => {
102
86
  return "application/octet-stream";
103
87
  };
104
88
 
105
- export const AssetsProvider = () =>
106
- Layer.effect(
107
- Assets,
108
- Effect.gen(function* () {
109
- const fs = yield* FileSystem.FileSystem;
110
- const path = yield* Path.Path;
111
- const createAssetUpload = yield* workers.createAssetUpload;
112
- const createScriptAssetUpload = yield* workers.createScriptAssetUpload;
89
+ const maybeReadString = Effect.fnUntraced(function* (file: string) {
90
+ const fs = yield* FileSystem.FileSystem;
91
+ return yield* fs.readFileString(file).pipe(
92
+ Effect.catchIf(
93
+ (error) =>
94
+ error._tag === "PlatformError" && error.reason._tag === "NotFound",
95
+ () => Effect.succeed(undefined),
96
+ ),
97
+ );
98
+ });
113
99
 
114
- const maybeReadString = Effect.fnUntraced(function* (file: string) {
115
- return yield* fs.readFileString(file).pipe(
116
- Effect.catchIf(
117
- (error) =>
118
- error._tag === "PlatformError" &&
119
- error.reason._tag === "NotFound",
120
- () => Effect.succeed(undefined),
121
- ),
122
- );
123
- });
100
+ const createIgnoreMatcher = Effect.fnUntraced(function* (patterns: string[]) {
101
+ const matcher = yield* Effect.promise(() =>
102
+ import("ignore").then(({ default: ignore }) => ignore().add(patterns)),
103
+ );
104
+ return (file: string) => matcher.ignores(file);
105
+ });
124
106
 
125
- const createIgnoreMatcher = Effect.fnUntraced(function* (
126
- patterns: string[],
127
- ) {
128
- const matcher = yield* Effect.promise(() =>
129
- import("ignore").then(({ default: ignore }) =>
130
- ignore().add(patterns),
131
- ),
132
- );
133
- return (file: string) => matcher.ignores(file);
134
- });
107
+ export const readAssets = Effect.fnUntraced(function* (props: AssetsProps) {
108
+ const fs = yield* FileSystem.FileSystem;
109
+ const path = yield* Path.Path;
110
+ const resolvedDirectory = path.resolve(props.directory);
111
+ const [files, ignore, _headers, _redirects] = yield* Effect.all([
112
+ fs.readDirectory(resolvedDirectory, { recursive: true }),
113
+ maybeReadString(path.join(resolvedDirectory, ".assetsignore")),
114
+ maybeReadString(path.join(resolvedDirectory, "_headers")),
115
+ maybeReadString(path.join(resolvedDirectory, "_redirects")),
116
+ ]);
117
+ const ignores = yield* createIgnoreMatcher([
118
+ ".assetsignore",
119
+ "_headers",
120
+ "_redirects",
121
+ ...(ignore
122
+ ?.split("\n")
123
+ .map((line) => line.trim())
124
+ .filter((line) => line.length > 0 && !line.startsWith("#")) ?? []),
125
+ ]);
126
+ const manifest = new Map<string, { hash: string; size: number }>();
127
+ let count = 0;
128
+ yield* Effect.forEach(
129
+ files,
130
+ Effect.fnUntraced(function* (name) {
131
+ if (ignores(name)) {
132
+ return;
133
+ }
134
+ const file = path.join(resolvedDirectory, name);
135
+ const stat = yield* fs.stat(file);
136
+ if (stat.type !== "File") {
137
+ return;
138
+ }
139
+ const size = Number(stat.size);
140
+ if (size > MAX_ASSET_SIZE) {
141
+ return yield* new AssetTooLargeError({
142
+ message: `Asset ${name} is too large (the maximum size is ${MAX_ASSET_SIZE / 1024 / 1024} MB; this asset is ${size / 1024 / 1024} MB)`,
143
+ name,
144
+ size,
145
+ });
146
+ }
147
+ const hash = yield* fs.readFile(file).pipe(
148
+ Effect.flatMap(sha256),
149
+ Effect.map((hash) => hash.slice(0, 32)),
150
+ );
151
+ count++;
152
+ if (count > MAX_ASSET_COUNT) {
153
+ return yield* new TooManyAssetsError({
154
+ message: `Too many assets (the maximum count is ${MAX_ASSET_COUNT}; this directory has ${count} assets)`,
155
+ directory: props.directory,
156
+ count,
157
+ });
158
+ }
159
+ manifest.set(
160
+ (name.startsWith("/") ? name : `/${name}`).replaceAll("\\", "/"),
161
+ {
162
+ hash,
163
+ size,
164
+ },
165
+ );
166
+ }),
167
+ );
168
+ const result = {
169
+ directory: props.directory,
170
+ config: props.config,
171
+ manifest: Object.fromEntries(
172
+ Array.from(manifest.entries()).sort((a, b) => a[0].localeCompare(b[0])),
173
+ ),
174
+ _headers,
175
+ _redirects,
176
+ };
177
+ return {
178
+ ...result,
179
+ hash: yield* sha256Object(result),
180
+ };
181
+ });
135
182
 
136
- return {
137
- read: Effect.fnUntraced(function* (props: AssetsProps) {
138
- const resolvedDirectory = path.resolve(props.directory);
139
- const [files, ignore, _headers, _redirects] = yield* Effect.all([
140
- fs.readDirectory(resolvedDirectory, { recursive: true }),
141
- maybeReadString(path.join(resolvedDirectory, ".assetsignore")),
142
- maybeReadString(path.join(resolvedDirectory, "_headers")),
143
- maybeReadString(path.join(resolvedDirectory, "_redirects")),
144
- ]);
145
- const ignores = yield* createIgnoreMatcher([
146
- ".assetsignore",
147
- "_headers",
148
- "_redirects",
149
- ...(ignore
150
- ?.split("\n")
151
- .map((line) => line.trim())
152
- .filter((line) => line.length > 0 && !line.startsWith("#")) ??
153
- []),
154
- ]);
155
- const manifest = new Map<string, { hash: string; size: number }>();
156
- let count = 0;
157
- yield* Effect.forEach(
158
- files,
159
- Effect.fnUntraced(function* (name) {
160
- if (ignores(name)) {
161
- return;
162
- }
163
- const file = path.join(resolvedDirectory, name);
164
- const stat = yield* fs.stat(file);
165
- if (stat.type !== "File") {
166
- return;
167
- }
168
- const size = Number(stat.size);
169
- if (size > MAX_ASSET_SIZE) {
170
- return yield* new AssetTooLargeError({
171
- message: `Asset ${name} is too large (the maximum size is ${MAX_ASSET_SIZE / 1024 / 1024} MB; this asset is ${size / 1024 / 1024} MB)`,
172
- name,
173
- size,
174
- });
175
- }
176
- const hash = yield* fs.readFile(file).pipe(
177
- Effect.flatMap(sha256),
178
- Effect.map((hash) => hash.slice(0, 32)),
179
- );
180
- count++;
181
- if (count > MAX_ASSET_COUNT) {
182
- return yield* new TooManyAssetsError({
183
- message: `Too many assets (the maximum count is ${MAX_ASSET_COUNT}; this directory has ${count} assets)`,
184
- directory: props.directory,
185
- count,
186
- });
187
- }
188
- manifest.set(
189
- (name.startsWith("/") ? name : `/${name}`).replaceAll(
190
- "\\",
191
- "/",
192
- ),
193
- {
194
- hash,
195
- size,
196
- },
197
- );
198
- }),
199
- );
200
- const result = {
201
- directory: props.directory,
202
- config: props.config,
203
- manifest: Object.fromEntries(
204
- Array.from(manifest.entries()).sort((a, b) =>
205
- a[0].localeCompare(b[0]),
206
- ),
207
- ),
208
- _headers,
209
- _redirects,
210
- };
211
- return {
212
- ...result,
213
- hash: yield* sha256Object(result),
214
- };
215
- }),
216
- upload: Effect.fnUntraced(function* (
217
- accountId: string,
218
- workerName: string,
219
- assets: AssetReadResult,
220
- { note }: ScopedPlanStatusSession,
221
- ) {
222
- yield* note("Checking assets...");
223
- const session = yield* createScriptAssetUpload({
224
- accountId,
225
- scriptName: workerName,
226
- manifest: assets.manifest,
227
- });
228
- if (!session.buckets?.length) {
229
- return { jwt: session.jwt ?? undefined };
230
- }
231
- if (!session.jwt) {
232
- return { jwt: undefined };
233
- }
234
- const uploadJwt = session.jwt;
235
- let uploaded = 0;
236
- const total = session.buckets.flat().length;
237
- yield* note(`Uploaded ${uploaded} of ${total} assets...`);
238
- const assetsByHash = new Map<string, string>();
239
- for (const [name, { hash }] of Object.entries(assets.manifest)) {
240
- assetsByHash.set(hash, name);
183
+ export const uploadAssets = Effect.fnUntraced(function* (
184
+ accountId: string,
185
+ workerName: string,
186
+ assets: AssetReadResult,
187
+ { note }: ScopedPlanStatusSession,
188
+ ) {
189
+ const fs = yield* FileSystem.FileSystem;
190
+ const path = yield* Path.Path;
191
+ const createScriptAssetUpload = yield* workers.createScriptAssetUpload;
192
+ const createAssetUpload = yield* workers.createAssetUpload;
193
+
194
+ yield* note("Checking assets...");
195
+ const session = yield* createScriptAssetUpload({
196
+ accountId,
197
+ scriptName: workerName,
198
+ manifest: assets.manifest,
199
+ });
200
+ if (!session.buckets?.length) {
201
+ return { jwt: session.jwt ?? undefined };
202
+ }
203
+ if (!session.jwt) {
204
+ return { jwt: undefined };
205
+ }
206
+ const uploadJwt = session.jwt;
207
+ let uploaded = 0;
208
+ const total = session.buckets.flat().length;
209
+ yield* note(`Uploaded ${uploaded} of ${total} assets...`);
210
+ const assetsByHash = new Map<string, string>();
211
+ for (const [name, { hash }] of Object.entries(assets.manifest)) {
212
+ assetsByHash.set(hash, name);
213
+ }
214
+ let jwt: string | undefined | null;
215
+ const directory = path.resolve(assets.directory);
216
+ yield* Effect.forEach(
217
+ session.buckets,
218
+ Effect.fnUntraced(function* (bucket) {
219
+ const body: Record<string, File> = {};
220
+ yield* Effect.forEach(
221
+ bucket,
222
+ Effect.fnUntraced(function* (hash) {
223
+ const name = assetsByHash.get(hash);
224
+ if (!name) {
225
+ return yield* new AssetNotFoundError({
226
+ message: `Asset ${hash} not found in manifest`,
227
+ hash,
228
+ });
241
229
  }
242
- let jwt: string | undefined | null;
243
- const directory = path.resolve(assets.directory);
244
- yield* Effect.forEach(
245
- session.buckets,
246
- Effect.fnUntraced(function* (bucket) {
247
- const body: Record<string, File> = {};
248
- yield* Effect.forEach(
249
- bucket,
250
- Effect.fnUntraced(function* (hash) {
251
- const name = assetsByHash.get(hash);
252
- if (!name) {
253
- return yield* new AssetNotFoundError({
254
- message: `Asset ${hash} not found in manifest`,
255
- hash,
256
- });
257
- }
258
- const file = yield* fs
259
- .readFile(path.join(directory, name))
260
- .pipe(
261
- Effect.mapError(
262
- (error) =>
263
- new FailedToReadAssetError({
264
- message: `Failed to read asset ${name}: ${error.message}`,
265
- name,
266
- cause: error,
267
- }),
268
- ),
269
- );
270
- body[hash] = new File(
271
- [Buffer.from(file).toString("base64")],
272
- hash,
273
- {
274
- type: getContentType(name),
275
- },
276
- );
230
+ const file = yield* fs.readFile(path.join(directory, name)).pipe(
231
+ Effect.mapError(
232
+ (error) =>
233
+ new FailedToReadAssetError({
234
+ message: `Failed to read asset ${name}: ${error.message}`,
235
+ name,
236
+ cause: error,
277
237
  }),
278
- );
279
- const result = yield* createAssetUpload({
280
- accountId,
281
- base64: true,
282
- body,
283
- jwtToken: uploadJwt,
284
- });
285
-
286
- uploaded += bucket.length;
287
- yield* note(`Uploaded ${uploaded} of ${total} assets...`);
288
- if (result.jwt) {
289
- jwt = result.jwt;
290
- }
291
- }),
238
+ ),
292
239
  );
293
- return { jwt: jwt ?? undefined };
240
+ body[hash] = new File([Buffer.from(file).toString("base64")], hash, {
241
+ type: getContentType(name),
242
+ });
294
243
  }),
295
- };
244
+ );
245
+ const result = yield* createAssetUpload({
246
+ accountId,
247
+ base64: true,
248
+ body,
249
+ jwtToken: uploadJwt,
250
+ });
251
+
252
+ uploaded += bucket.length;
253
+ yield* note(`Uploaded ${uploaded} of ${total} assets...`);
254
+ if (result.jwt) {
255
+ jwt = result.jwt;
256
+ }
296
257
  }),
297
258
  );
259
+ return { jwt: jwt ?? undefined };
260
+ });