alchemy 2.0.0-beta.25 → 2.0.0-beta.27
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.d.ts +2 -0
- package/bin/alchemy.d.ts.map +1 -0
- package/bin/alchemy.js +3 -57288
- package/bin/alchemy.js.map +1 -1
- package/bin/alchemy.ts +3 -59
- package/bin/exec.d.ts +2 -0
- package/bin/exec.d.ts.map +1 -0
- package/bin/exec.js +2360 -0
- package/bin/exec.js.map +1 -0
- package/bin/exec.ts +3 -21
- package/lib/AWS/AuthProvider.d.ts +1 -1
- package/lib/AWS/AuthProvider.d.ts.map +1 -1
- package/lib/AWS/Lambda/BucketEventSource.d.ts +1 -1
- package/lib/AWS/Lambda/BucketEventSource.d.ts.map +1 -1
- package/lib/AWS/Lambda/EventBridgeEventSource.d.ts +1 -1
- package/lib/AWS/Lambda/EventBridgeEventSource.d.ts.map +1 -1
- package/lib/AWS/Lambda/QueueEventSource.d.ts +1 -1
- package/lib/AWS/Lambda/QueueEventSource.d.ts.map +1 -1
- package/lib/AWS/Lambda/StreamEventSource.d.ts +1 -1
- package/lib/AWS/Lambda/StreamEventSource.d.ts.map +1 -1
- package/lib/AWS/Lambda/TableEventSource.d.ts +1 -1
- package/lib/AWS/Lambda/TableEventSource.d.ts.map +1 -1
- package/lib/AWS/Lambda/TopicEventSource.d.ts +1 -1
- package/lib/AWS/Lambda/TopicEventSource.d.ts.map +1 -1
- package/lib/Auth/AuthProvider.d.ts.map +1 -1
- package/lib/Auth/AuthProvider.js +20 -3
- package/lib/Auth/AuthProvider.js.map +1 -1
- package/lib/Auth/Lock.d.ts +12 -0
- package/lib/Auth/Lock.d.ts.map +1 -0
- package/lib/Auth/Lock.js +27 -0
- package/lib/Auth/Lock.js.map +1 -0
- package/lib/Axiom/AuthProvider.d.ts +1 -1
- package/lib/Axiom/AuthProvider.d.ts.map +1 -1
- package/lib/Axiom/Credentials.d.ts +1 -1
- package/lib/Axiom/Credentials.d.ts.map +1 -1
- package/lib/Bundle/Bundle.d.ts +3 -3
- package/lib/Cli/commands/_shared.d.ts +51 -0
- package/lib/Cli/commands/_shared.d.ts.map +1 -0
- package/lib/Cli/commands/_shared.js +176 -0
- package/lib/Cli/commands/_shared.js.map +1 -0
- package/lib/Cli/commands/bootstrap.d.ts +6 -0
- package/lib/Cli/commands/bootstrap.d.ts.map +1 -0
- package/lib/Cli/commands/bootstrap.js +106 -0
- package/lib/Cli/commands/bootstrap.js.map +1 -0
- package/lib/Cli/commands/deploy.d.ts +56 -0
- package/lib/Cli/commands/deploy.d.ts.map +1 -0
- package/lib/Cli/commands/deploy.js +128 -0
- package/lib/Cli/commands/deploy.js.map +1 -0
- package/lib/Cli/commands/dev.d.ts +10 -0
- package/lib/Cli/commands/dev.d.ts.map +1 -0
- package/lib/Cli/commands/dev.js +45 -0
- package/lib/Cli/commands/dev.js.map +1 -0
- package/lib/Cli/commands/login.d.ts +10 -0
- package/lib/Cli/commands/login.d.ts.map +1 -0
- package/lib/Cli/commands/login.js +59 -0
- package/lib/Cli/commands/login.js.map +1 -0
- package/lib/Cli/commands/logs.d.ts +12 -0
- package/lib/Cli/commands/logs.d.ts.map +1 -0
- package/lib/Cli/commands/logs.js +99 -0
- package/lib/Cli/commands/logs.js.map +1 -0
- package/lib/Cli/commands/profile.d.ts +3 -0
- package/lib/Cli/commands/profile.d.ts.map +1 -0
- package/lib/Cli/commands/profile.js +76 -0
- package/lib/Cli/commands/profile.js.map +1 -0
- package/lib/Cli/commands/state.d.ts +3 -0
- package/lib/Cli/commands/state.d.ts.map +1 -0
- package/lib/Cli/commands/state.js +221 -0
- package/lib/Cli/commands/state.js.map +1 -0
- package/lib/Cli/commands/tail.d.ts +9 -0
- package/lib/Cli/commands/tail.d.ts.map +1 -0
- package/lib/Cli/commands/tail.js +93 -0
- package/lib/Cli/commands/tail.js.map +1 -0
- package/lib/Cli/exec.d.ts +3 -0
- package/lib/Cli/exec.d.ts.map +1 -0
- package/lib/Cli/exec.js +12 -0
- package/lib/Cli/exec.js.map +1 -0
- package/lib/Cli/index.d.ts +2 -1
- package/lib/Cli/index.d.ts.map +1 -1
- package/lib/Cli/index.js +2 -5
- package/lib/Cli/index.js.map +1 -1
- package/lib/Cli/main.d.ts +3 -0
- package/lib/Cli/main.d.ts.map +1 -0
- package/lib/Cli/main.js +40 -0
- package/lib/Cli/main.js.map +1 -0
- package/lib/Cli/{InkCLI.d.ts → tui/InkCLI.d.ts} +1 -1
- package/lib/Cli/tui/InkCLI.d.ts.map +1 -0
- package/lib/Cli/{InkCLI.js → tui/InkCLI.js} +1 -1
- package/lib/Cli/tui/InkCLI.js.map +1 -0
- package/lib/Cli/{components → tui/components}/ApprovePlan.d.ts +1 -1
- package/lib/Cli/tui/components/ApprovePlan.d.ts.map +1 -0
- package/lib/Cli/tui/components/ApprovePlan.js.map +1 -0
- package/lib/Cli/{components → tui/components}/Plan.d.ts +1 -1
- package/lib/Cli/tui/components/Plan.d.ts.map +1 -0
- package/lib/Cli/{components → tui/components}/Plan.js +1 -1
- package/lib/Cli/tui/components/Plan.js.map +1 -0
- package/lib/Cli/{components → tui/components}/PlanProgress.d.ts +3 -3
- package/lib/Cli/tui/components/PlanProgress.d.ts.map +1 -0
- package/lib/Cli/{components → tui/components}/PlanProgress.js +1 -1
- package/lib/Cli/tui/components/PlanProgress.js.map +1 -0
- package/lib/Cloudflare/ApiToken/AccountApiToken.d.ts +1 -1
- package/lib/Cloudflare/ApiToken/AccountApiToken.d.ts.map +1 -1
- package/lib/Cloudflare/ApiToken/UserApiToken.d.ts +1 -1
- package/lib/Cloudflare/ApiToken/UserApiToken.d.ts.map +1 -1
- package/lib/Cloudflare/Auth/AuthProvider.d.ts +1 -1
- package/lib/Cloudflare/Auth/AuthProvider.d.ts.map +1 -1
- package/lib/Cloudflare/Auth/AuthProvider.js +0 -4
- package/lib/Cloudflare/Auth/AuthProvider.js.map +1 -1
- package/lib/Cloudflare/CloudflareEnvironment.d.ts +1 -1
- package/lib/Cloudflare/CloudflareEnvironment.d.ts.map +1 -1
- package/lib/Cloudflare/Container/ContainerApplication.d.ts +1 -1
- package/lib/Cloudflare/Container/ContainerApplication.d.ts.map +1 -1
- package/lib/Cloudflare/Credentials.d.ts +1 -1
- package/lib/Cloudflare/Credentials.d.ts.map +1 -1
- package/lib/Cloudflare/D1/D1Clone.d.ts +1 -1
- package/lib/Cloudflare/D1/D1Clone.d.ts.map +1 -1
- package/lib/Cloudflare/D1/D1Import.d.ts +1 -1
- package/lib/Cloudflare/D1/D1Import.d.ts.map +1 -1
- package/lib/Cloudflare/D1/D1Migrations.d.ts +1 -1
- package/lib/Cloudflare/D1/D1Migrations.d.ts.map +1 -1
- package/lib/Cloudflare/KV/KVNamespace.d.ts +1 -1
- package/lib/Cloudflare/KV/KVNamespace.d.ts.map +1 -1
- package/lib/Cloudflare/Logs.d.ts +1 -1
- package/lib/Cloudflare/Logs.d.ts.map +1 -1
- package/lib/Cloudflare/Queue/Queue.d.ts +1 -1
- package/lib/Cloudflare/Queue/Queue.d.ts.map +1 -1
- package/lib/Cloudflare/Queue/QueueConsumer.d.ts +1 -1
- package/lib/Cloudflare/Queue/QueueConsumer.d.ts.map +1 -1
- package/lib/Cloudflare/SecretsStore/Secret.d.ts +1 -1
- package/lib/Cloudflare/SecretsStore/Secret.d.ts.map +1 -1
- package/lib/Cloudflare/SecretsStore/SecretsStore.d.ts +1 -1
- package/lib/Cloudflare/SecretsStore/SecretsStore.d.ts.map +1 -1
- package/lib/Cloudflare/SecretsStore/SecretsStore.js +5 -5
- package/lib/Cloudflare/SecretsStore/SecretsStore.js.map +1 -1
- package/lib/Cloudflare/StateStore/Api.d.ts +13 -0
- package/lib/Cloudflare/StateStore/Api.d.ts.map +1 -1
- package/lib/Cloudflare/StateStore/Api.js +15 -2
- package/lib/Cloudflare/StateStore/Api.js.map +1 -1
- package/lib/Cloudflare/StateStore/State.d.ts +3 -3
- package/lib/Cloudflare/StateStore/State.d.ts.map +1 -1
- package/lib/Cloudflare/StateStore/State.js.map +1 -1
- package/lib/Cloudflare/Workers/Assets.d.ts +1 -1
- package/lib/Cloudflare/Workers/Assets.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/Worker.d.ts +2 -2
- package/lib/Cloudflare/Workers/Worker.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/Worker.js +14 -12
- package/lib/Cloudflare/Workers/Worker.js.map +1 -1
- package/lib/Cloudflare/Workers/Workflow.d.ts +1 -1
- package/lib/Cloudflare/Workers/Workflow.d.ts.map +1 -1
- package/lib/Deploy.d.ts +1 -1
- package/lib/Deploy.d.ts.map +1 -1
- package/lib/Destroy.d.ts +1 -1
- package/lib/Destroy.d.ts.map +1 -1
- package/lib/GitHub/AuthProvider.d.ts +1 -1
- package/lib/GitHub/AuthProvider.d.ts.map +1 -1
- package/lib/GitHub/Credentials.d.ts +1 -1
- package/lib/GitHub/Credentials.d.ts.map +1 -1
- package/lib/GitHub/Providers.d.ts +1 -1
- package/lib/GitHub/Providers.d.ts.map +1 -1
- package/lib/Output.d.ts.map +1 -1
- package/lib/Output.js +13 -2
- package/lib/Output.js.map +1 -1
- package/lib/Provider.d.ts +1 -1
- package/lib/Provider.d.ts.map +1 -1
- package/lib/Stack.js +2 -2
- package/lib/Stack.js.map +1 -1
- package/lib/Telemetry/Layer.d.ts.map +1 -1
- package/lib/Telemetry/Layer.js +13 -1
- package/lib/Telemetry/Layer.js.map +1 -1
- package/lib/Test/Bun.d.ts +59 -28
- package/lib/Test/Bun.d.ts.map +1 -1
- package/lib/Test/Bun.js +77 -81
- package/lib/Test/Bun.js.map +1 -1
- package/lib/Test/Core.d.ts +73 -0
- package/lib/Test/Core.d.ts.map +1 -0
- package/lib/Test/Core.js +98 -0
- package/lib/Test/Core.js.map +1 -0
- package/lib/Test/Vitest.d.ts +53 -72
- package/lib/Test/Vitest.d.ts.map +1 -1
- package/lib/Test/Vitest.js +73 -232
- package/lib/Test/Vitest.js.map +1 -1
- package/lib/Test/index.d.ts +0 -1
- package/lib/Test/index.d.ts.map +1 -1
- package/lib/Test/index.js +0 -1
- package/lib/Test/index.js.map +1 -1
- package/lib/Util/index.d.ts +1 -0
- package/lib/Util/index.d.ts.map +1 -1
- package/lib/Util/index.js +1 -0
- package/lib/Util/index.js.map +1 -1
- package/lib/index.d.ts +7 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +6 -0
- package/lib/index.js.map +1 -1
- package/lib/tsconfig.test.tsbuildinfo +1 -0
- package/package.json +68 -52
- package/src/Auth/AuthProvider.ts +22 -4
- package/src/Auth/Lock.ts +35 -0
- package/{bin → src/Cli}/commands/_shared.ts +5 -5
- package/{bin → src/Cli}/commands/bootstrap.ts +18 -14
- package/{bin → src/Cli}/commands/deploy.ts +16 -16
- package/{bin → src/Cli}/commands/dev.ts +13 -3
- package/{bin → src/Cli}/commands/login.ts +9 -9
- package/{bin → src/Cli}/commands/logs.ts +11 -11
- package/{bin → src/Cli}/commands/profile.ts +8 -8
- package/{bin → src/Cli}/commands/state.ts +47 -26
- package/{bin → src/Cli}/commands/tail.ts +11 -11
- package/src/Cli/exec.ts +23 -0
- package/src/Cli/index.ts +2 -5
- package/src/Cli/main.ts +60 -0
- package/src/Cli/{InkCLI.tsx → tui/InkCLI.tsx} +3 -3
- package/src/Cli/{components → tui/components}/ApprovePlan.tsx +1 -1
- package/src/Cli/{components → tui/components}/Plan.tsx +6 -2
- package/src/Cli/{components → tui/components}/PlanProgress.tsx +7 -3
- package/src/Cloudflare/Auth/AuthProvider.ts +0 -5
- package/src/Cloudflare/SecretsStore/SecretsStore.ts +5 -5
- package/src/Cloudflare/StateStore/Api.ts +15 -2
- package/src/Cloudflare/StateStore/State.ts +3 -9
- package/src/Cloudflare/Workers/Worker.ts +29 -44
- package/src/Output.ts +10 -2
- package/src/Provider.ts +1 -1
- package/src/Stack.ts +2 -2
- package/src/Telemetry/Layer.ts +13 -1
- package/src/Test/Bun.ts +164 -140
- package/src/Test/Core.ts +225 -0
- package/src/Test/Vitest.ts +157 -509
- package/src/Test/index.ts +0 -1
- package/src/Util/index.ts +1 -0
- package/src/index.ts +14 -0
- package/lib/Cli/InkCLI.d.ts.map +0 -1
- package/lib/Cli/InkCLI.js.map +0 -1
- package/lib/Cli/components/ApprovePlan.d.ts.map +0 -1
- package/lib/Cli/components/ApprovePlan.js.map +0 -1
- package/lib/Cli/components/Plan.d.ts.map +0 -1
- package/lib/Cli/components/Plan.js.map +0 -1
- package/lib/Cli/components/PlanProgress.d.ts.map +0 -1
- package/lib/Cli/components/PlanProgress.js.map +0 -1
- package/lib/cli/index.d.ts +0 -413
- package/lib/cli/index.d.ts.map +0 -1
- package/lib/cli/index.js +0 -10
- package/lib/cli/index.js.map +0 -1
- /package/lib/Cli/{components → tui/components}/ApprovePlan.js +0 -0
package/src/Test/Core.ts
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { ConfigProvider } from "effect/ConfigProvider";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import * as Layer from "effect/Layer";
|
|
4
|
+
import * as Logger from "effect/Logger";
|
|
5
|
+
import * as Option from "effect/Option";
|
|
6
|
+
import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient";
|
|
7
|
+
|
|
8
|
+
import type { AlchemyContext } from "../AlchemyContext.ts";
|
|
9
|
+
import { AlchemyContextLive } from "../AlchemyContext.ts";
|
|
10
|
+
import { apply } from "../Apply.ts";
|
|
11
|
+
import { provideFreshArtifactStore } from "../Artifacts.ts";
|
|
12
|
+
import { AuthProviders } from "../Auth/AuthProvider.ts";
|
|
13
|
+
import { withProfileOverride } from "../Auth/Profile.ts";
|
|
14
|
+
import { LoggingCli } from "../Cli/LoggingCli.ts";
|
|
15
|
+
import { deploy as _deploy } from "../Deploy.ts";
|
|
16
|
+
import { destroy as _destroy } from "../Destroy.ts";
|
|
17
|
+
import type { Input } from "../Input.ts";
|
|
18
|
+
import * as Plan from "../Plan.ts";
|
|
19
|
+
import {
|
|
20
|
+
type CompiledStack,
|
|
21
|
+
make as makeStack,
|
|
22
|
+
Stack,
|
|
23
|
+
type StackEffect,
|
|
24
|
+
type StackServices,
|
|
25
|
+
} from "../Stack.ts";
|
|
26
|
+
import { Stage } from "../Stage.ts";
|
|
27
|
+
import * as State from "../State/index.ts";
|
|
28
|
+
import { TelemetryLive } from "../Telemetry/Layer.ts";
|
|
29
|
+
import { loadConfigProvider } from "../Util/ConfigProvider.ts";
|
|
30
|
+
import { PlatformServices } from "../Util/PlatformServices.ts";
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Configuration shared by every test in a file. Pass to `Test.make(...)`.
|
|
34
|
+
*/
|
|
35
|
+
export interface MakeOptions<ROut = any> {
|
|
36
|
+
/** Provider layer for the stack — e.g. `AWS.providers()`, `Cloudflare.providers()`. */
|
|
37
|
+
providers: Layer.Layer<ROut, never, StackServices>;
|
|
38
|
+
/** State store for top-level `deploy(Stack)` / `destroy(Stack)`; defaults to {@link State.localState}. */
|
|
39
|
+
state?: Layer.Layer<State.State, never, StackServices>;
|
|
40
|
+
/** Override `ALCHEMY_PROFILE`; otherwise resolved from env / .env. */
|
|
41
|
+
profile?: string;
|
|
42
|
+
/** Default stage for deploy/destroy (default `"test"`). */
|
|
43
|
+
stage?: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export type TestEffect<A, Req = never> = StackEffect<A, any, Req>;
|
|
47
|
+
|
|
48
|
+
const platformLayer = Layer.mergeAll(PlatformServices, FetchHttpClient.layer);
|
|
49
|
+
|
|
50
|
+
const alchemyLayer = Layer.mergeAll(
|
|
51
|
+
LoggingCli,
|
|
52
|
+
Logger.layer([Logger.consolePretty()], { mergeWithExisting: true }),
|
|
53
|
+
AlchemyContextLive,
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Build the per-test runtime and return a self-contained Effect.
|
|
58
|
+
*
|
|
59
|
+
* Mirrors {@link "../bin/alchemy.ts"} composition: ConfigProvider via
|
|
60
|
+
* `loadConfigProvider` + `withProfileOverride`, an empty `AuthProviders`
|
|
61
|
+
* registry that the user's `providers` layer populates, the platform layers,
|
|
62
|
+
* and the configured state store. Adapters wrap this into runner-specific
|
|
63
|
+
* thunks (`bun.test` -> `runPromise`, `it.live` -> as-is).
|
|
64
|
+
*/
|
|
65
|
+
export const toEffect = <A>(
|
|
66
|
+
effect: TestEffect<A>,
|
|
67
|
+
options: MakeOptions,
|
|
68
|
+
): Effect.Effect<A, any, never> =>
|
|
69
|
+
Effect.gen(function* () {
|
|
70
|
+
const base = yield* loadConfigProvider(Option.none());
|
|
71
|
+
const configProvider = withProfileOverride(base, options.profile);
|
|
72
|
+
return yield* effect.pipe(
|
|
73
|
+
provideFreshArtifactStore,
|
|
74
|
+
Effect.provide(Layer.succeed(ConfigProvider, configProvider)),
|
|
75
|
+
);
|
|
76
|
+
}).pipe(
|
|
77
|
+
Effect.provideService(AuthProviders, {}),
|
|
78
|
+
Effect.provide(options.state ?? State.localState()),
|
|
79
|
+
Effect.provide(Layer.provideMerge(alchemyLayer, platformLayer)),
|
|
80
|
+
Effect.scoped,
|
|
81
|
+
) as Effect.Effect<A, any, never>;
|
|
82
|
+
|
|
83
|
+
/** Promise wrapper around {@link toEffect} for `bun.test`-style runners. */
|
|
84
|
+
export const run = <A>(
|
|
85
|
+
effect: TestEffect<A>,
|
|
86
|
+
options: MakeOptions,
|
|
87
|
+
): Promise<A> => Effect.runPromise(toEffect(effect, options));
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Wrap an effect so it runs with `options.providers` + a placeholder Stack +
|
|
91
|
+
* Stage in scope. Used by `test.provider` so user code can call provider SDK
|
|
92
|
+
* APIs (e.g. `DynamoDB.describeTable`) directly inside the test body.
|
|
93
|
+
*/
|
|
94
|
+
export const withProviders = <A, E, R, ROut>(
|
|
95
|
+
effect: Effect.Effect<A, E, R>,
|
|
96
|
+
options: MakeOptions<ROut>,
|
|
97
|
+
stackName: string,
|
|
98
|
+
): Effect.Effect<A, E, Exclude<R, ROut | Stack | Stage>> =>
|
|
99
|
+
effect.pipe(
|
|
100
|
+
Effect.provide(options.providers as Layer.Layer<any, never, any>),
|
|
101
|
+
Effect.provide(
|
|
102
|
+
Layer.succeed(Stack, {
|
|
103
|
+
name: stackName,
|
|
104
|
+
stage: options.stage ?? "test",
|
|
105
|
+
resources: {},
|
|
106
|
+
bindings: {},
|
|
107
|
+
}),
|
|
108
|
+
),
|
|
109
|
+
Effect.provide(Layer.succeed(Stage, options.stage ?? "test")),
|
|
110
|
+
) as Effect.Effect<A, E, Exclude<R, ROut | Stack | Stage>>;
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Curried `deploy` for the test factory: bakes in the configured stage and
|
|
114
|
+
* adds the telemetry layer the CLI uses, so `beforeAll(deploy(Stack))` works
|
|
115
|
+
* the same way as `alchemy deploy`.
|
|
116
|
+
*/
|
|
117
|
+
export const deploy = <A>(
|
|
118
|
+
options: MakeOptions,
|
|
119
|
+
stack: TestEffect<CompiledStack<A>, Stage | AlchemyContext>,
|
|
120
|
+
callOptions?: { stage?: string },
|
|
121
|
+
) =>
|
|
122
|
+
_deploy({
|
|
123
|
+
stack: stack as Effect.Effect<CompiledStack<A>, never, any>,
|
|
124
|
+
stage: callOptions?.stage ?? options.stage ?? "test",
|
|
125
|
+
}).pipe(Effect.provide(TelemetryLive));
|
|
126
|
+
|
|
127
|
+
export const destroy = (
|
|
128
|
+
options: MakeOptions,
|
|
129
|
+
stack: TestEffect<CompiledStack, Stage | AlchemyContext>,
|
|
130
|
+
callOptions?: { stage?: string },
|
|
131
|
+
) =>
|
|
132
|
+
_destroy({
|
|
133
|
+
stack: stack as Effect.Effect<CompiledStack, never, any>,
|
|
134
|
+
stage: callOptions?.stage ?? options.stage ?? "test",
|
|
135
|
+
}).pipe(Effect.provide(TelemetryLive));
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* In-test scratch stack handed to `test.provider(name, (stack) => ...)`.
|
|
139
|
+
*
|
|
140
|
+
* Each scratch stack owns a private in-memory state store that is shared
|
|
141
|
+
* between successive `deploy`/`destroy` calls AND visible to the user's test
|
|
142
|
+
* body (so `yield* State` / `state.get(...)` see the same store the deploys
|
|
143
|
+
* mutated). This makes create / update / replace / delete paths exercisable
|
|
144
|
+
* without polluting `.alchemy/` or other tests in the same file.
|
|
145
|
+
*/
|
|
146
|
+
export interface ScratchStack<ROut = any> {
|
|
147
|
+
readonly name: string;
|
|
148
|
+
/** The shared in-memory state Layer for this scratch. @internal */
|
|
149
|
+
readonly state: Layer.Layer<State.State, never, never>;
|
|
150
|
+
deploy<A, E, R>(
|
|
151
|
+
effect: Effect.Effect<A, E, R>,
|
|
152
|
+
): Effect.Effect<Input.Resolve<A>, any, Exclude<R, ROut | StackServices>>;
|
|
153
|
+
destroy(): Effect.Effect<void, any, never>;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const sanitizeStackName = (name: string) =>
|
|
157
|
+
name.replaceAll(/[^a-zA-Z0-9_]/g, "-").replace(/-+/g, "-");
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Build a fresh `ScratchStack` for `test.provider`. Allocates a private
|
|
161
|
+
* in-memory state store so the test is isolated from `.alchemy/` and from
|
|
162
|
+
* other tests in the same file.
|
|
163
|
+
*/
|
|
164
|
+
export const scratchStack = <ROut>(
|
|
165
|
+
options: MakeOptions<ROut>,
|
|
166
|
+
name: string,
|
|
167
|
+
): ScratchStack<ROut> => {
|
|
168
|
+
const stage = options.stage ?? "test";
|
|
169
|
+
const stackName = sanitizeStackName(name);
|
|
170
|
+
const inMemory: Record<
|
|
171
|
+
string,
|
|
172
|
+
Record<string, Record<string, State.ResourceState>>
|
|
173
|
+
> = {};
|
|
174
|
+
const stateLayer = Layer.succeed(
|
|
175
|
+
State.State,
|
|
176
|
+
State.InMemoryService(inMemory),
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
const buildAndApply = (effect: Effect.Effect<any, any, any>) =>
|
|
180
|
+
(effect as Effect.Effect<any, any, never>).pipe(
|
|
181
|
+
makeStack({
|
|
182
|
+
name: stackName,
|
|
183
|
+
providers: options.providers,
|
|
184
|
+
state: stateLayer,
|
|
185
|
+
} as any) as any,
|
|
186
|
+
Effect.flatMap((compiled: any) =>
|
|
187
|
+
Plan.make(compiled).pipe(
|
|
188
|
+
Effect.flatMap(apply),
|
|
189
|
+
Effect.provide(compiled.services),
|
|
190
|
+
),
|
|
191
|
+
),
|
|
192
|
+
Effect.provide(Layer.succeed(Stage, stage)),
|
|
193
|
+
provideFreshArtifactStore,
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
name: stackName,
|
|
198
|
+
state: stateLayer,
|
|
199
|
+
deploy: ((effect: Effect.Effect<any, any, any>) =>
|
|
200
|
+
buildAndApply(effect)) as ScratchStack<ROut>["deploy"],
|
|
201
|
+
destroy: () =>
|
|
202
|
+
Plan.make({
|
|
203
|
+
name: stackName,
|
|
204
|
+
stage,
|
|
205
|
+
resources: {},
|
|
206
|
+
bindings: {},
|
|
207
|
+
output: {},
|
|
208
|
+
}).pipe(
|
|
209
|
+
Effect.flatMap(apply),
|
|
210
|
+
Effect.asVoid,
|
|
211
|
+
Effect.provide(stateLayer),
|
|
212
|
+
Effect.provide(options.providers as Layer.Layer<any, never, any>),
|
|
213
|
+
Effect.provide(
|
|
214
|
+
Layer.succeed(Stack, {
|
|
215
|
+
name: stackName,
|
|
216
|
+
stage,
|
|
217
|
+
resources: {},
|
|
218
|
+
bindings: {},
|
|
219
|
+
}),
|
|
220
|
+
),
|
|
221
|
+
Effect.provide(Layer.succeed(Stage, stage)),
|
|
222
|
+
provideFreshArtifactStore,
|
|
223
|
+
) as Effect.Effect<void, any, never>,
|
|
224
|
+
};
|
|
225
|
+
};
|