alchemy 2.0.0-beta.1 → 2.0.0-beta.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 (144) hide show
  1. package/bin/alchemy-effect.js +31 -16
  2. package/bin/alchemy-effect.js.map +1 -1
  3. package/bin/alchemy-effect.sh +2 -3
  4. package/bin/alchemy-effect.ts +3 -3
  5. package/lib/cli/index.d.ts +39 -36
  6. package/lib/cli/index.d.ts.map +1 -1
  7. package/package.json +18 -9
  8. package/src/AWS/ACM/Certificate.ts +49 -46
  9. package/src/AWS/AutoScaling/AutoScalingGroup.ts +4 -1
  10. package/src/AWS/AutoScaling/LaunchTemplate.ts +3 -1
  11. package/src/AWS/AutoScaling/ScalingPolicy.ts +4 -1
  12. package/src/AWS/CloudFront/Distribution.ts +4 -1
  13. package/src/AWS/CloudFront/Function.ts +4 -1
  14. package/src/AWS/CloudFront/Invalidation.ts +4 -1
  15. package/src/AWS/CloudFront/KeyValueStore.ts +4 -1
  16. package/src/AWS/CloudFront/KvEntries.ts +4 -1
  17. package/src/AWS/CloudFront/KvRoutesUpdate.ts +4 -1
  18. package/src/AWS/CloudFront/OriginAccessControl.ts +4 -1
  19. package/src/AWS/CloudWatch/Alarm.ts +4 -1
  20. package/src/AWS/CloudWatch/AlarmMuteRule.ts +4 -1
  21. package/src/AWS/CloudWatch/AnomalyDetector.ts +4 -1
  22. package/src/AWS/CloudWatch/CompositeAlarm.ts +4 -1
  23. package/src/AWS/CloudWatch/Dashboard.ts +4 -1
  24. package/src/AWS/CloudWatch/InsightRule.ts +4 -1
  25. package/src/AWS/CloudWatch/MetricStream.ts +4 -1
  26. package/src/AWS/DynamoDB/Table.ts +3 -1
  27. package/src/AWS/EC2/EIP.ts +4 -1
  28. package/src/AWS/EC2/EgressOnlyInternetGateway.ts +4 -1
  29. package/src/AWS/EC2/Instance.ts +3 -1
  30. package/src/AWS/EC2/InternetGateway.ts +4 -1
  31. package/src/AWS/EC2/NatGateway.ts +4 -1
  32. package/src/AWS/EC2/NetworkAcl.ts +4 -1
  33. package/src/AWS/EC2/NetworkAclAssociation.ts +4 -1
  34. package/src/AWS/EC2/NetworkAclEntry.ts +4 -1
  35. package/src/AWS/EC2/Route.ts +4 -1
  36. package/src/AWS/EC2/RouteTable.ts +4 -1
  37. package/src/AWS/EC2/RouteTableAssociation.ts +4 -1
  38. package/src/AWS/EC2/SecurityGroup.ts +4 -1
  39. package/src/AWS/EC2/SecurityGroupRule.ts +4 -1
  40. package/src/AWS/EC2/Subnet.ts +4 -1
  41. package/src/AWS/EC2/Vpc.ts +4 -1
  42. package/src/AWS/EC2/VpcEndpoint.ts +4 -1
  43. package/src/AWS/ECR/Repository.ts +4 -1
  44. package/src/AWS/ECS/Cluster.ts +4 -1
  45. package/src/AWS/ECS/Service.ts +4 -1
  46. package/src/AWS/ECS/Task.ts +3 -1
  47. package/src/AWS/EKS/AccessEntry.ts +4 -1
  48. package/src/AWS/EKS/Addon.ts +4 -1
  49. package/src/AWS/EKS/Cluster.ts +3 -1
  50. package/src/AWS/EKS/PodIdentityAssociation.ts +4 -1
  51. package/src/AWS/ELBv2/Listener.ts +4 -1
  52. package/src/AWS/ELBv2/LoadBalancer.ts +4 -1
  53. package/src/AWS/ELBv2/TargetGroup.ts +4 -1
  54. package/src/AWS/EventBridge/EventBus.ts +4 -1
  55. package/src/AWS/EventBridge/Permission.ts +4 -1
  56. package/src/AWS/EventBridge/Rule.ts +4 -1
  57. package/src/AWS/IAM/AccessKey.ts +4 -1
  58. package/src/AWS/IAM/AccountAlias.ts +4 -1
  59. package/src/AWS/IAM/AccountPasswordPolicy.ts +4 -1
  60. package/src/AWS/IAM/Group.ts +4 -1
  61. package/src/AWS/IAM/GroupMembership.ts +4 -1
  62. package/src/AWS/IAM/InstanceProfile.ts +4 -1
  63. package/src/AWS/IAM/LoginProfile.ts +4 -1
  64. package/src/AWS/IAM/OpenIDConnectProvider.ts +4 -1
  65. package/src/AWS/IAM/Policy.ts +4 -1
  66. package/src/AWS/IAM/Role.ts +4 -1
  67. package/src/AWS/IAM/SAMLProvider.ts +4 -1
  68. package/src/AWS/IAM/SSHPublicKey.ts +4 -1
  69. package/src/AWS/IAM/ServerCertificate.ts +4 -1
  70. package/src/AWS/IAM/ServiceSpecificCredential.ts +4 -1
  71. package/src/AWS/IAM/SigningCertificate.ts +4 -1
  72. package/src/AWS/IAM/User.ts +4 -1
  73. package/src/AWS/IAM/VirtualMFADevice.ts +4 -1
  74. package/src/AWS/IdentityCenter/AccountAssignment.ts +4 -1
  75. package/src/AWS/IdentityCenter/Group.ts +4 -1
  76. package/src/AWS/IdentityCenter/Instance.ts +4 -1
  77. package/src/AWS/IdentityCenter/PermissionSet.ts +4 -1
  78. package/src/AWS/Kinesis/Stream.ts +4 -1
  79. package/src/AWS/Kinesis/StreamConsumer.ts +4 -1
  80. package/src/AWS/Lambda/EventSourceMapping.ts +4 -1
  81. package/src/AWS/Lambda/Function.ts +3 -1
  82. package/src/AWS/Lambda/Permission.ts +4 -1
  83. package/src/AWS/Logs/LogGroup.ts +4 -1
  84. package/src/AWS/Organizations/Account.ts +4 -1
  85. package/src/AWS/Organizations/DelegatedAdministrator.ts +4 -1
  86. package/src/AWS/Organizations/Organization.ts +4 -1
  87. package/src/AWS/Organizations/OrganizationResourcePolicy.ts +4 -1
  88. package/src/AWS/Organizations/OrganizationalUnit.ts +4 -1
  89. package/src/AWS/Organizations/Policy.ts +4 -1
  90. package/src/AWS/Organizations/PolicyAttachment.ts +4 -1
  91. package/src/AWS/Organizations/Root.ts +4 -1
  92. package/src/AWS/Organizations/RootPolicyType.ts +4 -1
  93. package/src/AWS/Organizations/TrustedServiceAccess.ts +4 -1
  94. package/src/AWS/Providers.ts +484 -438
  95. package/src/AWS/RDS/DBCluster.ts +4 -1
  96. package/src/AWS/RDS/DBClusterEndpoint.ts +4 -1
  97. package/src/AWS/RDS/DBClusterParameterGroup.ts +4 -1
  98. package/src/AWS/RDS/DBInstance.ts +4 -1
  99. package/src/AWS/RDS/DBParameterGroup.ts +4 -1
  100. package/src/AWS/RDS/DBProxy.ts +4 -1
  101. package/src/AWS/RDS/DBProxyEndpoint.ts +4 -1
  102. package/src/AWS/RDS/DBProxyTargetGroup.ts +4 -1
  103. package/src/AWS/RDS/DBSubnetGroup.ts +4 -1
  104. package/src/AWS/Route53/Record.ts +4 -1
  105. package/src/AWS/S3/Bucket.ts +3 -1
  106. package/src/AWS/SNS/Subscription.ts +4 -1
  107. package/src/AWS/SNS/Topic.ts +4 -1
  108. package/src/AWS/SQS/Queue.ts +3 -1
  109. package/src/AWS/Scheduler/Schedule.ts +4 -1
  110. package/src/AWS/Scheduler/ScheduleGroup.ts +4 -1
  111. package/src/AWS/SecretsManager/Secret.ts +4 -1
  112. package/src/AWS/Website/AssetDeployment.ts +4 -1
  113. package/src/Apply.ts +3 -3
  114. package/src/Binding.ts +18 -17
  115. package/src/Cloudflare/Container/Container.ts +122 -0
  116. package/src/Cloudflare/Container/ContainerApplication.ts +6 -1
  117. package/src/Cloudflare/Container/ContainerBinding.ts +1 -1
  118. package/src/Cloudflare/Container/StartContainer.ts +1 -1
  119. package/src/Cloudflare/D1/D1Database.ts +27 -5
  120. package/src/Cloudflare/KV/KVNamespace.ts +32 -4
  121. package/src/Cloudflare/Providers.ts +62 -60
  122. package/src/Cloudflare/R2/R2Bucket.ts +47 -1
  123. package/src/Cloudflare/R2/R2BucketBinding.ts +26 -31
  124. package/src/Cloudflare/Website/StaticSite.ts +57 -0
  125. package/src/Cloudflare/Website/Vite.ts +52 -8
  126. package/src/Cloudflare/Workers/{DurableObject.ts → DurableObjectNamespace.ts} +242 -0
  127. package/src/Cloudflare/Workers/DynamicWorkerLoader.ts +66 -7
  128. package/src/Cloudflare/Workers/WebSocket.ts +1 -1
  129. package/src/Cloudflare/Workers/Worker.ts +315 -8
  130. package/src/Cloudflare/Workers/Workflow.ts +56 -12
  131. package/src/Cloudflare/Workers/index.ts +1 -1
  132. package/src/GitHub/Comment.ts +224 -0
  133. package/src/GitHub/Secret.ts +212 -0
  134. package/src/GitHub/Variable.ts +123 -0
  135. package/src/GitHub/index.ts +3 -0
  136. package/src/Plan.ts +595 -596
  137. package/src/Platform.ts +9 -8
  138. package/src/Provider.ts +145 -23
  139. package/src/Resource.ts +10 -3
  140. package/src/Stack.ts +1 -1
  141. package/src/Util/dedent.ts +56 -0
  142. package/src/Util/index.ts +1 -0
  143. package/src/Util/service.ts +1 -1
  144. package/src/index.ts +10 -7
@@ -5,9 +5,10 @@ import { createPhysicalName } from "../../PhysicalName.ts";
5
5
  import * as Provider from "../../Provider.ts";
6
6
  import { Resource } from "../../Resource.ts";
7
7
  import { Account } from "../Account.ts";
8
+ import type { Providers } from "../Providers.ts";
8
9
  import { KVNamespaceBinding } from "./KVNamespaceBinding.ts";
9
10
 
10
- export type NamespaceProps = {
11
+ export type KVNamespaceProps = {
11
12
  /**
12
13
  * A human-readable string name for the namespace.
13
14
  * If omitted, a unique name will be generated.
@@ -18,20 +19,47 @@ export type NamespaceProps = {
18
19
 
19
20
  export type KVNamespace = Resource<
20
21
  "Cloudflare.KVNamespace",
21
- NamespaceProps,
22
+ KVNamespaceProps,
22
23
  {
23
24
  title: string;
24
25
  namespaceId: string;
25
26
  supportsUrlEncoding: boolean | undefined;
26
27
  accountId: string;
27
- }
28
+ },
29
+ never,
30
+ Providers
28
31
  >;
29
32
 
33
+ /**
34
+ * A Cloudflare Workers KV namespace for key-value storage at the edge.
35
+ *
36
+ * KV provides eventually-consistent, low-latency reads with global
37
+ * replication. Create a namespace as a resource, then bind it to a Worker
38
+ * to get/put values at runtime.
39
+ *
40
+ * @section Creating a Namespace
41
+ * @example Basic KV namespace
42
+ * ```typescript
43
+ * const kv = yield* Cloudflare.KVNamespace("MyKV");
44
+ * ```
45
+ *
46
+ * @section Binding to a Worker
47
+ * @example Using KV inside a Worker
48
+ * ```typescript
49
+ * const kv = yield* Cloudflare.KVNamespace.bind(MyKV);
50
+ *
51
+ * // Read a value
52
+ * const value = yield* kv.get("my-key");
53
+ *
54
+ * // Write a value
55
+ * yield* kv.put("my-key", "hello world");
56
+ * ```
57
+ */
30
58
  export const KVNamespace = Resource<KVNamespace>("Cloudflare.KVNamespace")({
31
59
  bind: KVNamespaceBinding.bind,
32
60
  });
33
61
 
34
- export const NamespaceProvider = () =>
62
+ export const KVNamespaceProvider = () =>
35
63
  Provider.effect(
36
64
  KVNamespace,
37
65
  Effect.gen(function* () {
@@ -1,75 +1,77 @@
1
1
  import * as Auth from "@distilled.cloud/cloudflare/Auth";
2
- import { pipe } from "effect/Function";
3
2
  import * as Layer from "effect/Layer";
4
- import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient";
5
3
  import * as Socket from "effect/unstable/socket/Socket";
6
- import { CommandProvider } from "../Build/Command.ts";
7
- import { RandomProvider } from "../Random.ts";
4
+ import { Command } from "../Build/Command.ts";
5
+ import * as Build from "../Build/index.ts";
6
+ import * as Provider from "../Provider.ts";
7
+ import { Random, RandomProvider } from "../Random.ts";
8
8
  import * as Account from "./Account.ts";
9
- import { ContainerProvider } from "./Container/ContainerApplication.ts";
9
+ import * as Containers from "./Container/index.ts";
10
10
  import * as D1 from "./D1/index.ts";
11
11
  import * as KV from "./KV/index.ts";
12
12
  import * as R2 from "./R2/index.ts";
13
- import { AssetsProvider } from "./Workers/Assets.ts";
14
- import { WorkerProvider } from "./Workers/Worker.ts";
15
- import { WorkflowProvider } from "./Workers/Workflow.ts";
13
+ import * as Assets from "./Workers/Assets.ts";
14
+ import * as Workers from "./Workers/index.ts";
15
+ import * as Workflows from "./Workers/Workflow.ts";
16
16
 
17
- // export type Providers = Extract<Layer.Success<_providers>, Provider<any>>;
17
+ export { Credentials } from "@distilled.cloud/cloudflare/Credentials";
18
18
 
19
- export interface Providers extends Layer.Layer<
20
- Layer.Success<_providers>,
21
- Layer.Error<_providers>,
22
- Layer.Services<_providers>
23
- > {}
19
+ export class Providers extends Provider.ProviderCollection<Providers>()(
20
+ "Cloudflare",
21
+ ) {}
22
+
23
+ export type ProviderRequirements = Layer.Services<ReturnType<typeof providers>>;
24
24
 
25
25
  /**
26
26
  * Cloudflare providers, bindings, and credentials for Worker-based stacks.
27
27
  */
28
- export const providers: () => Providers = () => _providers();
29
-
30
- type _providers = ReturnType<typeof _providers>;
31
- const _providers = () =>
32
- pipe(
33
- resources(),
34
- Layer.provideMerge(bindings()),
35
- Layer.provideMerge(utils()),
36
- Layer.provideMerge(credentials()),
28
+ export const providers = () =>
29
+ Layer.effect(
30
+ Providers,
31
+ Provider.collection([
32
+ Command,
33
+ Containers.Container,
34
+ D1.D1ConnectionPolicy,
35
+ D1.D1Database,
36
+ KV.KVNamespace,
37
+ KV.KVNamespaceBindingPolicy,
38
+ R2.R2Bucket,
39
+ R2.R2BucketBindingPolicy,
40
+ Random,
41
+ Workers.BindWorkerPolicy,
42
+ Workers.FetchPolicy,
43
+ Workers.Worker,
44
+ Workflows.WorkflowResource,
45
+ ]),
46
+ ).pipe(
47
+ Layer.provide(
48
+ Layer.mergeAll(
49
+ Containers.ContainerProvider(),
50
+ D1.D1ConnectionPolicyLive,
51
+ D1.DatabaseProvider(),
52
+ KV.KVNamespaceBindingPolicyLive,
53
+ KV.KVNamespaceProvider(),
54
+ R2.R2BucketBindingPolicyLive,
55
+ R2.R2BucketProvider(),
56
+ Workers.BindWorkerPolicyLive,
57
+ Workers.FetchPolicyLive,
58
+ Workers.WorkerProvider(),
59
+ Workflows.WorkflowProvider(),
60
+ ),
61
+ ),
62
+ Layer.provideMerge(
63
+ Layer.mergeAll(
64
+ Assets.AssetsProvider(),
65
+ Build.CommandProvider(),
66
+ RandomProvider(),
67
+ ),
68
+ ),
69
+ Layer.provideMerge(
70
+ Layer.mergeAll(
71
+ Account.fromStageConfig(),
72
+ Auth.fromEnv(),
73
+ Socket.layerWebSocketConstructorGlobal,
74
+ ),
75
+ ),
37
76
  Layer.orDie,
38
77
  );
39
-
40
- /**
41
- * Cloudflare account credentials and auth context.
42
- */
43
- export const credentials = () =>
44
- Layer.mergeAll(
45
- Account.fromStageConfig(),
46
- Layer.provideMerge(Auth.fromEnv(), FetchHttpClient.layer),
47
- );
48
-
49
- /**
50
- * All Cloudflare resource providers.
51
- */
52
- export const resources = () =>
53
- Layer.mergeAll(
54
- CommandProvider(),
55
- RandomProvider(),
56
- ContainerProvider(),
57
- WorkerProvider(),
58
- WorkflowProvider(),
59
- D1.DatabaseProvider(),
60
- KV.NamespaceProvider(),
61
- R2.R2BucketProvider(),
62
- );
63
-
64
- /**
65
- * All Cloudflare binding policies.
66
- */
67
- export const bindings = () =>
68
- Layer.mergeAll(
69
- D1.D1ConnectionPolicyLive,
70
- R2.R2BucketBindingPolicyLive,
71
- KV.KVNamespaceBindingPolicyLive,
72
- );
73
-
74
- const utils = () =>
75
- Layer.mergeAll(AssetsProvider(), Socket.layerWebSocketConstructorGlobal);
@@ -6,6 +6,7 @@ import { createPhysicalName } from "../../PhysicalName.ts";
6
6
  import * as Provider from "../../Provider.ts";
7
7
  import { Resource } from "../../Resource.ts";
8
8
  import { Account } from "../Account.ts";
9
+ import type { Providers } from "../Providers.ts";
9
10
  import { R2BucketBinding } from "./R2BucketBinding.ts";
10
11
 
11
12
  export type R2BucketName = string;
@@ -41,9 +42,54 @@ export type R2Bucket = Resource<
41
42
  jurisdiction: R2Bucket.Jurisdiction;
42
43
  location: R2Bucket.Location | undefined;
43
44
  accountId: string;
44
- }
45
+ },
46
+ never,
47
+ Providers
45
48
  >;
46
49
 
50
+ /**
51
+ * A Cloudflare R2 object storage bucket with S3-compatible API.
52
+ *
53
+ * R2 provides zero-egress-fee object storage. Create a bucket as a resource,
54
+ * then bind it to a Worker to read and write objects at runtime.
55
+ *
56
+ * @section Creating a Bucket
57
+ * @example Basic R2 bucket
58
+ * ```typescript
59
+ * const bucket = yield* Cloudflare.R2Bucket("MyBucket");
60
+ * ```
61
+ *
62
+ * @example Bucket with location hint
63
+ * ```typescript
64
+ * const bucket = yield* Cloudflare.R2Bucket("MyBucket", {
65
+ * locationHint: "wnam",
66
+ * });
67
+ * ```
68
+ *
69
+ * @section Binding to a Worker
70
+ * @example Reading and writing objects
71
+ * ```typescript
72
+ * const bucket = yield* Cloudflare.R2Bucket.bind(MyBucket);
73
+ *
74
+ * // Write an object
75
+ * yield* bucket.put("hello.txt", "Hello, World!");
76
+ *
77
+ * // Read an object
78
+ * const object = yield* bucket.get("hello.txt");
79
+ * if (object) {
80
+ * const text = yield* object.text();
81
+ * }
82
+ * ```
83
+ *
84
+ * @example Streaming upload with content length
85
+ * ```typescript
86
+ * const bucket = yield* Cloudflare.R2Bucket.bind(MyBucket);
87
+ *
88
+ * yield* bucket.put("upload.bin", request.stream, {
89
+ * contentLength: Number(request.headers["content-length"] ?? 0),
90
+ * });
91
+ * ```
92
+ */
47
93
  export const R2Bucket = Resource<R2Bucket>("Cloudflare.R2Bucket")({
48
94
  bind: R2BucketBinding.bind,
49
95
  });
@@ -142,7 +142,7 @@ export class R2BucketBinding extends Binding.Service<
142
142
  export const R2BucketBindingLive = Layer.effect(
143
143
  R2BucketBinding,
144
144
  Effect.gen(function* () {
145
- const bind = yield* BucketBindingPolicy;
145
+ const bind = yield* R2BucketBindingPolicy;
146
146
 
147
147
  return Effect.fn(function* (bucket: R2Bucket) {
148
148
  yield* bind(bucket);
@@ -306,37 +306,32 @@ export const R2BucketBindingLive = Layer.effect(
306
306
  }),
307
307
  );
308
308
 
309
- export class BucketBindingPolicy extends Binding.Policy<
310
- BucketBindingPolicy,
309
+ export class R2BucketBindingPolicy extends Binding.Policy<
310
+ R2BucketBindingPolicy,
311
311
  (bucket: R2Bucket) => Effect.Effect<void>
312
312
  >()("Cloudflare.R2Bucket") {}
313
313
 
314
- export const R2BucketBindingPolicyLive = BucketBindingPolicy.layer.succeed(
315
- (host, bucket) => R2BucketClient(host, bucket),
316
- );
317
-
318
- export const R2BucketClient = Effect.fn(function* (
319
- host: ResourceLike,
320
- bucket: R2Bucket,
321
- ) {
322
- if (isWorker(host)) {
323
- yield* host.bind`${bucket}`({
324
- bindings: [
325
- {
326
- type: "r2_bucket",
327
- name: bucket.LogicalId,
328
- bucketName: bucket.bucketName,
329
- jurisdiction: bucket.jurisdiction.pipe(
330
- Output.map((jurisdiction) =>
331
- jurisdiction === "default" ? undefined : jurisdiction,
314
+ export const R2BucketBindingPolicyLive = R2BucketBindingPolicy.layer.succeed(
315
+ Effect.fnUntraced(function* (host: ResourceLike, bucket: R2Bucket) {
316
+ if (isWorker(host)) {
317
+ yield* host.bind`${bucket}`({
318
+ bindings: [
319
+ {
320
+ type: "r2_bucket",
321
+ name: bucket.LogicalId,
322
+ bucketName: bucket.bucketName,
323
+ jurisdiction: bucket.jurisdiction.pipe(
324
+ Output.map((jurisdiction) =>
325
+ jurisdiction === "default" ? undefined : jurisdiction,
326
+ ),
332
327
  ),
333
- ),
334
- },
335
- ],
336
- });
337
- } else {
338
- return yield* Effect.die(
339
- new Error(`BucketBinding does not support runtime '${host.Type}'`),
340
- );
341
- }
342
- });
328
+ },
329
+ ],
330
+ });
331
+ } else {
332
+ return yield* Effect.die(
333
+ new Error(`BucketBinding does not support runtime '${host.Type}'`),
334
+ );
335
+ }
336
+ }),
337
+ );
@@ -19,6 +19,63 @@ export interface StaticSiteProps
19
19
 
20
20
  export type StaticSite = ReturnType<typeof StaticSite>;
21
21
 
22
+ /**
23
+ * A Cloudflare Worker that serves static assets built by a shell command.
24
+ *
25
+ * `StaticSite` runs a build command (e.g. `npm run build`), content-hashes
26
+ * the output directory, and deploys the result as a Cloudflare Worker with
27
+ * static assets. Use this when your site has its own build step that
28
+ * produces a directory of files — Hugo, Zola, Eleventy, or any custom
29
+ * pipeline.
30
+ *
31
+ * For Vite-based projects, prefer `Cloudflare.Vite` which handles
32
+ * building automatically.
33
+ *
34
+ * @resource
35
+ *
36
+ * @section Basic Usage
37
+ * Point `command` at your build script and `outdir` at where it writes
38
+ * output. Alchemy runs the command, hashes the output, and deploys it.
39
+ *
40
+ * @example Deploying a Hugo site
41
+ * ```typescript
42
+ * const site = yield* Cloudflare.StaticSite("Blog", {
43
+ * command: "hugo --minify",
44
+ * outdir: "public",
45
+ * });
46
+ * ```
47
+ *
48
+ * @section Asset Configuration
49
+ * Use `assetsConfig` to control how Cloudflare handles routing for
50
+ * your static files — HTML handling, not-found behavior, etc.
51
+ *
52
+ * @example SPA-style routing
53
+ * ```typescript
54
+ * const site = yield* Cloudflare.StaticSite("App", {
55
+ * command: "npm run build",
56
+ * outdir: "dist",
57
+ * assetsConfig: {
58
+ * htmlHandling: "auto-trailing-slash",
59
+ * notFoundHandling: "single-page-application",
60
+ * },
61
+ * });
62
+ * ```
63
+ *
64
+ * @section Custom Rebuild Scope
65
+ * By default, all non-gitignored files are hashed to decide whether
66
+ * the build should re-run. Use `memo` to narrow the scope.
67
+ *
68
+ * @example Narrowing the memo scope
69
+ * ```typescript
70
+ * const site = yield* Cloudflare.StaticSite("Docs", {
71
+ * command: "npm run build",
72
+ * outdir: "dist",
73
+ * memo: {
74
+ * include: ["content/**", "templates/**", "config.toml"],
75
+ * },
76
+ * });
77
+ * ```
78
+ */
22
79
  export const StaticSite = (id: string, props: InputProps<StaticSiteProps>) =>
23
80
  Effect.gen(function* () {
24
81
  // TODO(sam): local dev/hmr support?
@@ -21,22 +21,31 @@ export interface ViteProps extends Omit<WorkerProps, "vite" | "main"> {
21
21
  /**
22
22
  * A Cloudflare Worker deployed from a Vite project.
23
23
  *
24
- * `Vite` uses the Cloudflare Vite plugin to build both the server bundle and
25
- * client assets in a single `vite build` invocation — no manual `main`
26
- * entrypoint, build command, output directory, or Wrangler configuration
27
- * required.
24
+ * `Vite` uses the Cloudflare Vite plugin to build both the server bundle
25
+ * and client assets in a single `vite build` invocation — no manual
26
+ * `main` entrypoint, build command, output directory, or Wrangler
27
+ * configuration required.
28
28
  *
29
29
  * Input files are content-hashed (respecting `.gitignore` by default) so
30
30
  * unchanged projects skip the build and deploy entirely.
31
31
  *
32
+ * @resource
33
+ *
32
34
  * @section Deploying a Static Site
33
- * @example Basic Static Site
35
+ * For a pure static site (no SSR), a single call is all you need.
36
+ * Vite builds the project and Alchemy deploys the output as a
37
+ * Cloudflare Worker with static assets.
38
+ *
39
+ * @example Static Vite site
34
40
  * ```typescript
35
41
  * const site = yield* Cloudflare.Vite("Website");
36
42
  * ```
37
43
  *
38
- * @section Deploying a TanStack Start App
39
- * @example TanStack Start with SSR
44
+ * @section SSR Frameworks
45
+ * For SSR frameworks like TanStack Start, SolidStart, or Nuxt, enable
46
+ * `nodejs_compat` so the server bundle can use Node.js APIs.
47
+ *
48
+ * @example TanStack Start
40
49
  * ```typescript
41
50
  * const app = yield* Cloudflare.Vite("TanStackStart", {
42
51
  * compatibility: {
@@ -45,8 +54,43 @@ export interface ViteProps extends Omit<WorkerProps, "vite" | "main"> {
45
54
  * });
46
55
  * ```
47
56
  *
57
+ * @example SolidStart with worker-first routing
58
+ * ```typescript
59
+ * const app = yield* Cloudflare.Vite("SolidStart", {
60
+ * compatibility: {
61
+ * flags: ["nodejs_compat"],
62
+ * },
63
+ * assets: {
64
+ * config: { runWorkerFirst: true },
65
+ * },
66
+ * });
67
+ * ```
68
+ *
69
+ * @section Single-Page Applications
70
+ * For SPAs (React, Vue, etc.), configure asset handling so all
71
+ * routes fall back to `index.html`.
72
+ *
73
+ * @example Vue SPA
74
+ * ```typescript
75
+ * const app = yield* Cloudflare.Vite("Vue", {
76
+ * compatibility: {
77
+ * flags: ["nodejs_compat"],
78
+ * },
79
+ * assets: {
80
+ * config: {
81
+ * htmlHandling: "auto-trailing-slash",
82
+ * notFoundHandling: "single-page-application",
83
+ * },
84
+ * },
85
+ * });
86
+ * ```
87
+ *
48
88
  * @section Custom Rebuild Scope
49
- * @example Narrow the Memo Scope
89
+ * By default, every non-gitignored file is hashed to decide whether
90
+ * a rebuild is needed. Use `memo` to narrow the scope when your
91
+ * project has large directories that don't affect the build output.
92
+ *
93
+ * @example Narrowing the memo scope
50
94
  * ```typescript
51
95
  * const site = yield* Cloudflare.Vite("Docs", {
52
96
  * memo: {