effect-orpc 0.1.4 → 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
@@ -2,22 +2,12 @@ import {
2
2
  getCurrentFiberRefs
3
3
  } from "./chunk-VOWRLWZZ.js";
4
4
 
5
+ // src/contract.ts
6
+ import { isContractProcedure as isContractProcedure2 } from "@orpc/contract";
7
+ import { implement } from "@orpc/server";
8
+
5
9
  // src/effect-builder.ts
6
- import {
7
- mergeMeta as mergeMeta2,
8
- mergePrefix as mergePrefix2,
9
- mergeRoute as mergeRoute2,
10
- mergeTags,
11
- ORPCError as ORPCError2
12
- } from "@orpc/contract";
13
- import {
14
- addMiddleware as addMiddleware2,
15
- Builder,
16
- decorateMiddleware as decorateMiddleware2,
17
- fallbackConfig,
18
- lazy as lazy2
19
- } from "@orpc/server";
20
- import { Cause as Cause2, Effect, Exit, FiberRefs } from "effect";
10
+ import { Builder, fallbackConfig, lazy as lazy2 } from "@orpc/server";
21
11
 
22
12
  // src/effect-enhance-router.ts
23
13
  import {
@@ -44,6 +34,177 @@ import {
44
34
  Procedure
45
35
  } from "@orpc/server";
46
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
+
47
208
  // src/tagged-error.ts
48
209
  import {
49
210
  fallbackORPCErrorMessage,
@@ -178,98 +339,141 @@ function effectErrorMapToErrorMap(errorMap) {
178
339
  }
179
340
 
180
341
  // src/effect-procedure.ts
181
- var EffectProcedure = class extends Procedure {
182
- constructor(def) {
183
- super(def);
184
- this["~effect"] = def;
185
- }
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 }
186
350
  };
187
- var EffectDecoratedProcedure = class _EffectDecoratedProcedure extends EffectProcedure {
188
- constructor(def) {
189
- super(def);
190
- this["~effect"] = def;
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);
191
365
  }
192
- /**
193
- * Adds type-safe custom errors.
194
- * Supports both traditional oRPC error definitions and ORPCTaggedError classes.
195
- *
196
- * @see {@link https://orpc.dev/docs/error-handling#type%E2%80%90safe-error-handling Type-Safe Error Handling Docs}
197
- */
198
- errors(errors) {
199
- const newEffectErrorMap = {
200
- ...this["~effect"].effectErrorMap,
201
- ...errors
202
- };
203
- return new _EffectDecoratedProcedure({
204
- ...this["~effect"],
205
- effectErrorMap: newEffectErrorMap,
206
- errorMap: effectErrorMapToErrorMap(newEffectErrorMap)
207
- });
208
- }
209
- /**
210
- * Sets or updates the metadata.
211
- * The provided metadata is spared-merged with any existing metadata.
212
- *
213
- * @see {@link https://orpc.dev/docs/metadata Metadata Docs}
214
- */
215
- meta(meta) {
216
- return new _EffectDecoratedProcedure({
217
- ...this["~effect"],
218
- meta: mergeMeta(this["~effect"].meta, meta)
219
- });
220
- }
221
- /**
222
- * Sets or updates the route definition.
223
- * The provided route is spared-merged with any existing route.
224
- * This option is typically relevant when integrating with OpenAPI.
225
- *
226
- * @see {@link https://orpc.dev/docs/openapi/routing OpenAPI Routing Docs}
227
- * @see {@link https://orpc.dev/docs/openapi/input-output-structure OpenAPI Input/Output Structure Docs}
228
- */
229
- route(route) {
230
- return new _EffectDecoratedProcedure({
231
- ...this["~effect"],
232
- route: mergeRoute(this["~effect"].route, route)
233
- });
234
- }
235
- use(middleware, mapInput) {
236
- const mapped = mapInput ? decorateMiddleware(middleware).mapInput(mapInput) : middleware;
237
- return new _EffectDecoratedProcedure({
238
- ...this["~effect"],
239
- middlewares: addMiddleware(this["~effect"].middlewares, mapped)
240
- });
241
- }
242
- /**
243
- * Make this procedure callable (works like a function while still being a procedure).
244
- *
245
- * @see {@link https://orpc.dev/docs/client/server-side Server-side Client Docs}
246
- */
247
- callable(...rest) {
248
- const client = createProcedureClient(this, ...rest);
249
- return new Proxy(client, {
250
- get: (target, key) => {
251
- return Reflect.has(this, key) ? Reflect.get(this, key) : Reflect.get(target, key);
252
- },
253
- has: (target, key) => {
254
- 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);
255
382
  }
256
- });
257
- }
258
- /**
259
- * Make this procedure compatible with server action.
260
- *
261
- * @see {@link https://orpc.dev/docs/server-action Server Action Docs}
262
- */
263
- actionable(...rest) {
264
- const action = createActionableClient(createProcedureClient(this, ...rest));
265
- return new Proxy(action, {
266
- get: (target, key) => {
267
- return Reflect.has(this, key) ? Reflect.get(this, key) : Reflect.get(target, key);
268
- },
269
- has: (target, key) => {
270
- return Reflect.has(this, key) || Reflect.has(target, key);
383
+ if (!decorated) {
384
+ return unhandled();
271
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
272
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);
273
477
  }
274
478
  };
275
479
 
@@ -277,44 +481,44 @@ var EffectDecoratedProcedure = class _EffectDecoratedProcedure extends EffectPro
277
481
  function enhanceEffectRouter(router, options) {
278
482
  if (isLazy(router)) {
279
483
  const laziedMeta = getLazyMeta(router);
280
- const enhancedPrefix = laziedMeta?.prefix ? mergePrefix(options.prefix, laziedMeta?.prefix) : options.prefix;
484
+ const enhancedPrefix = laziedMeta?.prefix ? mergePrefix(options.prefix, laziedMeta.prefix) : options.prefix;
281
485
  const enhanced2 = lazy(
282
486
  async () => {
283
487
  const { default: unlaziedRouter } = await unlazy(router);
284
- const enhanced3 = enhanceEffectRouter(unlaziedRouter, options);
285
- return unlazy(enhanced3);
488
+ const wrappedRouter = enhanceEffectRouter(unlaziedRouter, options);
489
+ return unlazy(wrappedRouter);
286
490
  },
287
491
  {
288
492
  ...laziedMeta,
289
493
  prefix: enhancedPrefix
290
494
  }
291
495
  );
292
- const accessible = createAccessibleLazyRouter(enhanced2);
293
- return accessible;
496
+ return createAccessibleLazyRouter(enhanced2);
294
497
  }
295
498
  if (isProcedure(router)) {
296
- const newMiddlewares = mergeMiddlewares(
499
+ const source = unwrapEffectUpstream(router);
500
+ const sourceEffectErrorMap = getEffectErrorMap(router);
501
+ const middlewares = mergeMiddlewares(
297
502
  options.middlewares,
298
- router["~orpc"].middlewares,
503
+ source["~orpc"].middlewares,
299
504
  { dedupeLeading: options.dedupeLeadingMiddlewares }
300
505
  );
301
- const newMiddlewareAdded = newMiddlewares.length - router["~orpc"].middlewares.length;
506
+ const newMiddlewareAdded = middlewares.length - source["~orpc"].middlewares.length;
302
507
  const effectErrorMap = {
303
508
  ...options.errorMap,
304
- ...router["~orpc"].errorMap
509
+ ...sourceEffectErrorMap
305
510
  };
306
511
  const errorMap = effectErrorMapToErrorMap(effectErrorMap);
307
- const enhanced2 = new EffectProcedure({
308
- ...router["~orpc"],
309
- route: enhanceRoute(router["~orpc"].route, options),
512
+ return new EffectProcedure({
513
+ ...source["~orpc"],
514
+ route: enhanceRoute(source["~orpc"].route, options),
310
515
  effectErrorMap,
311
516
  errorMap,
312
- middlewares: newMiddlewares,
313
- inputValidationIndex: router["~orpc"].inputValidationIndex + newMiddlewareAdded,
314
- outputValidationIndex: router["~orpc"].outputValidationIndex + newMiddlewareAdded,
517
+ middlewares,
518
+ inputValidationIndex: source["~orpc"].inputValidationIndex + newMiddlewareAdded,
519
+ outputValidationIndex: source["~orpc"].outputValidationIndex + newMiddlewareAdded,
315
520
  runtime: options.runtime
316
521
  });
317
- return enhanced2;
318
522
  }
319
523
  const enhanced = {};
320
524
  for (const key in router) {
@@ -323,7 +527,220 @@ function enhanceEffectRouter(router, options) {
323
527
  return enhanced;
324
528
  }
325
529
 
530
+ // src/effect-runtime.ts
531
+ import { ORPCError as ORPCError2 } from "@orpc/contract";
532
+ import { Cause as Cause2, Effect, Exit, FiberRefs } from "effect";
533
+ function toORPCErrorFromCause(cause) {
534
+ return Cause2.match(cause, {
535
+ onDie(defect) {
536
+ return new ORPCError2("INTERNAL_SERVER_ERROR", {
537
+ cause: defect
538
+ });
539
+ },
540
+ onFail(error) {
541
+ if (isORPCTaggedError(error)) {
542
+ return error.toORPCError();
543
+ }
544
+ if (error instanceof ORPCError2) {
545
+ return error;
546
+ }
547
+ return new ORPCError2("INTERNAL_SERVER_ERROR", {
548
+ cause: error
549
+ });
550
+ },
551
+ onInterrupt(fiberId) {
552
+ return new ORPCError2("INTERNAL_SERVER_ERROR", {
553
+ cause: new Error(`${fiberId} Interrupted`)
554
+ });
555
+ },
556
+ onSequential(left) {
557
+ return left;
558
+ },
559
+ onEmpty: new ORPCError2("INTERNAL_SERVER_ERROR", {
560
+ cause: new Error("Unknown error")
561
+ }),
562
+ onParallel(left) {
563
+ return left;
564
+ }
565
+ });
566
+ }
567
+ function createEffectProcedureHandler(options) {
568
+ const {
569
+ runtime,
570
+ effectErrorMap,
571
+ effectFn,
572
+ spanConfig,
573
+ defaultCaptureStackTrace
574
+ } = options;
575
+ return async (opts) => {
576
+ const effectOpts = {
577
+ context: opts.context,
578
+ input: opts.input,
579
+ path: opts.path,
580
+ procedure: opts.procedure,
581
+ signal: opts.signal,
582
+ lastEventId: opts.lastEventId,
583
+ errors: createEffectErrorConstructorMap(effectErrorMap)
584
+ };
585
+ const spanName = spanConfig?.name ?? opts.path.join(".");
586
+ const captureStackTrace = spanConfig?.captureStackTrace ?? defaultCaptureStackTrace;
587
+ const resolver = Effect.fnUntraced(effectFn);
588
+ const tracedEffect = Effect.withSpan(resolver(effectOpts), spanName, {
589
+ captureStackTrace
590
+ });
591
+ const parentFiberRefs = getCurrentFiberRefs();
592
+ const effectWithRefs = parentFiberRefs ? Effect.fiberIdWith(
593
+ (fiberId) => Effect.flatMap(
594
+ Effect.getFiberRefs,
595
+ (fiberRefs) => Effect.setFiberRefs(
596
+ FiberRefs.joinAs(fiberRefs, fiberId, parentFiberRefs)
597
+ ).pipe(Effect.andThen(tracedEffect))
598
+ )
599
+ ) : tracedEffect;
600
+ const exit = await runtime.runPromiseExit(effectWithRefs, {
601
+ signal: opts.signal
602
+ });
603
+ if (Exit.isFailure(exit)) {
604
+ throw toORPCErrorFromCause(exit.cause);
605
+ }
606
+ return exit.value;
607
+ };
608
+ }
609
+
326
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
+ }
327
744
  function addSpanStackTrace() {
328
745
  const ErrorConstructor = Error;
329
746
  const limit = ErrorConstructor.stackTraceLimit;
@@ -344,381 +761,292 @@ function addSpanStackTrace() {
344
761
  }
345
762
  };
346
763
  }
347
- var EffectBuilder = class _EffectBuilder {
348
- constructor(def) {
764
+ var EffectBuilder = class {
765
+ constructor(def, builder) {
349
766
  const { runtime, spanConfig, effectErrorMap, ...orpcDef } = def;
350
- this["~orpc"] = orpcDef;
351
- this["~effect"] = { runtime, spanConfig, effectErrorMap, ...orpcDef };
352
- }
353
- /**
354
- * Sets or overrides the config.
355
- *
356
- * @see {@link https://orpc.dev/docs/client/server-side#middlewares-order Middlewares Order Docs}
357
- * @see {@link https://orpc.dev/docs/best-practices/dedupe-middleware#configuration Dedupe Middleware Docs}
358
- */
359
- $config(config) {
360
- const inputValidationCount = this["~effect"].inputValidationIndex - fallbackConfig(
361
- "initialInputValidationIndex",
362
- this["~effect"].config.initialInputValidationIndex
363
- );
364
- const outputValidationCount = this["~effect"].outputValidationIndex - fallbackConfig(
365
- "initialOutputValidationIndex",
366
- this["~effect"].config.initialOutputValidationIndex
367
- );
368
- return new _EffectBuilder({
369
- ...this["~effect"],
370
- config,
371
- dedupeLeadingMiddlewares: fallbackConfig(
372
- "dedupeLeadingMiddlewares",
373
- config.dedupeLeadingMiddlewares
374
- ),
375
- inputValidationIndex: fallbackConfig(
376
- "initialInputValidationIndex",
377
- config.initialInputValidationIndex
378
- ) + inputValidationCount,
379
- outputValidationIndex: fallbackConfig(
380
- "initialOutputValidationIndex",
381
- config.initialOutputValidationIndex
382
- ) + outputValidationCount
383
- });
384
- }
385
- /**
386
- * Set or override the initial context.
387
- *
388
- * @see {@link https://orpc.dev/docs/context Context Docs}
389
- */
390
- $context() {
391
- return new _EffectBuilder({
392
- ...this["~effect"],
393
- middlewares: [],
394
- inputValidationIndex: fallbackConfig(
395
- "initialInputValidationIndex",
396
- this["~effect"].config.initialInputValidationIndex
397
- ),
398
- outputValidationIndex: fallbackConfig(
399
- "initialOutputValidationIndex",
400
- this["~effect"].config.initialOutputValidationIndex
401
- )
402
- });
403
- }
404
- /**
405
- * Sets or overrides the initial meta.
406
- *
407
- * @see {@link https://orpc.dev/docs/metadata Metadata Docs}
408
- */
409
- $meta(initialMeta) {
410
- return new _EffectBuilder({
411
- ...this["~effect"],
412
- meta: initialMeta
413
- });
414
- }
415
- /**
416
- * Sets or overrides the initial route.
417
- * This option is typically relevant when integrating with OpenAPI.
418
- *
419
- * @see {@link https://orpc.dev/docs/openapi/routing OpenAPI Routing Docs}
420
- * @see {@link https://orpc.dev/docs/openapi/input-output-structure OpenAPI Input/Output Structure Docs}
421
- */
422
- $route(initialRoute) {
423
- return new _EffectBuilder({
424
- ...this["~effect"],
425
- route: initialRoute
426
- });
427
- }
428
- /**
429
- * Sets or overrides the initial input schema.
430
- *
431
- * @see {@link https://orpc.dev/docs/procedure#initial-configuration Initial Procedure Configuration Docs}
432
- */
433
- $input(initialInputSchema) {
434
- return new _EffectBuilder({
435
- ...this["~effect"],
436
- inputSchema: initialInputSchema
767
+ attachEffectState(this, builder ?? new Builder(orpcDef), {
768
+ effectErrorMap,
769
+ runtime,
770
+ spanConfig
437
771
  });
772
+ return createEffectBuilderProxy(this);
438
773
  }
439
- /**
440
- * Adds type-safe custom errors.
441
- * Supports both traditional oRPC error definitions and ORPCTaggedError classes.
442
- *
443
- * @example
444
- * ```ts
445
- * // Traditional format
446
- * builder.errors({ BAD_REQUEST: { status: 400, message: 'Bad request' } })
447
- *
448
- * // Tagged error class
449
- * builder.errors({ USER_NOT_FOUND: UserNotFoundError })
450
- *
451
- * // Mixed
452
- * builder.errors({
453
- * BAD_REQUEST: { status: 400 },
454
- * USER_NOT_FOUND: UserNotFoundError,
455
- * })
456
- * ```
457
- *
458
- * @see {@link https://orpc.dev/docs/error-handling#type%E2%80%90safe-error-handling Type-Safe Error Handling Docs}
459
- */
460
- errors(errors) {
461
- const newEffectErrorMap = {
462
- ...this["~effect"].effectErrorMap,
463
- ...errors
464
- };
465
- return new _EffectBuilder({
466
- ...this["~effect"],
467
- errorMap: effectErrorMapToErrorMap(newEffectErrorMap),
468
- effectErrorMap: newEffectErrorMap
469
- });
774
+ };
775
+ function makeEffectORPC(runtime, builder) {
776
+ const resolvedBuilder = builder ?? emptyBuilder();
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
+ );
787
+ }
788
+ function emptyBuilder() {
789
+ return new Builder({
790
+ config: {},
791
+ dedupeLeadingMiddlewares: true,
792
+ errorMap: {},
793
+ inputValidationIndex: fallbackConfig("initialInputValidationIndex"),
794
+ meta: {},
795
+ middlewares: [],
796
+ outputValidationIndex: fallbackConfig("initialOutputValidationIndex"),
797
+ route: {}
798
+ });
799
+ }
800
+
801
+ // src/eoc.ts
802
+ import { isContractProcedure, oc } from "@orpc/contract";
803
+ var effectContractSymbol = /* @__PURE__ */ Symbol.for(
804
+ "@orpc/effect/contract"
805
+ );
806
+ function isWrappableContractBuilder(value) {
807
+ return typeof value === "object" && value !== null && "~orpc" in value;
808
+ }
809
+ function mergeEffectErrorMaps(left, right) {
810
+ if (!left) {
811
+ return right;
470
812
  }
471
- use(middleware, mapInput) {
472
- const mapped = mapInput ? decorateMiddleware2(middleware).mapInput(mapInput) : middleware;
473
- return new _EffectBuilder({
474
- ...this["~effect"],
475
- middlewares: addMiddleware2(this["~effect"].middlewares, mapped)
476
- });
813
+ if (!right) {
814
+ return left;
477
815
  }
478
- /**
479
- * Sets or updates the metadata.
480
- * The provided metadata is spared-merged with any existing metadata.
481
- *
482
- * @see {@link https://orpc.dev/docs/metadata Metadata Docs}
483
- */
484
- meta(meta) {
485
- return new _EffectBuilder({
486
- ...this["~effect"],
487
- meta: mergeMeta2(this["~effect"].meta, meta)
488
- });
816
+ return {
817
+ ...left,
818
+ ...right
819
+ };
820
+ }
821
+ function setEffectContractErrorMap(value, effectErrorMap) {
822
+ if (!effectErrorMap) {
823
+ return;
489
824
  }
490
- /**
491
- * Sets or updates the route definition.
492
- * The provided route is spared-merged with any existing route.
493
- * This option is typically relevant when integrating with OpenAPI.
494
- *
495
- * @see {@link https://orpc.dev/docs/openapi/routing OpenAPI Routing Docs}
496
- * @see {@link https://orpc.dev/docs/openapi/input-output-structure OpenAPI Input/Output Structure Docs}
497
- */
498
- route(route) {
499
- return new _EffectBuilder({
500
- ...this["~effect"],
501
- route: mergeRoute2(this["~effect"].route, route)
502
- });
825
+ Object.defineProperty(value, effectContractSymbol, {
826
+ value: { errorMap: effectErrorMap },
827
+ enumerable: false,
828
+ configurable: true
829
+ });
830
+ }
831
+ function getEffectContractErrorMap(value) {
832
+ if (typeof value !== "object" || value === null) {
833
+ return void 0;
503
834
  }
504
- /**
505
- * Defines the input validation schema.
506
- *
507
- * @see {@link https://orpc.dev/docs/procedure#input-output-validation Input Validation Docs}
508
- */
509
- input(schema) {
510
- return new _EffectBuilder({
511
- ...this["~effect"],
512
- inputSchema: schema,
513
- inputValidationIndex: fallbackConfig(
514
- "initialInputValidationIndex",
515
- this["~effect"].config.initialInputValidationIndex
516
- ) + this["~effect"].middlewares.length
517
- // we cast to any because EffectProcedureBuilderWithInput is expecting
518
- // use() input type to be defined, and EffectBuilder types its use() input
519
- // to unknown to allow any middleware to be passed
520
- // ---
521
- // note: the original implentation of the builder also uses any for the same reason
522
- });
835
+ return value[effectContractSymbol]?.errorMap;
836
+ }
837
+ function applyEffectContractErrorMapToRouter(router, source, inheritedEffectErrorMap) {
838
+ const routerRecord = router;
839
+ const sourceRecord = source;
840
+ for (const key of Object.keys(routerRecord)) {
841
+ const routerValue = routerRecord[key];
842
+ const sourceValue = sourceRecord && typeof sourceRecord === "object" ? sourceRecord[key] : void 0;
843
+ if (!routerValue) {
844
+ continue;
845
+ }
846
+ if (isContractProcedure(routerValue)) {
847
+ const sourceEffectErrorMap = getEffectContractErrorMap(sourceValue);
848
+ setEffectContractErrorMap(
849
+ routerValue,
850
+ mergeEffectErrorMaps(inheritedEffectErrorMap, sourceEffectErrorMap)
851
+ );
852
+ continue;
853
+ }
854
+ if (typeof routerValue === "object") {
855
+ applyEffectContractErrorMapToRouter(
856
+ routerValue,
857
+ sourceValue,
858
+ inheritedEffectErrorMap
859
+ );
860
+ }
523
861
  }
524
- /**
525
- * Defines the output validation schema.
526
- *
527
- * @see {@link https://orpc.dev/docs/procedure#input-output-validation Output Validation Docs}
528
- */
529
- output(schema) {
530
- return new _EffectBuilder({
531
- ...this["~effect"],
532
- outputSchema: schema,
533
- outputValidationIndex: fallbackConfig(
534
- "initialOutputValidationIndex",
535
- this["~effect"].config.initialOutputValidationIndex
536
- ) + this["~effect"].middlewares.length
537
- });
862
+ }
863
+ function wrapEffectContractBuilder(builder, inheritedEffectErrorMap) {
864
+ const currentEffectErrorMap = inheritedEffectErrorMap ?? getEffectContractErrorMap(builder);
865
+ if (typeof builder === "object" && builder !== null) {
866
+ setEffectContractErrorMap(builder, currentEffectErrorMap);
538
867
  }
539
- /**
540
- * Adds a traceable span to the procedure for telemetry.
541
- * The span name is used for Effect tracing via `Effect.withSpan`.
542
- * Stack trace is captured at the call site for better error reporting.
543
- *
544
- * @param spanName - The name of the span for telemetry (e.g., 'users.getUser')
545
- * @returns An EffectBuilder with span tracing configured
546
- *
547
- * @example
548
- * ```ts
549
- * const getUser = effectOs
550
- * .input(z.object({ id: z.string() }))
551
- * .traced('users.getUser')
552
- * .effect(function* ({ input }) {
553
- * const userService = yield* UserService
554
- * return yield* userService.findById(input.id)
555
- * })
556
- * ```
557
- */
558
- traced(spanName) {
559
- return new _EffectBuilder({
560
- ...this["~effect"],
561
- spanConfig: {
562
- name: spanName,
563
- captureStackTrace: addSpanStackTrace()
868
+ const proxy = new Proxy(builder, {
869
+ get(target, prop, receiver) {
870
+ if (prop === effectContractSymbol) {
871
+ return currentEffectErrorMap ? { errorMap: currentEffectErrorMap } : void 0;
564
872
  }
565
- });
566
- }
567
- handler(handler) {
568
- return new EffectDecoratedProcedure({
569
- ...this["~effect"],
570
- handler
571
- });
572
- }
573
- /**
574
- * Defines the handler of the procedure using an Effect.
575
- * The Effect is executed using the ManagedRuntime provided during builder creation.
576
- * The effect is automatically wrapped with `Effect.withSpan`.
577
- *
578
- * @see {@link https://orpc.dev/docs/procedure Procedure Docs}
579
- */
580
- effect(effectFn) {
581
- const { runtime, spanConfig } = this["~effect"];
582
- const defaultCaptureStackTrace = addSpanStackTrace();
583
- return new EffectDecoratedProcedure({
584
- ...this["~effect"],
585
- handler: async (opts) => {
586
- const effectOpts = {
587
- context: opts.context,
588
- input: opts.input,
589
- path: opts.path,
590
- procedure: opts.procedure,
591
- signal: opts.signal,
592
- lastEventId: opts.lastEventId,
593
- errors: createEffectErrorConstructorMap(
594
- this["~effect"].effectErrorMap
595
- )
873
+ if (prop === "errors") {
874
+ return (errors) => {
875
+ const nextEffectErrorMap = mergeEffectErrorMaps(
876
+ currentEffectErrorMap,
877
+ errors
878
+ );
879
+ return wrapEffectContractBuilder(
880
+ Reflect.apply(Reflect.get(target, prop, receiver), target, [
881
+ effectErrorMapToErrorMap(errors)
882
+ ]),
883
+ nextEffectErrorMap
884
+ );
596
885
  };
597
- const spanName = spanConfig?.name ?? opts.path.join(".");
598
- const captureStackTrace = spanConfig?.captureStackTrace ?? defaultCaptureStackTrace;
599
- const resolver = Effect.fnUntraced(effectFn);
600
- const tracedEffect = Effect.withSpan(resolver(effectOpts), spanName, {
601
- captureStackTrace
602
- });
603
- const parentFiberRefs = getCurrentFiberRefs();
604
- const effectWithRefs = parentFiberRefs ? Effect.fiberIdWith(
605
- (fiberId) => Effect.flatMap(
606
- Effect.getFiberRefs,
607
- (fiberRefs) => Effect.setFiberRefs(
608
- FiberRefs.joinAs(fiberRefs, fiberId, parentFiberRefs)
609
- ).pipe(Effect.andThen(tracedEffect))
610
- )
611
- ) : tracedEffect;
612
- const exit = await runtime.runPromiseExit(effectWithRefs, {
613
- signal: opts.signal
614
- });
615
- if (Exit.isFailure(exit)) {
616
- throw Cause2.match(exit.cause, {
617
- onDie(defect) {
618
- return new ORPCError2("INTERNAL_SERVER_ERROR", {
619
- cause: defect
620
- });
621
- },
622
- onFail(error) {
623
- if (isORPCTaggedError(error)) {
624
- return error.toORPCError();
625
- }
626
- if (error instanceof ORPCError2) {
627
- return error;
628
- }
629
- return new ORPCError2("INTERNAL_SERVER_ERROR", {
630
- cause: error
631
- });
632
- },
633
- onInterrupt(fiberId) {
634
- return new ORPCError2("INTERNAL_SERVER_ERROR", {
635
- cause: new Error(`${fiberId} Interrupted`)
636
- });
637
- },
638
- onSequential(left) {
639
- return left;
640
- },
641
- onEmpty: new ORPCError2("INTERNAL_SERVER_ERROR", {
642
- cause: new Error("Unknown error")
643
- }),
644
- onParallel(left) {
645
- return left;
646
- }
647
- });
648
- }
649
- return exit.value;
650
886
  }
651
- });
652
- }
653
- /**
654
- * Prefixes all procedures in the router.
655
- * The provided prefix is post-appended to any existing router prefix.
656
- *
657
- * @note This option does not affect procedures that do not define a path in their route definition.
658
- *
659
- * @see {@link https://orpc.dev/docs/openapi/routing#route-prefixes OpenAPI Route Prefixes Docs}
660
- */
661
- prefix(prefix) {
662
- return new _EffectBuilder({
663
- ...this["~effect"],
664
- prefix: mergePrefix2(this["~effect"].prefix, prefix)
665
- });
666
- }
667
- /**
668
- * Adds tags to all procedures in the router.
669
- * This helpful when you want to group procedures together in the OpenAPI specification.
670
- *
671
- * @see {@link https://orpc.dev/docs/openapi/openapi-specification#operation-metadata OpenAPI Operation Metadata Docs}
672
- */
673
- tag(...tags) {
674
- return new _EffectBuilder({
675
- ...this["~effect"],
676
- tags: mergeTags(this["~effect"].tags, tags)
677
- });
678
- }
679
- /**
680
- * Applies all of the previously defined options to the specified router.
681
- *
682
- * @see {@link https://orpc.dev/docs/router#extending-router Extending Router Docs}
683
- */
684
- router(router) {
685
- return enhanceEffectRouter(router, {
686
- ...this["~effect"]
687
- });
688
- }
689
- /**
690
- * Create a lazy router
691
- * And applies all of the previously defined options to the specified router.
692
- *
693
- * @see {@link https://orpc.dev/docs/router#extending-router Extending Router Docs}
694
- */
695
- lazy(loader) {
696
- return enhanceEffectRouter(lazy2(loader), {
697
- ...this["~effect"]
698
- });
699
- }
700
- };
701
- function makeEffectORPC(runtime, builder) {
702
- const resolvedBuilder = builder ?? emptyBuilder();
703
- return new EffectBuilder({
704
- ...resolvedBuilder["~orpc"],
705
- errorMap: effectErrorMapToErrorMap(resolvedBuilder["~orpc"].errorMap),
706
- effectErrorMap: resolvedBuilder["~orpc"].errorMap,
707
- runtime
887
+ if (prop === "router") {
888
+ return (router) => {
889
+ const result = Reflect.apply(
890
+ Reflect.get(target, prop, receiver),
891
+ target,
892
+ [router]
893
+ );
894
+ applyEffectContractErrorMapToRouter(
895
+ result,
896
+ router,
897
+ currentEffectErrorMap
898
+ );
899
+ return result;
900
+ };
901
+ }
902
+ const value = Reflect.get(target, prop, receiver);
903
+ if (typeof value !== "function") {
904
+ return value;
905
+ }
906
+ return (...args) => {
907
+ const result = Reflect.apply(value, target, args);
908
+ return isWrappableContractBuilder(result) ? wrapEffectContractBuilder(result, currentEffectErrorMap) : result;
909
+ };
910
+ }
708
911
  });
912
+ setEffectContractErrorMap(proxy, currentEffectErrorMap);
913
+ return proxy;
709
914
  }
710
- function emptyBuilder() {
711
- return new Builder({
712
- config: {},
713
- route: {},
714
- meta: {},
715
- errorMap: {},
716
- inputValidationIndex: fallbackConfig("initialInputValidationIndex"),
717
- outputValidationIndex: fallbackConfig("initialOutputValidationIndex"),
915
+ var eoc = wrapEffectContractBuilder(
916
+ oc,
917
+ {}
918
+ );
919
+
920
+ // src/contract.ts
921
+ var CONTRACT_HIDDEN_METHODS = /* @__PURE__ */ new Set([
922
+ "$config",
923
+ "$context",
924
+ "$input",
925
+ "$meta",
926
+ "$route",
927
+ "errors",
928
+ "input",
929
+ "lazy",
930
+ "meta",
931
+ "middleware",
932
+ "output",
933
+ "prefix",
934
+ "route",
935
+ "router",
936
+ "tag"
937
+ ]);
938
+ function makeEnhanceOptions(runtime) {
939
+ return {
718
940
  middlewares: [],
719
- dedupeLeadingMiddlewares: true
941
+ errorMap: {},
942
+ dedupeLeadingMiddlewares: true,
943
+ runtime
944
+ };
945
+ }
946
+ function wrapContractNode(contract, target, runtime) {
947
+ const cache = /* @__PURE__ */ new Map();
948
+ return new Proxy(target, {
949
+ get(currentTarget, prop, receiver) {
950
+ if (cache.has(prop)) {
951
+ return cache.get(prop);
952
+ }
953
+ if (isContractProcedure2(contract)) {
954
+ if (prop === "effect") {
955
+ const effect = (effectFn) => {
956
+ const effectErrorMap = getEffectContractErrorMap(contract) ?? currentTarget["~orpc"].errorMap;
957
+ return new EffectDecoratedProcedure({
958
+ ...currentTarget["~orpc"],
959
+ errorMap: effectErrorMapToErrorMap(effectErrorMap),
960
+ effectErrorMap,
961
+ runtime,
962
+ handler: createEffectProcedureHandler({
963
+ runtime,
964
+ effectErrorMap,
965
+ effectFn,
966
+ defaultCaptureStackTrace: addSpanStackTrace()
967
+ })
968
+ });
969
+ };
970
+ cache.set(prop, effect);
971
+ return effect;
972
+ }
973
+ if (prop === "use") {
974
+ const use = (...args) => wrapContractNode(
975
+ contract,
976
+ Reflect.apply(
977
+ Reflect.get(currentTarget, prop, currentTarget),
978
+ currentTarget,
979
+ args
980
+ ),
981
+ runtime
982
+ );
983
+ cache.set(prop, use);
984
+ return use;
985
+ }
986
+ if (CONTRACT_HIDDEN_METHODS.has(String(prop))) {
987
+ return void 0;
988
+ }
989
+ } else {
990
+ if (prop === "$context" || prop === "$config" || prop === "use") {
991
+ const wrappedMethod = (...args) => wrapContractNode(
992
+ contract,
993
+ Reflect.apply(
994
+ Reflect.get(currentTarget, prop, currentTarget),
995
+ currentTarget,
996
+ args
997
+ ),
998
+ runtime
999
+ );
1000
+ cache.set(prop, wrappedMethod);
1001
+ return wrappedMethod;
1002
+ }
1003
+ if (prop === "router" || prop === "lazy") {
1004
+ const wrappedMethod = (...args) => enhanceEffectRouter(
1005
+ Reflect.apply(
1006
+ Reflect.get(currentTarget, prop, currentTarget),
1007
+ currentTarget,
1008
+ args
1009
+ ),
1010
+ makeEnhanceOptions(runtime)
1011
+ );
1012
+ cache.set(prop, wrappedMethod);
1013
+ return wrappedMethod;
1014
+ }
1015
+ if (typeof prop === "string" && prop in contract) {
1016
+ const child = wrapContractNode(
1017
+ contract[prop],
1018
+ Reflect.get(currentTarget, prop, receiver),
1019
+ runtime
1020
+ );
1021
+ cache.set(prop, child);
1022
+ return child;
1023
+ }
1024
+ }
1025
+ const value = Reflect.get(currentTarget, prop, receiver);
1026
+ return typeof value === "function" ? value.bind(currentTarget) : value;
1027
+ },
1028
+ has(currentTarget, prop) {
1029
+ if (isContractProcedure2(contract)) {
1030
+ if (prop === "effect") {
1031
+ return true;
1032
+ }
1033
+ if (CONTRACT_HIDDEN_METHODS.has(String(prop))) {
1034
+ return false;
1035
+ }
1036
+ } else if (typeof prop === "string" && prop in contract) {
1037
+ return true;
1038
+ }
1039
+ return Reflect.has(currentTarget, prop);
1040
+ }
720
1041
  });
721
1042
  }
1043
+ function implementEffect(contract, runtime) {
1044
+ return wrapContractNode(
1045
+ contract,
1046
+ implement(contract),
1047
+ runtime
1048
+ );
1049
+ }
722
1050
  export {
723
1051
  EffectBuilder,
724
1052
  EffectDecoratedProcedure,
@@ -727,6 +1055,8 @@ export {
727
1055
  addSpanStackTrace,
728
1056
  createEffectErrorConstructorMap,
729
1057
  effectErrorMapToErrorMap,
1058
+ eoc,
1059
+ implementEffect,
730
1060
  isORPCTaggedError,
731
1061
  isORPCTaggedErrorClass,
732
1062
  makeEffectORPC,