effect-orpc 0.3.0 → 0.5.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/README.md +133 -146
- package/dist/index.js +421 -133
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/contract.ts +15 -12
- package/src/effect-builder.ts +63 -40
- package/src/effect-enhance-router.ts +3 -3
- package/src/effect-procedure.ts +38 -14
- package/src/effect-runtime.ts +647 -115
- package/src/extension/state.ts +3 -5
- package/src/index.ts +1 -0
- package/src/runtime-source.ts +70 -12
- package/src/tagged-error.ts +1 -1
- package/src/tests/contract.test.ts +5 -8
- package/src/tests/effect-builder.proxy.test.ts +15 -17
- package/src/tests/effect-builder.test.ts +352 -161
- package/src/tests/effect-callback-shapes.test.ts +410 -0
- package/src/tests/effect-error-map.test.ts +12 -14
- package/src/tests/effect-procedure.test.ts +53 -11
- package/src/tests/parity-shared.ts +2 -2
- package/src/types/effect-builder-surface.ts +1 -1
- package/src/types/index.ts +76 -51
- package/src/types/variants.ts +5 -5
package/src/effect-runtime.ts
CHANGED
|
@@ -3,16 +3,17 @@ import type {
|
|
|
3
3
|
Context,
|
|
4
4
|
Middleware,
|
|
5
5
|
MiddlewareNextFnOptions,
|
|
6
|
+
MiddlewareOptions,
|
|
6
7
|
MiddlewareOutputFn,
|
|
7
8
|
MiddlewareResult,
|
|
8
9
|
ProcedureHandler,
|
|
9
10
|
ProcedureHandlerOptions,
|
|
10
11
|
} from "@orpc/server";
|
|
11
12
|
import type { Promisable } from "@orpc/shared";
|
|
12
|
-
import type { ManagedRuntime } from "effect";
|
|
13
13
|
import { Cause, Effect, Exit, FiberRefs, Option } from "effect";
|
|
14
14
|
|
|
15
|
-
import {
|
|
15
|
+
import { runWithFiberRefs } from "./fiber-context-bridge";
|
|
16
|
+
import type { EffectRuntimeRunner } from "./runtime-source";
|
|
16
17
|
import type { EffectErrorConstructorMap, EffectErrorMap } from "./tagged-error";
|
|
17
18
|
import {
|
|
18
19
|
createEffectErrorConstructorMap,
|
|
@@ -32,6 +33,35 @@ import type {
|
|
|
32
33
|
|
|
33
34
|
type EffectTag = import("effect").Context.Tag<any, any>;
|
|
34
35
|
|
|
36
|
+
const HybridContinuationSymbol = Symbol("effect-orpc/HybridContinuation");
|
|
37
|
+
|
|
38
|
+
type MiddlewareNextTracker<T> = ReturnType<
|
|
39
|
+
typeof createMiddlewareNextTracker<T>
|
|
40
|
+
>;
|
|
41
|
+
|
|
42
|
+
type EffectMiddlewareRuntimeOptions<
|
|
43
|
+
TCurrentContext extends Context,
|
|
44
|
+
TOutput,
|
|
45
|
+
TEffectErrorMap extends EffectErrorMap,
|
|
46
|
+
TRequirementsProvided,
|
|
47
|
+
TMeta extends Meta,
|
|
48
|
+
> = EffectMiddlewareOptions<
|
|
49
|
+
TCurrentContext,
|
|
50
|
+
TOutput,
|
|
51
|
+
TEffectErrorMap,
|
|
52
|
+
TRequirementsProvided,
|
|
53
|
+
TMeta
|
|
54
|
+
> & {
|
|
55
|
+
readonly nextTracker: MiddlewareNextTracker<
|
|
56
|
+
EffectMiddlewareResult<Context, TOutput>
|
|
57
|
+
>;
|
|
58
|
+
readonly output: EffectMiddlewareOutput<
|
|
59
|
+
TOutput,
|
|
60
|
+
TEffectErrorMap,
|
|
61
|
+
TRequirementsProvided
|
|
62
|
+
>;
|
|
63
|
+
};
|
|
64
|
+
|
|
35
65
|
function toORPCErrorFromCause(
|
|
36
66
|
cause: Cause.Cause<unknown>,
|
|
37
67
|
): ORPCError<string, unknown> {
|
|
@@ -78,7 +108,7 @@ export function createEffectProcedureHandler<
|
|
|
78
108
|
TRuntimeError,
|
|
79
109
|
TMeta extends Meta,
|
|
80
110
|
>(options: {
|
|
81
|
-
|
|
111
|
+
runner: EffectRuntimeRunner<TRequirementsProvided, TRuntimeError>;
|
|
82
112
|
effectErrorMap: TEffectErrorMap;
|
|
83
113
|
effectFn: EffectProcedureHandler<
|
|
84
114
|
TCurrentContext,
|
|
@@ -99,7 +129,7 @@ export function createEffectProcedureHandler<
|
|
|
99
129
|
TMeta & Record<never, never>
|
|
100
130
|
> {
|
|
101
131
|
const {
|
|
102
|
-
|
|
132
|
+
runner,
|
|
103
133
|
effectErrorMap,
|
|
104
134
|
effectFn,
|
|
105
135
|
effectSteps = [],
|
|
@@ -126,8 +156,7 @@ export function createEffectProcedureHandler<
|
|
|
126
156
|
const spanName = spanConfig?.name ?? opts.path.join(".");
|
|
127
157
|
const captureStackTrace =
|
|
128
158
|
spanConfig?.captureStackTrace ?? defaultCaptureStackTrace;
|
|
129
|
-
const
|
|
130
|
-
const handlerEffect = resolver(effectOpts);
|
|
159
|
+
const handlerEffect = callEffectCallback(effectFn, effectOpts);
|
|
131
160
|
const tracedEffect = Effect.withSpan(
|
|
132
161
|
runEffectPipeline({
|
|
133
162
|
baseOptions: effectOpts,
|
|
@@ -136,19 +165,19 @@ export function createEffectProcedureHandler<
|
|
|
136
165
|
Effect.map(
|
|
137
166
|
context === effectOpts.context
|
|
138
167
|
? handlerEffect
|
|
139
|
-
:
|
|
168
|
+
: callEffectCallback(effectFn, { ...effectOpts, context }),
|
|
140
169
|
(output) => ({ output, context: {} }),
|
|
141
170
|
),
|
|
142
171
|
input: opts.input,
|
|
172
|
+
runner,
|
|
143
173
|
steps: effectSteps,
|
|
144
174
|
}),
|
|
145
175
|
spanName,
|
|
146
176
|
{ captureStackTrace },
|
|
147
177
|
);
|
|
148
|
-
const exit = await
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
);
|
|
178
|
+
const exit = await runner.runPromiseExit(tracedEffect, {
|
|
179
|
+
signal: opts.signal,
|
|
180
|
+
});
|
|
152
181
|
|
|
153
182
|
if (Exit.isFailure(exit)) {
|
|
154
183
|
throw toORPCErrorFromCause(exit.cause);
|
|
@@ -166,7 +195,7 @@ export function createEffectPipelineMiddleware<
|
|
|
166
195
|
TRuntimeError,
|
|
167
196
|
TMeta extends Meta,
|
|
168
197
|
>(options: {
|
|
169
|
-
|
|
198
|
+
runner: EffectRuntimeRunner<TRequirementsProvided, TRuntimeError>;
|
|
170
199
|
effectErrorMap: TEffectErrorMap;
|
|
171
200
|
steps: readonly EffectPipelineStep[];
|
|
172
201
|
}): Middleware<
|
|
@@ -177,7 +206,7 @@ export function createEffectPipelineMiddleware<
|
|
|
177
206
|
any,
|
|
178
207
|
TMeta
|
|
179
208
|
> {
|
|
180
|
-
const {
|
|
209
|
+
const { runner, effectErrorMap, steps } = options;
|
|
181
210
|
|
|
182
211
|
return async (opts, input) => {
|
|
183
212
|
const baseOptions = makeEffectOptions<
|
|
@@ -186,7 +215,14 @@ export function createEffectPipelineMiddleware<
|
|
|
186
215
|
TEffectErrorMap,
|
|
187
216
|
TMeta
|
|
188
217
|
>(opts, input, effectErrorMap);
|
|
189
|
-
const effect = runEffectPipeline
|
|
218
|
+
const effect = runEffectPipeline<
|
|
219
|
+
TCurrentContext,
|
|
220
|
+
unknown,
|
|
221
|
+
TOutput,
|
|
222
|
+
TEffectErrorMap,
|
|
223
|
+
TRequirementsProvided,
|
|
224
|
+
TMeta
|
|
225
|
+
>({
|
|
190
226
|
baseOptions,
|
|
191
227
|
effectErrorMap,
|
|
192
228
|
final: (context) =>
|
|
@@ -198,12 +234,12 @@ export function createEffectPipelineMiddleware<
|
|
|
198
234
|
),
|
|
199
235
|
) as any,
|
|
200
236
|
input,
|
|
237
|
+
runner,
|
|
201
238
|
steps,
|
|
202
239
|
});
|
|
203
|
-
const exit = await
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
);
|
|
240
|
+
const exit = await runner.runPromiseExit(effect, {
|
|
241
|
+
signal: opts.signal,
|
|
242
|
+
});
|
|
207
243
|
|
|
208
244
|
if (Exit.isFailure(exit)) throw toORPCErrorFromCause(exit.cause);
|
|
209
245
|
|
|
@@ -211,6 +247,341 @@ export function createEffectPipelineMiddleware<
|
|
|
211
247
|
};
|
|
212
248
|
}
|
|
213
249
|
|
|
250
|
+
export function createEffectOrORPCMiddleware<
|
|
251
|
+
TCurrentContext extends Context,
|
|
252
|
+
TOutContext extends Context,
|
|
253
|
+
TInput,
|
|
254
|
+
TOutput,
|
|
255
|
+
TEffectErrorMap extends EffectErrorMap,
|
|
256
|
+
TRequirementsProvided,
|
|
257
|
+
TRuntimeError,
|
|
258
|
+
TMeta extends Meta,
|
|
259
|
+
>(options: {
|
|
260
|
+
runner: EffectRuntimeRunner<TRequirementsProvided, TRuntimeError>;
|
|
261
|
+
effectErrorMap: TEffectErrorMap;
|
|
262
|
+
middleware: EffectMiddleware<
|
|
263
|
+
TCurrentContext,
|
|
264
|
+
TOutContext,
|
|
265
|
+
TInput,
|
|
266
|
+
TOutput,
|
|
267
|
+
TEffectErrorMap,
|
|
268
|
+
TRequirementsProvided,
|
|
269
|
+
TMeta
|
|
270
|
+
>;
|
|
271
|
+
}): Middleware<TCurrentContext, TOutContext, TInput, TOutput, any, TMeta> {
|
|
272
|
+
const { runner, effectErrorMap, middleware } = options;
|
|
273
|
+
|
|
274
|
+
return async (opts, input, output) => {
|
|
275
|
+
const effectOptions = makeEffectMiddlewareOptions<
|
|
276
|
+
TCurrentContext,
|
|
277
|
+
TOutput,
|
|
278
|
+
TEffectErrorMap,
|
|
279
|
+
TRequirementsProvided,
|
|
280
|
+
TMeta
|
|
281
|
+
>({
|
|
282
|
+
effectErrorMap,
|
|
283
|
+
final: opts.next,
|
|
284
|
+
options: opts,
|
|
285
|
+
output,
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
return runEffectOrORPCMiddlewareCallback<
|
|
289
|
+
TCurrentContext,
|
|
290
|
+
TOutContext,
|
|
291
|
+
TInput,
|
|
292
|
+
TOutput,
|
|
293
|
+
TEffectErrorMap,
|
|
294
|
+
TRequirementsProvided,
|
|
295
|
+
TMeta
|
|
296
|
+
>({
|
|
297
|
+
effectOptions,
|
|
298
|
+
input,
|
|
299
|
+
middleware,
|
|
300
|
+
nativeNext: opts.next,
|
|
301
|
+
runner,
|
|
302
|
+
signal: opts.signal,
|
|
303
|
+
});
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
async function runEffectOrORPCMiddlewareCallback<
|
|
308
|
+
TCurrentContext extends Context,
|
|
309
|
+
TOutContext extends Context,
|
|
310
|
+
TInput,
|
|
311
|
+
TOutput,
|
|
312
|
+
TEffectErrorMap extends EffectErrorMap,
|
|
313
|
+
TRequirementsProvided,
|
|
314
|
+
TMeta extends Meta,
|
|
315
|
+
>(options: {
|
|
316
|
+
effectOptions: EffectMiddlewareRuntimeOptions<
|
|
317
|
+
TCurrentContext,
|
|
318
|
+
TOutput,
|
|
319
|
+
TEffectErrorMap,
|
|
320
|
+
TRequirementsProvided,
|
|
321
|
+
TMeta
|
|
322
|
+
>;
|
|
323
|
+
input: TInput;
|
|
324
|
+
middleware: EffectMiddleware<
|
|
325
|
+
TCurrentContext,
|
|
326
|
+
TOutContext,
|
|
327
|
+
TInput,
|
|
328
|
+
TOutput,
|
|
329
|
+
TEffectErrorMap,
|
|
330
|
+
TRequirementsProvided,
|
|
331
|
+
TMeta
|
|
332
|
+
>;
|
|
333
|
+
nativeNext: () => Promisable<MiddlewareResult<TOutContext, TOutput>>;
|
|
334
|
+
runner: EffectRuntimeRunner<TRequirementsProvided, unknown>;
|
|
335
|
+
signal: AbortSignal | undefined;
|
|
336
|
+
}): Promise<MiddlewareResult<TOutContext, TOutput>> {
|
|
337
|
+
const result = options.middleware(
|
|
338
|
+
options.effectOptions,
|
|
339
|
+
options.input,
|
|
340
|
+
options.effectOptions.output,
|
|
341
|
+
);
|
|
342
|
+
|
|
343
|
+
const classified = classifyEffectOrORPCMiddlewareResult<
|
|
344
|
+
TOutContext,
|
|
345
|
+
TOutput,
|
|
346
|
+
TRequirementsProvided
|
|
347
|
+
>(result);
|
|
348
|
+
|
|
349
|
+
switch (classified._tag) {
|
|
350
|
+
case "nativeContinuation":
|
|
351
|
+
return classified.result;
|
|
352
|
+
case "nativeGuardOnly":
|
|
353
|
+
return options.nativeNext();
|
|
354
|
+
case "nativeResult":
|
|
355
|
+
return classified.result;
|
|
356
|
+
case "effect": {
|
|
357
|
+
const exit = await options.runner.runPromiseExit(
|
|
358
|
+
runEffectMiddlewareResult({
|
|
359
|
+
autoNext: () => options.effectOptions.next(),
|
|
360
|
+
effect: classified.effect,
|
|
361
|
+
nextInvoked: options.effectOptions.nextTracker.nextInvoked,
|
|
362
|
+
nextResult: options.effectOptions.nextTracker.nextResult,
|
|
363
|
+
}),
|
|
364
|
+
{ signal: options.signal },
|
|
365
|
+
);
|
|
366
|
+
|
|
367
|
+
if (Exit.isFailure(exit)) throw toORPCErrorFromCause(exit.cause);
|
|
368
|
+
|
|
369
|
+
return exit.value as MiddlewareResult<TOutContext, TOutput>;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
type ClassifiedEffectOrORPCMiddlewareResult<
|
|
375
|
+
TOutContext extends Context,
|
|
376
|
+
TOutput,
|
|
377
|
+
TRequirementsProvided,
|
|
378
|
+
> =
|
|
379
|
+
| {
|
|
380
|
+
readonly _tag: "nativeContinuation";
|
|
381
|
+
readonly result: MiddlewareResult<TOutContext, TOutput>;
|
|
382
|
+
}
|
|
383
|
+
| { readonly _tag: "nativeGuardOnly" }
|
|
384
|
+
| {
|
|
385
|
+
readonly _tag: "nativeResult";
|
|
386
|
+
readonly result: MiddlewareResult<TOutContext, TOutput>;
|
|
387
|
+
}
|
|
388
|
+
| {
|
|
389
|
+
readonly _tag: "effect";
|
|
390
|
+
readonly effect: Effect.Effect<
|
|
391
|
+
EffectMiddlewareResult<TOutContext, TOutput> | void,
|
|
392
|
+
unknown,
|
|
393
|
+
TRequirementsProvided
|
|
394
|
+
>;
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
function classifyEffectOrORPCMiddlewareResult<
|
|
398
|
+
TOutContext extends Context,
|
|
399
|
+
TOutput,
|
|
400
|
+
TRequirementsProvided,
|
|
401
|
+
>(
|
|
402
|
+
result: unknown,
|
|
403
|
+
): ClassifiedEffectOrORPCMiddlewareResult<
|
|
404
|
+
TOutContext,
|
|
405
|
+
TOutput,
|
|
406
|
+
TRequirementsProvided
|
|
407
|
+
> {
|
|
408
|
+
if (isHybridContinuation(result)) {
|
|
409
|
+
return {
|
|
410
|
+
_tag: "nativeContinuation",
|
|
411
|
+
result: result as MiddlewareResult<TOutContext, TOutput>,
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (Effect.isEffect(result) || isGeneratorIterator(result)) {
|
|
416
|
+
return {
|
|
417
|
+
_tag: "effect",
|
|
418
|
+
effect: effectFromCallbackResult(result),
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
if (result === undefined) {
|
|
423
|
+
return { _tag: "nativeGuardOnly" };
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
return {
|
|
427
|
+
_tag: "nativeResult",
|
|
428
|
+
result: result as MiddlewareResult<TOutContext, TOutput>,
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function runEffectMiddlewareResult<
|
|
433
|
+
TContext extends Context,
|
|
434
|
+
TOutput,
|
|
435
|
+
TRequirementsProvided,
|
|
436
|
+
>(options: {
|
|
437
|
+
effect: Effect.Effect<
|
|
438
|
+
EffectMiddlewareResult<TContext, TOutput> | void,
|
|
439
|
+
unknown,
|
|
440
|
+
TRequirementsProvided
|
|
441
|
+
>;
|
|
442
|
+
nextInvoked: boolean;
|
|
443
|
+
nextResult: EffectMiddlewareResult<TContext, TOutput> | undefined;
|
|
444
|
+
autoNext: () => Effect.Effect<
|
|
445
|
+
EffectMiddlewareResult<TContext, TOutput>,
|
|
446
|
+
unknown,
|
|
447
|
+
TRequirementsProvided
|
|
448
|
+
>;
|
|
449
|
+
}): Effect.Effect<
|
|
450
|
+
EffectMiddlewareResult<TContext, TOutput>,
|
|
451
|
+
unknown,
|
|
452
|
+
TRequirementsProvided
|
|
453
|
+
> {
|
|
454
|
+
return Effect.flatMap(options.effect, (result) =>
|
|
455
|
+
resolveEffectMiddlewareContinuation({
|
|
456
|
+
autoNext: options.autoNext,
|
|
457
|
+
nextInvoked: options.nextInvoked,
|
|
458
|
+
nextResult: options.nextResult,
|
|
459
|
+
result,
|
|
460
|
+
}),
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
function makeEffectMiddlewareOptions<
|
|
465
|
+
TCurrentContext extends Context,
|
|
466
|
+
TOutput,
|
|
467
|
+
TEffectErrorMap extends EffectErrorMap,
|
|
468
|
+
TRequirementsProvided,
|
|
469
|
+
TMeta extends Meta,
|
|
470
|
+
>(options: {
|
|
471
|
+
effectErrorMap: TEffectErrorMap;
|
|
472
|
+
final: (
|
|
473
|
+
...rest: [MiddlewareNextFnOptions<Context>?]
|
|
474
|
+
) => Promisable<MiddlewareResult<Context, TOutput>>;
|
|
475
|
+
options: Omit<
|
|
476
|
+
MiddlewareOptions<
|
|
477
|
+
TCurrentContext,
|
|
478
|
+
TOutput,
|
|
479
|
+
EffectErrorConstructorMap<TEffectErrorMap>,
|
|
480
|
+
TMeta
|
|
481
|
+
>,
|
|
482
|
+
"next"
|
|
483
|
+
>;
|
|
484
|
+
output: MiddlewareOutputFn<TOutput>;
|
|
485
|
+
}): EffectMiddlewareRuntimeOptions<
|
|
486
|
+
TCurrentContext,
|
|
487
|
+
TOutput,
|
|
488
|
+
TEffectErrorMap,
|
|
489
|
+
TRequirementsProvided,
|
|
490
|
+
TMeta
|
|
491
|
+
> {
|
|
492
|
+
const nextTracker =
|
|
493
|
+
createMiddlewareNextTracker<EffectMiddlewareResult<Context, TOutput>>();
|
|
494
|
+
|
|
495
|
+
const effectOptions = {
|
|
496
|
+
context: options.options.context,
|
|
497
|
+
path: options.options.path,
|
|
498
|
+
procedure: options.options.procedure,
|
|
499
|
+
signal: options.options.signal,
|
|
500
|
+
lastEventId: options.options.lastEventId,
|
|
501
|
+
errors: createEffectErrorConstructorMap(options.effectErrorMap),
|
|
502
|
+
next: nextTracker.wrapNext((...rest: [MiddlewareNextFnOptions<Context>?]) =>
|
|
503
|
+
makeHybridMiddlewareContinuation({
|
|
504
|
+
final: options.final,
|
|
505
|
+
nextTracker,
|
|
506
|
+
rest,
|
|
507
|
+
}),
|
|
508
|
+
),
|
|
509
|
+
nextTracker,
|
|
510
|
+
output: makeEffectMiddlewareOutput<
|
|
511
|
+
TOutput,
|
|
512
|
+
TEffectErrorMap,
|
|
513
|
+
TRequirementsProvided
|
|
514
|
+
>(options.output),
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
return effectOptions;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
function makeHybridMiddlewareContinuation<TOutput>(options: {
|
|
521
|
+
final: (
|
|
522
|
+
...rest: [MiddlewareNextFnOptions<Context>?]
|
|
523
|
+
) => Promisable<MiddlewareResult<Context, TOutput>>;
|
|
524
|
+
nextTracker: MiddlewareNextTracker<EffectMiddlewareResult<Context, TOutput>>;
|
|
525
|
+
rest: [MiddlewareNextFnOptions<Context>?];
|
|
526
|
+
}): Effect.Effect<EffectMiddlewareResult<Context, TOutput>> {
|
|
527
|
+
const nextContext = options.rest[0]?.context ?? {};
|
|
528
|
+
const toEffectResult = (
|
|
529
|
+
result: any,
|
|
530
|
+
): EffectMiddlewareResult<Context, TOutput> => ({
|
|
531
|
+
output: result.output,
|
|
532
|
+
context: nextContext,
|
|
533
|
+
});
|
|
534
|
+
const effect = Effect.map(
|
|
535
|
+
withCurrentFiberContext(() => options.final(...options.rest) as any),
|
|
536
|
+
toEffectResult,
|
|
537
|
+
) as Effect.Effect<EffectMiddlewareResult<Context, TOutput>>;
|
|
538
|
+
|
|
539
|
+
return makeHybridContinuation(effect, {
|
|
540
|
+
onResult: options.nextTracker.setNextResult,
|
|
541
|
+
run: () => options.final(...options.rest) as any,
|
|
542
|
+
toEffectResult,
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
function makeHybridContinuation<TORPCResult, TEffectResult>(
|
|
547
|
+
effect: Effect.Effect<TEffectResult>,
|
|
548
|
+
options: {
|
|
549
|
+
run: () => Promisable<TORPCResult>;
|
|
550
|
+
toEffectResult: (result: TORPCResult) => TEffectResult;
|
|
551
|
+
onResult?: (result: TEffectResult) => void;
|
|
552
|
+
},
|
|
553
|
+
): Effect.Effect<TEffectResult> {
|
|
554
|
+
markHybridContinuation(effect);
|
|
555
|
+
attachPromiseLikeContinuation(effect, (onFulfilled, onRejected) =>
|
|
556
|
+
Promise.resolve(options.run() as any)
|
|
557
|
+
.then(options.toEffectResult)
|
|
558
|
+
.then((result) => {
|
|
559
|
+
options.onResult?.(result);
|
|
560
|
+
return result;
|
|
561
|
+
})
|
|
562
|
+
.then(onFulfilled, onRejected),
|
|
563
|
+
);
|
|
564
|
+
return effect;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
function markHybridContinuation(value: object): void {
|
|
568
|
+
Object.defineProperty(value, HybridContinuationSymbol, {
|
|
569
|
+
configurable: true,
|
|
570
|
+
value: true,
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
function attachPromiseLikeContinuation(
|
|
575
|
+
value: object,
|
|
576
|
+
then: (onFulfilled: any, onRejected: any) => PromiseLike<unknown>,
|
|
577
|
+
): void {
|
|
578
|
+
// oxlint-disable-next-line unicorn/no-thenable -- the hybrid continuation intentionally satisfies Effect and oRPC promise protocols.
|
|
579
|
+
Object.defineProperty(value, "then", {
|
|
580
|
+
configurable: true,
|
|
581
|
+
value: then,
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
|
|
214
585
|
export function createEffectProviderMiddleware<
|
|
215
586
|
TCurrentContext extends Context,
|
|
216
587
|
TInput,
|
|
@@ -220,7 +591,7 @@ export function createEffectProviderMiddleware<
|
|
|
220
591
|
TMeta extends Meta,
|
|
221
592
|
TTag extends EffectTag,
|
|
222
593
|
>(options: {
|
|
223
|
-
|
|
594
|
+
runner: EffectRuntimeRunner<TRequirementsProvided, TRuntimeError>;
|
|
224
595
|
effectErrorMap: TEffectErrorMap;
|
|
225
596
|
tag: TTag;
|
|
226
597
|
provider: EffectProvider<
|
|
@@ -232,7 +603,7 @@ export function createEffectProviderMiddleware<
|
|
|
232
603
|
TTag
|
|
233
604
|
>;
|
|
234
605
|
}): Middleware<TCurrentContext, Record<never, never>, TInput, any, any, TMeta> {
|
|
235
|
-
const {
|
|
606
|
+
const { runner, effectErrorMap, tag, provider } = options;
|
|
236
607
|
|
|
237
608
|
return async (opts, input) => {
|
|
238
609
|
const effectOpts = makeEffectOptions<
|
|
@@ -241,14 +612,16 @@ export function createEffectProviderMiddleware<
|
|
|
241
612
|
TEffectErrorMap,
|
|
242
613
|
TMeta
|
|
243
614
|
>(opts, input, effectErrorMap);
|
|
244
|
-
const effect = Effect.flatMap(
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
615
|
+
const effect = Effect.flatMap(
|
|
616
|
+
callEffectCallback(provider, effectOpts),
|
|
617
|
+
(service) =>
|
|
618
|
+
Effect.provideService(
|
|
619
|
+
withCurrentFiberContext(() => opts.next()),
|
|
620
|
+
tag,
|
|
621
|
+
service,
|
|
622
|
+
),
|
|
250
623
|
);
|
|
251
|
-
const exit = await
|
|
624
|
+
const exit = await runner.runPromiseExit(effect, {
|
|
252
625
|
signal: opts.signal,
|
|
253
626
|
});
|
|
254
627
|
|
|
@@ -267,7 +640,7 @@ export function createEffectOptionalProviderMiddleware<
|
|
|
267
640
|
TMeta extends Meta,
|
|
268
641
|
TTag extends EffectTag,
|
|
269
642
|
>(options: {
|
|
270
|
-
|
|
643
|
+
runner: EffectRuntimeRunner<TRequirementsProvided, TRuntimeError>;
|
|
271
644
|
effectErrorMap: TEffectErrorMap;
|
|
272
645
|
tag: TTag;
|
|
273
646
|
provider: EffectOptionalProvider<
|
|
@@ -279,7 +652,7 @@ export function createEffectOptionalProviderMiddleware<
|
|
|
279
652
|
TTag
|
|
280
653
|
>;
|
|
281
654
|
}): Middleware<TCurrentContext, Record<never, never>, TInput, any, any, TMeta> {
|
|
282
|
-
const {
|
|
655
|
+
const { runner, effectErrorMap, tag, provider } = options;
|
|
283
656
|
|
|
284
657
|
return async (opts, input) => {
|
|
285
658
|
const effectOpts = makeEffectOptions<
|
|
@@ -288,18 +661,20 @@ export function createEffectOptionalProviderMiddleware<
|
|
|
288
661
|
TEffectErrorMap,
|
|
289
662
|
TMeta
|
|
290
663
|
>(opts, input, effectErrorMap);
|
|
291
|
-
const effect = Effect.flatMap(
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
664
|
+
const effect = Effect.flatMap(
|
|
665
|
+
callEffectCallback(provider, effectOpts),
|
|
666
|
+
(service) =>
|
|
667
|
+
Option.match(service, {
|
|
668
|
+
onNone: () => withCurrentFiberContext(() => opts.next()),
|
|
669
|
+
onSome: (value) =>
|
|
670
|
+
Effect.provideService(
|
|
671
|
+
withCurrentFiberContext(() => opts.next()),
|
|
672
|
+
tag,
|
|
673
|
+
value,
|
|
674
|
+
),
|
|
675
|
+
}),
|
|
301
676
|
);
|
|
302
|
-
const exit = await
|
|
677
|
+
const exit = await runner.runPromiseExit(effect, {
|
|
303
678
|
signal: opts.signal,
|
|
304
679
|
});
|
|
305
680
|
|
|
@@ -309,6 +684,80 @@ export function createEffectOptionalProviderMiddleware<
|
|
|
309
684
|
};
|
|
310
685
|
}
|
|
311
686
|
|
|
687
|
+
function callEffectCallback(
|
|
688
|
+
callback: (...args: Array<any>) => unknown,
|
|
689
|
+
...args: Array<unknown>
|
|
690
|
+
): Effect.Effect<any, any, any> {
|
|
691
|
+
if (isGeneratorFunction(callback)) {
|
|
692
|
+
return Effect.fnUntraced(callback as any)(...args);
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
return Effect.suspend(() => {
|
|
696
|
+
try {
|
|
697
|
+
return effectFromCallbackResult(callback(...args));
|
|
698
|
+
} catch (error) {
|
|
699
|
+
return Effect.fail(error);
|
|
700
|
+
}
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
function effectFromCallbackResult(
|
|
705
|
+
result: unknown,
|
|
706
|
+
): Effect.Effect<any, any, any> {
|
|
707
|
+
if (Effect.isEffect(result)) {
|
|
708
|
+
return result;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
if (isGeneratorIterator(result)) {
|
|
712
|
+
return Effect.fnUntraced(function* () {
|
|
713
|
+
return yield* result as any;
|
|
714
|
+
})();
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
if (isPromiseLike(result)) {
|
|
718
|
+
return Effect.promise(() => result);
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
return Effect.succeed(result);
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
function isGeneratorIterator(value: unknown): value is Generator<unknown> {
|
|
725
|
+
return (
|
|
726
|
+
typeof value === "object" &&
|
|
727
|
+
value !== null &&
|
|
728
|
+
"next" in value &&
|
|
729
|
+
typeof value.next === "function" &&
|
|
730
|
+
"throw" in value &&
|
|
731
|
+
typeof value.throw === "function"
|
|
732
|
+
);
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
function isHybridContinuation(value: unknown): boolean {
|
|
736
|
+
return (
|
|
737
|
+
(typeof value === "object" || typeof value === "function") &&
|
|
738
|
+
value !== null &&
|
|
739
|
+
HybridContinuationSymbol in value
|
|
740
|
+
);
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
function isPromiseLike(value: unknown): value is PromiseLike<unknown> {
|
|
744
|
+
return (
|
|
745
|
+
(typeof value === "object" || typeof value === "function") &&
|
|
746
|
+
value !== null &&
|
|
747
|
+
"then" in value &&
|
|
748
|
+
typeof value.then === "function"
|
|
749
|
+
);
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
function isGeneratorFunction(
|
|
753
|
+
value: unknown,
|
|
754
|
+
): value is (...args: Array<any>) => Generator<unknown> {
|
|
755
|
+
return (
|
|
756
|
+
typeof value === "function" &&
|
|
757
|
+
value.constructor?.name === "GeneratorFunction"
|
|
758
|
+
);
|
|
759
|
+
}
|
|
760
|
+
|
|
312
761
|
// todo(utopy): make this check more comprehensive, maybe add a Symbol to the EffectMiddleware type
|
|
313
762
|
export function isEffectMiddleware(
|
|
314
763
|
value: unknown,
|
|
@@ -321,9 +770,12 @@ export function isEffectMiddleware(
|
|
|
321
770
|
unknown,
|
|
322
771
|
Meta
|
|
323
772
|
> {
|
|
773
|
+
return isGeneratorFunction(value);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
export function isDecoratedMiddleware(value: unknown): boolean {
|
|
324
777
|
return (
|
|
325
|
-
typeof value === "function" &&
|
|
326
|
-
value.constructor?.name === "GeneratorFunction"
|
|
778
|
+
typeof value === "function" && "mapInput" in value && "concat" in value
|
|
327
779
|
);
|
|
328
780
|
}
|
|
329
781
|
|
|
@@ -378,6 +830,7 @@ function runEffectPipeline<
|
|
|
378
830
|
TRequirementsProvided
|
|
379
831
|
>;
|
|
380
832
|
input: TInput;
|
|
833
|
+
runner: EffectRuntimeRunner<TRequirementsProvided, unknown>;
|
|
381
834
|
steps: readonly EffectPipelineStep[];
|
|
382
835
|
}): Effect.Effect<
|
|
383
836
|
EffectMiddlewareResult<Context, TOutput>,
|
|
@@ -398,18 +851,22 @@ function runEffectPipeline<
|
|
|
398
851
|
const stepOptions = { ...options.baseOptions, context };
|
|
399
852
|
|
|
400
853
|
if (step._tag === "provide") {
|
|
401
|
-
return Effect.flatMap(
|
|
402
|
-
|
|
854
|
+
return Effect.flatMap(
|
|
855
|
+
callEffectCallback(step.provider, stepOptions),
|
|
856
|
+
(service) =>
|
|
857
|
+
Effect.provideService(run(index + 1, context), step.tag, service),
|
|
403
858
|
);
|
|
404
859
|
}
|
|
405
860
|
|
|
406
861
|
if (step._tag === "provideOptional") {
|
|
407
|
-
return Effect.flatMap(
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
862
|
+
return Effect.flatMap(
|
|
863
|
+
callEffectCallback(step.provider, stepOptions),
|
|
864
|
+
(service) =>
|
|
865
|
+
Option.match(service, {
|
|
866
|
+
onNone: () => run(index + 1, context),
|
|
867
|
+
onSome: (value) =>
|
|
868
|
+
Effect.provideService(run(index + 1, context), step.tag, value),
|
|
869
|
+
}),
|
|
413
870
|
);
|
|
414
871
|
}
|
|
415
872
|
|
|
@@ -417,59 +874,75 @@ function runEffectPipeline<
|
|
|
417
874
|
return Effect.provide(run(index + 1, context), step.layer);
|
|
418
875
|
}
|
|
419
876
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
877
|
+
return Effect.flatMap(Effect.getFiberRefs, (fiberRefs) => {
|
|
878
|
+
const makeThenable = <A, E, R>(effect: Effect.Effect<A, E, R>) =>
|
|
879
|
+
makeThenableEffect(
|
|
880
|
+
effect,
|
|
881
|
+
options.runner as EffectRuntimeRunner<any, unknown>,
|
|
882
|
+
fiberRefs,
|
|
883
|
+
stepOptions.signal,
|
|
884
|
+
) as Effect.Effect<A, E, R>;
|
|
885
|
+
const nextTracker =
|
|
886
|
+
createMiddlewareNextTracker<EffectMiddlewareResult<Context, TOutput>>(
|
|
887
|
+
makeThenable,
|
|
888
|
+
);
|
|
889
|
+
const effectOptions: EffectMiddlewareOptions<
|
|
890
|
+
TCurrentContext,
|
|
891
|
+
TOutput,
|
|
892
|
+
TEffectErrorMap,
|
|
893
|
+
TRequirementsProvided,
|
|
894
|
+
TMeta
|
|
895
|
+
> = {
|
|
896
|
+
context,
|
|
897
|
+
path: stepOptions.path,
|
|
898
|
+
procedure: stepOptions.procedure,
|
|
899
|
+
signal: stepOptions.signal,
|
|
900
|
+
lastEventId: stepOptions.lastEventId,
|
|
901
|
+
errors: createEffectErrorConstructorMap(options.effectErrorMap),
|
|
902
|
+
next: nextTracker.wrapNext(
|
|
903
|
+
(...rest: [MiddlewareNextFnOptions<Context>?]) => {
|
|
904
|
+
const nextContext = rest[0]?.context ?? {};
|
|
905
|
+
return Effect.map(
|
|
906
|
+
run(index + 1, { ...context, ...nextContext }),
|
|
907
|
+
(result) => ({
|
|
908
|
+
output: result.output,
|
|
909
|
+
context: nextContext,
|
|
910
|
+
}),
|
|
911
|
+
) as Effect.Effect<EffectMiddlewareResult<Context, TOutput>>;
|
|
912
|
+
},
|
|
913
|
+
),
|
|
914
|
+
};
|
|
915
|
+
const effectOutput = makeEffectMiddlewareOutput<
|
|
916
|
+
TOutput,
|
|
917
|
+
TEffectErrorMap,
|
|
918
|
+
TRequirementsProvided
|
|
919
|
+
>((output) => ({ output, context: {} }), makeThenable);
|
|
920
|
+
const middlewareEffect = callEffectCallback(
|
|
921
|
+
step.middleware,
|
|
922
|
+
effectOptions,
|
|
923
|
+
options.input,
|
|
924
|
+
effectOutput,
|
|
925
|
+
) as Effect.Effect<EffectMiddlewareResult<Context, TOutput> | void>;
|
|
926
|
+
|
|
927
|
+
return Effect.flatMap(middlewareEffect, (result) =>
|
|
928
|
+
resolveEffectMiddlewareContinuation({
|
|
929
|
+
autoNext: () => effectOptions.next(),
|
|
930
|
+
nextInvoked: nextTracker.nextInvoked,
|
|
931
|
+
nextResult: nextTracker.nextResult,
|
|
932
|
+
result,
|
|
933
|
+
}),
|
|
934
|
+
);
|
|
935
|
+
});
|
|
467
936
|
};
|
|
468
937
|
|
|
469
938
|
return run(0, options.baseOptions.context);
|
|
470
939
|
}
|
|
471
940
|
|
|
472
|
-
function createMiddlewareNextTracker<T>(
|
|
941
|
+
function createMiddlewareNextTracker<T>(
|
|
942
|
+
makeThenable?: <A, E, R>(
|
|
943
|
+
effect: Effect.Effect<A, E, R>,
|
|
944
|
+
) => Effect.Effect<A, E, R>,
|
|
945
|
+
) {
|
|
473
946
|
let nextInvoked = false;
|
|
474
947
|
let nextResult: T | undefined;
|
|
475
948
|
|
|
@@ -480,15 +953,28 @@ function createMiddlewareNextTracker<T>() {
|
|
|
480
953
|
get nextResult() {
|
|
481
954
|
return nextResult;
|
|
482
955
|
},
|
|
956
|
+
setNextResult(result: T) {
|
|
957
|
+
nextResult = result;
|
|
958
|
+
},
|
|
483
959
|
wrapNext<Fn extends (...args: any) => Effect.Effect<T, any, any>>(
|
|
484
960
|
nextFn: Fn,
|
|
485
961
|
): Fn {
|
|
486
962
|
return ((...args: Parameters<Fn>) => {
|
|
487
963
|
nextInvoked = true;
|
|
488
|
-
|
|
964
|
+
const inner = nextFn(...args);
|
|
965
|
+
const tracked = Effect.map(inner, (result) => {
|
|
489
966
|
nextResult = result;
|
|
490
967
|
return result;
|
|
491
968
|
});
|
|
969
|
+
if (isHybridContinuation(inner)) {
|
|
970
|
+
markHybridContinuation(tracked);
|
|
971
|
+
attachPromiseLikeContinuation(
|
|
972
|
+
tracked,
|
|
973
|
+
(inner as any).then.bind(inner),
|
|
974
|
+
);
|
|
975
|
+
return tracked;
|
|
976
|
+
}
|
|
977
|
+
return makeThenable ? makeThenable(tracked) : tracked;
|
|
492
978
|
}) as Fn;
|
|
493
979
|
},
|
|
494
980
|
};
|
|
@@ -538,8 +1024,69 @@ function makeEffectMiddlewareOutput<
|
|
|
538
1024
|
TRequirementsProvided,
|
|
539
1025
|
>(
|
|
540
1026
|
output: MiddlewareOutputFn<TOutput>,
|
|
1027
|
+
makeThenable?: <A, E, R>(
|
|
1028
|
+
effect: Effect.Effect<A, E, R>,
|
|
1029
|
+
) => Effect.Effect<A, E, R>,
|
|
541
1030
|
): EffectMiddlewareOutput<TOutput, TEffectErrorMap, TRequirementsProvided> {
|
|
542
|
-
return (value: TOutput) =>
|
|
1031
|
+
return (value: TOutput) => {
|
|
1032
|
+
const effect = withCurrentFiberContext(() => output(value));
|
|
1033
|
+
return makeThenable ? makeThenable(effect) : effect;
|
|
1034
|
+
};
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
function makeThenableEffect<A, E, R>(
|
|
1038
|
+
effect: Effect.Effect<A, E, R>,
|
|
1039
|
+
runner: EffectRuntimeRunner<any, unknown>,
|
|
1040
|
+
fiberRefs: FiberRefs.FiberRefs,
|
|
1041
|
+
signal: AbortSignal | undefined,
|
|
1042
|
+
): Effect.Effect<A, E, R> {
|
|
1043
|
+
if ("then" in effect) {
|
|
1044
|
+
return effect;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
attachPromiseLikeContinuation(
|
|
1048
|
+
effect,
|
|
1049
|
+
(
|
|
1050
|
+
onFulfilled: ((value: A) => unknown) | undefined,
|
|
1051
|
+
onRejected: ((error: unknown) => unknown) | undefined,
|
|
1052
|
+
) =>
|
|
1053
|
+
runNestedEffect(effect, runner, fiberRefs, signal).then(
|
|
1054
|
+
onFulfilled,
|
|
1055
|
+
onRejected,
|
|
1056
|
+
),
|
|
1057
|
+
);
|
|
1058
|
+
|
|
1059
|
+
return effect;
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
async function runNestedEffect<A, E, R>(
|
|
1063
|
+
effect: Effect.Effect<A, E, R>,
|
|
1064
|
+
runner: EffectRuntimeRunner<any, unknown>,
|
|
1065
|
+
fiberRefs: FiberRefs.FiberRefs,
|
|
1066
|
+
signal: AbortSignal | undefined,
|
|
1067
|
+
): Promise<A> {
|
|
1068
|
+
const exit = await runner.runPromiseExit(withFiberRefs(effect, fiberRefs), {
|
|
1069
|
+
signal,
|
|
1070
|
+
});
|
|
1071
|
+
|
|
1072
|
+
if (Exit.isFailure(exit)) {
|
|
1073
|
+
throw toORPCErrorFromCause(exit.cause);
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
return exit.value;
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
function withFiberRefs<A, E, R>(
|
|
1080
|
+
effect: Effect.Effect<A, E, R>,
|
|
1081
|
+
parentFiberRefs: FiberRefs.FiberRefs,
|
|
1082
|
+
): Effect.Effect<A, E, R> {
|
|
1083
|
+
return Effect.fiberIdWith((fiberId) =>
|
|
1084
|
+
Effect.flatMap(Effect.getFiberRefs, (fiberRefs) =>
|
|
1085
|
+
Effect.setFiberRefs(
|
|
1086
|
+
FiberRefs.joinAs(fiberRefs, fiberId, parentFiberRefs),
|
|
1087
|
+
).pipe(Effect.andThen(effect)),
|
|
1088
|
+
),
|
|
1089
|
+
);
|
|
543
1090
|
}
|
|
544
1091
|
|
|
545
1092
|
function withCurrentFiberContext<T>(fn: () => Promisable<T>): Effect.Effect<T> {
|
|
@@ -549,18 +1096,3 @@ function withCurrentFiberContext<T>(fn: () => Promisable<T>): Effect.Effect<T> {
|
|
|
549
1096
|
),
|
|
550
1097
|
);
|
|
551
1098
|
}
|
|
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
|
-
}
|