effect-orpc 1.0.0-effect-v4.4 → 1.0.0-effect-v4.5

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();
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();
268
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) {
@@ -404,6 +611,139 @@ function createEffectProcedureHandler(options) {
404
611
  }
405
612
 
406
613
  // src/effect-builder.ts
614
+ var builderVirtualDescriptors = {
615
+ "~effect": { enumerable: true },
616
+ effect: { enumerable: false },
617
+ errors: { enumerable: false },
618
+ handler: { enumerable: false },
619
+ lazy: { enumerable: false },
620
+ router: { enumerable: false },
621
+ traced: { enumerable: false }
622
+ };
623
+ var builderVirtualKeys = [
624
+ "~effect",
625
+ "errors",
626
+ "effect",
627
+ "traced",
628
+ "handler",
629
+ "router",
630
+ "lazy"
631
+ ];
632
+ function isBuilderLike(value) {
633
+ return typeof value === "object" && value !== null && "~orpc" in value;
634
+ }
635
+ function getOrCreateVirtualMethod2(context, prop, factory) {
636
+ const cache = context.methodCache;
637
+ if (cache.has(prop)) {
638
+ return cache.get(prop);
639
+ }
640
+ const value = factory();
641
+ cache.set(prop, value);
642
+ return value;
643
+ }
644
+ function getEffectBuilderDef(context) {
645
+ return {
646
+ ...context.upstream["~orpc"],
647
+ effectErrorMap: context.state.effectErrorMap,
648
+ runtime: context.state.runtime,
649
+ spanConfig: context.state.spanConfig
650
+ };
651
+ }
652
+ function wrapBuilderLike(builder, state) {
653
+ return new EffectBuilder(
654
+ {
655
+ ...builder["~orpc"],
656
+ effectErrorMap: state.effectErrorMap,
657
+ runtime: state.runtime,
658
+ spanConfig: state.spanConfig
659
+ },
660
+ unwrapEffectUpstream(builder)
661
+ );
662
+ }
663
+ function createEffectBuilderProxy(target) {
664
+ return createNodeProxy(target, {
665
+ getVirtual(context, prop) {
666
+ const effectDef = getEffectBuilderDef(context);
667
+ if (prop === "~effect") {
668
+ return getEffectBuilderDef(context);
669
+ }
670
+ const { upstream: source, state } = context;
671
+ switch (prop) {
672
+ case "errors":
673
+ return getOrCreateVirtualMethod2(context, prop, () => {
674
+ return (errors) => {
675
+ const nextEffectErrorMap = {
676
+ ...state.effectErrorMap,
677
+ ...errors
678
+ };
679
+ const nextBuilder = Reflect.apply(
680
+ Reflect.get(source, "errors", source),
681
+ source,
682
+ [effectErrorMapToErrorMap(errors)]
683
+ );
684
+ return wrapBuilderLike(nextBuilder, {
685
+ ...state,
686
+ effectErrorMap: nextEffectErrorMap
687
+ });
688
+ };
689
+ });
690
+ case "effect":
691
+ return getOrCreateVirtualMethod2(context, prop, () => {
692
+ return (effectFn) => {
693
+ const defaultCaptureStackTrace = addSpanStackTrace();
694
+ return new EffectDecoratedProcedure({
695
+ ...effectDef,
696
+ handler: async (opts) => {
697
+ return createEffectProcedureHandler({
698
+ defaultCaptureStackTrace,
699
+ effectErrorMap: state.effectErrorMap,
700
+ effectFn,
701
+ runtime: state.runtime,
702
+ spanConfig: state.spanConfig
703
+ })(opts);
704
+ }
705
+ });
706
+ };
707
+ });
708
+ case "traced":
709
+ return getOrCreateVirtualMethod2(context, prop, () => {
710
+ return (spanName) => wrapBuilderLike(source, {
711
+ ...state,
712
+ spanConfig: {
713
+ captureStackTrace: addSpanStackTrace(),
714
+ name: spanName
715
+ }
716
+ });
717
+ });
718
+ case "handler":
719
+ return getOrCreateVirtualMethod2(context, prop, () => {
720
+ return (handler) => new EffectDecoratedProcedure({
721
+ ...effectDef,
722
+ handler
723
+ });
724
+ });
725
+ case "router":
726
+ return getOrCreateVirtualMethod2(context, prop, () => {
727
+ return (router) => enhanceEffectRouter(router, effectDef);
728
+ });
729
+ case "lazy":
730
+ return getOrCreateVirtualMethod2(context, prop, () => {
731
+ return (loader) => enhanceEffectRouter(lazy2(loader), effectDef);
732
+ });
733
+ default:
734
+ return unhandled();
735
+ }
736
+ },
737
+ virtualDescriptors: builderVirtualDescriptors,
738
+ virtualKeys: builderVirtualKeys,
739
+ wrapResult(context, _prop, result) {
740
+ if (!isBuilderLike(result)) {
741
+ return result;
742
+ }
743
+ return wrapBuilderLike(result, context.state);
744
+ }
745
+ });
746
+ }
407
747
  function addSpanStackTrace() {
408
748
  const ErrorConstructor = Error;
409
749
  const limit = ErrorConstructor.stackTraceLimit;
@@ -424,330 +764,40 @@ function addSpanStackTrace() {
424
764
  }
425
765
  };
426
766
  }
427
- var EffectBuilder = class _EffectBuilder {
428
- constructor(def) {
767
+ var EffectBuilder = class {
768
+ constructor(def, builder) {
429
769
  const { runtime, spanConfig, effectErrorMap, ...orpcDef } = def;
430
- this["~orpc"] = orpcDef;
431
- this["~effect"] = { runtime, spanConfig, effectErrorMap, ...orpcDef };
432
- }
433
- /**
434
- * Sets or overrides the config.
435
- *
436
- * @see {@link https://orpc.dev/docs/client/server-side#middlewares-order Middlewares Order Docs}
437
- * @see {@link https://orpc.dev/docs/best-practices/dedupe-middleware#configuration Dedupe Middleware Docs}
438
- */
439
- $config(config) {
440
- const inputValidationCount = this["~effect"].inputValidationIndex - fallbackConfig(
441
- "initialInputValidationIndex",
442
- this["~effect"].config.initialInputValidationIndex
443
- );
444
- const outputValidationCount = this["~effect"].outputValidationIndex - fallbackConfig(
445
- "initialOutputValidationIndex",
446
- this["~effect"].config.initialOutputValidationIndex
447
- );
448
- return new _EffectBuilder({
449
- ...this["~effect"],
450
- config,
451
- dedupeLeadingMiddlewares: fallbackConfig(
452
- "dedupeLeadingMiddlewares",
453
- config.dedupeLeadingMiddlewares
454
- ),
455
- inputValidationIndex: fallbackConfig(
456
- "initialInputValidationIndex",
457
- config.initialInputValidationIndex
458
- ) + inputValidationCount,
459
- outputValidationIndex: fallbackConfig(
460
- "initialOutputValidationIndex",
461
- config.initialOutputValidationIndex
462
- ) + outputValidationCount
463
- });
464
- }
465
- /**
466
- * Set or override the initial context.
467
- *
468
- * @see {@link https://orpc.dev/docs/context Context Docs}
469
- */
470
- $context() {
471
- return new _EffectBuilder({
472
- ...this["~effect"],
473
- middlewares: [],
474
- inputValidationIndex: fallbackConfig(
475
- "initialInputValidationIndex",
476
- this["~effect"].config.initialInputValidationIndex
477
- ),
478
- outputValidationIndex: fallbackConfig(
479
- "initialOutputValidationIndex",
480
- this["~effect"].config.initialOutputValidationIndex
481
- )
482
- });
483
- }
484
- /**
485
- * Sets or overrides the initial meta.
486
- *
487
- * @see {@link https://orpc.dev/docs/metadata Metadata Docs}
488
- */
489
- $meta(initialMeta) {
490
- return new _EffectBuilder({
491
- ...this["~effect"],
492
- meta: initialMeta
493
- });
494
- }
495
- /**
496
- * Sets or overrides the initial route.
497
- * This option is typically relevant when integrating with OpenAPI.
498
- *
499
- * @see {@link https://orpc.dev/docs/openapi/routing OpenAPI Routing Docs}
500
- * @see {@link https://orpc.dev/docs/openapi/input-output-structure OpenAPI Input/Output Structure Docs}
501
- */
502
- $route(initialRoute) {
503
- return new _EffectBuilder({
504
- ...this["~effect"],
505
- route: initialRoute
506
- });
507
- }
508
- /**
509
- * Sets or overrides the initial input schema.
510
- *
511
- * @see {@link https://orpc.dev/docs/procedure#initial-configuration Initial Procedure Configuration Docs}
512
- */
513
- $input(initialInputSchema) {
514
- return new _EffectBuilder({
515
- ...this["~effect"],
516
- inputSchema: initialInputSchema
517
- });
518
- }
519
- /**
520
- * Creates a middleware.
521
- *
522
- * @see {@link https://orpc.dev/docs/middleware Middleware Docs}
523
- */
524
- middleware(middleware) {
525
- return decorateMiddleware2(middleware);
526
- }
527
- /**
528
- * Adds type-safe custom errors.
529
- * Supports both traditional oRPC error definitions and ORPCTaggedError classes.
530
- *
531
- * @example
532
- * ```ts
533
- * // Traditional format
534
- * builder.errors({ BAD_REQUEST: { status: 400, message: 'Bad request' } })
535
- *
536
- * // Tagged error class
537
- * builder.errors({ USER_NOT_FOUND: UserNotFoundError })
538
- *
539
- * // Mixed
540
- * builder.errors({
541
- * BAD_REQUEST: { status: 400 },
542
- * USER_NOT_FOUND: UserNotFoundError,
543
- * })
544
- * ```
545
- *
546
- * @see {@link https://orpc.dev/docs/error-handling#type%E2%80%90safe-error-handling Type-Safe Error Handling Docs}
547
- */
548
- errors(errors) {
549
- const newEffectErrorMap = {
550
- ...this["~effect"].effectErrorMap,
551
- ...errors
552
- };
553
- return new _EffectBuilder({
554
- ...this["~effect"],
555
- errorMap: effectErrorMapToErrorMap(newEffectErrorMap),
556
- effectErrorMap: newEffectErrorMap
557
- });
558
- }
559
- use(middleware, mapInput) {
560
- const mapped = mapInput ? decorateMiddleware2(middleware).mapInput(mapInput) : middleware;
561
- return new _EffectBuilder({
562
- ...this["~effect"],
563
- middlewares: addMiddleware2(this["~effect"].middlewares, mapped)
564
- });
565
- }
566
- /**
567
- * Sets or updates the metadata.
568
- * The provided metadata is spared-merged with any existing metadata.
569
- *
570
- * @see {@link https://orpc.dev/docs/metadata Metadata Docs}
571
- */
572
- meta(meta) {
573
- return new _EffectBuilder({
574
- ...this["~effect"],
575
- meta: mergeMeta2(this["~effect"].meta, meta)
576
- });
577
- }
578
- /**
579
- * Sets or updates the route definition.
580
- * The provided route is spared-merged with any existing route.
581
- * This option is typically relevant when integrating with OpenAPI.
582
- *
583
- * @see {@link https://orpc.dev/docs/openapi/routing OpenAPI Routing Docs}
584
- * @see {@link https://orpc.dev/docs/openapi/input-output-structure OpenAPI Input/Output Structure Docs}
585
- */
586
- route(route) {
587
- return new _EffectBuilder({
588
- ...this["~effect"],
589
- route: mergeRoute2(this["~effect"].route, route)
590
- });
591
- }
592
- /**
593
- * Defines the input validation schema.
594
- *
595
- * @see {@link https://orpc.dev/docs/procedure#input-output-validation Input Validation Docs}
596
- */
597
- input(schema) {
598
- return new _EffectBuilder({
599
- ...this["~effect"],
600
- inputSchema: schema,
601
- inputValidationIndex: fallbackConfig(
602
- "initialInputValidationIndex",
603
- this["~effect"].config.initialInputValidationIndex
604
- ) + this["~effect"].middlewares.length
605
- // we cast to any because EffectProcedureBuilderWithInput is expecting
606
- // use() input type to be defined, and EffectBuilder types its use() input
607
- // to unknown to allow any middleware to be passed
608
- // ---
609
- // note: the original implentation of the builder also uses any for the same reason
610
- });
611
- }
612
- /**
613
- * Defines the output validation schema.
614
- *
615
- * @see {@link https://orpc.dev/docs/procedure#input-output-validation Output Validation Docs}
616
- */
617
- output(schema) {
618
- return new _EffectBuilder({
619
- ...this["~effect"],
620
- outputSchema: schema,
621
- outputValidationIndex: fallbackConfig(
622
- "initialOutputValidationIndex",
623
- this["~effect"].config.initialOutputValidationIndex
624
- ) + this["~effect"].middlewares.length
625
- });
626
- }
627
- /**
628
- * Adds a traceable span to the procedure for telemetry.
629
- * The span name is used for Effect tracing via `Effect.withSpan`.
630
- * Stack trace is captured at the call site for better error reporting.
631
- *
632
- * @param spanName - The name of the span for telemetry (e.g., 'users.getUser')
633
- * @returns An EffectBuilder with span tracing configured
634
- *
635
- * @example
636
- * ```ts
637
- * const getUser = effectOs
638
- * .input(z.object({ id: z.string() }))
639
- * .traced('users.getUser')
640
- * .effect(function* ({ input }) {
641
- * const userService = yield* UserService
642
- * return yield* userService.findById(input.id)
643
- * })
644
- * ```
645
- */
646
- traced(spanName) {
647
- return new _EffectBuilder({
648
- ...this["~effect"],
649
- spanConfig: {
650
- name: spanName,
651
- captureStackTrace: addSpanStackTrace()
652
- }
653
- });
654
- }
655
- handler(handler) {
656
- return new EffectDecoratedProcedure({
657
- ...this["~effect"],
658
- handler
659
- });
660
- }
661
- /**
662
- * Defines the handler of the procedure using an Effect.
663
- * The Effect is executed using the ManagedRuntime provided during builder creation.
664
- * The effect is automatically wrapped with `Effect.withSpan`.
665
- *
666
- * @see {@link https://orpc.dev/docs/procedure Procedure Docs}
667
- */
668
- effect(effectFn) {
669
- const { runtime, spanConfig } = this["~effect"];
670
- const defaultCaptureStackTrace = addSpanStackTrace();
671
- return new EffectDecoratedProcedure({
672
- ...this["~effect"],
673
- handler: async (opts) => {
674
- return createEffectProcedureHandler({
675
- runtime,
676
- effectErrorMap: this["~effect"].effectErrorMap,
677
- effectFn,
678
- spanConfig,
679
- defaultCaptureStackTrace
680
- })(opts);
681
- }
682
- });
683
- }
684
- /**
685
- * Prefixes all procedures in the router.
686
- * The provided prefix is post-appended to any existing router prefix.
687
- *
688
- * @note This option does not affect procedures that do not define a path in their route definition.
689
- *
690
- * @see {@link https://orpc.dev/docs/openapi/routing#route-prefixes OpenAPI Route Prefixes Docs}
691
- */
692
- prefix(prefix) {
693
- return new _EffectBuilder({
694
- ...this["~effect"],
695
- prefix: mergePrefix2(this["~effect"].prefix, prefix)
696
- });
697
- }
698
- /**
699
- * Adds tags to all procedures in the router.
700
- * This helpful when you want to group procedures together in the OpenAPI specification.
701
- *
702
- * @see {@link https://orpc.dev/docs/openapi/openapi-specification#operation-metadata OpenAPI Operation Metadata Docs}
703
- */
704
- tag(...tags) {
705
- return new _EffectBuilder({
706
- ...this["~effect"],
707
- tags: mergeTags(this["~effect"].tags, tags)
708
- });
709
- }
710
- /**
711
- * Applies all of the previously defined options to the specified router.
712
- *
713
- * @see {@link https://orpc.dev/docs/router#extending-router Extending Router Docs}
714
- */
715
- router(router) {
716
- return enhanceEffectRouter(router, {
717
- ...this["~effect"]
718
- });
719
- }
720
- /**
721
- * Create a lazy router
722
- * And applies all of the previously defined options to the specified router.
723
- *
724
- * @see {@link https://orpc.dev/docs/router#extending-router Extending Router Docs}
725
- */
726
- lazy(loader) {
727
- return enhanceEffectRouter(lazy2(loader), {
728
- ...this["~effect"]
770
+ attachEffectState(this, builder ?? new Builder(orpcDef), {
771
+ effectErrorMap,
772
+ runtime,
773
+ spanConfig
729
774
  });
775
+ return createEffectBuilderProxy(this);
730
776
  }
731
777
  };
732
778
  function makeEffectORPC(runtime, builder) {
733
779
  const resolvedBuilder = builder ?? emptyBuilder();
734
- return new EffectBuilder({
735
- ...resolvedBuilder["~orpc"],
736
- errorMap: effectErrorMapToErrorMap(resolvedBuilder["~orpc"].errorMap),
737
- effectErrorMap: resolvedBuilder["~orpc"].errorMap,
738
- runtime
739
- });
780
+ const effectErrorMap = getEffectErrorMap(resolvedBuilder);
781
+ return new EffectBuilder(
782
+ {
783
+ ...resolvedBuilder["~orpc"],
784
+ effectErrorMap,
785
+ errorMap: effectErrorMapToErrorMap(effectErrorMap),
786
+ runtime
787
+ },
788
+ unwrapEffectUpstream(resolvedBuilder)
789
+ );
740
790
  }
741
791
  function emptyBuilder() {
742
792
  return new Builder({
743
793
  config: {},
744
- route: {},
745
- meta: {},
794
+ dedupeLeadingMiddlewares: true,
746
795
  errorMap: {},
747
796
  inputValidationIndex: fallbackConfig("initialInputValidationIndex"),
748
- outputValidationIndex: fallbackConfig("initialOutputValidationIndex"),
797
+ meta: {},
749
798
  middlewares: [],
750
- dedupeLeadingMiddlewares: true
799
+ outputValidationIndex: fallbackConfig("initialOutputValidationIndex"),
800
+ route: {}
751
801
  });
752
802
  }
753
803