alchemy 2.0.0-beta.21 → 2.0.0-beta.23

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 (73) hide show
  1. package/bin/alchemy.js +3 -3
  2. package/bin/alchemy.js.map +1 -1
  3. package/bin/commands/logs.ts +1 -1
  4. package/bin/commands/tail.ts +1 -1
  5. package/lib/AWS/Lambda/Function.d.ts.map +1 -1
  6. package/lib/AWS/Lambda/Function.js +25 -21
  7. package/lib/AWS/Lambda/Function.js.map +1 -1
  8. package/lib/AWS/Lambda/HttpServer.d.ts.map +1 -1
  9. package/lib/AWS/Lambda/HttpServer.js +3 -1
  10. package/lib/AWS/Lambda/HttpServer.js.map +1 -1
  11. package/lib/Cloudflare/Container/Container.d.ts +55 -2
  12. package/lib/Cloudflare/Container/Container.d.ts.map +1 -1
  13. package/lib/Cloudflare/Container/Container.js +55 -2
  14. package/lib/Cloudflare/Container/Container.js.map +1 -1
  15. package/lib/Cloudflare/D1/D1Clone.d.ts +13 -0
  16. package/lib/Cloudflare/D1/D1Clone.d.ts.map +1 -0
  17. package/lib/Cloudflare/D1/D1Clone.js +31 -0
  18. package/lib/Cloudflare/D1/D1Clone.js.map +1 -0
  19. package/lib/Cloudflare/D1/D1Database.d.ts +150 -5
  20. package/lib/Cloudflare/D1/D1Database.d.ts.map +1 -1
  21. package/lib/Cloudflare/D1/D1Database.js +253 -0
  22. package/lib/Cloudflare/D1/D1Database.js.map +1 -1
  23. package/lib/Cloudflare/D1/D1Export.d.ts +24 -0
  24. package/lib/Cloudflare/D1/D1Export.d.ts.map +1 -0
  25. package/lib/Cloudflare/D1/D1Export.js +34 -0
  26. package/lib/Cloudflare/D1/D1Export.js.map +1 -0
  27. package/lib/Cloudflare/D1/D1Import.d.ts +21 -0
  28. package/lib/Cloudflare/D1/D1Import.d.ts.map +1 -0
  29. package/lib/Cloudflare/D1/D1Import.js +87 -0
  30. package/lib/Cloudflare/D1/D1Import.js.map +1 -0
  31. package/lib/Cloudflare/D1/D1Migrations.d.ts +16 -0
  32. package/lib/Cloudflare/D1/D1Migrations.d.ts.map +1 -0
  33. package/lib/Cloudflare/D1/D1Migrations.js +110 -0
  34. package/lib/Cloudflare/D1/D1Migrations.js.map +1 -0
  35. package/lib/Cloudflare/D1/D1SqlFile.d.ts +20 -0
  36. package/lib/Cloudflare/D1/D1SqlFile.d.ts.map +1 -0
  37. package/lib/Cloudflare/D1/D1SqlFile.js +46 -0
  38. package/lib/Cloudflare/D1/D1SqlFile.js.map +1 -0
  39. package/lib/Cloudflare/Providers.d.ts +1 -1
  40. package/lib/Cloudflare/Providers.d.ts.map +1 -1
  41. package/lib/Cloudflare/Queue/Queue.d.ts +34 -4
  42. package/lib/Cloudflare/Queue/Queue.d.ts.map +1 -1
  43. package/lib/Cloudflare/Queue/Queue.js +34 -4
  44. package/lib/Cloudflare/Queue/Queue.js.map +1 -1
  45. package/lib/Cloudflare/Queue/QueueBinding.d.ts +33 -0
  46. package/lib/Cloudflare/Queue/QueueBinding.d.ts.map +1 -1
  47. package/lib/Cloudflare/Queue/QueueBinding.js +33 -0
  48. package/lib/Cloudflare/Queue/QueueBinding.js.map +1 -1
  49. package/lib/Cloudflare/Workers/DurableObjectNamespace.d.ts +103 -6
  50. package/lib/Cloudflare/Workers/DurableObjectNamespace.d.ts.map +1 -1
  51. package/lib/Cloudflare/Workers/DurableObjectNamespace.js +103 -6
  52. package/lib/Cloudflare/Workers/DurableObjectNamespace.js.map +1 -1
  53. package/lib/Cloudflare/Workers/Workflow.d.ts +94 -0
  54. package/lib/Cloudflare/Workers/Workflow.d.ts.map +1 -1
  55. package/lib/Cloudflare/Workers/Workflow.js +94 -0
  56. package/lib/Cloudflare/Workers/Workflow.js.map +1 -1
  57. package/lib/Platform.js +1 -1
  58. package/lib/Platform.js.map +1 -1
  59. package/package.json +1 -1
  60. package/src/AWS/Lambda/Function.ts +30 -24
  61. package/src/AWS/Lambda/HttpServer.ts +3 -1
  62. package/src/Cloudflare/Container/Container.ts +55 -2
  63. package/src/Cloudflare/D1/D1Clone.ts +42 -0
  64. package/src/Cloudflare/D1/D1Database.ts +386 -3
  65. package/src/Cloudflare/D1/D1Export.ts +66 -0
  66. package/src/Cloudflare/D1/D1Import.ts +156 -0
  67. package/src/Cloudflare/D1/D1Migrations.ts +229 -0
  68. package/src/Cloudflare/D1/D1SqlFile.ts +55 -0
  69. package/src/Cloudflare/Queue/Queue.ts +34 -4
  70. package/src/Cloudflare/Queue/QueueBinding.ts +33 -0
  71. package/src/Cloudflare/Workers/DurableObjectNamespace.ts +103 -6
  72. package/src/Cloudflare/Workers/Workflow.ts +94 -0
  73. package/src/Platform.ts +1 -1
@@ -23,6 +23,7 @@ import { Platform, type Main, type PlatformProps } from "../../Platform.ts";
23
23
  import type { LogLine, LogsInput } from "../../Provider.ts";
24
24
  import * as Provider from "../../Provider.ts";
25
25
  import { Resource, type ResourceBinding } from "../../Resource.ts";
26
+ import { Self } from "../../Self.ts";
26
27
  import * as Serverless from "../../Serverless/index.ts";
27
28
  import { Stack } from "../../Stack.ts";
28
29
  import { createInternalTags, createTagsList, hasTags } from "../../Tags.ts";
@@ -617,7 +618,6 @@ export const FunctionProvider = () =>
617
618
  cwd,
618
619
  external: [
619
620
  /^@aws-sdk\//,
620
- /^@smithy\//,
621
621
  ...((props.build?.input?.external as string[]) ?? []),
622
622
  ],
623
623
  platform: "node",
@@ -627,8 +627,9 @@ export const FunctionProvider = () =>
627
627
  ...props.build?.output,
628
628
  format: "esm",
629
629
  sourcemap: rolldownSourcemap,
630
- minify: props.build?.output?.minify ?? true,
630
+ minify: props.build?.output?.minify ?? false,
631
631
  entryFileNames: "index.js",
632
+ codeSplitting: props.build?.output?.codeSplitting ?? false,
632
633
  },
633
634
  );
634
635
  });
@@ -639,30 +640,34 @@ export const FunctionProvider = () =>
639
640
  realMain,
640
641
  virtualEntryPlugin(
641
642
  (importPath) => `
642
- import { NodeServices } from "@effect/platform-node";
643
+ import { layer as nodeServicesLayer } from "@effect/platform-node/NodeServices";
643
644
  import { Stack } from "alchemy/Stack";
644
645
  import * as Config from "effect/Config";
645
646
  import * as ConfigProvider from "effect/ConfigProvider";
646
647
  import * as Credentials from "@distilled.cloud/aws/Credentials";
647
648
  import * as Effect from "effect/Effect";
648
- import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient";
649
+ import { layer as fetchHttpClientLayer } from "effect/unstable/http/FetchHttpClient";
649
650
  import * as Layer from "effect/Layer";
650
651
  import * as Logger from "effect/Logger";
651
652
  import * as Region from "@distilled.cloud/aws/Region";
652
653
  import * as Context from "effect/Context";
653
654
  import { MinimumLogLevel } from "effect/References";
654
655
 
655
- import { ${handler} as layer } from "${importPath}";
656
+ import entry from "${importPath}";
657
+
658
+ const tag = Context.Service("${Self.key}")
659
+ const layer =
660
+ typeof entry?.build === "function"
661
+ ? entry
662
+ : Layer.effect(tag, typeof entry?.asEffect === "function" ? entry.asEffect() : entry);
656
663
 
657
664
  const platform = Layer.mergeAll(
658
- NodeServices.layer,
659
- FetchHttpClient.layer,
665
+ nodeServicesLayer,
666
+ fetchHttpClientLayer,
660
667
  // TODO(sam): wire this up to telemetry more directly
661
668
  Logger.layer([Logger.consolePretty()]),
662
669
  );
663
670
 
664
- const tag = Context.Service("${FunctionTypeId}<${id}>")
665
-
666
671
  const stack = Layer.effect(
667
672
  Stack,
668
673
  Effect.all([
@@ -679,7 +684,8 @@ const stack = Layer.effect(
679
684
  );
680
685
 
681
686
  const handlerEffect = tag.asEffect().pipe(
682
- Effect.flatMap(func => func.ExecutionContext.exports.handler),
687
+ Effect.flatMap(func => func.ExecutionContext.exports),
688
+ Effect.flatMap(exports => exports.handler),
683
689
  Effect.provide(
684
690
  layer.pipe(
685
691
  Layer.provideMerge(stack),
@@ -714,23 +720,23 @@ export default await Effect.runPromise(handlerEffect)
714
720
  ? new TextEncoder().encode(mainFile.content)
715
721
  : mainFile.content;
716
722
 
717
- const sourceMapFile =
718
- uploadSourceMap && (sourcemap === true || sourcemap === "hidden")
719
- ? bundleOutput.files.find((f: Bundle.BundleFile) =>
720
- f.path.endsWith(".map"),
721
- )
722
- : undefined;
723
+ const includeSourceMaps =
724
+ uploadSourceMap && (sourcemap === true || sourcemap === "hidden");
725
+
726
+ const extraFiles = bundleOutput.files
727
+ .slice(1)
728
+ .filter(
729
+ (f: Bundle.BundleFile) =>
730
+ includeSourceMaps || !f.path.endsWith(".map"),
731
+ )
732
+ .map((f: Bundle.BundleFile) => ({
733
+ path: f.path,
734
+ content: f.content,
735
+ }));
723
736
 
724
737
  const archive = yield* zipCode(
725
738
  code,
726
- sourceMapFile
727
- ? [
728
- {
729
- path: sourceMapFile.path,
730
- content: sourceMapFile.content,
731
- },
732
- ]
733
- : undefined,
739
+ extraFiles.length > 0 ? extraFiles : undefined,
734
740
  );
735
741
  return {
736
742
  archive,
@@ -19,7 +19,9 @@ export const makeFunctionHttpHandler = <Req>(handler: Http.HttpEffect<Req>) => {
19
19
  const safeHandler = Http.safeHttpEffect(handler);
20
20
  return (event: any) => {
21
21
  if (isFunctionURLEvent(event)) {
22
- const request = HttpServerRequest.fromWeb(toWebRequest(event)).modify({
22
+ const webRequest = toWebRequest(event);
23
+ const request = HttpServerRequest.fromWeb(webRequest).modify({
24
+ url: webRequest.url,
23
25
  remoteAddress: Option.some(event.requestContext.http.sourceIp),
24
26
  });
25
27
  return safeHandler.pipe(
@@ -118,8 +118,10 @@ export type Container = {
118
118
  * @section Configuration
119
119
  * The props object accepts `main` (entrypoint file), `instanceType`
120
120
  * (compute size), `runtime` (`"bun"` or `"node"`), and
121
- * `observability` settings. Use `Stack.useSync` to vary config by
122
- * stage.
121
+ * `observability` settings. Use `Stack.useSync` to read the
122
+ * surrounding stack at declaration time and pick a beefier
123
+ * `instanceType` in prod while keeping the cheap `dev` instance for
124
+ * preview environments.
123
125
  *
124
126
  * @example Stage-dependent configuration
125
127
  * ```typescript
@@ -133,6 +135,57 @@ export type Container = {
133
135
  * ) {}
134
136
  * ```
135
137
  *
138
+ * @section Stack-level wiring
139
+ * The `.make()` `export default` is the side-effect that registers
140
+ * the container's runtime. It must be reachable from your
141
+ * `alchemy.run.ts` so the bundler emits the runtime entrypoint.
142
+ * Provide it on the Stack's generator with `Effect.provide`.
143
+ *
144
+ * @example Wiring SandboxLive into the Stack
145
+ * ```typescript
146
+ * // alchemy.run.ts
147
+ * import SandboxLive from "./src/Sandbox.runtime.ts";
148
+ *
149
+ * export default Alchemy.Stack(
150
+ * "MyApp",
151
+ * { providers: Cloudflare.providers(), state: Cloudflare.state() },
152
+ * Effect.gen(function* () {
153
+ * const worker = yield* Worker;
154
+ * return { url: worker.url };
155
+ * }).pipe(Effect.provide(SandboxLive)),
156
+ * );
157
+ * ```
158
+ *
159
+ * @section Calling from a Durable Object
160
+ * Use `Cloudflare.Container.bind(Sandbox)` in the **outer** init
161
+ * phase of a Durable Object — only the class is imported, so the
162
+ * DO bundle stays tiny. Then `Cloudflare.start(sandbox)` in the
163
+ * **inner** per-instance phase ensures the container is running
164
+ * and gives you a typed handle that exposes every method declared
165
+ * on the container's shape **plus** a `getTcpPort` helper.
166
+ *
167
+ * @example Binding and starting a container from a DO
168
+ * ```typescript
169
+ * export default class Agent extends Cloudflare.DurableObjectNamespace<Agent>()(
170
+ * "Agents",
171
+ * Effect.gen(function* () {
172
+ * // OUTER (init): only the class is referenced — the runtime
173
+ * // implementation in `Sandbox.runtime.ts` is tree-shaken out
174
+ * // of this DO's bundle.
175
+ * const sandbox = yield* Cloudflare.Container.bind(Sandbox);
176
+ *
177
+ * return Effect.gen(function* () {
178
+ * // INNER (per-instance): start the container and expose RPC.
179
+ * const container = yield* Cloudflare.start(sandbox);
180
+ *
181
+ * return {
182
+ * exec: (cmd: string) => container.exec(cmd),
183
+ * };
184
+ * });
185
+ * }),
186
+ * ) {}
187
+ * ```
188
+ *
136
189
  * @section Starting from a Durable Object
137
190
  * Use `Cloudflare.Container.bind` in the outer init phase to bind
138
191
  * the container class, then `Cloudflare.start` in the inner
@@ -0,0 +1,42 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as HttpClient from "effect/unstable/http/HttpClient";
3
+ import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest";
4
+ import { exportD1Database } from "./D1Export.ts";
5
+ import { importD1Database } from "./D1Import.ts";
6
+
7
+ export interface CloneD1DatabaseOptions {
8
+ accountId: string;
9
+ sourceDatabaseId: string;
10
+ targetDatabaseId: string;
11
+ }
12
+
13
+ /**
14
+ * Clone a D1 database by exporting from the source and importing into the
15
+ * target. Fetches the SQL dump from the export's signed URL and streams the
16
+ * payload through the import flow.
17
+ */
18
+ export const cloneD1Database = (options: CloneD1DatabaseOptions) =>
19
+ Effect.gen(function* () {
20
+ const exportResult = yield* exportD1Database({
21
+ accountId: options.accountId,
22
+ databaseId: options.sourceDatabaseId,
23
+ });
24
+
25
+ const client = yield* HttpClient.HttpClient;
26
+ const dumpRes = yield* client
27
+ .execute(HttpClientRequest.get(exportResult.signedUrl))
28
+ .pipe(Effect.orDie);
29
+ if (dumpRes.status < 200 || dumpRes.status >= 300) {
30
+ return yield* Effect.die(
31
+ `Failed to fetch D1 export dump (${dumpRes.status})`,
32
+ );
33
+ }
34
+ const sqlData = yield* dumpRes.text.pipe(Effect.orDie);
35
+
36
+ return yield* importD1Database({
37
+ accountId: options.accountId,
38
+ databaseId: options.targetDatabaseId,
39
+ sqlData,
40
+ filename: exportResult.filename,
41
+ });
42
+ });