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.
@@ -7,11 +7,15 @@ import type {
7
7
  } from "@orpc/contract";
8
8
  import type { Context, Router } from "@orpc/server";
9
9
  import { Builder, fallbackConfig, lazy } from "@orpc/server";
10
- import type { ManagedRuntime } from "effect";
10
+ import { Layer, ManagedRuntime } from "effect";
11
11
 
12
12
  import { enhanceEffectRouter } from "./effect-enhance-router";
13
13
  import { EffectDecoratedProcedure } from "./effect-procedure";
14
- import { createEffectProcedureHandler } from "./effect-runtime";
14
+ import {
15
+ createEffectPipelineMiddleware,
16
+ createEffectProcedureHandler,
17
+ isEffectMiddleware,
18
+ } from "./effect-runtime";
15
19
  import {
16
20
  createNodeProxy,
17
21
  unhandled,
@@ -23,6 +27,8 @@ import {
23
27
  unwrapEffectUpstream,
24
28
  type EffectProxyTarget,
25
29
  } from "./extension/state";
30
+ import type { EffectRuntimeSource } from "./runtime-source";
31
+ import { toManagedRuntime } from "./runtime-source";
26
32
  import type { EffectErrorMap, MergedEffectErrorMap } from "./tagged-error";
27
33
  import { effectErrorMapToErrorMap } from "./tagged-error";
28
34
  import type {
@@ -43,6 +49,9 @@ const builderVirtualDescriptors = {
43
49
  errors: { enumerable: false },
44
50
  handler: { enumerable: false },
45
51
  lazy: { enumerable: false },
52
+ middleware: { enumerable: false },
53
+ provide: { enumerable: false },
54
+ provideOptional: { enumerable: false },
46
55
  router: { enumerable: false },
47
56
  traced: { enumerable: false },
48
57
  } as const;
@@ -51,6 +60,9 @@ const builderVirtualKeys = [
51
60
  "~effect",
52
61
  "errors",
53
62
  "effect",
63
+ "middleware",
64
+ "provide",
65
+ "provideOptional",
54
66
  "traced",
55
67
  "handler",
56
68
  "router",
@@ -96,6 +108,8 @@ function getEffectBuilderDef(
96
108
  effectErrorMap: context.state.effectErrorMap,
97
109
  runtime: context.state.runtime,
98
110
  spanConfig: context.state.spanConfig,
111
+ effectSteps: context.state.effectSteps,
112
+ effectHandler: context.state.effectHandler,
99
113
  };
100
114
  }
101
115
 
@@ -109,11 +123,55 @@ function wrapBuilderLike(
109
123
  effectErrorMap: state.effectErrorMap,
110
124
  runtime: state.runtime,
111
125
  spanConfig: state.spanConfig,
126
+ effectSteps: state.effectSteps,
127
+ effectHandler: state.effectHandler,
112
128
  },
113
129
  unwrapEffectUpstream(builder),
114
130
  );
115
131
  }
116
132
 
133
+ function appendEffectStep(
134
+ state: NodeProxyContext<EffectBuilderTarget, AnyBuilderLike>["state"],
135
+ step: NonNullable<
136
+ NodeProxyContext<
137
+ EffectBuilderTarget,
138
+ AnyBuilderLike
139
+ >["state"]["effectSteps"]
140
+ >[number],
141
+ ): NodeProxyContext<EffectBuilderTarget, AnyBuilderLike>["state"] {
142
+ return {
143
+ ...state,
144
+ effectSteps: [...(state.effectSteps ?? []), step],
145
+ };
146
+ }
147
+
148
+ function flushEffectSteps(
149
+ builder: AnyBuilderLike,
150
+ state: NodeProxyContext<EffectBuilderTarget, AnyBuilderLike>["state"],
151
+ ): {
152
+ builder: AnyBuilderLike;
153
+ state: NodeProxyContext<EffectBuilderTarget, AnyBuilderLike>["state"];
154
+ } {
155
+ if (!state.effectSteps?.length) {
156
+ return { builder, state };
157
+ }
158
+
159
+ const middleware = createEffectPipelineMiddleware({
160
+ effectErrorMap: state.effectErrorMap,
161
+ runtime: state.runtime,
162
+ steps: state.effectSteps,
163
+ });
164
+ return {
165
+ builder: Reflect.apply(Reflect.get(builder, "use", builder), builder, [
166
+ middleware,
167
+ ]) as AnyBuilderLike,
168
+ state: {
169
+ ...state,
170
+ effectSteps: undefined,
171
+ },
172
+ };
173
+ }
174
+
117
175
  function createEffectBuilderProxy(
118
176
  target: EffectBuilderTarget,
119
177
  ): EffectBuilderTarget {
@@ -166,13 +224,20 @@ function createEffectBuilderProxy(
166
224
  >[0],
167
225
  ) => {
168
226
  const defaultCaptureStackTrace = addSpanStackTrace();
227
+ const effectHandler = {
228
+ defaultCaptureStackTrace,
229
+ effectFn,
230
+ spanConfig: state.spanConfig,
231
+ };
169
232
  return new EffectDecoratedProcedure({
170
233
  ...effectDef,
234
+ effectHandler,
171
235
  handler: async (opts) => {
172
236
  return createEffectProcedureHandler({
173
237
  defaultCaptureStackTrace,
174
238
  effectErrorMap: state.effectErrorMap,
175
239
  effectFn,
240
+ effectSteps: state.effectSteps,
176
241
  runtime: state.runtime,
177
242
  spanConfig: state.spanConfig,
178
243
  })(opts as any);
@@ -180,6 +245,82 @@ function createEffectBuilderProxy(
180
245
  });
181
246
  };
182
247
  });
248
+ case "middleware":
249
+ return getOrCreateVirtualMethod(context, prop, () => {
250
+ return (middleware: any) => {
251
+ if (isEffectMiddleware(middleware)) {
252
+ const effectMiddleware = createEffectPipelineMiddleware({
253
+ effectErrorMap: state.effectErrorMap,
254
+ runtime: state.runtime,
255
+ steps: [
256
+ ...(state.effectSteps ?? []),
257
+ { _tag: "middleware" as const, middleware },
258
+ ],
259
+ });
260
+
261
+ return Reflect.apply(
262
+ Reflect.get(source, "middleware", source),
263
+ source,
264
+ [effectMiddleware],
265
+ );
266
+ }
267
+
268
+ return Reflect.apply(
269
+ Reflect.get(source, "middleware", source),
270
+ source,
271
+ [middleware],
272
+ );
273
+ };
274
+ });
275
+ case "provide":
276
+ return getOrCreateVirtualMethod(context, prop, () => {
277
+ return (tagOrLayer: any, provider?: any) => {
278
+ return wrapBuilderLike(
279
+ source,
280
+ appendEffectStep(
281
+ state,
282
+ Layer.isLayer(tagOrLayer)
283
+ ? { _tag: "provideLayer", layer: tagOrLayer }
284
+ : { _tag: "provide", provider, tag: tagOrLayer },
285
+ ),
286
+ );
287
+ };
288
+ });
289
+ case "provideOptional":
290
+ return getOrCreateVirtualMethod(context, prop, () => {
291
+ return (tag: any, provider: any) => {
292
+ return wrapBuilderLike(
293
+ source,
294
+ appendEffectStep(state, {
295
+ _tag: "provideOptional",
296
+ provider,
297
+ tag,
298
+ }),
299
+ );
300
+ };
301
+ });
302
+ case "use":
303
+ return getOrCreateVirtualMethod(context, prop, () => {
304
+ return (middleware: any, ...rest: unknown[]) => {
305
+ if (isEffectMiddleware(middleware) && rest.length === 0) {
306
+ return wrapBuilderLike(
307
+ source,
308
+ appendEffectStep(state, {
309
+ _tag: "middleware",
310
+ middleware,
311
+ }),
312
+ );
313
+ }
314
+
315
+ const flushed = flushEffectSteps(source, state);
316
+ const nextBuilder: AnyBuilderLike = Reflect.apply(
317
+ Reflect.get(flushed.builder, "use", flushed.builder),
318
+ flushed.builder,
319
+ [middleware, ...rest],
320
+ );
321
+ return wrapBuilderLike(nextBuilder, flushed.state);
322
+ };
323
+ });
183
324
  case "traced":
184
325
  return getOrCreateVirtualMethod(context, prop, () => {
185
326
  return (spanName: string) =>
@@ -292,6 +433,12 @@ export class EffectBuilder<
292
433
  TRequirementsProvided,
293
434
  TRuntimeError
294
435
  > {
436
+ /**
437
+ * Sets or overrides the config.
438
+ *
439
+ * @see {@link https://orpc.dev/docs/client/server-side#middlewares-order Middlewares Order Docs}
440
+ * @see {@link https://orpc.dev/docs/best-practices/dedupe-middleware#configuration Dedupe Middleware Docs}
441
+ */
295
442
  declare $config: EffectBuilderSurface<
296
443
  TInitialContext,
297
444
  TCurrentContext,
@@ -302,6 +449,11 @@ export class EffectBuilder<
302
449
  TRequirementsProvided,
303
450
  TRuntimeError
304
451
  >["$config"];
452
+ /**
453
+ * Set or override the initial context.
454
+ *
455
+ * @see {@link https://orpc.dev/docs/context Context Docs}
456
+ */
305
457
  declare $context: EffectBuilderSurface<
306
458
  TInitialContext,
307
459
  TCurrentContext,
@@ -312,6 +464,11 @@ export class EffectBuilder<
312
464
  TRequirementsProvided,
313
465
  TRuntimeError
314
466
  >["$context"];
467
+ /**
468
+ * Sets or overrides the initial meta.
469
+ *
470
+ * @see {@link https://orpc.dev/docs/metadata Metadata Docs}
471
+ */
315
472
  declare $meta: EffectBuilderSurface<
316
473
  TInitialContext,
317
474
  TCurrentContext,
@@ -322,6 +479,13 @@ export class EffectBuilder<
322
479
  TRequirementsProvided,
323
480
  TRuntimeError
324
481
  >["$meta"];
482
+ /**
483
+ * Sets or overrides the initial route.
484
+ * This option is typically relevant when integrating with OpenAPI.
485
+ *
486
+ * @see {@link https://orpc.dev/docs/openapi/routing OpenAPI Routing Docs}
487
+ * @see {@link https://orpc.dev/docs/openapi/input-output-structure OpenAPI Input/Output Structure Docs}
488
+ */
325
489
  declare $route: EffectBuilderSurface<
326
490
  TInitialContext,
327
491
  TCurrentContext,
@@ -332,6 +496,11 @@ export class EffectBuilder<
332
496
  TRequirementsProvided,
333
497
  TRuntimeError
334
498
  >["$route"];
499
+ /**
500
+ * Sets or overrides the initial input schema.
501
+ *
502
+ * @see {@link https://orpc.dev/docs/procedure#initial-configuration Initial Procedure Configuration Docs}
503
+ */
335
504
  declare $input: EffectBuilderSurface<
336
505
  TInitialContext,
337
506
  TCurrentContext,
@@ -342,6 +511,9 @@ export class EffectBuilder<
342
511
  TRequirementsProvided,
343
512
  TRuntimeError
344
513
  >["$input"];
514
+ /**
515
+ * This property holds the defined options and the effect-specific properties.
516
+ */
345
517
  declare "~effect": EffectBuilderDef<
346
518
  TInputSchema,
347
519
  TOutputSchema,
@@ -350,6 +522,9 @@ export class EffectBuilder<
350
522
  TRequirementsProvided,
351
523
  TRuntimeError
352
524
  >;
525
+ /**
526
+ * This property holds the defined options.
527
+ */
353
528
  declare "~orpc": EffectBuilderSurface<
354
529
  TInitialContext,
355
530
  TCurrentContext,
@@ -360,6 +535,11 @@ export class EffectBuilder<
360
535
  TRequirementsProvided,
361
536
  TRuntimeError
362
537
  >["~orpc"];
538
+ /**
539
+ * Creates a middleware.
540
+ *
541
+ * @see {@link https://orpc.dev/docs/middleware Middleware Docs}
542
+ */
363
543
  declare middleware: EffectBuilderSurface<
364
544
  TInitialContext,
365
545
  TCurrentContext,
@@ -370,6 +550,27 @@ export class EffectBuilder<
370
550
  TRequirementsProvided,
371
551
  TRuntimeError
372
552
  >["middleware"];
553
+ /**
554
+ * Adds type-safe custom errors.
555
+ * Supports both traditional oRPC error definitions and ORPCTaggedError classes.
556
+ *
557
+ * @example
558
+ * ```ts
559
+ * // Traditional format
560
+ * builder.errors({ BAD_REQUEST: { status: 400, message: 'Bad request' } })
561
+ *
562
+ * // Tagged error class
563
+ * builder.errors({ USER_NOT_FOUND: UserNotFoundError })
564
+ *
565
+ * // Mixed
566
+ * builder.errors({
567
+ * BAD_REQUEST: { status: 400 },
568
+ * USER_NOT_FOUND: UserNotFoundError,
569
+ * })
570
+ * ```
571
+ *
572
+ * @see {@link https://orpc.dev/docs/error-handling#type%E2%80%90safe-error-handling Type-Safe Error Handling Docs}
573
+ */
373
574
  declare errors: EffectBuilderSurface<
374
575
  TInitialContext,
375
576
  TCurrentContext,
@@ -380,6 +581,13 @@ export class EffectBuilder<
380
581
  TRequirementsProvided,
381
582
  TRuntimeError
382
583
  >["errors"];
584
+ /**
585
+ * Uses a middleware to modify the context or improve the pipeline.
586
+ *
587
+ * @info Supports both normal middleware and inline middleware implementations.
588
+ * @note The current context must be satisfy middleware dependent-context
589
+ * @see {@link https://orpc.dev/docs/middleware Middleware Docs}
590
+ */
383
591
  declare use: EffectBuilderSurface<
384
592
  TInitialContext,
385
593
  TCurrentContext,
@@ -390,6 +598,38 @@ export class EffectBuilder<
390
598
  TRequirementsProvided,
391
599
  TRuntimeError
392
600
  >["use"];
601
+ /**
602
+ * Provides a request-scoped Effect service to downstream procedures.
603
+ */
604
+ declare provide: EffectBuilderSurface<
605
+ TInitialContext,
606
+ TCurrentContext,
607
+ TInputSchema,
608
+ TOutputSchema,
609
+ TEffectErrorMap,
610
+ TMeta,
611
+ TRequirementsProvided,
612
+ TRuntimeError
613
+ >["provide"];
614
+ /**
615
+ * Optionally provides a request-scoped Effect service to downstream procedures.
616
+ */
617
+ declare provideOptional: EffectBuilderSurface<
618
+ TInitialContext,
619
+ TCurrentContext,
620
+ TInputSchema,
621
+ TOutputSchema,
622
+ TEffectErrorMap,
623
+ TMeta,
624
+ TRequirementsProvided,
625
+ TRuntimeError
626
+ >["provideOptional"];
627
+ /**
628
+ * Sets or updates the metadata.
629
+ * The provided metadata is spared-merged with any existing metadata.
630
+ *
631
+ * @see {@link https://orpc.dev/docs/metadata Metadata Docs}
632
+ */
393
633
  declare meta: EffectBuilderSurface<
394
634
  TInitialContext,
395
635
  TCurrentContext,
@@ -400,6 +640,14 @@ export class EffectBuilder<
400
640
  TRequirementsProvided,
401
641
  TRuntimeError
402
642
  >["meta"];
643
+ /**
644
+ * Sets or updates the route definition.
645
+ * The provided route is spared-merged with any existing route.
646
+ * This option is typically relevant when integrating with OpenAPI.
647
+ *
648
+ * @see {@link https://orpc.dev/docs/openapi/routing OpenAPI Routing Docs}
649
+ * @see {@link https://orpc.dev/docs/openapi/input-output-structure OpenAPI Input/Output Structure Docs}
650
+ */
403
651
  declare route: EffectBuilderSurface<
404
652
  TInitialContext,
405
653
  TCurrentContext,
@@ -410,6 +658,11 @@ export class EffectBuilder<
410
658
  TRequirementsProvided,
411
659
  TRuntimeError
412
660
  >["route"];
661
+ /**
662
+ * Defines the input validation schema.
663
+ *
664
+ * @see {@link https://orpc.dev/docs/procedure#input-output-validation Input Validation Docs}
665
+ */
413
666
  declare input: EffectBuilderSurface<
414
667
  TInitialContext,
415
668
  TCurrentContext,
@@ -420,6 +673,11 @@ export class EffectBuilder<
420
673
  TRequirementsProvided,
421
674
  TRuntimeError
422
675
  >["input"];
676
+ /**
677
+ * Defines the output validation schema.
678
+ *
679
+ * @see {@link https://orpc.dev/docs/procedure#input-output-validation Output Validation Docs}
680
+ */
423
681
  declare output: EffectBuilderSurface<
424
682
  TInitialContext,
425
683
  TCurrentContext,
@@ -430,6 +688,25 @@ export class EffectBuilder<
430
688
  TRequirementsProvided,
431
689
  TRuntimeError
432
690
  >["output"];
691
+ /**
692
+ * Adds a traceable span to the procedure for telemetry.
693
+ * The span name is used for Effect tracing via `Effect.withSpan`.
694
+ * Stack trace is captured at the call site for better error reporting.
695
+ *
696
+ * @param spanName - The name of the span for telemetry (e.g., 'users.getUser')
697
+ * @returns An EffectBuilder with span tracing configured
698
+ *
699
+ * @example
700
+ * ```ts
701
+ * const getUser = effectOs
702
+ * .input(z.object({ id: z.string() }))
703
+ * .traced('users.getUser')
704
+ * .effect(function* ({ input }) {
705
+ * const userService = yield* UserService
706
+ * return yield* userService.findById(input.id)
707
+ * })
708
+ * ```
709
+ */
433
710
  declare traced: EffectBuilderSurface<
434
711
  TInitialContext,
435
712
  TCurrentContext,
@@ -440,6 +717,11 @@ export class EffectBuilder<
440
717
  TRequirementsProvided,
441
718
  TRuntimeError
442
719
  >["traced"];
720
+ /**
721
+ * Defines the handler of the procedure using a standard async/sync function.
722
+ *
723
+ * @see {@link https://orpc.dev/docs/procedure Procedure Docs}
724
+ */
443
725
  declare handler: EffectBuilderSurface<
444
726
  TInitialContext,
445
727
  TCurrentContext,
@@ -450,6 +732,13 @@ export class EffectBuilder<
450
732
  TRequirementsProvided,
451
733
  TRuntimeError
452
734
  >["handler"];
735
+ /**
736
+ * Defines the handler of the procedure using an Effect.
737
+ * The Effect is executed using the ManagedRuntime provided during builder creation.
738
+ * The effect is automatically wrapped with `Effect.withSpan`.
739
+ *
740
+ * @see {@link https://orpc.dev/docs/procedure Procedure Docs}
741
+ */
453
742
  declare effect: EffectBuilderSurface<
454
743
  TInitialContext,
455
744
  TCurrentContext,
@@ -460,6 +749,14 @@ export class EffectBuilder<
460
749
  TRequirementsProvided,
461
750
  TRuntimeError
462
751
  >["effect"];
752
+ /**
753
+ * Prefixes all procedures in the router.
754
+ * The provided prefix is post-appended to any existing router prefix.
755
+ *
756
+ * @note This option does not affect procedures that do not define a path in their route definition.
757
+ *
758
+ * @see {@link https://orpc.dev/docs/openapi/routing#route-prefixes OpenAPI Route Prefixes Docs}
759
+ */
463
760
  declare prefix: EffectBuilderSurface<
464
761
  TInitialContext,
465
762
  TCurrentContext,
@@ -470,6 +767,12 @@ export class EffectBuilder<
470
767
  TRequirementsProvided,
471
768
  TRuntimeError
472
769
  >["prefix"];
770
+ /**
771
+ * Adds tags to all procedures in the router.
772
+ * This helpful when you want to group procedures together in the OpenAPI specification.
773
+ *
774
+ * @see {@link https://orpc.dev/docs/openapi/openapi-specification#operation-metadata OpenAPI Operation Metadata Docs}
775
+ */
473
776
  declare tag: EffectBuilderSurface<
474
777
  TInitialContext,
475
778
  TCurrentContext,
@@ -480,6 +783,11 @@ export class EffectBuilder<
480
783
  TRequirementsProvided,
481
784
  TRuntimeError
482
785
  >["tag"];
786
+ /**
787
+ * Applies all of the previously defined options to the specified router.
788
+ *
789
+ * @see {@link https://orpc.dev/docs/router#extending-router Extending Router Docs}
790
+ */
483
791
  declare router: EffectBuilderSurface<
484
792
  TInitialContext,
485
793
  TCurrentContext,
@@ -490,6 +798,12 @@ export class EffectBuilder<
490
798
  TRequirementsProvided,
491
799
  TRuntimeError
492
800
  >["router"];
801
+ /**
802
+ * Create a lazy router
803
+ * And applies all of the previously defined options to the specified router.
804
+ *
805
+ * @see {@link https://orpc.dev/docs/router#extending-router Extending Router Docs}
806
+ */
493
807
  declare lazy: EffectBuilderSurface<
494
808
  TInitialContext,
495
809
  TCurrentContext,
@@ -512,9 +826,12 @@ export class EffectBuilder<
512
826
  >,
513
827
  builder?: AnyBuilderLike,
514
828
  ) {
515
- const { runtime, spanConfig, effectErrorMap, ...orpcDef } = def;
829
+ const { runtime, spanConfig, effectErrorMap, effectSteps, ...orpcDef } =
830
+ def;
516
831
 
517
832
  attachEffectState(this, builder ?? new Builder(orpcDef), {
833
+ effectSteps,
834
+ effectHandler: def.effectHandler,
518
835
  effectErrorMap,
519
836
  runtime,
520
837
  spanConfig,
@@ -524,12 +841,76 @@ export class EffectBuilder<
524
841
  }
525
842
  }
526
843
 
844
+ /**
845
+ * Creates an Effect-aware procedure builder with the specified Layer.
846
+ * Uses the default builder shape from `@orpc/server`.
847
+ *
848
+ * @param layer - The Layer that provides services for Effect procedures
849
+ * @returns An EffectBuilder instance for creating Effect-native procedures
850
+ *
851
+ * @example
852
+ * ```ts
853
+ * import { makeEffectORPC } from '@orpc/effect'
854
+ * import { Effect, Layer } from 'effect'
855
+ *
856
+ * const effectOs = makeEffectORPC(Layer.empty)
857
+ *
858
+ * const hello = effectOs.effect(() => Effect.succeed('Hello!'))
859
+ * ```
860
+ */
861
+ export function makeEffectORPC(): EffectBuilder<
862
+ Context,
863
+ Context,
864
+ Schema<unknown, unknown>,
865
+ Schema<unknown, unknown>,
866
+ Record<never, never>,
867
+ Record<never, never>,
868
+ never,
869
+ never
870
+ >;
871
+
872
+ export function makeEffectORPC<
873
+ TBuilder extends AnyBuilderLike<
874
+ TInputSchema,
875
+ TOutputSchema,
876
+ TErrorMap,
877
+ TMeta
878
+ >,
879
+ TInputSchema extends AnySchema,
880
+ TOutputSchema extends AnySchema,
881
+ TErrorMap extends ErrorMap,
882
+ TMeta extends Meta,
883
+ >(
884
+ builder: TBuilder,
885
+ ): EffectBuilder<
886
+ InferBuilderInitialContext<TBuilder>,
887
+ InferBuilderCurrentContext<TBuilder>,
888
+ InferBuilderInputSchema<TBuilder>,
889
+ InferBuilderOutputSchema<TBuilder>,
890
+ InferBuilderErrorMap<TBuilder>,
891
+ InferBuilderMeta<TBuilder>,
892
+ never,
893
+ never
894
+ >;
895
+
896
+ export function makeEffectORPC<TRequirementsProvided, TRuntimeError>(
897
+ layer: Layer.Layer<TRequirementsProvided, TRuntimeError, never>,
898
+ ): EffectBuilder<
899
+ Context,
900
+ Context,
901
+ Schema<unknown, unknown>,
902
+ Schema<unknown, unknown>,
903
+ Record<never, never>,
904
+ Record<never, never>,
905
+ TRequirementsProvided,
906
+ TRuntimeError
907
+ >;
908
+
527
909
  /**
528
910
  * Creates an Effect-aware procedure builder with the specified ManagedRuntime.
529
911
  * Uses the default builder shape from `@orpc/server`.
530
912
  *
531
913
  * @param runtime - The ManagedRuntime that provides services for Effect procedures
532
- * @returns An EffectBuilder instance for creating Effect-native procedures
533
914
  */
534
915
  export function makeEffectORPC<TRequirementsProvided, TRuntimeError>(
535
916
  runtime: ManagedRuntime.ManagedRuntime<TRequirementsProvided, TRuntimeError>,
@@ -546,11 +927,33 @@ export function makeEffectORPC<TRequirementsProvided, TRuntimeError>(
546
927
 
547
928
  /**
548
929
  * Creates an Effect-aware procedure builder by wrapping an existing oRPC Builder
549
- * with the specified ManagedRuntime.
930
+ * with the specified Layer.
550
931
  *
551
- * @param runtime - The ManagedRuntime that provides services for Effect procedures
552
- * @param builder - The oRPC Builder instance to wrap
932
+ * @param layer - The Layer that provides services for Effect procedures
933
+ * @param builder - The oRPC Builder instance to wrap (e.g., a customized `os`)
553
934
  * @returns An EffectBuilder instance that extends the original builder with Effect support
935
+ *
936
+ * @example
937
+ * ```ts
938
+ * import { makeEffectORPC } from '@orpc/effect'
939
+ * import { os } from '@orpc/server'
940
+ * import { Effect, Layer } from 'effect'
941
+ *
942
+ * // Create a customized builder
943
+ * const authedOs = os.use(authMiddleware)
944
+ *
945
+ * // Wrap it with Effect support
946
+ * const effectOs = makeEffectORPC(UserServiceLive, authedOs)
947
+ *
948
+ * const getUser = effectOs
949
+ * .input(z.object({ id: z.string() }))
950
+ * .effect(
951
+ * Effect.fn(function* ({ input }) {
952
+ * const userService = yield* UserService
953
+ * return yield* userService.findById(input.id)
954
+ * })
955
+ * )
956
+ * ```
554
957
  */
555
958
  export function makeEffectORPC<
556
959
  TBuilder extends AnyBuilderLike<
@@ -566,7 +969,7 @@ export function makeEffectORPC<
566
969
  TRequirementsProvided,
567
970
  TRuntimeError,
568
971
  >(
569
- runtime: ManagedRuntime.ManagedRuntime<TRequirementsProvided, TRuntimeError>,
972
+ layer: Layer.Layer<TRequirementsProvided, TRuntimeError, never>,
570
973
  builder: TBuilder,
571
974
  ): EffectBuilder<
572
975
  InferBuilderInitialContext<TBuilder>,
@@ -579,21 +982,52 @@ export function makeEffectORPC<
579
982
  TRuntimeError
580
983
  >;
581
984
 
582
- export function makeEffectORPC<TRequirementsProvided, TRuntimeError>(
985
+ /**
986
+ * Creates an Effect-aware procedure builder by wrapping an existing oRPC Builder
987
+ * with the specified ManagedRuntime.
988
+ *
989
+ * @param runtime - The ManagedRuntime that provides services for Effect procedures
990
+ * @param builder - The oRPC Builder instance to wrap (e.g., a customized `os`)
991
+ */
992
+ export function makeEffectORPC<
993
+ TBuilder extends AnyBuilderLike<
994
+ TInputSchema,
995
+ TOutputSchema,
996
+ TErrorMap,
997
+ TMeta
998
+ >,
999
+ TInputSchema extends AnySchema,
1000
+ TOutputSchema extends AnySchema,
1001
+ TErrorMap extends ErrorMap,
1002
+ TMeta extends Meta,
1003
+ TRequirementsProvided,
1004
+ TRuntimeError,
1005
+ >(
583
1006
  runtime: ManagedRuntime.ManagedRuntime<TRequirementsProvided, TRuntimeError>,
584
- builder?: AnyBuilderLike,
1007
+ builder: TBuilder,
585
1008
  ): EffectBuilder<
586
- any,
587
- any,
588
- any,
589
- any,
590
- any,
591
- any,
1009
+ InferBuilderInitialContext<TBuilder>,
1010
+ InferBuilderCurrentContext<TBuilder>,
1011
+ InferBuilderInputSchema<TBuilder>,
1012
+ InferBuilderOutputSchema<TBuilder>,
1013
+ InferBuilderErrorMap<TBuilder>,
1014
+ InferBuilderMeta<TBuilder>,
592
1015
  TRequirementsProvided,
593
1016
  TRuntimeError
594
- > {
595
- const resolvedBuilder = builder ?? emptyBuilder();
1017
+ >;
1018
+
1019
+ export function makeEffectORPC(
1020
+ source?: EffectRuntimeSource<any, any> | AnyBuilderLike,
1021
+ builder?: AnyBuilderLike,
1022
+ ): any {
1023
+ const sourceIsBuilder = source !== undefined && isBuilderLike(source);
1024
+ const resolvedBuilder = sourceIsBuilder
1025
+ ? source
1026
+ : (builder ?? emptyBuilder());
596
1027
  const effectErrorMap = getEffectErrorMap(resolvedBuilder);
1028
+ const runtime = toManagedRuntime(
1029
+ sourceIsBuilder || source === undefined ? Layer.empty : source,
1030
+ );
597
1031
  return new EffectBuilder(
598
1032
  {
599
1033
  ...resolvedBuilder["~orpc"],