alchemy 2.0.0-beta.3 → 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.
- package/bin/{alchemy-effect.js → alchemy.js} +172 -172
- package/bin/alchemy.js.map +1 -0
- package/bin/{alchemy-effect.sh → alchemy.sh} +2 -2
- package/lib/cli/index.js +169 -169
- package/lib/cli/index.js.map +1 -1
- package/package.json +60 -61
- package/src/AWS/AGENTS.md +10 -10
- package/src/AWS/Assets.ts +1 -1
- package/src/AWS/Credentials.ts +0 -1
- package/src/AWS/DynamoDB/Table.ts +63 -10
- package/src/AWS/EC2/hosted.ts +1 -1
- package/src/AWS/ECS/Task.ts +1 -1
- package/src/AWS/Kinesis/Stream.ts +44 -8
- package/src/AWS/Lambda/Function.ts +218 -10
- package/src/AWS/S3/Bucket.ts +26 -19
- package/src/AWS/S3/BucketNotifications.ts +1 -1
- package/src/AWS/SNS/Topic.ts +47 -10
- package/src/AWS/SQS/Queue.ts +66 -0
- package/src/Cloudflare/Container/ContainerApplication.ts +3 -3
- package/src/Cloudflare/Container/ContainerBinding.ts +2 -4
- package/src/Cloudflare/Container/StartContainer.ts +1 -1
- package/src/Cloudflare/Providers.ts +1 -6
- package/src/Cloudflare/R2/R2Bucket.ts +2 -2
- package/src/Cloudflare/Website/StaticSite.ts +22 -6
- package/src/Cloudflare/Website/Vite.ts +15 -5
- package/src/Cloudflare/Workers/Assets.ts +170 -207
- package/src/Cloudflare/Workers/DurableObjectNamespace.ts +230 -370
- package/src/Cloudflare/Workers/DurableObjectState.ts +63 -0
- package/src/Cloudflare/Workers/DurableObjectStorage.ts +256 -0
- package/src/Cloudflare/Workers/InferEnv.ts +11 -7
- package/src/Cloudflare/Workers/Rpc.ts +13 -2
- package/src/Cloudflare/Workers/ScheduledEvents.ts +185 -0
- package/src/Cloudflare/Workers/WebSocket.ts +1 -1
- package/src/Cloudflare/Workers/Worker.ts +95 -43
- package/src/Cloudflare/Workers/index.ts +3 -0
- package/src/Construct.ts +2 -2
- package/src/GitHub/Secret.ts +52 -7
- package/src/GitHub/Variable.ts +45 -2
- package/src/Kubernetes/client.ts +2 -2
- package/src/Output.ts +1 -1
- package/src/Platform.ts +1 -1
- package/src/Test/Vitest.ts +4 -3
- package/src/Util/PlatformServices.ts +21 -0
- package/src/Util/dedent.ts +4 -1
- package/bin/alchemy-effect.js.map +0 -1
- package/src/Daemon/Client.ts +0 -116
- package/src/Daemon/Config.ts +0 -28
- package/src/Daemon/Errors.ts +0 -48
- package/src/Daemon/Lock.ts +0 -162
- package/src/Daemon/ProcessRegistry.ts +0 -284
- package/src/Daemon/RpcSchema.ts +0 -43
- package/src/Daemon/RpcServer.ts +0 -231
- package/src/Daemon/index.ts +0 -27
- package/src/Spawn.ts +0 -23
- /package/bin/{alchemy-effect.ts → alchemy.ts} +0 -0
|
@@ -26,7 +26,7 @@ import { findCwdForBundle } from "../../Bundle/TempRoot.ts";
|
|
|
26
26
|
import type { ScopedPlanStatusSession } from "../../Cli/Cli.ts";
|
|
27
27
|
import { isResolved } from "../../Diff.ts";
|
|
28
28
|
import type { HttpEffect } from "../../Http.ts";
|
|
29
|
-
import type {
|
|
29
|
+
import type { InputProps } from "../../Input.ts";
|
|
30
30
|
import * as Output from "../../Output.ts";
|
|
31
31
|
import { createPhysicalName } from "../../PhysicalName.ts";
|
|
32
32
|
import {
|
|
@@ -47,10 +47,20 @@ import { fromCloudflareFetcher } from "../Fetcher.ts";
|
|
|
47
47
|
import { CloudflareLogs } from "../Logs.ts";
|
|
48
48
|
import type { Providers } from "../Providers.ts";
|
|
49
49
|
import type { R2Bucket } from "../R2/R2Bucket.ts";
|
|
50
|
-
import
|
|
51
|
-
|
|
50
|
+
import {
|
|
51
|
+
isAssets,
|
|
52
|
+
readAssets,
|
|
53
|
+
uploadAssets,
|
|
54
|
+
type Assets,
|
|
55
|
+
type AssetsConfig,
|
|
56
|
+
type AssetsProps,
|
|
57
|
+
} from "./Assets.ts";
|
|
52
58
|
import cloudflare_workers from "./cloudflare_workers.ts";
|
|
53
|
-
import {
|
|
59
|
+
import {
|
|
60
|
+
isDurableObjectExport,
|
|
61
|
+
isDurableObjectNamespaceLike,
|
|
62
|
+
type DurableObjectNamespaceLike,
|
|
63
|
+
} from "./DurableObjectNamespace.ts";
|
|
54
64
|
import { workersHttpHandler } from "./HttpServer.ts";
|
|
55
65
|
import { Request } from "./Request.ts";
|
|
56
66
|
import { makeRpcStub } from "./Rpc.ts";
|
|
@@ -105,12 +115,12 @@ export interface AssetsWithHash {
|
|
|
105
115
|
/**
|
|
106
116
|
* Path to the assets directory.
|
|
107
117
|
*/
|
|
108
|
-
path:
|
|
118
|
+
path: string;
|
|
109
119
|
/**
|
|
110
120
|
* Pre-computed hash of the assets. When provided, this hash is used for diffing
|
|
111
121
|
* to determine if the worker needs to be redeployed.
|
|
112
122
|
*/
|
|
113
|
-
hash:
|
|
123
|
+
hash: string;
|
|
114
124
|
/**
|
|
115
125
|
* Optional assets configuration.
|
|
116
126
|
*/
|
|
@@ -161,7 +171,11 @@ export type WorkerServices = Worker | WorkerEnvironment | Request;
|
|
|
161
171
|
|
|
162
172
|
export type WorkerShape = Main<WorkerServices>;
|
|
163
173
|
|
|
164
|
-
export type WorkerBindingResource =
|
|
174
|
+
export type WorkerBindingResource =
|
|
175
|
+
| Assets
|
|
176
|
+
| R2Bucket
|
|
177
|
+
| D1Database
|
|
178
|
+
| DurableObjectNamespaceLike<any>;
|
|
165
179
|
|
|
166
180
|
export type WorkerBindings = {
|
|
167
181
|
[bindingName in string]: WorkerBindingResource;
|
|
@@ -173,8 +187,26 @@ export type WorkerBindingProps = {
|
|
|
173
187
|
| Effect.Effect<WorkerBindingResource, any, any>;
|
|
174
188
|
};
|
|
175
189
|
|
|
190
|
+
type NormalizedBindings<
|
|
191
|
+
Bindings extends WorkerBindingProps = {},
|
|
192
|
+
AssetsConfig extends WorkerAssetsConfig | undefined = undefined,
|
|
193
|
+
> = {
|
|
194
|
+
[B in keyof Bindings]: Bindings[B] extends Effect.Effect<
|
|
195
|
+
infer T extends WorkerBindingResource,
|
|
196
|
+
any,
|
|
197
|
+
any
|
|
198
|
+
>
|
|
199
|
+
? T
|
|
200
|
+
: Extract<Bindings[B], WorkerBindingResource>;
|
|
201
|
+
} & (undefined extends AssetsConfig ? {} : { ASSETS: Assets });
|
|
202
|
+
|
|
203
|
+
export type WorkerAssetsConfig = string | AssetsProps | AssetsWithHash;
|
|
204
|
+
|
|
176
205
|
export interface WorkerProps<
|
|
177
206
|
Bindings extends WorkerBindingProps = any,
|
|
207
|
+
Assets extends WorkerAssetsConfig | undefined =
|
|
208
|
+
| WorkerAssetsConfig
|
|
209
|
+
| undefined,
|
|
178
210
|
> extends PlatformProps {
|
|
179
211
|
/**
|
|
180
212
|
* Worker name override. If omitted, Alchemy derives a deterministic physical
|
|
@@ -192,11 +224,7 @@ export interface WorkerProps<
|
|
|
192
224
|
* - An AssetsProps object with directory and config
|
|
193
225
|
* - An object with path and hash (e.g., from a Build resource)
|
|
194
226
|
*/
|
|
195
|
-
assets?:
|
|
196
|
-
| string
|
|
197
|
-
| AssetsProps
|
|
198
|
-
| AssetsWithHash
|
|
199
|
-
| (AssetsWithHash & { [K: string]: any });
|
|
227
|
+
assets?: Assets;
|
|
200
228
|
subdomain?: {
|
|
201
229
|
enabled?: boolean;
|
|
202
230
|
previewsEnabled?: boolean;
|
|
@@ -295,6 +323,10 @@ export type Worker<Bindings extends WorkerBindings = any> = Resource<
|
|
|
295
323
|
* at runtime, and `Cloudflare.InferEnv` to extract a fully typed
|
|
296
324
|
* `env` object from those bindings.
|
|
297
325
|
*
|
|
326
|
+
* See the {@link https://alchemy.run/guides/async-worker | Async Workers Guide}
|
|
327
|
+
* for a comprehensive walkthrough of all binding types (R2, D1,
|
|
328
|
+
* Durable Objects, Assets, and more).
|
|
329
|
+
*
|
|
298
330
|
* @example Defining an async Worker in your stack
|
|
299
331
|
* ```typescript
|
|
300
332
|
* // alchemy.run.ts
|
|
@@ -579,18 +611,18 @@ export const Worker: Platform<
|
|
|
579
611
|
WorkerShape,
|
|
580
612
|
WorkerExecutionContext
|
|
581
613
|
> & {
|
|
582
|
-
<
|
|
614
|
+
<
|
|
615
|
+
const Bindings extends WorkerBindingProps,
|
|
616
|
+
const Assets extends WorkerAssetsConfig | undefined = undefined,
|
|
617
|
+
>(
|
|
583
618
|
id: string,
|
|
584
|
-
props: InputProps<WorkerProps<Bindings>>,
|
|
619
|
+
props: InputProps<WorkerProps<Bindings, Assets>>,
|
|
585
620
|
): Effect.Effect<
|
|
586
621
|
Worker<{
|
|
587
|
-
[
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
>
|
|
592
|
-
? T
|
|
593
|
-
: Extract<Bindings[B], WorkerBindingResource>;
|
|
622
|
+
[binding in keyof NormalizedBindings<
|
|
623
|
+
Bindings,
|
|
624
|
+
Assets
|
|
625
|
+
>]: NormalizedBindings<Bindings, Assets>[binding];
|
|
594
626
|
}>
|
|
595
627
|
>;
|
|
596
628
|
} = Platform(WorkerTypeId, {
|
|
@@ -608,31 +640,45 @@ export const Worker: Platform<
|
|
|
608
640
|
? yield* bindingEff
|
|
609
641
|
: bindingEff;
|
|
610
642
|
|
|
611
|
-
const bindingMeta: InputProps<WorkerBinding> | undefined =
|
|
612
|
-
binding
|
|
643
|
+
const bindingMeta: InputProps<WorkerBinding> | undefined = isAssets(
|
|
644
|
+
binding,
|
|
645
|
+
)
|
|
646
|
+
? {
|
|
647
|
+
type: "assets",
|
|
648
|
+
name: bindingName,
|
|
649
|
+
}
|
|
650
|
+
: isDurableObjectNamespaceLike(binding)
|
|
613
651
|
? {
|
|
614
|
-
type: "
|
|
615
|
-
id: binding.databaseId,
|
|
652
|
+
type: "durable_object_namespace",
|
|
616
653
|
name: bindingName,
|
|
654
|
+
className: binding.className ?? binding.name,
|
|
617
655
|
}
|
|
618
|
-
: binding.Type === "Cloudflare.
|
|
656
|
+
: binding.Type === "Cloudflare.D1Database"
|
|
619
657
|
? {
|
|
620
|
-
type: "
|
|
658
|
+
type: "d1",
|
|
659
|
+
id: binding.databaseId,
|
|
621
660
|
name: bindingName,
|
|
622
|
-
bucketName: binding.bucketName,
|
|
623
|
-
jurisdiction: binding.jurisdiction.pipe(
|
|
624
|
-
Output.map((jurisdiction) =>
|
|
625
|
-
jurisdiction === "default" ? undefined : jurisdiction,
|
|
626
|
-
),
|
|
627
|
-
),
|
|
628
661
|
}
|
|
629
|
-
:
|
|
630
|
-
|
|
662
|
+
: binding.Type === "Cloudflare.R2Bucket"
|
|
663
|
+
? {
|
|
664
|
+
type: "r2_bucket",
|
|
665
|
+
name: bindingName,
|
|
666
|
+
bucketName: binding.bucketName,
|
|
667
|
+
jurisdiction: binding.jurisdiction.pipe(
|
|
668
|
+
Output.map((jurisdiction) =>
|
|
669
|
+
jurisdiction === "default" ? undefined : jurisdiction,
|
|
670
|
+
),
|
|
671
|
+
),
|
|
672
|
+
}
|
|
673
|
+
: // TODO(sam): handle others
|
|
674
|
+
undefined;
|
|
631
675
|
|
|
632
676
|
if (bindingMeta) {
|
|
633
677
|
yield* resource.bind`${bindingName}`({
|
|
634
678
|
bindings: [bindingMeta],
|
|
635
679
|
});
|
|
680
|
+
} else {
|
|
681
|
+
return yield* Effect.die(`Unknown binding type: ${bindingName}`);
|
|
636
682
|
}
|
|
637
683
|
}
|
|
638
684
|
}
|
|
@@ -857,7 +903,6 @@ export const WorkerProvider = () =>
|
|
|
857
903
|
const virtualEntryPlugin = yield* Bundle.virtualEntryPlugin;
|
|
858
904
|
const stack = yield* Stack;
|
|
859
905
|
|
|
860
|
-
const { read, upload } = yield* Assets.Assets;
|
|
861
906
|
const createScriptSubdomain = yield* workers.createScriptSubdomain;
|
|
862
907
|
const createScriptTail = yield* workers.createScriptTail;
|
|
863
908
|
const deleteScript = yield* workers.deleteScript;
|
|
@@ -974,7 +1019,9 @@ export const WorkerProvider = () =>
|
|
|
974
1019
|
const prepareAssets = Effect.fnUntraced(function* (
|
|
975
1020
|
assets: WorkerProps["assets"],
|
|
976
1021
|
) {
|
|
977
|
-
if (!assets)
|
|
1022
|
+
if (!assets) {
|
|
1023
|
+
return undefined;
|
|
1024
|
+
}
|
|
978
1025
|
|
|
979
1026
|
// Handle AssetsWithHash (from Build resource)
|
|
980
1027
|
// Props are resolved by Plan, so Input<string> values are already strings at runtime
|
|
@@ -983,7 +1030,7 @@ export const WorkerProvider = () =>
|
|
|
983
1030
|
"path" in assets &&
|
|
984
1031
|
"hash" in assets
|
|
985
1032
|
) {
|
|
986
|
-
const result = yield*
|
|
1033
|
+
const result = yield* readAssets({
|
|
987
1034
|
directory: assets.path as string,
|
|
988
1035
|
config: assets.config,
|
|
989
1036
|
});
|
|
@@ -994,7 +1041,7 @@ export const WorkerProvider = () =>
|
|
|
994
1041
|
}
|
|
995
1042
|
|
|
996
1043
|
// Handle string path or AssetsProps
|
|
997
|
-
return yield*
|
|
1044
|
+
return yield* readAssets(
|
|
998
1045
|
typeof assets === "string" ? { directory: assets } : assets,
|
|
999
1046
|
);
|
|
1000
1047
|
});
|
|
@@ -1065,8 +1112,8 @@ import * as Stream from "effect/Stream";
|
|
|
1065
1112
|
import { env, DurableObject${hasWfClasses ? ", WorkflowEntrypoint" : ""} } from "cloudflare:workers";
|
|
1066
1113
|
import { MinimumLogLevel } from "effect/References";
|
|
1067
1114
|
import { NodeServices } from "@effect/platform-node";
|
|
1068
|
-
import { Stack } from "alchemy
|
|
1069
|
-
import { WorkerEnvironment, makeDurableObjectBridge${hasWfClasses ? ", makeWorkflowBridge" : ""}, ExportedHandlerMethods } from "alchemy
|
|
1115
|
+
import { Stack } from "alchemy/Stack";
|
|
1116
|
+
import { WorkerEnvironment, makeDurableObjectBridge${hasWfClasses ? ", makeWorkflowBridge" : ""}, ExportedHandlerMethods } from "alchemy/Cloudflare";
|
|
1070
1117
|
|
|
1071
1118
|
import entry from "${importPath}";
|
|
1072
1119
|
|
|
@@ -1232,7 +1279,7 @@ ${[
|
|
|
1232
1279
|
const [assets, bundle] = yield* Effect.all(
|
|
1233
1280
|
[
|
|
1234
1281
|
assetsDirectory
|
|
1235
|
-
?
|
|
1282
|
+
? readAssets({
|
|
1236
1283
|
directory: assetsDirectory,
|
|
1237
1284
|
config:
|
|
1238
1285
|
typeof props.assets === "object" && "config" in props.assets
|
|
@@ -1312,7 +1359,12 @@ ${[
|
|
|
1312
1359
|
yield* Effect.logInfo(
|
|
1313
1360
|
`Cloudflare Worker ${olds ? "update" : "create"}: uploading assets for ${name}`,
|
|
1314
1361
|
);
|
|
1315
|
-
const { jwt } = yield*
|
|
1362
|
+
const { jwt } = yield* uploadAssets(
|
|
1363
|
+
accountId,
|
|
1364
|
+
name,
|
|
1365
|
+
assets,
|
|
1366
|
+
session,
|
|
1367
|
+
);
|
|
1316
1368
|
metadataAssets = {
|
|
1317
1369
|
jwt,
|
|
1318
1370
|
config: assets.config,
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
export * from "./Assets.ts";
|
|
2
2
|
export * from "./ConfigProvider.ts";
|
|
3
3
|
export * from "./DurableObjectNamespace.ts";
|
|
4
|
+
export * from "./DurableObjectState.ts";
|
|
5
|
+
export * from "./DurableObjectStorage.ts";
|
|
4
6
|
export * from "./DynamicWorkerLoader.ts";
|
|
5
7
|
export * from "./Fetch.ts";
|
|
6
8
|
export * from "./HttpServer.ts";
|
|
7
9
|
export * from "./InferEnv.ts";
|
|
8
10
|
export * from "./Request.ts";
|
|
9
11
|
export * from "./Rpc.ts";
|
|
12
|
+
export * from "./ScheduledEvents.ts";
|
|
10
13
|
export * from "./WebSocket.ts";
|
|
11
14
|
export * from "./Worker.ts";
|
|
12
15
|
export * from "./Workflow.ts";
|
package/src/Construct.ts
CHANGED
|
@@ -12,8 +12,8 @@ import * as Namespace from "./Namespace.ts";
|
|
|
12
12
|
* @section Creating Constructs
|
|
13
13
|
* @example Simple Reusable Construct
|
|
14
14
|
* ```typescript
|
|
15
|
-
* import * as Construct from "alchemy
|
|
16
|
-
* import { Bucket } from "alchemy
|
|
15
|
+
* import * as Construct from "alchemy/Construct";
|
|
16
|
+
* import { Bucket } from "alchemy/AWS/S3";
|
|
17
17
|
*
|
|
18
18
|
* export const Logs = Construct.fn(function* (
|
|
19
19
|
* id: string,
|
package/src/GitHub/Secret.ts
CHANGED
|
@@ -53,11 +53,19 @@ export interface Secret extends Resource<
|
|
|
53
53
|
/**
|
|
54
54
|
* A GitHub Actions repository or environment secret.
|
|
55
55
|
*
|
|
56
|
+
* `Secret` manages the lifecycle of an encrypted secret in GitHub Actions.
|
|
56
57
|
* Secrets are encrypted using the repository's (or environment's) public
|
|
57
|
-
* key before being stored. The resource is idempotent —
|
|
58
|
-
* the same name will update the secret value in place.
|
|
58
|
+
* key via `libsodium` before being stored. The resource is idempotent —
|
|
59
|
+
* calling it with the same name will update the secret value in place.
|
|
60
|
+
*
|
|
61
|
+
* Authentication is resolved in order: explicit `token` prop,
|
|
62
|
+
* `GITHUB_ACCESS_TOKEN` env var, `GITHUB_TOKEN` env var. The token needs
|
|
63
|
+
* `repo` scope for private repositories or `public_repo` for public ones.
|
|
59
64
|
*
|
|
60
65
|
* @section Repository Secrets
|
|
66
|
+
* Store secrets accessible to all GitHub Actions workflows in the
|
|
67
|
+
* repository.
|
|
68
|
+
*
|
|
61
69
|
* @example Create a Repository Secret
|
|
62
70
|
* ```typescript
|
|
63
71
|
* yield* GitHub.Secret("aws-role", {
|
|
@@ -69,6 +77,10 @@ export interface Secret extends Resource<
|
|
|
69
77
|
* ```
|
|
70
78
|
*
|
|
71
79
|
* @section Environment Secrets
|
|
80
|
+
* Scope a secret to a specific GitHub Actions environment (e.g.
|
|
81
|
+
* `production`, `staging`). Environment secrets require environment
|
|
82
|
+
* protection rules to be satisfied before workflows can access them.
|
|
83
|
+
*
|
|
72
84
|
* @example Create an Environment Secret
|
|
73
85
|
* ```typescript
|
|
74
86
|
* yield* GitHub.Secret("deploy-key", {
|
|
@@ -79,6 +91,42 @@ export interface Secret extends Resource<
|
|
|
79
91
|
* value: Redacted.make("my-secret-value"),
|
|
80
92
|
* });
|
|
81
93
|
* ```
|
|
94
|
+
*
|
|
95
|
+
* @section Wiring with Other Resources
|
|
96
|
+
* A common pattern is wiring the output of another resource — like an
|
|
97
|
+
* IAM role ARN or a database URL — directly into a GitHub secret so
|
|
98
|
+
* that CI workflows can use it.
|
|
99
|
+
*
|
|
100
|
+
* @example Store an IAM Role ARN for CI
|
|
101
|
+
* ```typescript
|
|
102
|
+
* const role = yield* AWS.IAM.Role("ci-role", { ... });
|
|
103
|
+
*
|
|
104
|
+
* yield* GitHub.Secret("ci-role-arn", {
|
|
105
|
+
* owner: "my-org",
|
|
106
|
+
* repository: "my-repo",
|
|
107
|
+
* name: "AWS_ROLE_ARN",
|
|
108
|
+
* value: Redacted.make(role.roleArn),
|
|
109
|
+
* });
|
|
110
|
+
* ```
|
|
111
|
+
*
|
|
112
|
+
* @example Store Multiple Secrets
|
|
113
|
+
* ```typescript
|
|
114
|
+
* yield* GitHub.Secret("db-url", {
|
|
115
|
+
* owner: "my-org",
|
|
116
|
+
* repository: "my-repo",
|
|
117
|
+
* environment: "production",
|
|
118
|
+
* name: "DATABASE_URL",
|
|
119
|
+
* value: Redacted.make(database.connectionString),
|
|
120
|
+
* });
|
|
121
|
+
*
|
|
122
|
+
* yield* GitHub.Secret("api-key", {
|
|
123
|
+
* owner: "my-org",
|
|
124
|
+
* repository: "my-repo",
|
|
125
|
+
* environment: "production",
|
|
126
|
+
* name: "API_KEY",
|
|
127
|
+
* value: Redacted.make(apiKey),
|
|
128
|
+
* });
|
|
129
|
+
* ```
|
|
82
130
|
*/
|
|
83
131
|
export const Secret = Resource<Secret>("GitHub.Secret");
|
|
84
132
|
|
|
@@ -98,10 +146,7 @@ async function encryptValue(
|
|
|
98
146
|
): Promise<string> {
|
|
99
147
|
const sodium = await import("libsodium-wrappers");
|
|
100
148
|
await sodium.ready;
|
|
101
|
-
const binKey = sodium.from_base64(
|
|
102
|
-
publicKey,
|
|
103
|
-
sodium.base64_variants.ORIGINAL,
|
|
104
|
-
);
|
|
149
|
+
const binKey = sodium.from_base64(publicKey, sodium.base64_variants.ORIGINAL);
|
|
105
150
|
const binMessage = sodium.from_string(plaintext);
|
|
106
151
|
const encrypted = sodium.crypto_box_seal(binMessage, binKey);
|
|
107
152
|
return sodium.to_base64(encrypted, sodium.base64_variants.ORIGINAL);
|
|
@@ -115,7 +160,7 @@ export const SecretProvider = () =>
|
|
|
115
160
|
return { updatedAt: new Date().toISOString() };
|
|
116
161
|
}),
|
|
117
162
|
|
|
118
|
-
update: Effect.fn(function* ({ news, olds
|
|
163
|
+
update: Effect.fn(function* ({ news, olds }) {
|
|
119
164
|
const octokit = createClient(news);
|
|
120
165
|
|
|
121
166
|
const wasEnv = !!olds.environment;
|
package/src/GitHub/Variable.ts
CHANGED
|
@@ -45,10 +45,20 @@ export interface Variable extends Resource<
|
|
|
45
45
|
/**
|
|
46
46
|
* A GitHub Actions repository variable.
|
|
47
47
|
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
48
|
+
* `Variable` manages the lifecycle of a plain-text configuration variable
|
|
49
|
+
* in GitHub Actions. Variables are visible in workflow logs and are
|
|
50
|
+
* suitable for non-sensitive configuration like region names, environment
|
|
51
|
+
* labels, or feature flags. For sensitive values, use `GitHub.Secret`
|
|
52
|
+
* instead.
|
|
53
|
+
*
|
|
54
|
+
* Authentication is resolved in order: explicit `token` prop,
|
|
55
|
+
* `GITHUB_ACCESS_TOKEN` env var, `GITHUB_TOKEN` env var. The token needs
|
|
56
|
+
* `repo` scope for private repositories or `public_repo` for public ones.
|
|
50
57
|
*
|
|
51
58
|
* @section Repository Variables
|
|
59
|
+
* Store variables accessible to all GitHub Actions workflows in the
|
|
60
|
+
* repository.
|
|
61
|
+
*
|
|
52
62
|
* @example Create a Repository Variable
|
|
53
63
|
* ```typescript
|
|
54
64
|
* yield* GitHub.Variable("aws-region", {
|
|
@@ -58,6 +68,39 @@ export interface Variable extends Resource<
|
|
|
58
68
|
* value: "us-east-1",
|
|
59
69
|
* });
|
|
60
70
|
* ```
|
|
71
|
+
*
|
|
72
|
+
* @section Wiring with Other Resources
|
|
73
|
+
* Pass output attributes from other resources into GitHub variables so
|
|
74
|
+
* that CI workflows can reference them.
|
|
75
|
+
*
|
|
76
|
+
* @example Store a Worker URL for CI
|
|
77
|
+
* ```typescript
|
|
78
|
+
* const worker = yield* Cloudflare.Worker("Api", { ... });
|
|
79
|
+
*
|
|
80
|
+
* yield* GitHub.Variable("api-url", {
|
|
81
|
+
* owner: "my-org",
|
|
82
|
+
* repository: "my-repo",
|
|
83
|
+
* name: "API_URL",
|
|
84
|
+
* value: worker.url!,
|
|
85
|
+
* });
|
|
86
|
+
* ```
|
|
87
|
+
*
|
|
88
|
+
* @example Multiple Variables
|
|
89
|
+
* ```typescript
|
|
90
|
+
* yield* GitHub.Variable("region", {
|
|
91
|
+
* owner: "my-org",
|
|
92
|
+
* repository: "my-repo",
|
|
93
|
+
* name: "AWS_REGION",
|
|
94
|
+
* value: "us-east-1",
|
|
95
|
+
* });
|
|
96
|
+
*
|
|
97
|
+
* yield* GitHub.Variable("stage", {
|
|
98
|
+
* owner: "my-org",
|
|
99
|
+
* repository: "my-repo",
|
|
100
|
+
* name: "DEPLOY_STAGE",
|
|
101
|
+
* value: "production",
|
|
102
|
+
* });
|
|
103
|
+
* ```
|
|
61
104
|
*/
|
|
62
105
|
export const Variable = Resource<Variable>("GitHub.Variable");
|
|
63
106
|
|
package/src/Kubernetes/client.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { AwsClient } from "aws4fetch";
|
|
2
1
|
import { Credentials } from "@distilled.cloud/aws/Credentials";
|
|
3
2
|
import { Region } from "@distilled.cloud/aws/Region";
|
|
3
|
+
import { AwsClient } from "aws4fetch";
|
|
4
4
|
import * as Data from "effect/Data";
|
|
5
5
|
import * as Effect from "effect/Effect";
|
|
6
6
|
import * as Redacted from "effect/Redacted";
|
|
@@ -28,7 +28,7 @@ export interface KubernetesClusterConnection {
|
|
|
28
28
|
certificateAuthorityData: string;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
const fieldManager = "alchemy
|
|
31
|
+
const fieldManager = "alchemy";
|
|
32
32
|
|
|
33
33
|
const createBearerToken = Effect.fn(function* (clusterName: string) {
|
|
34
34
|
const credentials = yield* yield* Credentials;
|
package/src/Output.ts
CHANGED
|
@@ -90,7 +90,7 @@ export abstract class BaseExpr<A = any, Req = any> implements Output<A, Req> {
|
|
|
90
90
|
declare readonly A: A;
|
|
91
91
|
declare readonly src: ResourceLike;
|
|
92
92
|
declare readonly req: Req;
|
|
93
|
-
// we use a kind tag instead of instanceof to protect ourselves from duplicate alchemy
|
|
93
|
+
// we use a kind tag instead of instanceof to protect ourselves from duplicate alchemy module imports
|
|
94
94
|
constructor() {}
|
|
95
95
|
as<T>(): Output<T, Req> {
|
|
96
96
|
return this as any;
|
package/src/Platform.ts
CHANGED
package/src/Test/Vitest.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type * as aws from "@distilled.cloud/aws";
|
|
2
2
|
import * as cf from "@distilled.cloud/cloudflare";
|
|
3
|
-
import { NodeServices } from "@effect/platform-node";
|
|
3
|
+
// import { NodeServices } from "@effect/platform-node";
|
|
4
4
|
import { expect, it } from "@effect/vitest";
|
|
5
5
|
import { Logger } from "effect";
|
|
6
6
|
import * as Config from "effect/Config";
|
|
@@ -44,6 +44,7 @@ import * as Serverless from "../Serverless/index.ts";
|
|
|
44
44
|
import * as Stack from "../Stack.ts";
|
|
45
45
|
import * as Stage from "../Stage.ts";
|
|
46
46
|
import * as State from "../State/index.ts";
|
|
47
|
+
import { PlatformServices } from "../Util/PlatformServices.ts";
|
|
47
48
|
import { TestCli } from "./TestCli.ts";
|
|
48
49
|
|
|
49
50
|
export const expectEmptyObject = () =>
|
|
@@ -90,7 +91,7 @@ const quietLogger = Logger.make(() => {
|
|
|
90
91
|
});
|
|
91
92
|
|
|
92
93
|
const platform = Layer.mergeAll(
|
|
93
|
-
|
|
94
|
+
PlatformServices,
|
|
94
95
|
FetchHttpClient.layer,
|
|
95
96
|
Logger.layer([process.env.VERBOSE ? Logger.consolePretty() : quietLogger]),
|
|
96
97
|
);
|
|
@@ -212,7 +213,7 @@ const runWithContext = <A, Err>(
|
|
|
212
213
|
MinimumLogLevel,
|
|
213
214
|
process.env.DEBUG ? "Debug" : "Info",
|
|
214
215
|
),
|
|
215
|
-
Effect.provide(
|
|
216
|
+
Effect.provide(PlatformServices),
|
|
216
217
|
);
|
|
217
218
|
};
|
|
218
219
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { BunServices } from "@effect/platform-bun/BunServices";
|
|
2
|
+
import type { NodeServices } from "@effect/platform-node/NodeServices";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import * as Layer from "effect/Layer";
|
|
5
|
+
|
|
6
|
+
const isBun = typeof Bun !== "undefined";
|
|
7
|
+
|
|
8
|
+
export const PlatformServices: Layer.Layer<
|
|
9
|
+
NodeServices | BunServices,
|
|
10
|
+
never,
|
|
11
|
+
never
|
|
12
|
+
> = Effect.promise(() => {
|
|
13
|
+
if (isBun) {
|
|
14
|
+
return import("@effect/platform-bun").then(
|
|
15
|
+
(platform) => platform.BunServices.layer,
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
return import("@effect/platform-node").then(
|
|
19
|
+
(platform) => platform.NodeServices.layer,
|
|
20
|
+
);
|
|
21
|
+
}).pipe(Layer.unwrap);
|
package/src/Util/dedent.ts
CHANGED
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
*
|
|
4
4
|
* If the first line is empty, it will be ignored.
|
|
5
5
|
*/
|
|
6
|
-
export function dedent(
|
|
6
|
+
export function dedent(
|
|
7
|
+
strings: TemplateStringsArray,
|
|
8
|
+
...values: unknown[]
|
|
9
|
+
): string;
|
|
7
10
|
export function dedent(text: string): string;
|
|
8
11
|
export function dedent(
|
|
9
12
|
stringsOrText: TemplateStringsArray | string,
|