alchemy-effect 0.6.3 → 0.7.0

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/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(([output, stack, services]) => ({
60
- output,
61
- services,
62
- ...stack,
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,
@@ -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>>
@@ -337,17 +339,24 @@ export namespace test {
337
339
  effect: Effect.Effect<A, Err, Req>,
338
340
  ): Effect.Effect<Input.Resolve<A>, Err, Req | Stack.Stack> =>
339
341
  Stack.Stack.use((stack) => {
340
- return effect.pipe(
341
- // Effect.tap(Effect.logInfo),
342
- // @ts-expect-error
343
- Stack.make(stack.name, Layer.effectServices(Effect.services<never>()), {
344
- ...stack,
345
- resources: {},
346
- bindings: {},
347
- }),
348
- Effect.flatMap(Plan.make),
349
- Effect.tap((plan) => Effect.logInfo(formatPlan(plan))),
350
- Effect.flatMap(apply),
342
+ return provideFreshArtifactStore(
343
+ effect.pipe(
344
+ // Effect.tap(Effect.logInfo),
345
+ // @ts-expect-error
346
+ Stack.make(
347
+ stack.name,
348
+ Layer.effectServices(Effect.services<never>()),
349
+ {
350
+ ...stack,
351
+ resources: {},
352
+ bindings: {},
353
+ output: {},
354
+ },
355
+ ),
356
+ Effect.flatMap(Plan.make),
357
+ Effect.tap((plan) => Effect.logInfo(formatPlan(plan))),
358
+ Effect.flatMap(apply),
359
+ ),
351
360
  );
352
361
  });
353
362
  }
@@ -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;