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.
package/dist/index.js CHANGED
@@ -7,14 +7,7 @@ import { isContractProcedure as isContractProcedure2 } from "@orpc/contract";
7
7
  import { implement } from "@orpc/server";
8
8
 
9
9
  // src/effect-builder.ts
10
- import { mergeMeta as mergeMeta2, mergePrefix as mergePrefix2, mergeRoute as mergeRoute2, mergeTags } from "@orpc/contract";
11
- import {
12
- addMiddleware as addMiddleware2,
13
- Builder,
14
- decorateMiddleware as decorateMiddleware2,
15
- fallbackConfig,
16
- lazy as lazy2
17
- } from "@orpc/server";
10
+ import { Builder, fallbackConfig, lazy as lazy2 } from "@orpc/server";
18
11
 
19
12
  // src/effect-enhance-router.ts
20
13
  import {
@@ -41,6 +34,177 @@ import {
41
34
  Procedure
42
35
  } from "@orpc/server";
43
36
 
37
+ // src/extension/compose-surfaces.ts
38
+ function composeSurfaceProxy(surface, target) {
39
+ return new Proxy(target, {
40
+ get(currentTarget, prop, receiver) {
41
+ return Reflect.has(surface, prop) ? Reflect.get(surface, prop, surface) : Reflect.get(currentTarget, prop, receiver);
42
+ },
43
+ has(currentTarget, prop) {
44
+ return Reflect.has(surface, prop) || Reflect.has(currentTarget, prop);
45
+ }
46
+ });
47
+ }
48
+
49
+ // src/extension/state.ts
50
+ var effectInternalsSymbol = /* @__PURE__ */ Symbol("effect-orpc/internals");
51
+ function attachEffectState(target, upstream, state) {
52
+ Object.defineProperties(target, {
53
+ [effectInternalsSymbol]: {
54
+ configurable: true,
55
+ value: {
56
+ methodCache: /* @__PURE__ */ new Map(),
57
+ state,
58
+ upstream
59
+ }
60
+ }
61
+ });
62
+ }
63
+ function getEffectInternals(target) {
64
+ return target[effectInternalsSymbol];
65
+ }
66
+ function getEffectUpstream(target) {
67
+ return getEffectInternals(target).upstream;
68
+ }
69
+ function hasEffectState(value) {
70
+ return typeof value === "object" && value !== null && effectInternalsSymbol in value;
71
+ }
72
+ function assertEffectState(value) {
73
+ if (!hasEffectState(value)) {
74
+ throw new Error("Expected effect state to be attached");
75
+ }
76
+ }
77
+ function getEffectErrorMap(value) {
78
+ return value["~effect"]?.effectErrorMap ?? value["~orpc"].errorMap;
79
+ }
80
+ function unwrapEffectUpstream(value) {
81
+ return hasEffectState(value) ? getEffectUpstream(value) : value;
82
+ }
83
+
84
+ // src/extension/create-node-proxy.ts
85
+ var unhandledProperty = /* @__PURE__ */ Symbol("effect-orpc/unhandledProperty");
86
+ function createNodeProxyContext(target) {
87
+ const internals = getEffectInternals(target);
88
+ return {
89
+ methodCache: internals.methodCache,
90
+ state: internals.state,
91
+ target,
92
+ upstream: internals.upstream
93
+ };
94
+ }
95
+ function createBoundMethod(context, prop, value, config, receiver) {
96
+ const cache = context.methodCache;
97
+ if (cache.has(prop)) {
98
+ return cache.get(prop);
99
+ }
100
+ const wrapped = (...args) => {
101
+ const result = Reflect.apply(value, context.upstream, args);
102
+ return config.wrapResult?.(context, prop, result, receiver) ?? result;
103
+ };
104
+ cache.set(prop, wrapped);
105
+ return wrapped;
106
+ }
107
+ function createNodeProxy(target, config) {
108
+ const privateKeys = /* @__PURE__ */ new Set([effectInternalsSymbol]);
109
+ const virtualKeys = new Set(config.virtualKeys ?? []);
110
+ return new Proxy(target, {
111
+ get(currentTarget, prop, receiver) {
112
+ if (privateKeys.has(prop)) {
113
+ return Reflect.get(currentTarget, prop, receiver);
114
+ }
115
+ const context = createNodeProxyContext(
116
+ currentTarget
117
+ );
118
+ const virtualValue = config.getVirtual?.(context, prop, receiver);
119
+ if (virtualValue !== void 0 && virtualValue !== unhandledProperty) {
120
+ return virtualValue;
121
+ }
122
+ const propertyValue = config.getProperty?.(context, prop, receiver);
123
+ if (propertyValue !== void 0 && propertyValue !== unhandledProperty) {
124
+ return propertyValue;
125
+ }
126
+ const sourceValue = Reflect.get(context.upstream, prop, context.upstream);
127
+ if (Reflect.has(context.upstream, prop)) {
128
+ if (typeof sourceValue === "function") {
129
+ return createBoundMethod(
130
+ context,
131
+ prop,
132
+ sourceValue,
133
+ config,
134
+ receiver
135
+ );
136
+ }
137
+ return sourceValue;
138
+ }
139
+ return Reflect.get(currentTarget, prop, receiver);
140
+ },
141
+ has(currentTarget, prop) {
142
+ if (virtualKeys.has(prop)) {
143
+ return true;
144
+ }
145
+ const context = createNodeProxyContext(currentTarget);
146
+ return Reflect.has(context.upstream, prop) || Reflect.has(currentTarget, prop);
147
+ },
148
+ ownKeys(currentTarget) {
149
+ const keys = /* @__PURE__ */ new Set();
150
+ for (const key of Reflect.ownKeys(currentTarget)) {
151
+ if (!privateKeys.has(key)) {
152
+ keys.add(key);
153
+ }
154
+ }
155
+ const context = createNodeProxyContext(
156
+ currentTarget
157
+ );
158
+ for (const key of Reflect.ownKeys(context.upstream)) {
159
+ keys.add(key);
160
+ }
161
+ for (const key of virtualKeys) {
162
+ keys.add(key);
163
+ }
164
+ return [...keys];
165
+ },
166
+ getOwnPropertyDescriptor(currentTarget, prop) {
167
+ const context = createNodeProxyContext(
168
+ currentTarget
169
+ );
170
+ if (virtualKeys.has(prop)) {
171
+ const value = config.getVirtual?.(context, prop, currentTarget);
172
+ if (value !== void 0 && value !== unhandledProperty) {
173
+ return {
174
+ configurable: true,
175
+ enumerable: config.virtualDescriptors?.[prop]?.enumerable ?? false,
176
+ value,
177
+ writable: false
178
+ };
179
+ }
180
+ }
181
+ const descriptor = Reflect.getOwnPropertyDescriptor(
182
+ context.upstream,
183
+ prop
184
+ );
185
+ if (descriptor === void 0) {
186
+ return Reflect.getOwnPropertyDescriptor(currentTarget, prop);
187
+ }
188
+ if ("value" in descriptor && typeof descriptor.value === "function") {
189
+ return {
190
+ ...descriptor,
191
+ value: createBoundMethod(
192
+ context,
193
+ prop,
194
+ descriptor.value,
195
+ config,
196
+ currentTarget
197
+ )
198
+ };
199
+ }
200
+ return descriptor;
201
+ }
202
+ });
203
+ }
204
+ function unhandled() {
205
+ return unhandledProperty;
206
+ }
207
+
44
208
  // src/tagged-error.ts
45
209
  import {
46
210
  fallbackORPCErrorMessage,
@@ -175,98 +339,141 @@ function effectErrorMapToErrorMap(errorMap) {
175
339
  }
176
340
 
177
341
  // src/effect-procedure.ts
178
- var EffectProcedure = class extends Procedure {
179
- constructor(def) {
180
- super(def);
181
- this["~effect"] = def;
182
- }
342
+ var procedureVirtualDescriptors = {
343
+ "~effect": { enumerable: true },
344
+ actionable: { enumerable: false },
345
+ callable: { enumerable: false },
346
+ errors: { enumerable: false },
347
+ meta: { enumerable: false },
348
+ route: { enumerable: false },
349
+ use: { enumerable: false }
183
350
  };
184
- var EffectDecoratedProcedure = class _EffectDecoratedProcedure extends EffectProcedure {
185
- constructor(def) {
186
- super(def);
187
- this["~effect"] = def;
188
- }
189
- /**
190
- * Adds type-safe custom errors.
191
- * Supports both traditional oRPC error definitions and ORPCTaggedError classes.
192
- *
193
- * @see {@link https://orpc.dev/docs/error-handling#type%E2%80%90safe-error-handling Type-Safe Error Handling Docs}
194
- */
195
- errors(errors) {
196
- const newEffectErrorMap = {
197
- ...this["~effect"].effectErrorMap,
198
- ...errors
199
- };
200
- return new _EffectDecoratedProcedure({
201
- ...this["~effect"],
202
- effectErrorMap: newEffectErrorMap,
203
- errorMap: effectErrorMapToErrorMap(newEffectErrorMap)
204
- });
205
- }
206
- /**
207
- * Sets or updates the metadata.
208
- * The provided metadata is spared-merged with any existing metadata.
209
- *
210
- * @see {@link https://orpc.dev/docs/metadata Metadata Docs}
211
- */
212
- meta(meta) {
213
- return new _EffectDecoratedProcedure({
214
- ...this["~effect"],
215
- meta: mergeMeta(this["~effect"].meta, meta)
216
- });
217
- }
218
- /**
219
- * Sets or updates the route definition.
220
- * The provided route is spared-merged with any existing route.
221
- * This option is typically relevant when integrating with OpenAPI.
222
- *
223
- * @see {@link https://orpc.dev/docs/openapi/routing OpenAPI Routing Docs}
224
- * @see {@link https://orpc.dev/docs/openapi/input-output-structure OpenAPI Input/Output Structure Docs}
225
- */
226
- route(route) {
227
- return new _EffectDecoratedProcedure({
228
- ...this["~effect"],
229
- route: mergeRoute(this["~effect"].route, route)
230
- });
231
- }
232
- use(middleware, mapInput) {
233
- const mapped = mapInput ? decorateMiddleware(middleware).mapInput(mapInput) : middleware;
234
- return new _EffectDecoratedProcedure({
235
- ...this["~effect"],
236
- middlewares: addMiddleware(this["~effect"].middlewares, mapped)
237
- });
351
+ var baseProcedureVirtualKeys = ["~effect"];
352
+ var decoratedProcedureVirtualKeys = [
353
+ ...baseProcedureVirtualKeys,
354
+ "errors",
355
+ "meta",
356
+ "route",
357
+ "use",
358
+ "callable",
359
+ "actionable"
360
+ ];
361
+ function getOrCreateVirtualMethod(context, prop, factory) {
362
+ const cache = context.methodCache;
363
+ if (cache.has(prop)) {
364
+ return cache.get(prop);
238
365
  }
239
- /**
240
- * Make this procedure callable (works like a function while still being a procedure).
241
- *
242
- * @see {@link https://orpc.dev/docs/client/server-side Server-side Client Docs}
243
- */
244
- callable(...rest) {
245
- const client = createProcedureClient(this, ...rest);
246
- return new Proxy(client, {
247
- get: (target, key) => {
248
- return Reflect.has(this, key) ? Reflect.get(this, key) : Reflect.get(target, key);
249
- },
250
- has: (target, key) => {
251
- return Reflect.has(this, key) || Reflect.has(target, key);
366
+ const value = factory();
367
+ cache.set(prop, value);
368
+ return value;
369
+ }
370
+ function getEffectProcedureDef(context) {
371
+ return {
372
+ ...context.upstream["~orpc"],
373
+ effectErrorMap: context.state.effectErrorMap,
374
+ runtime: context.state.runtime
375
+ };
376
+ }
377
+ function createEffectProcedureProxy(target, decorated) {
378
+ return createNodeProxy(target, {
379
+ getVirtual(context, prop, receiver) {
380
+ if (prop === "~effect") {
381
+ return getEffectProcedureDef(context);
252
382
  }
253
- });
254
- }
255
- /**
256
- * Make this procedure compatible with server action.
257
- *
258
- * @see {@link https://orpc.dev/docs/server-action Server Action Docs}
259
- */
260
- actionable(...rest) {
261
- const action = createActionableClient(createProcedureClient(this, ...rest));
262
- return new Proxy(action, {
263
- get: (target, key) => {
264
- return Reflect.has(this, key) ? Reflect.get(this, key) : Reflect.get(target, key);
265
- },
266
- has: (target, key) => {
267
- return Reflect.has(this, key) || Reflect.has(target, key);
383
+ if (!decorated) {
384
+ return unhandled();
268
385
  }
386
+ const state = context.state;
387
+ switch (prop) {
388
+ case "errors":
389
+ return getOrCreateVirtualMethod(context, prop, () => {
390
+ return (errors) => {
391
+ const nextEffectErrorMap = {
392
+ ...state.effectErrorMap,
393
+ ...errors
394
+ };
395
+ return new EffectDecoratedProcedure({
396
+ ...getEffectProcedureDef(context),
397
+ effectErrorMap: nextEffectErrorMap,
398
+ errorMap: effectErrorMapToErrorMap(nextEffectErrorMap)
399
+ });
400
+ };
401
+ });
402
+ case "meta":
403
+ return getOrCreateVirtualMethod(context, prop, () => {
404
+ return (meta) => new EffectDecoratedProcedure({
405
+ ...getEffectProcedureDef(context),
406
+ meta: mergeMeta(getEffectProcedureDef(context).meta, meta)
407
+ });
408
+ });
409
+ case "route":
410
+ return getOrCreateVirtualMethod(context, prop, () => {
411
+ return (route) => new EffectDecoratedProcedure({
412
+ ...getEffectProcedureDef(context),
413
+ route: mergeRoute(getEffectProcedureDef(context).route, route)
414
+ });
415
+ });
416
+ case "use":
417
+ return getOrCreateVirtualMethod(context, prop, () => {
418
+ return (middleware, mapInput) => {
419
+ const mapped = mapInput ? decorateMiddleware(middleware).mapInput(mapInput) : middleware;
420
+ return new EffectDecoratedProcedure({
421
+ ...getEffectProcedureDef(context),
422
+ middlewares: addMiddleware(
423
+ getEffectProcedureDef(context).middlewares,
424
+ mapped
425
+ )
426
+ });
427
+ };
428
+ });
429
+ case "callable":
430
+ return (...rest) => {
431
+ const client = createProcedureClient(
432
+ receiver,
433
+ ...rest
434
+ );
435
+ return composeSurfaceProxy(
436
+ receiver,
437
+ client
438
+ );
439
+ };
440
+ case "actionable":
441
+ return (...rest) => {
442
+ const client = createProcedureClient(
443
+ receiver,
444
+ ...rest
445
+ );
446
+ const action = createActionableClient(client);
447
+ return composeSurfaceProxy(
448
+ receiver,
449
+ action
450
+ );
451
+ };
452
+ default:
453
+ return unhandled();
454
+ }
455
+ },
456
+ virtualDescriptors: procedureVirtualDescriptors,
457
+ virtualKeys: decorated ? decoratedProcedureVirtualKeys : baseProcedureVirtualKeys
458
+ });
459
+ }
460
+ var EffectProcedure = class _EffectProcedure extends Procedure {
461
+ constructor(def, procedure) {
462
+ super(def);
463
+ attachEffectState(this, procedure ?? new Procedure(def), {
464
+ effectErrorMap: def.effectErrorMap,
465
+ runtime: def.runtime
269
466
  });
467
+ if (new.target === _EffectProcedure) {
468
+ return createEffectProcedureProxy(this, false);
469
+ }
470
+ }
471
+ };
472
+ var EffectDecoratedProcedure = class extends EffectProcedure {
473
+ constructor(def, procedure) {
474
+ super(def, procedure);
475
+ assertEffectState(this);
476
+ return createEffectProcedureProxy(this, true);
270
477
  }
271
478
  };
272
479
 
@@ -274,44 +481,44 @@ var EffectDecoratedProcedure = class _EffectDecoratedProcedure extends EffectPro
274
481
  function enhanceEffectRouter(router, options) {
275
482
  if (isLazy(router)) {
276
483
  const laziedMeta = getLazyMeta(router);
277
- const enhancedPrefix = laziedMeta?.prefix ? mergePrefix(options.prefix, laziedMeta?.prefix) : options.prefix;
484
+ const enhancedPrefix = laziedMeta?.prefix ? mergePrefix(options.prefix, laziedMeta.prefix) : options.prefix;
278
485
  const enhanced2 = lazy(
279
486
  async () => {
280
487
  const { default: unlaziedRouter } = await unlazy(router);
281
- const enhanced3 = enhanceEffectRouter(unlaziedRouter, options);
282
- return unlazy(enhanced3);
488
+ const wrappedRouter = enhanceEffectRouter(unlaziedRouter, options);
489
+ return unlazy(wrappedRouter);
283
490
  },
284
491
  {
285
492
  ...laziedMeta,
286
493
  prefix: enhancedPrefix
287
494
  }
288
495
  );
289
- const accessible = createAccessibleLazyRouter(enhanced2);
290
- return accessible;
496
+ return createAccessibleLazyRouter(enhanced2);
291
497
  }
292
498
  if (isProcedure(router)) {
293
- const newMiddlewares = mergeMiddlewares(
499
+ const source = unwrapEffectUpstream(router);
500
+ const sourceEffectErrorMap = getEffectErrorMap(router);
501
+ const middlewares = mergeMiddlewares(
294
502
  options.middlewares,
295
- router["~orpc"].middlewares,
503
+ source["~orpc"].middlewares,
296
504
  { dedupeLeading: options.dedupeLeadingMiddlewares }
297
505
  );
298
- const newMiddlewareAdded = newMiddlewares.length - router["~orpc"].middlewares.length;
506
+ const newMiddlewareAdded = middlewares.length - source["~orpc"].middlewares.length;
299
507
  const effectErrorMap = {
300
508
  ...options.errorMap,
301
- ...router["~orpc"].errorMap
509
+ ...sourceEffectErrorMap
302
510
  };
303
511
  const errorMap = effectErrorMapToErrorMap(effectErrorMap);
304
- const enhanced2 = new EffectProcedure({
305
- ...router["~orpc"],
306
- route: enhanceRoute(router["~orpc"].route, options),
512
+ return new EffectProcedure({
513
+ ...source["~orpc"],
514
+ route: enhanceRoute(source["~orpc"].route, options),
307
515
  effectErrorMap,
308
516
  errorMap,
309
- middlewares: newMiddlewares,
310
- inputValidationIndex: router["~orpc"].inputValidationIndex + newMiddlewareAdded,
311
- outputValidationIndex: router["~orpc"].outputValidationIndex + newMiddlewareAdded,
517
+ middlewares,
518
+ inputValidationIndex: source["~orpc"].inputValidationIndex + newMiddlewareAdded,
519
+ outputValidationIndex: source["~orpc"].outputValidationIndex + newMiddlewareAdded,
312
520
  runtime: options.runtime
313
521
  });
314
- return enhanced2;
315
522
  }
316
523
  const enhanced = {};
317
524
  for (const key in router) {
@@ -401,6 +608,139 @@ function createEffectProcedureHandler(options) {
401
608
  }
402
609
 
403
610
  // src/effect-builder.ts
611
+ var builderVirtualDescriptors = {
612
+ "~effect": { enumerable: true },
613
+ effect: { enumerable: false },
614
+ errors: { enumerable: false },
615
+ handler: { enumerable: false },
616
+ lazy: { enumerable: false },
617
+ router: { enumerable: false },
618
+ traced: { enumerable: false }
619
+ };
620
+ var builderVirtualKeys = [
621
+ "~effect",
622
+ "errors",
623
+ "effect",
624
+ "traced",
625
+ "handler",
626
+ "router",
627
+ "lazy"
628
+ ];
629
+ function isBuilderLike(value) {
630
+ return typeof value === "object" && value !== null && "~orpc" in value;
631
+ }
632
+ function getOrCreateVirtualMethod2(context, prop, factory) {
633
+ const cache = context.methodCache;
634
+ if (cache.has(prop)) {
635
+ return cache.get(prop);
636
+ }
637
+ const value = factory();
638
+ cache.set(prop, value);
639
+ return value;
640
+ }
641
+ function getEffectBuilderDef(context) {
642
+ return {
643
+ ...context.upstream["~orpc"],
644
+ effectErrorMap: context.state.effectErrorMap,
645
+ runtime: context.state.runtime,
646
+ spanConfig: context.state.spanConfig
647
+ };
648
+ }
649
+ function wrapBuilderLike(builder, state) {
650
+ return new EffectBuilder(
651
+ {
652
+ ...builder["~orpc"],
653
+ effectErrorMap: state.effectErrorMap,
654
+ runtime: state.runtime,
655
+ spanConfig: state.spanConfig
656
+ },
657
+ unwrapEffectUpstream(builder)
658
+ );
659
+ }
660
+ function createEffectBuilderProxy(target) {
661
+ return createNodeProxy(target, {
662
+ getVirtual(context, prop) {
663
+ const effectDef = getEffectBuilderDef(context);
664
+ if (prop === "~effect") {
665
+ return getEffectBuilderDef(context);
666
+ }
667
+ const { upstream: source, state } = context;
668
+ switch (prop) {
669
+ case "errors":
670
+ return getOrCreateVirtualMethod2(context, prop, () => {
671
+ return (errors) => {
672
+ const nextEffectErrorMap = {
673
+ ...state.effectErrorMap,
674
+ ...errors
675
+ };
676
+ const nextBuilder = Reflect.apply(
677
+ Reflect.get(source, "errors", source),
678
+ source,
679
+ [effectErrorMapToErrorMap(errors)]
680
+ );
681
+ return wrapBuilderLike(nextBuilder, {
682
+ ...state,
683
+ effectErrorMap: nextEffectErrorMap
684
+ });
685
+ };
686
+ });
687
+ case "effect":
688
+ return getOrCreateVirtualMethod2(context, prop, () => {
689
+ return (effectFn) => {
690
+ const defaultCaptureStackTrace = addSpanStackTrace();
691
+ return new EffectDecoratedProcedure({
692
+ ...effectDef,
693
+ handler: async (opts) => {
694
+ return createEffectProcedureHandler({
695
+ defaultCaptureStackTrace,
696
+ effectErrorMap: state.effectErrorMap,
697
+ effectFn,
698
+ runtime: state.runtime,
699
+ spanConfig: state.spanConfig
700
+ })(opts);
701
+ }
702
+ });
703
+ };
704
+ });
705
+ case "traced":
706
+ return getOrCreateVirtualMethod2(context, prop, () => {
707
+ return (spanName) => wrapBuilderLike(source, {
708
+ ...state,
709
+ spanConfig: {
710
+ captureStackTrace: addSpanStackTrace(),
711
+ name: spanName
712
+ }
713
+ });
714
+ });
715
+ case "handler":
716
+ return getOrCreateVirtualMethod2(context, prop, () => {
717
+ return (handler) => new EffectDecoratedProcedure({
718
+ ...effectDef,
719
+ handler
720
+ });
721
+ });
722
+ case "router":
723
+ return getOrCreateVirtualMethod2(context, prop, () => {
724
+ return (router) => enhanceEffectRouter(router, effectDef);
725
+ });
726
+ case "lazy":
727
+ return getOrCreateVirtualMethod2(context, prop, () => {
728
+ return (loader) => enhanceEffectRouter(lazy2(loader), effectDef);
729
+ });
730
+ default:
731
+ return unhandled();
732
+ }
733
+ },
734
+ virtualDescriptors: builderVirtualDescriptors,
735
+ virtualKeys: builderVirtualKeys,
736
+ wrapResult(context, _prop, result) {
737
+ if (!isBuilderLike(result)) {
738
+ return result;
739
+ }
740
+ return wrapBuilderLike(result, context.state);
741
+ }
742
+ });
743
+ }
404
744
  function addSpanStackTrace() {
405
745
  const ErrorConstructor = Error;
406
746
  const limit = ErrorConstructor.stackTraceLimit;
@@ -421,330 +761,40 @@ function addSpanStackTrace() {
421
761
  }
422
762
  };
423
763
  }
424
- var EffectBuilder = class _EffectBuilder {
425
- constructor(def) {
764
+ var EffectBuilder = class {
765
+ constructor(def, builder) {
426
766
  const { runtime, spanConfig, effectErrorMap, ...orpcDef } = def;
427
- this["~orpc"] = orpcDef;
428
- this["~effect"] = { runtime, spanConfig, effectErrorMap, ...orpcDef };
429
- }
430
- /**
431
- * Sets or overrides the config.
432
- *
433
- * @see {@link https://orpc.dev/docs/client/server-side#middlewares-order Middlewares Order Docs}
434
- * @see {@link https://orpc.dev/docs/best-practices/dedupe-middleware#configuration Dedupe Middleware Docs}
435
- */
436
- $config(config) {
437
- const inputValidationCount = this["~effect"].inputValidationIndex - fallbackConfig(
438
- "initialInputValidationIndex",
439
- this["~effect"].config.initialInputValidationIndex
440
- );
441
- const outputValidationCount = this["~effect"].outputValidationIndex - fallbackConfig(
442
- "initialOutputValidationIndex",
443
- this["~effect"].config.initialOutputValidationIndex
444
- );
445
- return new _EffectBuilder({
446
- ...this["~effect"],
447
- config,
448
- dedupeLeadingMiddlewares: fallbackConfig(
449
- "dedupeLeadingMiddlewares",
450
- config.dedupeLeadingMiddlewares
451
- ),
452
- inputValidationIndex: fallbackConfig(
453
- "initialInputValidationIndex",
454
- config.initialInputValidationIndex
455
- ) + inputValidationCount,
456
- outputValidationIndex: fallbackConfig(
457
- "initialOutputValidationIndex",
458
- config.initialOutputValidationIndex
459
- ) + outputValidationCount
460
- });
461
- }
462
- /**
463
- * Set or override the initial context.
464
- *
465
- * @see {@link https://orpc.dev/docs/context Context Docs}
466
- */
467
- $context() {
468
- return new _EffectBuilder({
469
- ...this["~effect"],
470
- middlewares: [],
471
- inputValidationIndex: fallbackConfig(
472
- "initialInputValidationIndex",
473
- this["~effect"].config.initialInputValidationIndex
474
- ),
475
- outputValidationIndex: fallbackConfig(
476
- "initialOutputValidationIndex",
477
- this["~effect"].config.initialOutputValidationIndex
478
- )
479
- });
480
- }
481
- /**
482
- * Sets or overrides the initial meta.
483
- *
484
- * @see {@link https://orpc.dev/docs/metadata Metadata Docs}
485
- */
486
- $meta(initialMeta) {
487
- return new _EffectBuilder({
488
- ...this["~effect"],
489
- meta: initialMeta
490
- });
491
- }
492
- /**
493
- * Sets or overrides the initial route.
494
- * This option is typically relevant when integrating with OpenAPI.
495
- *
496
- * @see {@link https://orpc.dev/docs/openapi/routing OpenAPI Routing Docs}
497
- * @see {@link https://orpc.dev/docs/openapi/input-output-structure OpenAPI Input/Output Structure Docs}
498
- */
499
- $route(initialRoute) {
500
- return new _EffectBuilder({
501
- ...this["~effect"],
502
- route: initialRoute
503
- });
504
- }
505
- /**
506
- * Sets or overrides the initial input schema.
507
- *
508
- * @see {@link https://orpc.dev/docs/procedure#initial-configuration Initial Procedure Configuration Docs}
509
- */
510
- $input(initialInputSchema) {
511
- return new _EffectBuilder({
512
- ...this["~effect"],
513
- inputSchema: initialInputSchema
514
- });
515
- }
516
- /**
517
- * Creates a middleware.
518
- *
519
- * @see {@link https://orpc.dev/docs/middleware Middleware Docs}
520
- */
521
- middleware(middleware) {
522
- return decorateMiddleware2(middleware);
523
- }
524
- /**
525
- * Adds type-safe custom errors.
526
- * Supports both traditional oRPC error definitions and ORPCTaggedError classes.
527
- *
528
- * @example
529
- * ```ts
530
- * // Traditional format
531
- * builder.errors({ BAD_REQUEST: { status: 400, message: 'Bad request' } })
532
- *
533
- * // Tagged error class
534
- * builder.errors({ USER_NOT_FOUND: UserNotFoundError })
535
- *
536
- * // Mixed
537
- * builder.errors({
538
- * BAD_REQUEST: { status: 400 },
539
- * USER_NOT_FOUND: UserNotFoundError,
540
- * })
541
- * ```
542
- *
543
- * @see {@link https://orpc.dev/docs/error-handling#type%E2%80%90safe-error-handling Type-Safe Error Handling Docs}
544
- */
545
- errors(errors) {
546
- const newEffectErrorMap = {
547
- ...this["~effect"].effectErrorMap,
548
- ...errors
549
- };
550
- return new _EffectBuilder({
551
- ...this["~effect"],
552
- errorMap: effectErrorMapToErrorMap(newEffectErrorMap),
553
- effectErrorMap: newEffectErrorMap
554
- });
555
- }
556
- use(middleware, mapInput) {
557
- const mapped = mapInput ? decorateMiddleware2(middleware).mapInput(mapInput) : middleware;
558
- return new _EffectBuilder({
559
- ...this["~effect"],
560
- middlewares: addMiddleware2(this["~effect"].middlewares, mapped)
561
- });
562
- }
563
- /**
564
- * Sets or updates the metadata.
565
- * The provided metadata is spared-merged with any existing metadata.
566
- *
567
- * @see {@link https://orpc.dev/docs/metadata Metadata Docs}
568
- */
569
- meta(meta) {
570
- return new _EffectBuilder({
571
- ...this["~effect"],
572
- meta: mergeMeta2(this["~effect"].meta, meta)
573
- });
574
- }
575
- /**
576
- * Sets or updates the route definition.
577
- * The provided route is spared-merged with any existing route.
578
- * This option is typically relevant when integrating with OpenAPI.
579
- *
580
- * @see {@link https://orpc.dev/docs/openapi/routing OpenAPI Routing Docs}
581
- * @see {@link https://orpc.dev/docs/openapi/input-output-structure OpenAPI Input/Output Structure Docs}
582
- */
583
- route(route) {
584
- return new _EffectBuilder({
585
- ...this["~effect"],
586
- route: mergeRoute2(this["~effect"].route, route)
587
- });
588
- }
589
- /**
590
- * Defines the input validation schema.
591
- *
592
- * @see {@link https://orpc.dev/docs/procedure#input-output-validation Input Validation Docs}
593
- */
594
- input(schema) {
595
- return new _EffectBuilder({
596
- ...this["~effect"],
597
- inputSchema: schema,
598
- inputValidationIndex: fallbackConfig(
599
- "initialInputValidationIndex",
600
- this["~effect"].config.initialInputValidationIndex
601
- ) + this["~effect"].middlewares.length
602
- // we cast to any because EffectProcedureBuilderWithInput is expecting
603
- // use() input type to be defined, and EffectBuilder types its use() input
604
- // to unknown to allow any middleware to be passed
605
- // ---
606
- // note: the original implentation of the builder also uses any for the same reason
607
- });
608
- }
609
- /**
610
- * Defines the output validation schema.
611
- *
612
- * @see {@link https://orpc.dev/docs/procedure#input-output-validation Output Validation Docs}
613
- */
614
- output(schema) {
615
- return new _EffectBuilder({
616
- ...this["~effect"],
617
- outputSchema: schema,
618
- outputValidationIndex: fallbackConfig(
619
- "initialOutputValidationIndex",
620
- this["~effect"].config.initialOutputValidationIndex
621
- ) + this["~effect"].middlewares.length
622
- });
623
- }
624
- /**
625
- * Adds a traceable span to the procedure for telemetry.
626
- * The span name is used for Effect tracing via `Effect.withSpan`.
627
- * Stack trace is captured at the call site for better error reporting.
628
- *
629
- * @param spanName - The name of the span for telemetry (e.g., 'users.getUser')
630
- * @returns An EffectBuilder with span tracing configured
631
- *
632
- * @example
633
- * ```ts
634
- * const getUser = effectOs
635
- * .input(z.object({ id: z.string() }))
636
- * .traced('users.getUser')
637
- * .effect(function* ({ input }) {
638
- * const userService = yield* UserService
639
- * return yield* userService.findById(input.id)
640
- * })
641
- * ```
642
- */
643
- traced(spanName) {
644
- return new _EffectBuilder({
645
- ...this["~effect"],
646
- spanConfig: {
647
- name: spanName,
648
- captureStackTrace: addSpanStackTrace()
649
- }
650
- });
651
- }
652
- handler(handler) {
653
- return new EffectDecoratedProcedure({
654
- ...this["~effect"],
655
- handler
656
- });
657
- }
658
- /**
659
- * Defines the handler of the procedure using an Effect.
660
- * The Effect is executed using the ManagedRuntime provided during builder creation.
661
- * The effect is automatically wrapped with `Effect.withSpan`.
662
- *
663
- * @see {@link https://orpc.dev/docs/procedure Procedure Docs}
664
- */
665
- effect(effectFn) {
666
- const { runtime, spanConfig } = this["~effect"];
667
- const defaultCaptureStackTrace = addSpanStackTrace();
668
- return new EffectDecoratedProcedure({
669
- ...this["~effect"],
670
- handler: async (opts) => {
671
- return createEffectProcedureHandler({
672
- runtime,
673
- effectErrorMap: this["~effect"].effectErrorMap,
674
- effectFn,
675
- spanConfig,
676
- defaultCaptureStackTrace
677
- })(opts);
678
- }
679
- });
680
- }
681
- /**
682
- * Prefixes all procedures in the router.
683
- * The provided prefix is post-appended to any existing router prefix.
684
- *
685
- * @note This option does not affect procedures that do not define a path in their route definition.
686
- *
687
- * @see {@link https://orpc.dev/docs/openapi/routing#route-prefixes OpenAPI Route Prefixes Docs}
688
- */
689
- prefix(prefix) {
690
- return new _EffectBuilder({
691
- ...this["~effect"],
692
- prefix: mergePrefix2(this["~effect"].prefix, prefix)
693
- });
694
- }
695
- /**
696
- * Adds tags to all procedures in the router.
697
- * This helpful when you want to group procedures together in the OpenAPI specification.
698
- *
699
- * @see {@link https://orpc.dev/docs/openapi/openapi-specification#operation-metadata OpenAPI Operation Metadata Docs}
700
- */
701
- tag(...tags) {
702
- return new _EffectBuilder({
703
- ...this["~effect"],
704
- tags: mergeTags(this["~effect"].tags, tags)
705
- });
706
- }
707
- /**
708
- * Applies all of the previously defined options to the specified router.
709
- *
710
- * @see {@link https://orpc.dev/docs/router#extending-router Extending Router Docs}
711
- */
712
- router(router) {
713
- return enhanceEffectRouter(router, {
714
- ...this["~effect"]
715
- });
716
- }
717
- /**
718
- * Create a lazy router
719
- * And applies all of the previously defined options to the specified router.
720
- *
721
- * @see {@link https://orpc.dev/docs/router#extending-router Extending Router Docs}
722
- */
723
- lazy(loader) {
724
- return enhanceEffectRouter(lazy2(loader), {
725
- ...this["~effect"]
767
+ attachEffectState(this, builder ?? new Builder(orpcDef), {
768
+ effectErrorMap,
769
+ runtime,
770
+ spanConfig
726
771
  });
772
+ return createEffectBuilderProxy(this);
727
773
  }
728
774
  };
729
775
  function makeEffectORPC(runtime, builder) {
730
776
  const resolvedBuilder = builder ?? emptyBuilder();
731
- return new EffectBuilder({
732
- ...resolvedBuilder["~orpc"],
733
- errorMap: effectErrorMapToErrorMap(resolvedBuilder["~orpc"].errorMap),
734
- effectErrorMap: resolvedBuilder["~orpc"].errorMap,
735
- runtime
736
- });
777
+ const effectErrorMap = getEffectErrorMap(resolvedBuilder);
778
+ return new EffectBuilder(
779
+ {
780
+ ...resolvedBuilder["~orpc"],
781
+ effectErrorMap,
782
+ errorMap: effectErrorMapToErrorMap(effectErrorMap),
783
+ runtime
784
+ },
785
+ unwrapEffectUpstream(resolvedBuilder)
786
+ );
737
787
  }
738
788
  function emptyBuilder() {
739
789
  return new Builder({
740
790
  config: {},
741
- route: {},
742
- meta: {},
791
+ dedupeLeadingMiddlewares: true,
743
792
  errorMap: {},
744
793
  inputValidationIndex: fallbackConfig("initialInputValidationIndex"),
745
- outputValidationIndex: fallbackConfig("initialOutputValidationIndex"),
794
+ meta: {},
746
795
  middlewares: [],
747
- dedupeLeadingMiddlewares: true
796
+ outputValidationIndex: fallbackConfig("initialOutputValidationIndex"),
797
+ route: {}
748
798
  });
749
799
  }
750
800