Skip to content

Conversation

sam-goodwin
Copy link
Collaborator

@sam-goodwin sam-goodwin commented Aug 20, 2025

This changes Resource to produce an interface that is compatible with both Effects and Promises.

This new approach encourages not calling await until the end (although there's nothing stopping you). You can also yield* Worker(..) to use Alchemy natively in Effect.

  • We plan to leverage this lazy approach to also include support for planning.
  • Discussed the possibility of using a Layer to provide/swap-out the runtime, infrastructure or ui implementations of a resource (a better, effect-native approach to the previous runtime bootstrap that tried to do the same with esbuild).

How to define a Resource natively in Effect:

export type MyEffectResource = typeof MyEffectResource.output;

export const MyEffectResource = Resource("my-effect-resource", {
  input: {
    key: Schema.String,
  },
  output: {
    /**
     * The value of the resource.
     */
    value: Schema.Number,
  },
})(function* (id, props) {
  // ...

  yield* Console.log(id);

  return {
    value: 1,
  };
});

How to use without await:

import type { Rune } from "alchemy";
import alchemy, { type } from "alchemy";
import {
  DurableObjectNamespace,
  Queue,
  R2Bucket,
  Worker,
  Workflow,
} from "alchemy/cloudflare";
import * as Effect from "effect/Effect";
import type { HelloWorldDO } from "./src/do.ts";
import type MyRPC from "./src/rpc.ts";

export default alchemy("cloudflare-worker");

export const queue = Queue<{
  name: string;
  email: string;
}>("queue", {
  adopt: true,
});

export const rpc = Worker("rpc", {
  entrypoint: "./src/rpc.ts",
  rpc: type<MyRPC>,
  adopt: true,
});

const bucket = R2Bucket("bucket", {
  adopt: true,
});

export const worker = Worker("worker", {
  entrypoint: "./src/worker.ts",
  bindings: {
    RPC: rpc,
    BUCKET: bucket,
    QUEUE: queue,
    WORKFLOW: Workflow("OFACWorkflow", {
      className: "OFACWorkflow",
      workflowName: "ofac-workflow",
    }),
    DO: DurableObjectNamespace<HelloWorldDO>("HelloWorldDO", {
      className: "HelloWorldDO",
      sqlite: true,
    }),
  },
  url: true,
  eventSources: [queue],
  bundle: {
    metafile: true,
    format: "esm",
    target: "es2020",
  },
  adopt: true,
});

const DO = await worker.Env.DO.getByName("");

await worker.Env.QUEUE.send({
  name: "John Doe",
  email: "[email protected]",
});

await worker.Env.RPC.hello("John Doe");

Effect.gen(function* () {
  const DO = yield* worker.Env.DO.getByName("");

  const res = yield* worker.Env.RPC.hello("John Doe");
});

console.log({
  url: await worker.url,
});

@sam-goodwin sam-goodwin changed the title feat(core): Rune feat(core): hybrid Effect/Promise interface & no-await design Aug 20, 2025
@sam-goodwin sam-goodwin moved this to Ready in alchemy Aug 25, 2025
Base automatically changed from sam/generated-names to main August 26, 2025 00:01
@artimath
Copy link

Hell yeah, brother.

@sam-goodwin sam-goodwin force-pushed the main branch 2 times, most recently from a3a4abd to 1eeb802 Compare September 29, 2025 04:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants