effect-orpc 0.2.2 → 0.4.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.
@@ -1,21 +1,38 @@
1
- import { ORPCError } from "@orpc/contract";
1
+ import { ORPCError, type Meta } from "@orpc/contract";
2
2
  import type {
3
3
  Context,
4
+ Middleware,
5
+ MiddlewareNextFnOptions,
6
+ MiddlewareOutputFn,
7
+ MiddlewareResult,
4
8
  ProcedureHandler,
5
9
  ProcedureHandlerOptions,
6
10
  } from "@orpc/server";
7
- import type { ManagedRuntime } from "effect";
8
- import { Cause, Effect, Exit, FiberRefs } from "effect";
11
+ import type { Promisable } from "@orpc/shared";
12
+ import { Cause, Effect, Exit, Option } from "effect";
9
13
 
10
- import { getCurrentFiberRefs } from "./fiber-context-bridge";
14
+ import { runWithFiberRefs } from "./fiber-context-bridge";
15
+ import type { EffectRuntimeRunner } from "./runtime-source";
11
16
  import type { EffectErrorConstructorMap, EffectErrorMap } from "./tagged-error";
12
17
  import {
13
18
  createEffectErrorConstructorMap,
14
19
  isORPCTaggedError,
15
20
  } from "./tagged-error";
16
- import type { EffectProcedureHandler, EffectSpanConfig } from "./types";
21
+ import type {
22
+ EffectMiddleware,
23
+ EffectMiddlewareOptions,
24
+ EffectMiddlewareOutput,
25
+ EffectMiddlewareResult,
26
+ EffectOptionalProvider,
27
+ EffectPipelineStep,
28
+ EffectProcedureHandler,
29
+ EffectProvider,
30
+ EffectSpanConfig,
31
+ } from "./types";
32
+
33
+ type EffectTag = import("effect").Context.Tag<any, any>;
17
34
 
18
- export function toORPCErrorFromCause(
35
+ function toORPCErrorFromCause(
19
36
  cause: Cause.Cause<unknown>,
20
37
  ): ORPCError<string, unknown> {
21
38
  return Cause.match(cause, {
@@ -59,9 +76,9 @@ export function createEffectProcedureHandler<
59
76
  TEffectErrorMap extends EffectErrorMap,
60
77
  TRequirementsProvided,
61
78
  TRuntimeError,
62
- TMeta,
79
+ TMeta extends Meta,
63
80
  >(options: {
64
- runtime: ManagedRuntime.ManagedRuntime<TRequirementsProvided, TRuntimeError>;
81
+ runner: EffectRuntimeRunner<TRequirementsProvided, TRuntimeError>;
65
82
  effectErrorMap: TEffectErrorMap;
66
83
  effectFn: EffectProcedureHandler<
67
84
  TCurrentContext,
@@ -71,6 +88,7 @@ export function createEffectProcedureHandler<
71
88
  TRequirementsProvided,
72
89
  any
73
90
  >;
91
+ effectSteps?: readonly EffectPipelineStep[];
74
92
  spanConfig?: EffectSpanConfig;
75
93
  defaultCaptureStackTrace: () => string | undefined;
76
94
  }): ProcedureHandler<
@@ -81,9 +99,10 @@ export function createEffectProcedureHandler<
81
99
  TMeta & Record<never, never>
82
100
  > {
83
101
  const {
84
- runtime,
102
+ runner,
85
103
  effectErrorMap,
86
104
  effectFn,
105
+ effectSteps = [],
87
106
  spanConfig,
88
107
  defaultCaptureStackTrace,
89
108
  } = options;
@@ -108,20 +127,25 @@ export function createEffectProcedureHandler<
108
127
  const captureStackTrace =
109
128
  spanConfig?.captureStackTrace ?? defaultCaptureStackTrace;
110
129
  const resolver = Effect.fnUntraced(effectFn as any);
111
- const tracedEffect = Effect.withSpan(resolver(effectOpts), spanName, {
112
- captureStackTrace,
113
- });
114
- const parentFiberRefs = getCurrentFiberRefs();
115
- const effectWithRefs = parentFiberRefs
116
- ? Effect.fiberIdWith((fiberId) =>
117
- Effect.flatMap(Effect.getFiberRefs, (fiberRefs) =>
118
- Effect.setFiberRefs(
119
- FiberRefs.joinAs(fiberRefs, fiberId, parentFiberRefs),
120
- ).pipe(Effect.andThen(tracedEffect)),
130
+ const handlerEffect = resolver(effectOpts);
131
+ const tracedEffect = Effect.withSpan(
132
+ runEffectPipeline({
133
+ baseOptions: effectOpts,
134
+ effectErrorMap,
135
+ final: (context) =>
136
+ Effect.map(
137
+ context === effectOpts.context
138
+ ? handlerEffect
139
+ : resolver({ ...effectOpts, context }),
140
+ (output) => ({ output, context: {} }),
121
141
  ),
122
- )
123
- : tracedEffect;
124
- const exit = await runtime.runPromiseExit(effectWithRefs, {
142
+ input: opts.input,
143
+ steps: effectSteps,
144
+ }),
145
+ spanName,
146
+ { captureStackTrace },
147
+ );
148
+ const exit = await runner.runPromiseExit(tracedEffect, {
125
149
  signal: opts.signal,
126
150
  });
127
151
 
@@ -129,6 +153,404 @@ export function createEffectProcedureHandler<
129
153
  throw toORPCErrorFromCause(exit.cause);
130
154
  }
131
155
 
132
- return exit.value as TOutput;
156
+ return exit.value.output as TOutput;
157
+ };
158
+ }
159
+
160
+ export function createEffectPipelineMiddleware<
161
+ TCurrentContext extends Context,
162
+ TOutput,
163
+ TEffectErrorMap extends EffectErrorMap,
164
+ TRequirementsProvided,
165
+ TRuntimeError,
166
+ TMeta extends Meta,
167
+ >(options: {
168
+ runner: EffectRuntimeRunner<TRequirementsProvided, TRuntimeError>;
169
+ effectErrorMap: TEffectErrorMap;
170
+ steps: readonly EffectPipelineStep[];
171
+ }): Middleware<
172
+ TCurrentContext,
173
+ Record<never, never>,
174
+ unknown,
175
+ TOutput,
176
+ any,
177
+ TMeta
178
+ > {
179
+ const { runner, effectErrorMap, steps } = options;
180
+
181
+ return async (opts, input) => {
182
+ const baseOptions = makeEffectOptions<
183
+ TCurrentContext,
184
+ unknown,
185
+ TEffectErrorMap,
186
+ TMeta
187
+ >(opts, input, effectErrorMap);
188
+ const effect = runEffectPipeline<
189
+ TCurrentContext,
190
+ unknown,
191
+ TOutput,
192
+ TEffectErrorMap,
193
+ TRequirementsProvided,
194
+ TMeta
195
+ >({
196
+ baseOptions,
197
+ effectErrorMap,
198
+ final: (context) =>
199
+ withCurrentFiberContext(() =>
200
+ opts.next(
201
+ context === opts.context
202
+ ? undefined
203
+ : { context: context as Record<PropertyKey, unknown> },
204
+ ),
205
+ ) as any,
206
+ input,
207
+ steps,
208
+ });
209
+ const exit = await runner.runPromiseExit(effect, {
210
+ signal: opts.signal,
211
+ });
212
+
213
+ if (Exit.isFailure(exit)) throw toORPCErrorFromCause(exit.cause);
214
+
215
+ return exit.value as MiddlewareResult<Record<never, never>, TOutput>;
216
+ };
217
+ }
218
+
219
+ export function createEffectProviderMiddleware<
220
+ TCurrentContext extends Context,
221
+ TInput,
222
+ TEffectErrorMap extends EffectErrorMap,
223
+ TRequirementsProvided,
224
+ TRuntimeError,
225
+ TMeta extends Meta,
226
+ TTag extends EffectTag,
227
+ >(options: {
228
+ runner: EffectRuntimeRunner<TRequirementsProvided, TRuntimeError>;
229
+ effectErrorMap: TEffectErrorMap;
230
+ tag: TTag;
231
+ provider: EffectProvider<
232
+ TCurrentContext,
233
+ TInput,
234
+ TEffectErrorMap,
235
+ TRequirementsProvided,
236
+ TMeta,
237
+ TTag
238
+ >;
239
+ }): Middleware<TCurrentContext, Record<never, never>, TInput, any, any, TMeta> {
240
+ const { runner, effectErrorMap, tag, provider } = options;
241
+
242
+ return async (opts, input) => {
243
+ const effectOpts = makeEffectOptions<
244
+ TCurrentContext,
245
+ TInput,
246
+ TEffectErrorMap,
247
+ TMeta
248
+ >(opts, input, effectErrorMap);
249
+ const effect = Effect.flatMap(provider(effectOpts), (service) =>
250
+ Effect.provideService(
251
+ withCurrentFiberContext(() => opts.next()),
252
+ tag,
253
+ service,
254
+ ),
255
+ );
256
+ const exit = await runner.runPromiseExit(effect, {
257
+ signal: opts.signal,
258
+ });
259
+
260
+ if (Exit.isFailure(exit)) throw toORPCErrorFromCause(exit.cause);
261
+
262
+ return exit.value;
133
263
  };
134
264
  }
265
+
266
+ export function createEffectOptionalProviderMiddleware<
267
+ TCurrentContext extends Context,
268
+ TInput,
269
+ TEffectErrorMap extends EffectErrorMap,
270
+ TRequirementsProvided,
271
+ TRuntimeError,
272
+ TMeta extends Meta,
273
+ TTag extends EffectTag,
274
+ >(options: {
275
+ runner: EffectRuntimeRunner<TRequirementsProvided, TRuntimeError>;
276
+ effectErrorMap: TEffectErrorMap;
277
+ tag: TTag;
278
+ provider: EffectOptionalProvider<
279
+ TCurrentContext,
280
+ TInput,
281
+ TEffectErrorMap,
282
+ TRequirementsProvided,
283
+ TMeta,
284
+ TTag
285
+ >;
286
+ }): Middleware<TCurrentContext, Record<never, never>, TInput, any, any, TMeta> {
287
+ const { runner, effectErrorMap, tag, provider } = options;
288
+
289
+ return async (opts, input) => {
290
+ const effectOpts = makeEffectOptions<
291
+ TCurrentContext,
292
+ TInput,
293
+ TEffectErrorMap,
294
+ TMeta
295
+ >(opts, input, effectErrorMap);
296
+ const effect = Effect.flatMap(provider(effectOpts), (service) =>
297
+ Option.match(service, {
298
+ onNone: () => withCurrentFiberContext(() => opts.next()),
299
+ onSome: (value) =>
300
+ Effect.provideService(
301
+ withCurrentFiberContext(() => opts.next()),
302
+ tag,
303
+ value,
304
+ ),
305
+ }),
306
+ );
307
+ const exit = await runner.runPromiseExit(effect, {
308
+ signal: opts.signal,
309
+ });
310
+
311
+ if (Exit.isFailure(exit)) throw toORPCErrorFromCause(exit.cause);
312
+
313
+ return exit.value;
314
+ };
315
+ }
316
+
317
+ // todo(utopy): make this check more comprehensive, maybe add a Symbol to the EffectMiddleware type
318
+ export function isEffectMiddleware(
319
+ value: unknown,
320
+ ): value is EffectMiddleware<
321
+ Context,
322
+ Context,
323
+ unknown,
324
+ unknown,
325
+ EffectErrorMap,
326
+ unknown,
327
+ Meta
328
+ > {
329
+ return (
330
+ typeof value === "function" &&
331
+ value.constructor?.name === "GeneratorFunction"
332
+ );
333
+ }
334
+
335
+ function makeEffectOptions<
336
+ TCurrentContext extends Context,
337
+ TInput,
338
+ TEffectErrorMap extends EffectErrorMap,
339
+ TMeta extends Meta,
340
+ >(
341
+ opts: Parameters<
342
+ Middleware<TCurrentContext, any, TInput, any, any, TMeta>
343
+ >[0],
344
+ input: TInput,
345
+ effectErrorMap: TEffectErrorMap,
346
+ ): ProcedureHandlerOptions<
347
+ TCurrentContext,
348
+ TInput,
349
+ EffectErrorConstructorMap<TEffectErrorMap>,
350
+ TMeta & Record<never, never>
351
+ > {
352
+ return {
353
+ context: opts.context,
354
+ input,
355
+ path: opts.path,
356
+ procedure: opts.procedure as any,
357
+ signal: opts.signal,
358
+ lastEventId: opts.lastEventId,
359
+ errors: createEffectErrorConstructorMap(effectErrorMap),
360
+ };
361
+ }
362
+
363
+ function runEffectPipeline<
364
+ TCurrentContext extends Context,
365
+ TInput,
366
+ TOutput,
367
+ TEffectErrorMap extends EffectErrorMap,
368
+ TRequirementsProvided,
369
+ TMeta extends Meta,
370
+ >(options: {
371
+ baseOptions: ProcedureHandlerOptions<
372
+ TCurrentContext,
373
+ TInput,
374
+ EffectErrorConstructorMap<TEffectErrorMap>,
375
+ TMeta
376
+ >;
377
+ effectErrorMap: TEffectErrorMap;
378
+ final: (
379
+ context: TCurrentContext,
380
+ ) => Effect.Effect<
381
+ EffectMiddlewareResult<Record<never, never>, TOutput>,
382
+ unknown,
383
+ TRequirementsProvided
384
+ >;
385
+ input: TInput;
386
+ steps: readonly EffectPipelineStep[];
387
+ }): Effect.Effect<
388
+ EffectMiddlewareResult<Context, TOutput>,
389
+ unknown,
390
+ TRequirementsProvided
391
+ > {
392
+ const run = (
393
+ index: number,
394
+ context: TCurrentContext,
395
+ ): Effect.Effect<
396
+ EffectMiddlewareResult<Context, TOutput>,
397
+ unknown,
398
+ TRequirementsProvided
399
+ > => {
400
+ const step = options.steps[index];
401
+ if (!step) return options.final(context);
402
+
403
+ const stepOptions = { ...options.baseOptions, context };
404
+
405
+ if (step._tag === "provide") {
406
+ return Effect.flatMap(step.provider(stepOptions as any), (service) =>
407
+ Effect.provideService(run(index + 1, context), step.tag, service),
408
+ );
409
+ }
410
+
411
+ if (step._tag === "provideOptional") {
412
+ return Effect.flatMap(step.provider(stepOptions as any), (service) =>
413
+ Option.match(service, {
414
+ onNone: () => run(index + 1, context),
415
+ onSome: (value) =>
416
+ Effect.provideService(run(index + 1, context), step.tag, value),
417
+ }),
418
+ );
419
+ }
420
+
421
+ if (step._tag === "provideLayer") {
422
+ return Effect.provide(run(index + 1, context), step.layer);
423
+ }
424
+
425
+ const nextTracker =
426
+ createMiddlewareNextTracker<EffectMiddlewareResult<Context, TOutput>>();
427
+ const effectOptions: EffectMiddlewareOptions<
428
+ TCurrentContext,
429
+ TOutput,
430
+ TEffectErrorMap,
431
+ TRequirementsProvided,
432
+ TMeta
433
+ > = {
434
+ context,
435
+ path: stepOptions.path,
436
+ procedure: stepOptions.procedure,
437
+ signal: stepOptions.signal,
438
+ lastEventId: stepOptions.lastEventId,
439
+ errors: createEffectErrorConstructorMap(options.effectErrorMap),
440
+ next: nextTracker.wrapNext(
441
+ (...rest: [MiddlewareNextFnOptions<Context>?]) => {
442
+ const nextContext = rest[0]?.context ?? {};
443
+ return Effect.map(
444
+ run(index + 1, { ...context, ...nextContext }),
445
+ (result) => ({
446
+ output: result.output,
447
+ context: nextContext,
448
+ }),
449
+ ) as Effect.Effect<EffectMiddlewareResult<Context, TOutput>>;
450
+ },
451
+ ),
452
+ };
453
+ const effectOutput = makeEffectMiddlewareOutput<
454
+ TOutput,
455
+ TEffectErrorMap,
456
+ TRequirementsProvided
457
+ >((output) => ({ output, context: {} }));
458
+ const middlewareEffect = Effect.fnUntraced(step.middleware)(
459
+ effectOptions,
460
+ options.input,
461
+ effectOutput,
462
+ ) as Effect.Effect<EffectMiddlewareResult<Context, TOutput> | void>;
463
+
464
+ return Effect.flatMap(middlewareEffect, (result) =>
465
+ resolveEffectMiddlewareContinuation({
466
+ autoNext: () => effectOptions.next(),
467
+ nextInvoked: nextTracker.nextInvoked,
468
+ nextResult: nextTracker.nextResult,
469
+ result,
470
+ }),
471
+ );
472
+ };
473
+
474
+ return run(0, options.baseOptions.context);
475
+ }
476
+
477
+ function createMiddlewareNextTracker<T>() {
478
+ let nextInvoked = false;
479
+ let nextResult: T | undefined;
480
+
481
+ return {
482
+ get nextInvoked() {
483
+ return nextInvoked;
484
+ },
485
+ get nextResult() {
486
+ return nextResult;
487
+ },
488
+ wrapNext<Fn extends (...args: any) => Effect.Effect<T, any, any>>(
489
+ nextFn: Fn,
490
+ ): Fn {
491
+ return ((...args: Parameters<Fn>) => {
492
+ nextInvoked = true;
493
+ return Effect.map(nextFn(...args), (result) => {
494
+ nextResult = result;
495
+ return result;
496
+ });
497
+ }) as Fn;
498
+ },
499
+ };
500
+ }
501
+
502
+ function resolveEffectMiddlewareContinuation<
503
+ TContext extends Context,
504
+ TOutput,
505
+ TRequirementsProvided,
506
+ >(options: {
507
+ result: EffectMiddlewareResult<TContext, TOutput> | void;
508
+ nextInvoked: boolean;
509
+ nextResult: EffectMiddlewareResult<TContext, TOutput> | undefined;
510
+ autoNext: () => Effect.Effect<
511
+ EffectMiddlewareResult<TContext, TOutput>,
512
+ unknown,
513
+ TRequirementsProvided
514
+ >;
515
+ }): Effect.Effect<
516
+ EffectMiddlewareResult<TContext, TOutput>,
517
+ unknown,
518
+ TRequirementsProvided
519
+ > {
520
+ const { result, nextInvoked, nextResult, autoNext } = options;
521
+
522
+ if (result !== undefined) {
523
+ return Effect.succeed(result);
524
+ }
525
+
526
+ if (nextInvoked) {
527
+ if (nextResult === undefined) {
528
+ return Effect.die(
529
+ new Error(
530
+ "Effect middleware invoked next() but did not return its result",
531
+ ),
532
+ );
533
+ }
534
+ return Effect.succeed(nextResult);
535
+ }
536
+
537
+ return autoNext();
538
+ }
539
+
540
+ function makeEffectMiddlewareOutput<
541
+ TOutput,
542
+ TEffectErrorMap extends EffectErrorMap,
543
+ TRequirementsProvided,
544
+ >(
545
+ output: MiddlewareOutputFn<TOutput>,
546
+ ): EffectMiddlewareOutput<TOutput, TEffectErrorMap, TRequirementsProvided> {
547
+ return (value: TOutput) => withCurrentFiberContext(() => output(value));
548
+ }
549
+
550
+ function withCurrentFiberContext<T>(fn: () => Promisable<T>): Effect.Effect<T> {
551
+ return Effect.flatMap(Effect.getFiberRefs, (fiberRefs) =>
552
+ Effect.promise(() =>
553
+ runWithFiberRefs(fiberRefs, () => Promise.resolve(fn())),
554
+ ),
555
+ );
556
+ }
@@ -43,6 +43,12 @@ interface NodeProxyInternalConfig<
43
43
  result: unknown,
44
44
  receiver: unknown,
45
45
  ) => unknown;
46
+ wrapArgs?: (
47
+ context: NodeProxyContext<TTarget, TSource>,
48
+ prop: PropertyKey,
49
+ args: unknown[],
50
+ receiver: unknown,
51
+ ) => unknown[];
46
52
  }
47
53
 
48
54
  /**
@@ -93,6 +99,15 @@ export interface NodeProxyConfig<
93
99
  result: unknown,
94
100
  receiver: unknown,
95
101
  ) => unknown;
102
+ /**
103
+ * Rewrites arguments before forwarding upstream methods.
104
+ */
105
+ wrapArgs?: (
106
+ context: NodeProxyContext<TTarget, TSource>,
107
+ prop: PropertyKey,
108
+ args: unknown[],
109
+ receiver: unknown,
110
+ ) => unknown[];
96
111
  }
97
112
 
98
113
  function createNodeProxyContext<
@@ -124,7 +139,8 @@ function createBoundMethod<
124
139
  }
125
140
 
126
141
  const wrapped = (...args: unknown[]) => {
127
- const result = Reflect.apply(value, context.upstream, args);
142
+ const nextArgs = config.wrapArgs?.(context, prop, args, receiver) ?? args;
143
+ const result = Reflect.apply(value, context.upstream, nextArgs);
128
144
  return config.wrapResult?.(context, prop, result, receiver) ?? result;
129
145
  };
130
146
 
@@ -1,7 +1,10 @@
1
- import type { ManagedRuntime } from "effect";
2
-
1
+ import type { EffectRuntimeRunner } from "../runtime-source";
3
2
  import type { EffectErrorMap } from "../tagged-error";
4
- import type { EffectSpanConfig } from "../types";
3
+ import type {
4
+ EffectPipelineStep,
5
+ EffectProcedureHandlerConfig,
6
+ EffectSpanConfig,
7
+ } from "../types";
5
8
 
6
9
  export interface EffectExtensionState<
7
10
  TRequirementsProvided = any,
@@ -13,15 +16,20 @@ export interface EffectExtensionState<
13
16
  */
14
17
  effectErrorMap: EffectErrorMap;
15
18
  /**
16
- * The Effect ManagedRuntime that provides services for Effect procedures.
17
- * @see {@link ManagedRuntime.ManagedRuntime}
19
+ * Executes Effect values with the services configured for this builder.
18
20
  */
19
- runtime: ManagedRuntime.ManagedRuntime<TRequirementsProvided, TRuntimeError>;
21
+ runner: EffectRuntimeRunner<TRequirementsProvided, TRuntimeError>;
20
22
  /**
21
23
  * Configuration for Effect span tracing.
22
24
  * @see {@link EffectSpanConfig}
23
25
  */
24
26
  spanConfig?: EffectSpanConfig;
27
+ /**
28
+ * Pending Effect-native provider / middleware steps that have not been flushed
29
+ * into the oRPC middleware chain.
30
+ */
31
+ effectSteps?: readonly EffectPipelineStep[];
32
+ effectHandler?: EffectProcedureHandlerConfig;
25
33
  }
26
34
 
27
35
  export interface EffectInternals<TUpstream extends object = object> {
@@ -62,25 +70,13 @@ export function getEffectInternals<TUpstream extends object>(
62
70
  return target[effectInternalsSymbol];
63
71
  }
64
72
 
65
- export function getEffectUpstream<TUpstream extends object>(
73
+ function getEffectUpstream<TUpstream extends object>(
66
74
  target: EffectProxyTarget<TUpstream>,
67
75
  ): TUpstream {
68
76
  return getEffectInternals(target).upstream;
69
77
  }
70
78
 
71
- export function getEffectState(
72
- target: EffectProxyTarget,
73
- ): EffectExtensionState {
74
- return getEffectInternals(target).state;
75
- }
76
-
77
- export function getEffectMethodCache(
78
- target: EffectProxyTarget,
79
- ): Map<PropertyKey, unknown> {
80
- return getEffectInternals(target).methodCache;
81
- }
82
-
83
- export function hasEffectState(value: unknown): value is EffectProxyTarget {
79
+ function hasEffectState(value: unknown): value is EffectProxyTarget {
84
80
  return (
85
81
  typeof value === "object" &&
86
82
  value !== null &&
@@ -2,6 +2,10 @@ import type { FiberRefs } from "effect";
2
2
 
3
3
  export interface FiberContextBridge {
4
4
  readonly getCurrentFiberRefs: () => FiberRefs.FiberRefs | undefined;
5
+ readonly runWithFiberRefs?: <T>(
6
+ fiberRefs: FiberRefs.FiberRefs,
7
+ fn: () => Promise<T>,
8
+ ) => Promise<T>;
5
9
  }
6
10
 
7
11
  let bridge: FiberContextBridge | undefined;
@@ -15,3 +19,12 @@ export function installFiberContextBridge(
15
19
  export function getCurrentFiberRefs(): FiberRefs.FiberRefs | undefined {
16
20
  return bridge?.getCurrentFiberRefs();
17
21
  }
22
+
23
+ export function runWithFiberRefs<T>(
24
+ fiberRefs: FiberRefs.FiberRefs,
25
+ fn: () => Promise<T>,
26
+ ): Promise<T> {
27
+ return bridge?.runWithFiberRefs
28
+ ? bridge.runWithFiberRefs(fiberRefs, fn)
29
+ : fn();
30
+ }
package/src/index.ts CHANGED
@@ -16,6 +16,7 @@ export type {
16
16
  export {
17
17
  addSpanStackTrace,
18
18
  EffectBuilder,
19
+ eos,
19
20
  makeEffectORPC,
20
21
  } from "./effect-builder";
21
22
  export { EffectDecoratedProcedure } from "./effect-procedure";
package/src/node.ts CHANGED
@@ -12,12 +12,13 @@ const fiberRefsStorage = new AsyncLocalStorage<FiberRefs.FiberRefs>();
12
12
 
13
13
  const bridge: FiberContextBridge = {
14
14
  getCurrentFiberRefs: () => fiberRefsStorage.getStore(),
15
+ runWithFiberRefs: (fiberRefs, fn) => fiberRefsStorage.run(fiberRefs, fn),
15
16
  };
16
17
 
17
18
  installFiberContextBridge(bridge);
18
19
 
19
20
  export function withFiberContext<T>(fn: () => Promise<T>): Effect.Effect<T> {
20
21
  return Effect.flatMap(Effect.getFiberRefs, (fiberRefs) =>
21
- Effect.promise(() => fiberRefsStorage.run(fiberRefs, fn)),
22
+ Effect.promise(() => bridge.runWithFiberRefs!(fiberRefs, fn)),
22
23
  );
23
24
  }