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