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
21 changes: 12 additions & 9 deletions packages/alchemy/src/Cloudflare/Workers/InferEnv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type * as Effect from "effect/Effect";
import type { Redacted } from "effect/Redacted";
import type * as Stream from "effect/Stream";
import type { Rpc } from "../../Rpc.ts";
import type { WorkflowLike } from "../Workflows/Workflow.ts";
// NOTE: import the service modules directly rather than `import * as Cloudflare
// from "../index.ts"`. Importing the whole Cloudflare barrel here creates a
// circular re-export when the barrel does `export * from "./Workers/index.ts"`
Expand Down Expand Up @@ -79,15 +80,17 @@ export type GetBindingType<T> =
? WorkerVersionMetadata
: T extends WorkerLoaderResource
? WorkerLoader
: T extends DurableObjectLike
? DurableObjectNamespace<
Exclude<T["Shape"], undefined>
>
: T extends Redacted<any>
? // redacteds are always stored as secret_text, so are always string
// we JSON.stringify when not a Redacted<string>
string
: T;
: T extends WorkflowLike<infer Params>
? Workflow<Params>
: T extends DurableObjectLike
? DurableObjectNamespace<
Exclude<T["Shape"], undefined>
>
: T extends Redacted<any>
? // redacteds are always stored as secret_text, so are always string
// we JSON.stringify when not a Redacted<string>
string
: T;

/**
* Cloudflare service-binding wire shape for an Effect-native Worker.
Expand Down
23 changes: 23 additions & 0 deletions packages/alchemy/src/Cloudflare/Workers/WorkerAsyncBindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { isQueue } from "../Queues/Queue.ts";
import { isBucket } from "../R2/Bucket.ts";
import { isSecret } from "../SecretsStore/Secret.ts";
import { isIndex } from "../Vectorize/VectorizeIndex.ts";
import { isWorkflowLike, WorkflowResource } from "../Workflows/Workflow.ts";
import { isAssets } from "./Assets.ts";
import { isBrowser } from "./Browser.ts";
import { isDurableObjectLike } from "./DurableObject.ts";
Expand Down Expand Up @@ -64,6 +65,20 @@ export const bindWorkerAsyncBindings = Effect.fn(function* (
? getHyperdriveDevOrigin(binding)
: undefined,
});

// A locally-hosted Workflow (no `scriptName`) must be registered with
// Cloudflare via `putWorkflow` once the host Worker exists. Cross-script
// references (with `scriptName`) are reference-only — the host owns the
// workflow resource. `scriptName: resource.workerName` makes the
// WorkflowResource depend on the Worker so it reconciles afterwards.
if (isWorkflowLike(binding) && !binding.scriptName) {
const workflowName = binding.workflowName ?? binding.name;
yield* WorkflowResource(workflowName, {
workflowName,
className: binding.className ?? binding.name,
scriptName: resource.workerName,
});
}
} else {
return yield* Effect.die(`Unknown binding type: ${bindingName}`);
}
Expand Down Expand Up @@ -155,6 +170,14 @@ const toBinding = (
className: binding.className ?? binding.name,
scriptName: binding.scriptName,
};
} else if (isWorkflowLike(binding)) {
return {
type: "workflow",
name: bindingName,
workflowName: binding.workflowName ?? binding.name,
className: binding.className ?? binding.name,
scriptName: binding.scriptName,
};
} else if (isDatabase(binding)) {
return {
type: "d1",
Expand Down
4 changes: 3 additions & 1 deletion packages/alchemy/src/Cloudflare/Workers/WorkerBinding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import type { Queue } from "../Queues/Queue.ts";
import type { Bucket } from "../R2/Bucket.ts";
import type { Secret } from "../SecretsStore/Secret.ts";
import type { Index as VectorizeIndex } from "../Vectorize/VectorizeIndex.ts";
import type { WorkflowLike } from "../Workflows/Workflow.ts";
import type { Assets } from "./Assets.ts";
import type { BrowserBinding } from "./BrowserBinding.ts";
import type { DurableObjectLike } from "./DurableObject.ts";
Expand Down Expand Up @@ -66,7 +67,8 @@ export type WorkerBindingResource =
| Worker
| WorkerLoader
| VersionMetadataBinding
| DurableObjectLike<any>;
| DurableObjectLike<any>
| WorkflowLike<any>;

export type WorkerBindings = {
[bindingName in string]: WorkerBindingResource;
Expand Down
Loading
Loading