alchemy-effect 0.6.4 → 0.7.1
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 +96 -24
- package/bin/alchemy-effect.js.map +1 -1
- package/bin/alchemy-effect.ts +36 -24
- package/lib/cli/index.d.ts +1 -0
- package/lib/cli/index.d.ts.map +1 -1
- package/package.json +13 -3
- package/src/AWS/Website/StaticSite.ts +5 -7
- package/src/AWS/Website/shared.ts +15 -3
- package/src/Apply.ts +143 -107
- package/src/Artifacts.ts +147 -0
- package/src/Build/Command.ts +21 -99
- package/src/Build/Memo.ts +187 -0
- package/src/Bundle/Bundle.ts +23 -17
- package/src/Cloudflare/Website/Vite.ts +65 -0
- package/src/Cloudflare/Website/index.ts +1 -1
- package/src/Cloudflare/Workers/Assets.ts +7 -2
- package/src/Cloudflare/Workers/Worker.ts +189 -59
- package/src/Destroy.ts +3 -2
- package/src/Output.ts +4 -0
- package/src/Plan.ts +618 -585
- package/src/Stack.ts +18 -6
- package/src/Test/Vitest.ts +35 -13
- package/src/Util/gitignore-rules-to-globs.ts +80 -0
- package/src/Cloudflare/Website/TanstackStart.ts +0 -14
package/src/Stack.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type { Scope } from "effect/Scope";
|
|
|
7
7
|
import * as ServiceMap from "effect/ServiceMap";
|
|
8
8
|
import type { HttpClient } from "effect/unstable/http/HttpClient";
|
|
9
9
|
import type { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner";
|
|
10
|
+
import type { Artifacts } from "./Artifacts.ts";
|
|
10
11
|
import { DotAlchemy } from "./Config.ts";
|
|
11
12
|
import type { ResourceBinding, ResourceLike } from "./Resource.ts";
|
|
12
13
|
import { Stage } from "./Stage.ts";
|
|
@@ -19,7 +20,8 @@ export type StackServices =
|
|
|
19
20
|
| Path
|
|
20
21
|
| DotAlchemy
|
|
21
22
|
| HttpClient
|
|
22
|
-
| ChildProcessSpawner
|
|
23
|
+
| ChildProcessSpawner
|
|
24
|
+
| Artifacts;
|
|
23
25
|
|
|
24
26
|
export class Stack extends ServiceMap.Service<
|
|
25
27
|
Stack,
|
|
@@ -39,6 +41,10 @@ export interface StackSpec<Output = any> {
|
|
|
39
41
|
output: Output;
|
|
40
42
|
}
|
|
41
43
|
|
|
44
|
+
export interface CompiledStack<Output, Services> extends StackSpec<Output> {
|
|
45
|
+
services: ServiceMap.ServiceMap<Services>;
|
|
46
|
+
}
|
|
47
|
+
|
|
42
48
|
export const StackName = Stack.use((stack) => Effect.succeed(stack.name));
|
|
43
49
|
|
|
44
50
|
export const make =
|
|
@@ -56,11 +62,17 @@ export const make =
|
|
|
56
62
|
Stack.asEffect(),
|
|
57
63
|
Effect.services<ROut | StackServices>(),
|
|
58
64
|
]).pipe(
|
|
59
|
-
Effect.map(
|
|
60
|
-
output,
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
65
|
+
Effect.map(
|
|
66
|
+
([output, stack, services]) =>
|
|
67
|
+
({
|
|
68
|
+
output,
|
|
69
|
+
services,
|
|
70
|
+
...stack,
|
|
71
|
+
}) satisfies CompiledStack<A, ROut | StackServices> as CompiledStack<
|
|
72
|
+
A,
|
|
73
|
+
ROut | StackServices
|
|
74
|
+
>,
|
|
75
|
+
),
|
|
64
76
|
Effect.provide(providers),
|
|
65
77
|
Effect.provideServiceEffect(
|
|
66
78
|
Stack,
|
package/src/Test/Vitest.ts
CHANGED
|
@@ -23,6 +23,7 @@ import * as Cloudflare from "../Cloudflare/index.ts";
|
|
|
23
23
|
|
|
24
24
|
import type { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner";
|
|
25
25
|
import { apply } from "../Apply.ts";
|
|
26
|
+
import { Artifacts, provideFreshArtifactStore } from "../Artifacts.ts";
|
|
26
27
|
import * as Credentials from "../AWS/Credentials.ts";
|
|
27
28
|
import * as Region from "../AWS/Region.ts";
|
|
28
29
|
import type { Command } from "../Build/Command.ts";
|
|
@@ -78,6 +79,7 @@ type Provided =
|
|
|
78
79
|
| Server.ServerHost
|
|
79
80
|
| Serverless.FunctionContext
|
|
80
81
|
| AWS.StageConfig
|
|
82
|
+
| Artifacts
|
|
81
83
|
| Provider<Command>
|
|
82
84
|
| Layer.Success<ReturnType<typeof AWS.providers>>
|
|
83
85
|
| Layer.Success<ReturnType<typeof Cloudflare.providers>>
|
|
@@ -146,7 +148,10 @@ const deriveStackName = (testPath: string, suffix: string) => {
|
|
|
146
148
|
const runWithContext = <A, Err>(
|
|
147
149
|
stackName: string,
|
|
148
150
|
effect: Effect.Effect<A, Err, Provided>,
|
|
149
|
-
options: {
|
|
151
|
+
options: {
|
|
152
|
+
state?: Layer.Layer<State.State, never, Stack.Stack>;
|
|
153
|
+
providers?: boolean;
|
|
154
|
+
} = {},
|
|
150
155
|
): Effect.Effect<
|
|
151
156
|
A,
|
|
152
157
|
aws.Credentials.CredentialsError | Config.ConfigError | Err,
|
|
@@ -192,7 +197,9 @@ const runWithContext = <A, Err>(
|
|
|
192
197
|
}).pipe(
|
|
193
198
|
Effect.provide(
|
|
194
199
|
Layer.provideMerge(
|
|
195
|
-
|
|
200
|
+
options.providers === false
|
|
201
|
+
? Layer.empty
|
|
202
|
+
: Layer.mergeAll(awsProviders, cfProviders),
|
|
196
203
|
Layer.provideMerge(alchemy, platform),
|
|
197
204
|
),
|
|
198
205
|
),
|
|
@@ -214,6 +221,7 @@ export function test(
|
|
|
214
221
|
options: {
|
|
215
222
|
timeout?: number;
|
|
216
223
|
state?: Layer.Layer<State.State, never, Stack.Stack>;
|
|
224
|
+
providers?: boolean;
|
|
217
225
|
},
|
|
218
226
|
testCase: Effect.Effect<void, any, Provided>,
|
|
219
227
|
): void;
|
|
@@ -230,6 +238,7 @@ export function test(
|
|
|
230
238
|
{
|
|
231
239
|
timeout?: number;
|
|
232
240
|
state?: Layer.Layer<State.State, never, Stack.Stack>;
|
|
241
|
+
providers?: boolean;
|
|
233
242
|
},
|
|
234
243
|
Effect.Effect<void, any, Provided>,
|
|
235
244
|
]
|
|
@@ -251,6 +260,7 @@ export namespace test {
|
|
|
251
260
|
options: {
|
|
252
261
|
timeout?: number;
|
|
253
262
|
state?: Layer.Layer<State.State, never, Stack.Stack>;
|
|
263
|
+
providers?: boolean;
|
|
254
264
|
},
|
|
255
265
|
testCase: Effect.Effect<void, any, Provided>,
|
|
256
266
|
): void;
|
|
@@ -267,6 +277,7 @@ export namespace test {
|
|
|
267
277
|
{
|
|
268
278
|
timeout?: number;
|
|
269
279
|
state?: Layer.Layer<State.State, never, Stack.Stack>;
|
|
280
|
+
providers?: boolean;
|
|
270
281
|
},
|
|
271
282
|
Effect.Effect<void, any, Provided>,
|
|
272
283
|
]
|
|
@@ -285,6 +296,7 @@ export namespace test {
|
|
|
285
296
|
{
|
|
286
297
|
timeout?: number;
|
|
287
298
|
state?: Layer.Layer<State.State, never, Stack.Stack>;
|
|
299
|
+
providers?: boolean;
|
|
288
300
|
},
|
|
289
301
|
Effect.Effect<void, any, Provided>,
|
|
290
302
|
]
|
|
@@ -337,17 +349,24 @@ export namespace test {
|
|
|
337
349
|
effect: Effect.Effect<A, Err, Req>,
|
|
338
350
|
): Effect.Effect<Input.Resolve<A>, Err, Req | Stack.Stack> =>
|
|
339
351
|
Stack.Stack.use((stack) => {
|
|
340
|
-
return
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
352
|
+
return provideFreshArtifactStore(
|
|
353
|
+
effect.pipe(
|
|
354
|
+
// Effect.tap(Effect.logInfo),
|
|
355
|
+
// @ts-expect-error
|
|
356
|
+
Stack.make(
|
|
357
|
+
stack.name,
|
|
358
|
+
Layer.effectServices(Effect.services<never>()),
|
|
359
|
+
{
|
|
360
|
+
...stack,
|
|
361
|
+
resources: {},
|
|
362
|
+
bindings: {},
|
|
363
|
+
output: {},
|
|
364
|
+
},
|
|
365
|
+
),
|
|
366
|
+
Effect.flatMap(Plan.make),
|
|
367
|
+
Effect.tap((plan) => Effect.logInfo(formatPlan(plan))),
|
|
368
|
+
Effect.flatMap(apply),
|
|
369
|
+
),
|
|
351
370
|
);
|
|
352
371
|
});
|
|
353
372
|
}
|
|
@@ -427,6 +446,7 @@ export function skip(
|
|
|
427
446
|
options: {
|
|
428
447
|
timeout?: number;
|
|
429
448
|
state?: Layer.Layer<State.State, never, Stack.Stack>;
|
|
449
|
+
providers?: boolean;
|
|
430
450
|
},
|
|
431
451
|
testCase: Effect.Effect<void, any, Provided>,
|
|
432
452
|
): void;
|
|
@@ -443,6 +463,7 @@ export function skip(
|
|
|
443
463
|
{
|
|
444
464
|
timeout?: number;
|
|
445
465
|
state?: Layer.Layer<State.State, never, Stack.Stack>;
|
|
466
|
+
providers?: boolean;
|
|
446
467
|
},
|
|
447
468
|
Effect.Effect<void, any, Provided>,
|
|
448
469
|
]
|
|
@@ -461,6 +482,7 @@ export function skipIf(condition: boolean) {
|
|
|
461
482
|
{
|
|
462
483
|
timeout?: number;
|
|
463
484
|
state?: Layer.Layer<State.State, never, Stack.Stack>;
|
|
485
|
+
providers?: boolean;
|
|
464
486
|
},
|
|
465
487
|
Effect.Effect<void, any, Provided>,
|
|
466
488
|
]
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts gitignore-style rules into glob patterns for tools like fast-glob's {@link https://github.com/mrmlnc/fast-glob#ignore `ignore`} option.
|
|
3
|
+
*
|
|
4
|
+
* Gitignore and glob differ (ordering, negation, escapes). This maps the common cases:
|
|
5
|
+
*
|
|
6
|
+
* - Rules with no `/` match at any depth → \`**\/name\`, \`**\/name\/\*\/`
|
|
7
|
+
* - A leading `/` anchors to the ignore file's directory (use the same `cwd` in fast-glob) → `name`, `name/**`
|
|
8
|
+
* - A `/` in the pattern (not only leading) uses path-aware matching → `a/b`, `a/b/**`
|
|
9
|
+
* - A trailing `/` restricts to directories → adds `/**` as needed
|
|
10
|
+
* - Lines starting with `!` (negation) are returned with a `!` prefix for use in **positive** glob
|
|
11
|
+
* lists; they are not valid in fast-glob's `ignore` array (filter them out if you only pass `ignore`)
|
|
12
|
+
*
|
|
13
|
+
* Does not implement full gitignore escaping (e.g. `\\ ` for trailing space) or `**` edge cases
|
|
14
|
+
* identical to git; escape handling can be added later if needed.
|
|
15
|
+
*/
|
|
16
|
+
export function gitignoreRulesToGlobs(rules: ReadonlyArray<string>): string[] {
|
|
17
|
+
const out: string[] = [];
|
|
18
|
+
for (const raw of rules) {
|
|
19
|
+
const line = raw.trim();
|
|
20
|
+
if (line.length === 0 || line.startsWith("#")) {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
let negated = false;
|
|
24
|
+
let rest = line;
|
|
25
|
+
if (rest.startsWith("!")) {
|
|
26
|
+
negated = true;
|
|
27
|
+
rest = rest.slice(1).trim();
|
|
28
|
+
if (rest.length === 0 || rest.startsWith("#")) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
let dirOnly = rest.endsWith("/");
|
|
33
|
+
if (dirOnly) {
|
|
34
|
+
rest = rest.slice(0, -1);
|
|
35
|
+
}
|
|
36
|
+
rest = rest.replace(/\\/g, "/");
|
|
37
|
+
if (rest.length === 0) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const anchored = rest.startsWith("/");
|
|
41
|
+
if (anchored) {
|
|
42
|
+
rest = rest.slice(1);
|
|
43
|
+
}
|
|
44
|
+
if (rest.length === 0) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
const hasSlash = rest.includes("/");
|
|
48
|
+
const globs = ruleBodyToGlobs(rest, { anchored, hasSlash, dirOnly });
|
|
49
|
+
for (const g of globs) {
|
|
50
|
+
out.push(negated ? `!${g}` : g);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function ruleBodyToGlobs(
|
|
57
|
+
body: string,
|
|
58
|
+
opts: { anchored: boolean; hasSlash: boolean; dirOnly: boolean },
|
|
59
|
+
): string[] {
|
|
60
|
+
const { anchored, hasSlash, dirOnly } = opts;
|
|
61
|
+
if (dirOnly) {
|
|
62
|
+
if (anchored) {
|
|
63
|
+
return [`${body}/**`];
|
|
64
|
+
}
|
|
65
|
+
if (hasSlash) {
|
|
66
|
+
return [`${body}/**`];
|
|
67
|
+
}
|
|
68
|
+
return [`**/${body}/**`];
|
|
69
|
+
}
|
|
70
|
+
if (anchored) {
|
|
71
|
+
if (hasSlash) {
|
|
72
|
+
return [body, `${body}/**`];
|
|
73
|
+
}
|
|
74
|
+
return [body, `${body}/**`];
|
|
75
|
+
}
|
|
76
|
+
if (hasSlash) {
|
|
77
|
+
return [body, `${body}/**`];
|
|
78
|
+
}
|
|
79
|
+
return [`**/${body}`, `**/${body}/**`];
|
|
80
|
+
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import * as Effect from "effect/Effect";
|
|
2
|
-
import { Worker } from "../Workers/Worker.ts";
|
|
3
|
-
|
|
4
|
-
export const TanstackStart: typeof Worker = ((...args: any[]) =>
|
|
5
|
-
args.length === 0
|
|
6
|
-
? (...args: [string, Effect.Effect<any>]) => TanstackStart(...args)
|
|
7
|
-
: Worker(
|
|
8
|
-
args[0],
|
|
9
|
-
{
|
|
10
|
-
// TODO(sam): main entrypoint should be the Tanstack Start entrypoint (that is assumed to import and run this)
|
|
11
|
-
main: import.meta.filename,
|
|
12
|
-
},
|
|
13
|
-
args[1],
|
|
14
|
-
)) as any;
|