Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion packages/alchemy/src/Local/RpcProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import * as Predicate from "effect/Predicate";
import type { Scope } from "effect/Scope";
import * as Stream from "effect/Stream";
import { AlchemyContext } from "../AlchemyContext.ts";
import { Artifacts, ArtifactStore, makeScopedArtifacts } from "../Artifacts.ts";
import { InstanceId } from "../InstanceId.ts";
import type { Platform } from "../Platform.ts";
import * as Provider from "../Provider.ts";
Expand Down Expand Up @@ -188,6 +189,7 @@ export const effect = <
const client = yield* Effect.serviceOption(RpcProviderProxy);
const context = yield* Effect.context();
const stack = yield* Stack;
const store = yield* ArtifactStore;

if (client._tag === "None") {
const provider = withDefaultList(yield* eff);
Expand All @@ -203,7 +205,13 @@ export const effect = <
layerFallback(Stage, stack.stage),
Predicate.hasProperty(args[0], "instanceId") &&
Predicate.isString(args[0].instanceId)
? layerFallback(InstanceId, args[0].instanceId)
? Layer.merge(
layerFallback(InstanceId, args[0].instanceId),
Layer.succeed(
Artifacts,
makeScopedArtifacts(store, args[0].instanceId),
),
)
: Layer.empty,
);
return result.pipe(
Expand Down
10 changes: 9 additions & 1 deletion packages/alchemy/src/Local/RpcServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import * as Layer from "effect/Layer";
import type * as Scope from "effect/Scope";
import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient";
import type { HttpClient } from "effect/unstable/http/HttpClient";
import { ArtifactStore, createArtifactStore } from "../Artifacts.ts";
import type { ProviderService } from "../Provider.ts";
import type { ResourceLike } from "../Resource.ts";
import {
Expand Down Expand Up @@ -78,12 +79,19 @@ export const launch = <ROut, E>(
| RpcServerEnvironment.RpcEnvironmentServices
| PlatformServices
| HttpClient
| ArtifactStore
>,
) =>
serverPlatformLayer.pipe(
Layer.provide(providers),
Layer.provide(RpcServerEnvironment.fromEnv()),
Layer.provide(Layer.merge(PlatformServices, FetchHttpClient.layer)),
Layer.provide(
Layer.mergeAll(
PlatformServices,
FetchHttpClient.layer,
Layer.sync(ArtifactStore, createArtifactStore),
),
),
Layer.launch,
Effect.scoped,
runMain,
Expand Down
4 changes: 3 additions & 1 deletion packages/alchemy/src/Stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import type { HttpClient } from "effect/unstable/http/HttpClient";
import type { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner";
import type { ActionLike } from "./Action.ts";
import { AlchemyContext, AlchemyContextLive } from "./AlchemyContext.ts";
import { provideFreshArtifactStore } from "./Artifacts.ts";
import { type ArtifactStore, provideFreshArtifactStore } from "./Artifacts.ts";
import { AuthProviders } from "./Auth/AuthProvider.ts";
import { CredentialsStore, CredentialsStoreLive } from "./Auth/Credentials.ts";
import { AlchemyProfile, ProfileLive } from "./Auth/Profile.ts";
Expand All @@ -39,6 +39,7 @@ export type StackServices =
| ChildProcessSpawner
| AuthProviders
| AlchemyProfile
| ArtifactStore
| CredentialsStore
| Cli;

Expand All @@ -53,6 +54,7 @@ export type StackEffect<A, Err = never, Req = never> = Effect.Effect<
| Cli
| AlchemyProfile
| CredentialsStore
| ArtifactStore
| State
| Req
>;
Expand Down
3 changes: 3 additions & 0 deletions packages/alchemy/test/Cloudflare/Providers.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { AlchemyContext } from "@/AlchemyContext.ts";
import { ArtifactStore, createArtifactStore } from "@/Artifacts.ts";
import { AuthProviders } from "@/Auth/AuthProvider.ts";
import * as Cloudflare from "@/Cloudflare";
import { Stack } from "@/Stack.ts";
Expand All @@ -10,6 +11,7 @@ import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient";
import { v4 as uuidv4 } from "uuid";

it.live(
"building the Cloudflare provider layers should not fail for unknown profile",
() =>
Expand Down Expand Up @@ -38,6 +40,7 @@ it.live(
ALCHEMY_PROFILE: `non-existent-${uuidv4()}`,
}),
),
Layer.sync(ArtifactStore, createArtifactStore),
NodeServices.layer,
FetchHttpClient.layer,
),
Expand Down
62 changes: 58 additions & 4 deletions packages/alchemy/test/Local/RpcProvider.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { AlchemyContext } from "@/AlchemyContext.ts";
import * as Artifacts from "@/Artifacts.ts";
import { InstanceId } from "@/InstanceId.ts";
import * as RpcProvider from "@/Local/RpcProvider.ts";
import type { ProviderService } from "@/Provider.ts";
Expand All @@ -13,7 +14,7 @@ import * as Stream from "effect/Stream";
interface TestResource extends Resource<
"Local.RpcProvider.Test",
{},
{ ok: boolean }
{ ok: boolean; artifact: string }
> {}
const TestResource = Resource<TestResource>("Local.RpcProvider.Test");

Expand All @@ -23,6 +24,7 @@ interface Capture {
stack?: StackShape;
stage?: string;
instanceId?: string;
artifact?: string;
}

const defaultStack: StackShape = {
Expand All @@ -49,7 +51,7 @@ describe("Local.RpcProvider.effect", () => {
bindings: [],
}),
);
expect(result).toEqual({ ok: true });
expect(result).toMatchObject({ ok: true });
expect(capture.stack).toBe(defaultStack);
expect(capture.stage).toBe(defaultStack.stage);
expect(capture.instanceId).toBe("inst-from-arg");
Expand Down Expand Up @@ -97,7 +99,7 @@ describe("Local.RpcProvider.effect", () => {
id: "r",
instanceId: "inst-from-arg",
props: {},
output: { ok: true },
output: { ok: true, artifact: "artifact" },
}).pipe(Stream.runCollect),
);
expect(items.length).toBe(1);
Expand Down Expand Up @@ -128,18 +130,69 @@ describe("Local.RpcProvider.effect", () => {
expect(capture.instanceId).toBeUndefined();
}),
);

it.effect("caches artifacts", () =>
Effect.gen(function* () {
const [capture, result] = yield* useProvider((provider) =>
provider.diff!({
id: "r",
news: {},
olds: undefined,
output: undefined,
oldBindings: [],
newBindings: [],
instanceId: "inst-from-arg",
}).pipe(
Effect.andThen(
Effect.all({
sameInstanceId: provider.reconcile({
id: "r",
instanceId: "inst-from-arg",
news: {},
olds: undefined,
output: undefined,
session: undefined as any,
bindings: [],
}),
differentInstanceId: provider.reconcile({
id: "r",
instanceId: "inst-from-arg-2",
news: {},
olds: undefined,
output: undefined,
session: undefined as any,
bindings: [],
}),
}),
),
),
);
expect(capture.artifact).toBeDefined();
expect(result.sameInstanceId.artifact).toBeDefined();
expect(result.differentInstanceId.artifact).toBeDefined();
expect(capture.artifact).toBe(result.sameInstanceId.artifact);
expect(capture.artifact).not.toBe(result.differentInstanceId.artifact);
}),
);
});

const artifact = Effect.sync(() => crypto.randomUUID()).pipe(
Artifacts.cached("artifact"),
);

const TestResourceProvider = (capture: Capture) =>
RpcProvider.effect(
TestResource,
"ignored://entry",
Effect.succeed({
diff: Effect.fn(function* () {
capture.artifact = yield* artifact;
}),
reconcile: Effect.fn(function* (_input: any) {
capture.stack = yield* Stack;
capture.stage = yield* Stage;
capture.instanceId = yield* InstanceId;
return { ok: true };
return { ok: true, artifact: yield* artifact };
}),
tail: (_input: any) =>
Stream.fromEffect(
Expand Down Expand Up @@ -167,6 +220,7 @@ const useProvider = <A, E, R>(
Layer.mergeAll(
Layer.succeed(Stack, defaultStack),
Layer.succeed(Stage, defaultStack.stage),
Layer.sync(Artifacts.ArtifactStore, Artifacts.createArtifactStore),
Layer.succeed(AlchemyContext, {
dotAlchemy: "/tmp/.alchemy",
dev: false,
Expand Down
3 changes: 2 additions & 1 deletion packages/alchemy/test/test.resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import * as Provider from "@/Provider.ts";
import { Resource, type ResourceBinding } from "@/Resource";
import * as State from "@/State/index";
import { isUnknown } from "@/Util/unknown";
import * as Context from "effect/Context";
import { Data } from "effect";
import * as Context from "effect/Context";
import * as Duration from "effect/Duration";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
Expand Down Expand Up @@ -639,6 +639,7 @@ export const OverrideStablesResource = Resource<OverrideStablesResource>(

export const overrideStablesResourceProvider = () =>
Provider.succeed(OverrideStablesResource, {
list: () => Effect.succeed([]),
stables: ["providerStable", "sharedStable"],
diff: Effect.fn(function* ({ news = {}, olds = {} }) {
if (!isResolved(news)) return undefined;
Expand Down
Loading