effect-orpc 0.2.0 → 0.2.1

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.
@@ -2,55 +2,32 @@ import type {
2
2
  AnySchema,
3
3
  ContractRouter,
4
4
  ErrorMap,
5
- HTTPPath,
6
- InferSchemaOutput,
7
5
  Meta,
8
- Route,
9
6
  Schema,
10
7
  } from "@orpc/contract";
11
- import { mergeMeta, mergePrefix, mergeRoute, mergeTags } from "@orpc/contract";
12
- import type {
13
- AnyMiddleware,
14
- BuilderConfig,
15
- BuilderDef,
16
- Context,
17
- DecoratedMiddleware,
18
- Lazy,
19
- MapInputMiddleware,
20
- MergedCurrentContext,
21
- MergedInitialContext,
22
- Middleware,
23
- ProcedureHandler,
24
- Router,
25
- } from "@orpc/server";
26
- import {
27
- addMiddleware,
28
- Builder,
29
- decorateMiddleware,
30
- fallbackConfig,
31
- lazy,
32
- } from "@orpc/server";
33
- import type { IntersectPick } from "@orpc/shared";
8
+ import type { Context, Router } from "@orpc/server";
9
+ import { Builder, fallbackConfig, lazy } from "@orpc/server";
34
10
  import type { ManagedRuntime } from "effect";
35
11
 
36
12
  import { enhanceEffectRouter } from "./effect-enhance-router";
37
13
  import { EffectDecoratedProcedure } from "./effect-procedure";
38
14
  import { createEffectProcedureHandler } from "./effect-runtime";
39
- import type {
40
- EffectErrorConstructorMap,
41
- EffectErrorMap,
42
- MergedEffectErrorMap,
43
- } from "./tagged-error";
15
+ import {
16
+ createNodeProxy,
17
+ unhandled,
18
+ type NodeProxyContext,
19
+ } from "./extension/create-node-proxy";
20
+ import {
21
+ attachEffectState,
22
+ getEffectErrorMap,
23
+ unwrapEffectUpstream,
24
+ type EffectProxyTarget,
25
+ } from "./extension/state";
26
+ import type { EffectErrorMap, MergedEffectErrorMap } from "./tagged-error";
44
27
  import { effectErrorMapToErrorMap } from "./tagged-error";
45
28
  import type {
46
29
  AnyBuilderLike,
47
30
  EffectBuilderDef,
48
- EffectErrorMapToErrorMap,
49
- EffectProcedureBuilderWithInput,
50
- EffectProcedureBuilderWithOutput,
51
- EffectProcedureHandler,
52
- EffectRouterBuilder,
53
- EnhancedEffectRouter,
54
31
  InferBuilderCurrentContext,
55
32
  InferBuilderErrorMap,
56
33
  InferBuilderInitialContext,
@@ -58,12 +35,215 @@ import type {
58
35
  InferBuilderMeta,
59
36
  InferBuilderOutputSchema,
60
37
  } from "./types";
38
+ import type { EffectBuilderSurface } from "./types/effect-builder-surface";
39
+
40
+ const builderVirtualDescriptors = {
41
+ "~effect": { enumerable: true },
42
+ effect: { enumerable: false },
43
+ errors: { enumerable: false },
44
+ handler: { enumerable: false },
45
+ lazy: { enumerable: false },
46
+ router: { enumerable: false },
47
+ traced: { enumerable: false },
48
+ } as const;
49
+
50
+ const builderVirtualKeys = [
51
+ "~effect",
52
+ "errors",
53
+ "effect",
54
+ "traced",
55
+ "handler",
56
+ "router",
57
+ "lazy",
58
+ ] as const;
59
+
60
+ type EffectBuilderTarget = EffectBuilder<
61
+ any,
62
+ any,
63
+ any,
64
+ any,
65
+ any,
66
+ any,
67
+ any,
68
+ any
69
+ > &
70
+ EffectProxyTarget<AnyBuilderLike>;
71
+
72
+ function isBuilderLike(value: unknown): value is AnyBuilderLike {
73
+ return typeof value === "object" && value !== null && "~orpc" in value;
74
+ }
75
+
76
+ function getOrCreateVirtualMethod<T>(
77
+ context: NodeProxyContext<EffectBuilderTarget, AnyBuilderLike>,
78
+ prop: PropertyKey,
79
+ factory: () => T,
80
+ ): T {
81
+ const cache = context.methodCache;
82
+ if (cache.has(prop)) {
83
+ return cache.get(prop) as T;
84
+ }
85
+
86
+ const value = factory();
87
+ cache.set(prop, value);
88
+ return value;
89
+ }
90
+
91
+ function getEffectBuilderDef(
92
+ context: NodeProxyContext<EffectBuilderTarget, AnyBuilderLike>,
93
+ ): EffectBuilderDef<any, any, any, any, any, any> {
94
+ return {
95
+ ...context.upstream["~orpc"],
96
+ effectErrorMap: context.state.effectErrorMap,
97
+ runtime: context.state.runtime,
98
+ spanConfig: context.state.spanConfig,
99
+ };
100
+ }
101
+
102
+ function wrapBuilderLike(
103
+ builder: AnyBuilderLike,
104
+ state: NodeProxyContext<EffectBuilderTarget, AnyBuilderLike>["state"],
105
+ ): EffectBuilder<any, any, any, any, any, any, any, any> {
106
+ return new EffectBuilder(
107
+ {
108
+ ...builder["~orpc"],
109
+ effectErrorMap: state.effectErrorMap,
110
+ runtime: state.runtime,
111
+ spanConfig: state.spanConfig,
112
+ },
113
+ unwrapEffectUpstream(builder),
114
+ );
115
+ }
116
+
117
+ function createEffectBuilderProxy(
118
+ target: EffectBuilderTarget,
119
+ ): EffectBuilderTarget {
120
+ return createNodeProxy<EffectBuilderTarget, AnyBuilderLike>(target, {
121
+ getVirtual(context, prop) {
122
+ const effectDef = getEffectBuilderDef(context);
123
+ if (prop === "~effect") {
124
+ return getEffectBuilderDef(context);
125
+ }
126
+
127
+ const { upstream: source, state } = context;
128
+
129
+ switch (prop) {
130
+ case "errors":
131
+ return getOrCreateVirtualMethod(context, prop, () => {
132
+ return <U extends EffectErrorMap>(errors: U) => {
133
+ const nextEffectErrorMap: MergedEffectErrorMap<
134
+ typeof state.effectErrorMap,
135
+ U
136
+ > = {
137
+ ...state.effectErrorMap,
138
+ ...errors,
139
+ };
140
+ const nextBuilder: AnyBuilderLike = Reflect.apply(
141
+ Reflect.get(source, "errors", source),
142
+ source,
143
+ [effectErrorMapToErrorMap(errors)],
144
+ );
145
+
146
+ return wrapBuilderLike(nextBuilder, {
147
+ ...state,
148
+ effectErrorMap: nextEffectErrorMap,
149
+ });
150
+ };
151
+ });
152
+ case "effect":
153
+ return getOrCreateVirtualMethod(context, prop, () => {
154
+ return (
155
+ effectFn: Parameters<
156
+ EffectBuilderSurface<
157
+ any,
158
+ any,
159
+ any,
160
+ any,
161
+ any,
162
+ any,
163
+ any,
164
+ any
165
+ >["effect"]
166
+ >[0],
167
+ ) => {
168
+ const defaultCaptureStackTrace = addSpanStackTrace();
169
+ return new EffectDecoratedProcedure({
170
+ ...effectDef,
171
+ handler: async (opts) => {
172
+ return createEffectProcedureHandler({
173
+ defaultCaptureStackTrace,
174
+ effectErrorMap: state.effectErrorMap,
175
+ effectFn,
176
+ runtime: state.runtime,
177
+ spanConfig: state.spanConfig,
178
+ })(opts as any);
179
+ },
180
+ });
181
+ };
182
+ });
183
+ case "traced":
184
+ return getOrCreateVirtualMethod(context, prop, () => {
185
+ return (spanName: string) =>
186
+ wrapBuilderLike(source, {
187
+ ...state,
188
+ spanConfig: {
189
+ captureStackTrace: addSpanStackTrace(),
190
+ name: spanName,
191
+ },
192
+ });
193
+ });
194
+ case "handler":
195
+ return getOrCreateVirtualMethod(context, prop, () => {
196
+ return (
197
+ handler: Parameters<
198
+ EffectBuilderSurface<
199
+ any,
200
+ any,
201
+ any,
202
+ any,
203
+ any,
204
+ any,
205
+ any,
206
+ any
207
+ >["handler"]
208
+ >[0],
209
+ ) =>
210
+ new EffectDecoratedProcedure({
211
+ ...effectDef,
212
+ handler,
213
+ });
214
+ });
215
+ case "router":
216
+ return getOrCreateVirtualMethod(context, prop, () => {
217
+ return (router: Router<ContractRouter<any>, any>) =>
218
+ enhanceEffectRouter(router, effectDef) as any;
219
+ });
220
+ case "lazy":
221
+ return getOrCreateVirtualMethod(context, prop, () => {
222
+ return (
223
+ loader: () => Promise<{
224
+ default: Router<ContractRouter<any>, any>;
225
+ }>,
226
+ ) => enhanceEffectRouter(lazy(loader), effectDef) as any;
227
+ });
228
+ default:
229
+ return unhandled();
230
+ }
231
+ },
232
+ virtualDescriptors: builderVirtualDescriptors,
233
+ virtualKeys: builderVirtualKeys,
234
+ wrapResult(context, _prop, result) {
235
+ if (!isBuilderLike(result)) {
236
+ return result;
237
+ }
238
+
239
+ return wrapBuilderLike(result, context.state);
240
+ },
241
+ });
242
+ }
61
243
 
62
244
  /**
63
245
  * Captures the stack trace at the call site for better error reporting in spans.
64
246
  * This is called at procedure definition time to capture where the procedure was defined.
65
- *
66
- * @returns A function that lazily extracts the relevant stack frame
67
247
  */
68
248
  export function addSpanStackTrace(): () => string | undefined {
69
249
  const ErrorConstructor = Error as typeof Error & {
@@ -102,49 +282,37 @@ export class EffectBuilder<
102
282
  TMeta extends Meta,
103
283
  TRequirementsProvided,
104
284
  TRuntimeError,
285
+ > implements EffectBuilderSurface<
286
+ TInitialContext,
287
+ TCurrentContext,
288
+ TInputSchema,
289
+ TOutputSchema,
290
+ TEffectErrorMap,
291
+ TMeta,
292
+ TRequirementsProvided,
293
+ TRuntimeError
105
294
  > {
106
- /**
107
- * This property holds the defined options and the effect-specific properties.
108
- */
109
- declare "~effect": EffectBuilderDef<
295
+ declare $config: EffectBuilderSurface<
296
+ TInitialContext,
297
+ TCurrentContext,
110
298
  TInputSchema,
111
299
  TOutputSchema,
112
300
  TEffectErrorMap,
113
301
  TMeta,
114
302
  TRequirementsProvided,
115
303
  TRuntimeError
116
- >;
117
- declare "~orpc": BuilderDef<
304
+ >["$config"];
305
+ declare $context: EffectBuilderSurface<
306
+ TInitialContext,
307
+ TCurrentContext,
118
308
  TInputSchema,
119
309
  TOutputSchema,
120
- EffectErrorMapToErrorMap<TEffectErrorMap>,
121
- TMeta
122
- >;
123
-
124
- constructor(
125
- def: EffectBuilderDef<
126
- TInputSchema,
127
- TOutputSchema,
128
- TEffectErrorMap,
129
- TMeta,
130
- TRequirementsProvided,
131
- TRuntimeError
132
- >,
133
- ) {
134
- const { runtime, spanConfig, effectErrorMap, ...orpcDef } = def;
135
- this["~orpc"] = orpcDef;
136
- this["~effect"] = { runtime, spanConfig, effectErrorMap, ...orpcDef };
137
- }
138
-
139
- /**
140
- * Sets or overrides the config.
141
- *
142
- * @see {@link https://orpc.dev/docs/client/server-side#middlewares-order Middlewares Order Docs}
143
- * @see {@link https://orpc.dev/docs/best-practices/dedupe-middleware#configuration Dedupe Middleware Docs}
144
- */
145
- $config(
146
- config: BuilderConfig,
147
- ): EffectBuilder<
310
+ TEffectErrorMap,
311
+ TMeta,
312
+ TRequirementsProvided,
313
+ TRuntimeError
314
+ >["$context"];
315
+ declare $meta: EffectBuilderSurface<
148
316
  TInitialContext,
149
317
  TCurrentContext,
150
318
  TInputSchema,
@@ -153,110 +321,36 @@ export class EffectBuilder<
153
321
  TMeta,
154
322
  TRequirementsProvided,
155
323
  TRuntimeError
156
- > {
157
- const inputValidationCount =
158
- this["~effect"].inputValidationIndex -
159
- fallbackConfig(
160
- "initialInputValidationIndex",
161
- this["~effect"].config.initialInputValidationIndex,
162
- );
163
- const outputValidationCount =
164
- this["~effect"].outputValidationIndex -
165
- fallbackConfig(
166
- "initialOutputValidationIndex",
167
- this["~effect"].config.initialOutputValidationIndex,
168
- );
169
-
170
- return new EffectBuilder({
171
- ...this["~effect"],
172
- config,
173
- dedupeLeadingMiddlewares: fallbackConfig(
174
- "dedupeLeadingMiddlewares",
175
- config.dedupeLeadingMiddlewares,
176
- ),
177
- inputValidationIndex:
178
- fallbackConfig(
179
- "initialInputValidationIndex",
180
- config.initialInputValidationIndex,
181
- ) + inputValidationCount,
182
- outputValidationIndex:
183
- fallbackConfig(
184
- "initialOutputValidationIndex",
185
- config.initialOutputValidationIndex,
186
- ) + outputValidationCount,
187
- });
188
- }
189
-
190
- /**
191
- * Set or override the initial context.
192
- *
193
- * @see {@link https://orpc.dev/docs/context Context Docs}
194
- */
195
- $context<U extends Context>(): EffectBuilder<
196
- U & Record<never, never>,
197
- U,
324
+ >["$meta"];
325
+ declare $route: EffectBuilderSurface<
326
+ TInitialContext,
327
+ TCurrentContext,
198
328
  TInputSchema,
199
329
  TOutputSchema,
200
330
  TEffectErrorMap,
201
331
  TMeta,
202
332
  TRequirementsProvided,
203
333
  TRuntimeError
204
- > {
205
- /**
206
- * We need `& Record<never, never>` to deal with `has no properties in common with type` error
207
- */
208
-
209
- return new EffectBuilder({
210
- ...this["~effect"],
211
- middlewares: [],
212
- inputValidationIndex: fallbackConfig(
213
- "initialInputValidationIndex",
214
- this["~effect"].config.initialInputValidationIndex,
215
- ),
216
- outputValidationIndex: fallbackConfig(
217
- "initialOutputValidationIndex",
218
- this["~effect"].config.initialOutputValidationIndex,
219
- ),
220
- });
221
- }
222
-
223
- /**
224
- * Sets or overrides the initial meta.
225
- *
226
- * @see {@link https://orpc.dev/docs/metadata Metadata Docs}
227
- */
228
- $meta<U extends Meta>(
229
- initialMeta: U,
230
- ): EffectBuilder<
334
+ >["$route"];
335
+ declare $input: EffectBuilderSurface<
231
336
  TInitialContext,
232
337
  TCurrentContext,
233
338
  TInputSchema,
234
339
  TOutputSchema,
235
340
  TEffectErrorMap,
236
- U & Record<never, never>,
341
+ TMeta,
237
342
  TRequirementsProvided,
238
343
  TRuntimeError
239
- > {
240
- /**
241
- * We need `& Record<never, never>` to deal with `has no properties in common with type` error
242
- */
243
-
244
- return new EffectBuilder({
245
- ...this["~effect"],
246
- meta: initialMeta,
247
- });
248
- }
249
-
250
- /**
251
- * Sets or overrides the initial route.
252
- * This option is typically relevant when integrating with OpenAPI.
253
- *
254
- * @see {@link https://orpc.dev/docs/openapi/routing OpenAPI Routing Docs}
255
- * @see {@link https://orpc.dev/docs/openapi/input-output-structure OpenAPI Input/Output Structure Docs}
256
- */
257
- $route(
258
- initialRoute: Route,
259
- ): EffectBuilder<
344
+ >["$input"];
345
+ declare "~effect": EffectBuilderDef<
346
+ TInputSchema,
347
+ TOutputSchema,
348
+ TEffectErrorMap,
349
+ TMeta,
350
+ TRequirementsProvided,
351
+ TRuntimeError
352
+ >;
353
+ declare "~orpc": EffectBuilderSurface<
260
354
  TInitialContext,
261
355
  TCurrentContext,
262
356
  TInputSchema,
@@ -265,162 +359,38 @@ export class EffectBuilder<
265
359
  TMeta,
266
360
  TRequirementsProvided,
267
361
  TRuntimeError
268
- > {
269
- return new EffectBuilder({
270
- ...this["~effect"],
271
- route: initialRoute,
272
- });
273
- }
274
-
275
- /**
276
- * Sets or overrides the initial input schema.
277
- *
278
- * @see {@link https://orpc.dev/docs/procedure#initial-configuration Initial Procedure Configuration Docs}
279
- */
280
- $input<U extends AnySchema>(
281
- initialInputSchema?: U,
282
- ): EffectBuilder<
362
+ >["~orpc"];
363
+ declare middleware: EffectBuilderSurface<
283
364
  TInitialContext,
284
365
  TCurrentContext,
285
- U,
366
+ TInputSchema,
286
367
  TOutputSchema,
287
368
  TEffectErrorMap,
288
369
  TMeta,
289
370
  TRequirementsProvided,
290
371
  TRuntimeError
291
- > {
292
- return new EffectBuilder({
293
- ...this["~effect"],
294
- inputSchema: initialInputSchema,
295
- });
296
- }
297
-
298
- /**
299
- * Creates a middleware.
300
- *
301
- * @see {@link https://orpc.dev/docs/middleware Middleware Docs}
302
- */
303
- middleware<
304
- UOutContext extends IntersectPick<TCurrentContext, UOutContext>,
305
- TInput,
306
- TOutput = any,
307
- >(
308
- middleware: Middleware<
309
- TInitialContext,
310
- UOutContext,
311
- TInput,
312
- TOutput,
313
- EffectErrorConstructorMap<TEffectErrorMap>,
314
- TMeta
315
- >,
316
- ): DecoratedMiddleware<
317
- TInitialContext,
318
- UOutContext,
319
- TInput,
320
- TOutput,
321
- any,
322
- TMeta
323
- > {
324
- return decorateMiddleware(middleware);
325
- }
326
-
327
- /**
328
- * Adds type-safe custom errors.
329
- * Supports both traditional oRPC error definitions and ORPCTaggedError classes.
330
- *
331
- * @example
332
- * ```ts
333
- * // Traditional format
334
- * builder.errors({ BAD_REQUEST: { status: 400, message: 'Bad request' } })
335
- *
336
- * // Tagged error class
337
- * builder.errors({ USER_NOT_FOUND: UserNotFoundError })
338
- *
339
- * // Mixed
340
- * builder.errors({
341
- * BAD_REQUEST: { status: 400 },
342
- * USER_NOT_FOUND: UserNotFoundError,
343
- * })
344
- * ```
345
- *
346
- * @see {@link https://orpc.dev/docs/error-handling#type%E2%80%90safe-error-handling Type-Safe Error Handling Docs}
347
- */
348
- errors<U extends EffectErrorMap>(
349
- errors: U,
350
- ): EffectBuilder<
372
+ >["middleware"];
373
+ declare errors: EffectBuilderSurface<
351
374
  TInitialContext,
352
375
  TCurrentContext,
353
376
  TInputSchema,
354
377
  TOutputSchema,
355
- MergedEffectErrorMap<TEffectErrorMap, U>,
378
+ TEffectErrorMap,
356
379
  TMeta,
357
380
  TRequirementsProvided,
358
381
  TRuntimeError
359
- > {
360
- const newEffectErrorMap: MergedEffectErrorMap<TEffectErrorMap, U> = {
361
- ...this["~effect"].effectErrorMap,
362
- ...errors,
363
- };
364
- return new EffectBuilder({
365
- ...this["~effect"],
366
- errorMap: effectErrorMapToErrorMap(newEffectErrorMap),
367
- effectErrorMap: newEffectErrorMap,
368
- });
369
- }
370
-
371
- /**
372
- * Uses a middleware to modify the context or improve the pipeline.
373
- *
374
- * @info Supports both normal middleware and inline middleware implementations.
375
- * @note The current context must be satisfy middleware dependent-context
376
- * @see {@link https://orpc.dev/docs/middleware Middleware Docs}
377
- */
378
- use<
379
- UOutContext extends IntersectPick<TCurrentContext, UOutContext>,
380
- UInContext extends Context = TCurrentContext,
381
- >(
382
- middleware: Middleware<
383
- UInContext | TCurrentContext,
384
- UOutContext,
385
- InferSchemaOutput<TInputSchema>,
386
- unknown,
387
- EffectErrorConstructorMap<TEffectErrorMap>,
388
- TMeta
389
- >,
390
- ): EffectBuilder<
391
- MergedInitialContext<TInitialContext, UInContext, TCurrentContext>,
392
- MergedCurrentContext<TCurrentContext, UOutContext>,
382
+ >["errors"];
383
+ declare use: EffectBuilderSurface<
384
+ TInitialContext,
385
+ TCurrentContext,
393
386
  TInputSchema,
394
387
  TOutputSchema,
395
388
  TEffectErrorMap,
396
389
  TMeta,
397
390
  TRequirementsProvided,
398
391
  TRuntimeError
399
- >;
400
-
401
- use(
402
- middleware: AnyMiddleware,
403
- mapInput?: MapInputMiddleware<any, any>,
404
- ): EffectBuilder<any, any, any, any, any, any, any, any> {
405
- const mapped = mapInput
406
- ? decorateMiddleware(middleware).mapInput(mapInput)
407
- : middleware;
408
-
409
- return new EffectBuilder({
410
- ...this["~effect"],
411
- middlewares: addMiddleware(this["~effect"].middlewares, mapped),
412
- });
413
- }
414
-
415
- /**
416
- * Sets or updates the metadata.
417
- * The provided metadata is spared-merged with any existing metadata.
418
- *
419
- * @see {@link https://orpc.dev/docs/metadata Metadata Docs}
420
- */
421
- meta(
422
- meta: TMeta,
423
- ): EffectBuilder<
392
+ >["use"];
393
+ declare meta: EffectBuilderSurface<
424
394
  TInitialContext,
425
395
  TCurrentContext,
426
396
  TInputSchema,
@@ -429,24 +399,8 @@ export class EffectBuilder<
429
399
  TMeta,
430
400
  TRequirementsProvided,
431
401
  TRuntimeError
432
- > {
433
- return new EffectBuilder({
434
- ...this["~effect"],
435
- meta: mergeMeta(this["~effect"].meta, meta),
436
- });
437
- }
438
-
439
- /**
440
- * Sets or updates the route definition.
441
- * The provided route is spared-merged with any existing route.
442
- * This option is typically relevant when integrating with OpenAPI.
443
- *
444
- * @see {@link https://orpc.dev/docs/openapi/routing OpenAPI Routing Docs}
445
- * @see {@link https://orpc.dev/docs/openapi/input-output-structure OpenAPI Input/Output Structure Docs}
446
- */
447
- route(
448
- route: Route,
449
- ): EffectBuilder<
402
+ >["meta"];
403
+ declare route: EffectBuilderSurface<
450
404
  TInitialContext,
451
405
  TCurrentContext,
452
406
  TInputSchema,
@@ -455,98 +409,28 @@ export class EffectBuilder<
455
409
  TMeta,
456
410
  TRequirementsProvided,
457
411
  TRuntimeError
458
- > {
459
- return new EffectBuilder({
460
- ...this["~effect"],
461
- route: mergeRoute(this["~effect"].route, route),
462
- });
463
- }
464
-
465
- /**
466
- * Defines the input validation schema.
467
- *
468
- * @see {@link https://orpc.dev/docs/procedure#input-output-validation Input Validation Docs}
469
- */
470
- input<USchema extends AnySchema>(
471
- schema: USchema,
472
- ): EffectProcedureBuilderWithInput<
412
+ >["route"];
413
+ declare input: EffectBuilderSurface<
473
414
  TInitialContext,
474
415
  TCurrentContext,
475
- USchema,
416
+ TInputSchema,
476
417
  TOutputSchema,
477
418
  TEffectErrorMap,
478
419
  TMeta,
479
420
  TRequirementsProvided,
480
421
  TRuntimeError
481
- > {
482
- return new EffectBuilder({
483
- ...this["~effect"],
484
- inputSchema: schema,
485
- inputValidationIndex:
486
- fallbackConfig(
487
- "initialInputValidationIndex",
488
- this["~effect"].config.initialInputValidationIndex,
489
- ) + this["~effect"].middlewares.length,
490
- // we cast to any because EffectProcedureBuilderWithInput is expecting
491
- // use() input type to be defined, and EffectBuilder types its use() input
492
- // to unknown to allow any middleware to be passed
493
- // ---
494
- // note: the original implentation of the builder also uses any for the same reason
495
- }) as any;
496
- }
497
-
498
- /**
499
- * Defines the output validation schema.
500
- *
501
- * @see {@link https://orpc.dev/docs/procedure#input-output-validation Output Validation Docs}
502
- */
503
- output<USchema extends AnySchema>(
504
- schema: USchema,
505
- ): EffectProcedureBuilderWithOutput<
422
+ >["input"];
423
+ declare output: EffectBuilderSurface<
506
424
  TInitialContext,
507
425
  TCurrentContext,
508
426
  TInputSchema,
509
- USchema,
427
+ TOutputSchema,
510
428
  TEffectErrorMap,
511
429
  TMeta,
512
430
  TRequirementsProvided,
513
431
  TRuntimeError
514
- > {
515
- // We cast to any because EffectProcedureBuilderWithOutput narrows
516
- // handler/effect output typing based on the declared output schema.
517
- return new EffectBuilder({
518
- ...this["~effect"],
519
- outputSchema: schema,
520
- outputValidationIndex:
521
- fallbackConfig(
522
- "initialOutputValidationIndex",
523
- this["~effect"].config.initialOutputValidationIndex,
524
- ) + this["~effect"].middlewares.length,
525
- }) as any;
526
- }
527
-
528
- /**
529
- * Adds a traceable span to the procedure for telemetry.
530
- * The span name is used for Effect tracing via `Effect.withSpan`.
531
- * Stack trace is captured at the call site for better error reporting.
532
- *
533
- * @param spanName - The name of the span for telemetry (e.g., 'users.getUser')
534
- * @returns An EffectBuilder with span tracing configured
535
- *
536
- * @example
537
- * ```ts
538
- * const getUser = effectOs
539
- * .input(z.object({ id: z.string() }))
540
- * .traced('users.getUser')
541
- * .effect(function* ({ input }) {
542
- * const userService = yield* UserService
543
- * return yield* userService.findById(input.id)
544
- * })
545
- * ```
546
- */
547
- traced(
548
- spanName: string,
549
- ): EffectBuilder<
432
+ >["output"];
433
+ declare traced: EffectBuilderSurface<
550
434
  TInitialContext,
551
435
  TCurrentContext,
552
436
  TInputSchema,
@@ -555,184 +439,97 @@ export class EffectBuilder<
555
439
  TMeta,
556
440
  TRequirementsProvided,
557
441
  TRuntimeError
558
- > {
559
- return new EffectBuilder({
560
- ...this["~effect"],
561
- spanConfig: {
562
- name: spanName,
563
- captureStackTrace: addSpanStackTrace(),
564
- },
565
- });
566
- }
567
-
568
- handler<UFuncOutput>(
569
- handler: ProcedureHandler<
570
- TCurrentContext,
571
- InferSchemaOutput<TInputSchema>,
572
- UFuncOutput,
573
- EffectErrorMapToErrorMap<TEffectErrorMap>,
574
- TMeta
575
- >,
576
- ): EffectDecoratedProcedure<
442
+ >["traced"];
443
+ declare handler: EffectBuilderSurface<
577
444
  TInitialContext,
578
445
  TCurrentContext,
579
446
  TInputSchema,
580
- Schema<UFuncOutput, UFuncOutput>,
447
+ TOutputSchema,
581
448
  TEffectErrorMap,
582
449
  TMeta,
583
450
  TRequirementsProvided,
584
451
  TRuntimeError
585
- > {
586
- return new EffectDecoratedProcedure({
587
- ...this["~effect"],
588
- handler,
589
- });
590
- }
591
-
592
- /**
593
- * Defines the handler of the procedure using an Effect.
594
- * The Effect is executed using the ManagedRuntime provided during builder creation.
595
- * The effect is automatically wrapped with `Effect.withSpan`.
596
- *
597
- * @see {@link https://orpc.dev/docs/procedure Procedure Docs}
598
- */
599
- effect<UFuncOutput>(
600
- effectFn: EffectProcedureHandler<
601
- TCurrentContext,
602
- TInputSchema,
603
- UFuncOutput,
604
- TEffectErrorMap,
605
- TRequirementsProvided,
606
- TMeta
607
- >,
608
- ): EffectDecoratedProcedure<
452
+ >["handler"];
453
+ declare effect: EffectBuilderSurface<
609
454
  TInitialContext,
610
455
  TCurrentContext,
611
456
  TInputSchema,
612
- Schema<UFuncOutput, UFuncOutput>,
457
+ TOutputSchema,
613
458
  TEffectErrorMap,
614
459
  TMeta,
615
460
  TRequirementsProvided,
616
461
  TRuntimeError
617
- > {
618
- const { runtime, spanConfig } = this["~effect"];
619
- // Capture stack trace at definition time for default tracing
620
- const defaultCaptureStackTrace = addSpanStackTrace();
621
- return new EffectDecoratedProcedure({
622
- ...this["~effect"],
623
- handler: async (opts) => {
624
- return createEffectProcedureHandler({
625
- runtime,
626
- effectErrorMap: this["~effect"].effectErrorMap,
627
- effectFn,
628
- spanConfig,
629
- defaultCaptureStackTrace,
630
- })(opts as any);
631
- },
632
- });
633
- }
634
-
635
- /**
636
- * Prefixes all procedures in the router.
637
- * The provided prefix is post-appended to any existing router prefix.
638
- *
639
- * @note This option does not affect procedures that do not define a path in their route definition.
640
- *
641
- * @see {@link https://orpc.dev/docs/openapi/routing#route-prefixes OpenAPI Route Prefixes Docs}
642
- */
643
- prefix(
644
- prefix: HTTPPath,
645
- ): EffectRouterBuilder<
462
+ >["effect"];
463
+ declare prefix: EffectBuilderSurface<
646
464
  TInitialContext,
647
465
  TCurrentContext,
466
+ TInputSchema,
467
+ TOutputSchema,
648
468
  TEffectErrorMap,
649
469
  TMeta,
650
470
  TRequirementsProvided,
651
471
  TRuntimeError
652
- > {
653
- return new EffectBuilder({
654
- ...this["~effect"],
655
- prefix: mergePrefix(this["~effect"].prefix, prefix),
656
- }) as any;
657
- }
658
-
659
- /**
660
- * Adds tags to all procedures in the router.
661
- * This helpful when you want to group procedures together in the OpenAPI specification.
662
- *
663
- * @see {@link https://orpc.dev/docs/openapi/openapi-specification#operation-metadata OpenAPI Operation Metadata Docs}
664
- */
665
- tag(
666
- ...tags: string[]
667
- ): EffectRouterBuilder<
472
+ >["prefix"];
473
+ declare tag: EffectBuilderSurface<
668
474
  TInitialContext,
669
475
  TCurrentContext,
476
+ TInputSchema,
477
+ TOutputSchema,
670
478
  TEffectErrorMap,
671
479
  TMeta,
672
480
  TRequirementsProvided,
673
481
  TRuntimeError
674
- > {
675
- return new EffectBuilder({
676
- ...this["~effect"],
677
- tags: mergeTags(this["~effect"].tags, tags),
678
- }) as any;
679
- }
680
-
681
- /**
682
- * Applies all of the previously defined options to the specified router.
683
- *
684
- * @see {@link https://orpc.dev/docs/router#extending-router Extending Router Docs}
685
- */
686
- router<U extends Router<ContractRouter<TMeta>, TCurrentContext>>(
687
- router: U,
688
- ): EnhancedEffectRouter<
689
- U,
482
+ >["tag"];
483
+ declare router: EffectBuilderSurface<
690
484
  TInitialContext,
691
485
  TCurrentContext,
692
- TEffectErrorMap
693
- > {
694
- return enhanceEffectRouter(router, {
695
- ...this["~effect"],
696
- }) as any; // Type instantiation is excessively deep and possibly infinite
697
- }
698
-
699
- /**
700
- * Create a lazy router
701
- * And applies all of the previously defined options to the specified router.
702
- *
703
- * @see {@link https://orpc.dev/docs/router#extending-router Extending Router Docs}
704
- */
705
- lazy<U extends Router<ContractRouter<TMeta>, TCurrentContext>>(
706
- loader: () => Promise<{ default: U }>,
707
- ): EnhancedEffectRouter<
708
- Lazy<U>,
486
+ TInputSchema,
487
+ TOutputSchema,
488
+ TEffectErrorMap,
489
+ TMeta,
490
+ TRequirementsProvided,
491
+ TRuntimeError
492
+ >["router"];
493
+ declare lazy: EffectBuilderSurface<
709
494
  TInitialContext,
710
495
  TCurrentContext,
711
- TEffectErrorMap
712
- > {
713
- return enhanceEffectRouter(lazy(loader), {
714
- ...this["~effect"],
715
- }) as any; // Type instantiation is excessively deep and possibly infinite
496
+ TInputSchema,
497
+ TOutputSchema,
498
+ TEffectErrorMap,
499
+ TMeta,
500
+ TRequirementsProvided,
501
+ TRuntimeError
502
+ >["lazy"];
503
+
504
+ constructor(
505
+ def: EffectBuilderDef<
506
+ TInputSchema,
507
+ TOutputSchema,
508
+ TEffectErrorMap,
509
+ TMeta,
510
+ TRequirementsProvided,
511
+ TRuntimeError
512
+ >,
513
+ builder?: AnyBuilderLike,
514
+ ) {
515
+ const { runtime, spanConfig, effectErrorMap, ...orpcDef } = def;
516
+
517
+ attachEffectState(this, builder ?? new Builder(orpcDef), {
518
+ effectErrorMap,
519
+ runtime,
520
+ spanConfig,
521
+ });
522
+
523
+ return createEffectBuilderProxy(this);
716
524
  }
717
525
  }
718
526
 
719
527
  /**
720
528
  * Creates an Effect-aware procedure builder with the specified ManagedRuntime.
721
- * Uses the default `os` builder from `@orpc/server`.
529
+ * Uses the default builder shape from `@orpc/server`.
722
530
  *
723
531
  * @param runtime - The ManagedRuntime that provides services for Effect procedures
724
532
  * @returns An EffectBuilder instance for creating Effect-native procedures
725
- *
726
- * @example
727
- * ```ts
728
- * import { makeEffectORPC } from '@orpc/effect'
729
- * import { Effect, Layer, ManagedRuntime } from 'effect'
730
- *
731
- * const runtime = ManagedRuntime.make(Layer.empty)
732
- * const effectOs = makeEffectORPC(runtime)
733
- *
734
- * const hello = effectOs.effect(() => Effect.succeed('Hello!'))
735
- * ```
736
533
  */
737
534
  export function makeEffectORPC<TRequirementsProvided, TRuntimeError>(
738
535
  runtime: ManagedRuntime.ManagedRuntime<TRequirementsProvided, TRuntimeError>,
@@ -752,31 +549,8 @@ export function makeEffectORPC<TRequirementsProvided, TRuntimeError>(
752
549
  * with the specified ManagedRuntime.
753
550
  *
754
551
  * @param runtime - The ManagedRuntime that provides services for Effect procedures
755
- * @param builder - The oRPC Builder instance to wrap (e.g., a customized `os`)
552
+ * @param builder - The oRPC Builder instance to wrap
756
553
  * @returns An EffectBuilder instance that extends the original builder with Effect support
757
- *
758
- * @example
759
- * ```ts
760
- * import { makeEffectORPC } from '@orpc/effect'
761
- * import { os } from '@orpc/server'
762
- * import { Effect, Layer, ManagedRuntime } from 'effect'
763
- *
764
- * // Create a customized builder
765
- * const authedOs = os.use(authMiddleware)
766
- *
767
- * // Wrap it with Effect support
768
- * const runtime = ManagedRuntime.make(UserServiceLive)
769
- * const effectOs = makeEffectORPC(runtime, authedOs)
770
- *
771
- * const getUser = effectOs
772
- * .input(z.object({ id: z.string() }))
773
- * .effect(
774
- * Effect.fn(function* ({ input }) {
775
- * const userService = yield* UserService
776
- * return yield* userService.findById(input.id)
777
- * })
778
- * )
779
- * ```
780
554
  */
781
555
  export function makeEffectORPC<
782
556
  TBuilder extends AnyBuilderLike<
@@ -819,23 +593,27 @@ export function makeEffectORPC<TRequirementsProvided, TRuntimeError>(
819
593
  TRuntimeError
820
594
  > {
821
595
  const resolvedBuilder = builder ?? emptyBuilder();
822
- return new EffectBuilder({
823
- ...resolvedBuilder["~orpc"],
824
- errorMap: effectErrorMapToErrorMap(resolvedBuilder["~orpc"].errorMap),
825
- effectErrorMap: resolvedBuilder["~orpc"].errorMap,
826
- runtime,
827
- });
596
+ const effectErrorMap = getEffectErrorMap(resolvedBuilder);
597
+ return new EffectBuilder(
598
+ {
599
+ ...resolvedBuilder["~orpc"],
600
+ effectErrorMap: effectErrorMap,
601
+ errorMap: effectErrorMapToErrorMap(effectErrorMap),
602
+ runtime,
603
+ },
604
+ unwrapEffectUpstream(resolvedBuilder),
605
+ );
828
606
  }
829
607
 
830
608
  function emptyBuilder(): AnyBuilderLike {
831
609
  return new Builder({
832
610
  config: {},
833
- route: {},
834
- meta: {},
611
+ dedupeLeadingMiddlewares: true,
835
612
  errorMap: {},
836
613
  inputValidationIndex: fallbackConfig("initialInputValidationIndex"),
837
- outputValidationIndex: fallbackConfig("initialOutputValidationIndex"),
614
+ meta: {},
838
615
  middlewares: [],
839
- dedupeLeadingMiddlewares: true,
616
+ outputValidationIndex: fallbackConfig("initialOutputValidationIndex"),
617
+ route: {},
840
618
  });
841
619
  }