effect-cf 0.34.0 → 0.36.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/dist/Mcp.d.mts ADDED
@@ -0,0 +1,122 @@
1
+ import { n as NativeRequest } from "./Worker-Dl6_Pijl.mjs";
2
+ import { Effect, Option } from "effect";
3
+ import { CreateMcpHandlerOptions, ServerOptions } from "@modelcontextprotocol/server";
4
+ import { Tool, Toolkit } from "effect/unstable/ai";
5
+ //#region src/Mcp.d.ts
6
+ /**
7
+ * CORS headers applied to MCP responses.
8
+ */
9
+ interface CorsOptions {
10
+ readonly origin?: string;
11
+ readonly methods?: string;
12
+ readonly headers?: string;
13
+ readonly maxAge?: number;
14
+ readonly exposeHeaders?: string;
15
+ }
16
+ /**
17
+ * Options for {@link fromToolkit}.
18
+ *
19
+ * Extends the MCP SDK `createMcpHandler` options (`legacy`, `responseMode`,
20
+ * `onerror`, ...) with the server identity advertised to MCP clients and the
21
+ * Worker edge options (`route`, `corsOptions`, `allowedHostnames`,
22
+ * `allowedOriginHostnames`).
23
+ */
24
+ interface FromToolkitOptions extends CreateMcpHandlerOptions {
25
+ /** Server name advertised in the MCP implementation info. */
26
+ readonly name: string;
27
+ /** Server version advertised in the MCP implementation info. */
28
+ readonly version: string;
29
+ /** Optional human-readable server title advertised to MCP clients. */
30
+ readonly title?: string;
31
+ /**
32
+ * Options passed to the underlying MCP SDK `McpServer` (capabilities,
33
+ * instructions, ...).
34
+ */
35
+ readonly serverOptions?: ServerOptions;
36
+ /** Pathname served by the MCP handler. @default "/mcp" */
37
+ readonly route?: string;
38
+ /** CORS headers applied to every MCP response. Pass `false` to disable. */
39
+ readonly corsOptions?: CorsOptions | false;
40
+ /**
41
+ * Restrict `Host` headers to these hostnames. Localhost and `workers.dev`
42
+ * endpoints receive matching defaults; custom domains rely on Cloudflare
43
+ * routing unless this option is set.
44
+ */
45
+ readonly allowedHostnames?: ReadonlyArray<string>;
46
+ /**
47
+ * Restrict present browser `Origin` headers to these hostnames. Requests
48
+ * without an Origin (including non-browser MCP clients) remain valid. The
49
+ * default includes localhost-class Origins, the endpoint's `workers.dev`
50
+ * hostname, and a concrete `corsOptions.origin` hostname. Pass `"*"` only
51
+ * when equivalent Origin validation runs in trusted middleware upstream.
52
+ */
53
+ readonly allowedOriginHostnames?: ReadonlyArray<string> | "*";
54
+ }
55
+ /**
56
+ * Services required to execute the tools of a toolkit: the tool handlers plus
57
+ * every service those handlers and their parameter/result codecs need.
58
+ */
59
+ type ToolkitServices<Tools extends Record<string, Tool.Any>> = Tool.HandlersFor<Tools> | Tool.HandlerServices<Tools[keyof Tools]>;
60
+ /**
61
+ * Context required by the Effect returned from {@link fromToolkit}: the native
62
+ * request provided by `Worker.make` plus the toolkit services.
63
+ */
64
+ type FromToolkitContext<Tools extends Record<string, Tool.Any>> = NativeRequest | ToolkitServices<Tools>;
65
+ /**
66
+ * Creates an Effect that serves a toolkit over MCP with the MCP SDK's
67
+ * stateless Streamable HTTP handler (`createMcpHandler` from
68
+ * `@modelcontextprotocol/server`), plus the Worker edge concerns Cloudflare's
69
+ * remote MCP deployments expect: CORS, `Host` header validation, and browser
70
+ * `Origin` validation.
71
+ *
72
+ * **Details**
73
+ *
74
+ * Yield the returned Effect inside a `Worker.make` fetch handler before other
75
+ * routing. When the request pathname matches `route` (default `"/mcp"`), the
76
+ * request is served by a fresh MCP server exposing every tool in the toolkit
77
+ * and the Effect succeeds with `Option.some(response)`. Any other pathname
78
+ * succeeds with `Option.none()` so the request can fall through to the rest of
79
+ * the fetch handler.
80
+ *
81
+ * Tool calls run through the caller's Effect context: provide the toolkit
82
+ * handlers (`toolkit.toLayer(...)`) and their services in the Worker layer or
83
+ * event layer. Tool parameters are decoded and results encoded with each
84
+ * tool's Effect `Schema`; parameter validation failures become MCP
85
+ * invalid-params errors, declared tool failures become tool results with
86
+ * `isError: true`, and unexpected errors are logged and reported to the client
87
+ * as an internal tool error.
88
+ *
89
+ * In-flight tool calls are awaited before the HTTP response is returned, so
90
+ * tool handlers always run to completion inside the fetch event, including on
91
+ * transport lanes that stream results into the response body.
92
+ *
93
+ * **Example**
94
+ *
95
+ * ```ts
96
+ * import { Effect, Layer, Option } from "effect"
97
+ * import { HttpRouter } from "effect/unstable/http"
98
+ * import { Worker } from "effect-cf"
99
+ * import * as Mcp from "effect-cf/mcp"
100
+ *
101
+ * const mcp = Mcp.fromToolkit(ActivityToolkit, {
102
+ * name: "strava-ingestor",
103
+ * version: "1.0.0",
104
+ * route: "/mcp",
105
+ * })
106
+ *
107
+ * const fetch = Effect.gen(function* () {
108
+ * const response = yield* mcp
109
+ * if (Option.isSome(response)) return response.value
110
+ * const router = yield* HttpRouter.HttpRouter
111
+ * return yield* router.asHttpEffect()
112
+ * })
113
+ *
114
+ * export default Worker.make(
115
+ * Layer.mergeAll(ApplicationLive, ActivityToolkitLive),
116
+ * { fetch },
117
+ * )
118
+ * ```
119
+ */
120
+ declare const fromToolkit: <Tools extends Record<string, Tool.Any>>(toolkit: Toolkit.Toolkit<Tools>, options: FromToolkitOptions) => Effect.Effect<Option.Option<Response>, unknown, FromToolkitContext<Tools>>;
121
+ //#endregion
122
+ export { CorsOptions, FromToolkitContext, FromToolkitOptions, ToolkitServices, fromToolkit };
package/dist/Mcp.mjs ADDED
@@ -0,0 +1,270 @@
1
+ import { r as runNativeCallback } from "./WorkerContext-Yobnfw-K.mjs";
2
+ import { n as NativeRequest } from "./Worker-XsGJsCJX.mjs";
3
+ import { Cause, Context, Effect, Exit, Option, Predicate, Schema, Sink, Stream } from "effect";
4
+ import { McpServer, createMcpHandler, hostHeaderValidationResponse, localhostAllowedHostnames, localhostAllowedOrigins, originValidationResponse } from "@modelcontextprotocol/server";
5
+ import { AiError, Tool } from "effect/unstable/ai";
6
+ //#region src/Mcp.ts
7
+ const DEFAULT_CORS_OPTIONS = {
8
+ origin: "*",
9
+ headers: "Content-Type, Accept, Authorization, mcp-session-id, MCP-Protocol-Version, Mcp-Method, Mcp-Name",
10
+ methods: "GET, POST, DELETE, OPTIONS",
11
+ exposeHeaders: "mcp-session-id",
12
+ maxAge: 86400
13
+ };
14
+ const corsHeaders = (options) => {
15
+ const merged = {
16
+ ...DEFAULT_CORS_OPTIONS,
17
+ ...options
18
+ };
19
+ return new Headers({
20
+ "Access-Control-Allow-Headers": merged.headers,
21
+ "Access-Control-Allow-Methods": merged.methods,
22
+ "Access-Control-Allow-Origin": merged.origin,
23
+ "Access-Control-Expose-Headers": merged.exposeHeaders,
24
+ "Access-Control-Max-Age": String(merged.maxAge)
25
+ });
26
+ };
27
+ const withCors = (response, options) => {
28
+ if (options === false) return response;
29
+ const headers = new Headers(response.headers);
30
+ for (const [headerName, value] of corsHeaders(options ?? {})) headers.set(headerName, value);
31
+ return new Response(response.body, {
32
+ status: response.status,
33
+ statusText: response.statusText,
34
+ headers
35
+ });
36
+ };
37
+ const corsOriginHostname = (corsOptions) => {
38
+ const origin = corsOptions === false ? void 0 : corsOptions?.origin;
39
+ if (origin === void 0 || !URL.canParse(origin)) return;
40
+ const url = new URL(origin);
41
+ return (url.protocol === "http:" || url.protocol === "https:") && url.hostname !== "" ? url.hostname : void 0;
42
+ };
43
+ /**
44
+ * `Host` and `Origin` validation for the MCP route, using the MCP SDK's
45
+ * validation primitives with the same defaults Cloudflare applies: localhost
46
+ * and `workers.dev` endpoints validate the `Host` header against themselves,
47
+ * and present browser `Origin` headers must match localhost-class Origins, the
48
+ * endpoint's `workers.dev` hostname, or a concrete configured CORS origin.
49
+ */
50
+ const edgeGuardResponse = (request, requestUrl, options) => {
51
+ const localEndpoint = localhostAllowedHostnames().includes(requestUrl.hostname);
52
+ const workersDevEndpoint = requestUrl.hostname.endsWith(".workers.dev");
53
+ const acceptedHostnames = options.allowedHostnames ?? (localEndpoint ? localhostAllowedHostnames() : workersDevEndpoint ? [requestUrl.hostname] : void 0);
54
+ if (acceptedHostnames !== void 0) {
55
+ const hostRejection = hostHeaderValidationResponse(request, [...acceptedHostnames]);
56
+ if (hostRejection !== void 0) return hostRejection;
57
+ }
58
+ if (options.allowedOriginHostnames === "*") return;
59
+ let acceptedOriginHostnames = options.allowedOriginHostnames;
60
+ if (acceptedOriginHostnames === void 0) {
61
+ const defaults = new Set(localhostAllowedOrigins());
62
+ if (workersDevEndpoint) defaults.add(requestUrl.hostname);
63
+ const configuredOriginHostname = corsOriginHostname(options.corsOptions);
64
+ if (configuredOriginHostname !== void 0) defaults.add(configuredOriginHostname);
65
+ acceptedOriginHostnames = [...defaults];
66
+ }
67
+ return originValidationResponse(request, [...acceptedOriginHostnames]);
68
+ };
69
+ const INTERNAL_TOOL_ERROR_MESSAGE = "Tool execution failed due to an internal server error.";
70
+ const toolErrorResult = (message) => ({
71
+ isError: true,
72
+ content: [{
73
+ type: "text",
74
+ text: message
75
+ }]
76
+ });
77
+ /**
78
+ * Wraps a JSON Schema produced by Effect `Schema` as the Standard Schema shape
79
+ * accepted by MCP SDK v2 `registerTool`, with a passthrough `validate`.
80
+ *
81
+ * Used for output schemas and for dynamic tools whose parameters are already a
82
+ * JSON Schema: the toolkit performs its own decoding, so there is nothing
83
+ * additional to validate here.
84
+ */
85
+ const standardSchemaFromJsonSchema = (jsonSchema) => ({ "~standard": {
86
+ version: 1,
87
+ vendor: "effect-cf",
88
+ validate: (value) => ({ value }),
89
+ jsonSchema: {
90
+ input: () => jsonSchema,
91
+ output: () => jsonSchema
92
+ }
93
+ } });
94
+ /**
95
+ * Builds the tool input schema registered with the MCP SDK.
96
+ *
97
+ * The SDK validates incoming `tools/call` arguments with `~standard.validate`
98
+ * and answers validation failures with an MCP invalid-params error, so the
99
+ * tool's Effect parameters `Schema` backs `validate`. On success the original
100
+ * encoded arguments are kept: the toolkit decodes them itself when the tool
101
+ * handler executes.
102
+ */
103
+ const standardInputSchema = (tool) => {
104
+ const jsonSchema = Tool.getJsonSchema(tool);
105
+ if (!Schema.isSchema(tool.parametersSchema)) return standardSchemaFromJsonSchema(jsonSchema);
106
+ const parametersSchema = tool.parametersSchema;
107
+ const validate = Schema.toStandardSchemaV1(parametersSchema)["~standard"].validate;
108
+ return { "~standard": {
109
+ version: 1,
110
+ vendor: "effect-cf",
111
+ validate: (value) => {
112
+ const result = validate(value);
113
+ if (result instanceof Promise) return result.then((awaited) => awaited.issues === void 0 ? { value } : awaited);
114
+ return result.issues === void 0 ? { value } : result;
115
+ },
116
+ jsonSchema: {
117
+ input: () => jsonSchema,
118
+ output: () => jsonSchema
119
+ }
120
+ } };
121
+ };
122
+ const handleToolCall = (built, tool, params) => {
123
+ const isDeclaredFailure = Schema.is(tool.failureSchema);
124
+ return built.handle(tool.name, params).pipe(Stream.unwrap, Stream.run(Sink.last()), Effect.flatMap(Effect.fromOption), Effect.map((result) => {
125
+ if (result.isFailure) return toolErrorResult(JSON.stringify(result.encodedResult));
126
+ const structuredContent = Predicate.isObject(result.encodedResult) ? result.encodedResult : void 0;
127
+ const content = result.encodedResult === void 0 ? [] : [{
128
+ type: "text",
129
+ text: JSON.stringify(result.encodedResult)
130
+ }];
131
+ if (structuredContent === void 0) return {
132
+ isError: false,
133
+ content
134
+ };
135
+ return {
136
+ isError: false,
137
+ structuredContent,
138
+ content
139
+ };
140
+ }), Effect.tapCause(Effect.logError), Effect.catch((error) => {
141
+ if (AiError.isAiError(error)) {
142
+ const reason = error.reason;
143
+ return Effect.succeed(toolErrorResult(reason._tag === "ToolParameterValidationError" ? reason.message : INTERNAL_TOOL_ERROR_MESSAGE));
144
+ }
145
+ if (isDeclaredFailure(error)) {
146
+ const message = error instanceof Error ? error.message : INTERNAL_TOOL_ERROR_MESSAGE;
147
+ return Effect.succeed(toolErrorResult(message));
148
+ }
149
+ return Effect.succeed(toolErrorResult(INTERNAL_TOOL_ERROR_MESSAGE));
150
+ }), Effect.catchDefect(() => Effect.succeed(toolErrorResult(INTERNAL_TOOL_ERROR_MESSAGE))));
151
+ };
152
+ const registerToolkitTool = (server, built, tool, run) => {
153
+ const outputJsonSchema = Tool.getJsonSchemaFromSchema(tool.successSchema);
154
+ const outputSchema = outputJsonSchema.type === "object" ? standardSchemaFromJsonSchema(outputJsonSchema) : void 0;
155
+ const title = Context.getOrUndefined(tool.annotations, Tool.Title);
156
+ const toolMeta = Context.getOrUndefined(tool.annotations, Tool.Meta);
157
+ server.registerTool(tool.name, {
158
+ title,
159
+ description: Tool.getDescription(tool),
160
+ inputSchema: standardInputSchema(tool),
161
+ outputSchema,
162
+ annotations: {
163
+ title,
164
+ readOnlyHint: Context.get(tool.annotations, Tool.Readonly),
165
+ destructiveHint: Context.get(tool.annotations, Tool.Destructive),
166
+ idempotentHint: Context.get(tool.annotations, Tool.Idempotent),
167
+ openWorldHint: Context.get(tool.annotations, Tool.OpenWorld)
168
+ },
169
+ _meta: toolMeta
170
+ }, async (args) => {
171
+ const exit = await run(handleToolCall(built, tool, args ?? {}));
172
+ if (Exit.isSuccess(exit)) return exit.value;
173
+ throw Cause.squash(exit.cause);
174
+ });
175
+ };
176
+ /**
177
+ * Creates an Effect that serves a toolkit over MCP with the MCP SDK's
178
+ * stateless Streamable HTTP handler (`createMcpHandler` from
179
+ * `@modelcontextprotocol/server`), plus the Worker edge concerns Cloudflare's
180
+ * remote MCP deployments expect: CORS, `Host` header validation, and browser
181
+ * `Origin` validation.
182
+ *
183
+ * **Details**
184
+ *
185
+ * Yield the returned Effect inside a `Worker.make` fetch handler before other
186
+ * routing. When the request pathname matches `route` (default `"/mcp"`), the
187
+ * request is served by a fresh MCP server exposing every tool in the toolkit
188
+ * and the Effect succeeds with `Option.some(response)`. Any other pathname
189
+ * succeeds with `Option.none()` so the request can fall through to the rest of
190
+ * the fetch handler.
191
+ *
192
+ * Tool calls run through the caller's Effect context: provide the toolkit
193
+ * handlers (`toolkit.toLayer(...)`) and their services in the Worker layer or
194
+ * event layer. Tool parameters are decoded and results encoded with each
195
+ * tool's Effect `Schema`; parameter validation failures become MCP
196
+ * invalid-params errors, declared tool failures become tool results with
197
+ * `isError: true`, and unexpected errors are logged and reported to the client
198
+ * as an internal tool error.
199
+ *
200
+ * In-flight tool calls are awaited before the HTTP response is returned, so
201
+ * tool handlers always run to completion inside the fetch event, including on
202
+ * transport lanes that stream results into the response body.
203
+ *
204
+ * **Example**
205
+ *
206
+ * ```ts
207
+ * import { Effect, Layer, Option } from "effect"
208
+ * import { HttpRouter } from "effect/unstable/http"
209
+ * import { Worker } from "effect-cf"
210
+ * import * as Mcp from "effect-cf/mcp"
211
+ *
212
+ * const mcp = Mcp.fromToolkit(ActivityToolkit, {
213
+ * name: "strava-ingestor",
214
+ * version: "1.0.0",
215
+ * route: "/mcp",
216
+ * })
217
+ *
218
+ * const fetch = Effect.gen(function* () {
219
+ * const response = yield* mcp
220
+ * if (Option.isSome(response)) return response.value
221
+ * const router = yield* HttpRouter.HttpRouter
222
+ * return yield* router.asHttpEffect()
223
+ * })
224
+ *
225
+ * export default Worker.make(
226
+ * Layer.mergeAll(ApplicationLive, ActivityToolkitLive),
227
+ * { fetch },
228
+ * )
229
+ * ```
230
+ */
231
+ const fromToolkit = (toolkit, options) => {
232
+ const { name, version, title, serverOptions, route = "/mcp", corsOptions, allowedHostnames, allowedOriginHostnames, ...handlerOptions } = options;
233
+ const serverInfo = {
234
+ name,
235
+ version,
236
+ title
237
+ };
238
+ return Effect.gen(function* () {
239
+ const request = yield* NativeRequest;
240
+ const requestUrl = new URL(request.url);
241
+ if (requestUrl.pathname !== route) return Option.none();
242
+ const rejection = edgeGuardResponse(request, requestUrl, {
243
+ corsOptions,
244
+ allowedHostnames,
245
+ allowedOriginHostnames
246
+ });
247
+ if (rejection !== void 0) return Option.some(withCors(rejection, corsOptions));
248
+ if (request.method === "OPTIONS" && corsOptions !== false) return Option.some(new Response(null, { headers: corsHeaders(corsOptions ?? {}) }));
249
+ const built = yield* toolkit;
250
+ const response = yield* runNativeCallback(async (run) => {
251
+ const pending = /* @__PURE__ */ new Set();
252
+ const trackedRun = (effect) => {
253
+ const promise = run(effect);
254
+ pending.add(promise);
255
+ promise.finally(() => pending.delete(promise));
256
+ return promise;
257
+ };
258
+ const response = await createMcpHandler(() => {
259
+ const server = new McpServer(serverInfo, serverOptions);
260
+ for (const tool of Object.values(built.tools)) registerToolkitTool(server, built, tool, trackedRun);
261
+ return server;
262
+ }, handlerOptions).fetch(request);
263
+ while (pending.size > 0) await Promise.allSettled([...pending]);
264
+ return response;
265
+ });
266
+ return Option.some(withCors(response, corsOptions));
267
+ });
268
+ };
269
+ //#endregion
270
+ export { fromToolkit };
package/dist/Vitest.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { c as WorkerEnvironment, s as WorkerEnv } from "./Binding-DKcLrQCM.mjs";
2
- import { n as DurableObjectStateService, t as DurableObjectState } from "./DurableObjectState-CrEunfQ-.mjs";
3
- import { o as WorkflowInstanceStatusName } from "./WorkflowBinding-BumVQTuW.mjs";
2
+ import { n as DurableObjectStateService, t as DurableObjectState } from "./DurableObjectState-CpgAmFqo.mjs";
3
+ import { o as WorkflowInstanceStatusName } from "./WorkflowBinding-CvJT9hB5.mjs";
4
4
  import { Effect, Layer, Schema } from "effect";
5
5
  import { DurableObject } from "cloudflare:workers";
6
6
  import { adminSecretsStore as adminSecretsStore$1, getQueueResult, introspectWorkflow as introspectWorkflow$1, introspectWorkflowInstance as introspectWorkflowInstance$1 } from "cloudflare:test";
package/dist/Vitest.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  import { a as WorkerEnvironment, i as WorkerConfig } from "./ErrorMessage-Byx2OgNd.mjs";
2
- import { r as fromDurableObjectState, t as DurableObjectState } from "./DurableObjectState-De4ZojhY.mjs";
2
+ import { r as runNativeCallback } from "./WorkerContext-Yobnfw-K.mjs";
3
+ import { r as fromDurableObjectState, t as DurableObjectState } from "./DurableObjectState-DCh4PZMN.mjs";
3
4
  import { ConfigProvider, Effect, Exit, Layer } from "effect";
4
5
  import { env } from "cloudflare:workers";
5
6
  import { abortAllDurableObjects as abortAllDurableObjects$1, adminSecretsStore as adminSecretsStore$1, applyD1Migrations as applyD1Migrations$1, createExecutionContext, createMessageBatch, createPagesEventContext, createScheduledController, evictAllDurableObjects as evictAllDurableObjects$1, evictDurableObject as evictDurableObject$1, getQueueResult, introspectWorkflow as introspectWorkflow$1, introspectWorkflowInstance as introspectWorkflowInstance$1, listDurableObjectIds as listDurableObjectIds$1, reset as reset$1, runDurableObjectAlarm as runDurableObjectAlarm$1, runInDurableObject as runInDurableObject$1, waitOnExecutionContext } from "cloudflare:test";
@@ -63,12 +64,10 @@ const queue = Effect.fn("effect-cf/vitest/queue")(function* (Worker, queueName,
63
64
  * passed to `use` for direct access.
64
65
  */
65
66
  const runInDurableObject = Effect.fn("effect-cf/vitest/runInDurableObject")(function* (stub, use) {
66
- const context = yield* Effect.context();
67
- return yield* yield* Effect.promise(() => runInDurableObject$1(stub, (instance, nativeState) => {
67
+ return yield* yield* runNativeCallback((run) => runInDurableObject$1(stub, (instance, nativeState) => {
68
68
  const state = fromDurableObjectState(nativeState);
69
- const effect = Effect.provideService(use(instance, state), DurableObjectState, state);
70
- return Effect.runPromiseExitWith(context)(effect);
71
- }));
69
+ return run(Effect.provideService(Effect.suspend(() => use(instance, state)), DurableObjectState, state));
70
+ })).pipe(Effect.orDie);
72
71
  });
73
72
  const runDurableObjectAlarm = Effect.fn("effect-cf/vitest/runDurableObjectAlarm")(function* (stub) {
74
73
  return yield* Effect.promise(() => runDurableObjectAlarm$1(stub));
@@ -119,12 +118,11 @@ const wrapWorkflowModifier = (modifier) => ({
119
118
  forceEventTimeout: (step) => Effect.promise(() => modifier.forceEventTimeout(step))
120
119
  });
121
120
  const runWorkflowModifier = Effect.fnUntraced(function* (register, use) {
122
- const context = yield* Effect.context();
123
- return yield* yield* Effect.promise(async () => {
121
+ return yield* yield* runNativeCallback(async (run) => {
124
122
  let callbackExit;
125
123
  try {
126
124
  await register(async (modifier) => {
127
- callbackExit = await Effect.runPromiseExitWith(context)(use(wrapWorkflowModifier(modifier)));
125
+ callbackExit = await run(Effect.suspend(() => use(wrapWorkflowModifier(modifier))));
128
126
  if (Exit.isFailure(callbackExit)) throw callbackExit;
129
127
  });
130
128
  } catch (cause) {
@@ -133,7 +131,7 @@ const runWorkflowModifier = Effect.fnUntraced(function* (register, use) {
133
131
  }
134
132
  if (callbackExit === void 0) throw new Error("The Workers Vitest plugin did not invoke the Workflow modifier callback");
135
133
  return callbackExit;
136
- });
134
+ }).pipe(Effect.orDie);
137
135
  });
138
136
  const wrapWorkflowInstanceIntrospector = (introspector) => ({
139
137
  raw: introspector,