effect-orpc 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "effect-orpc",
3
- "version": "0.3.0",
3
+ "version": "0.5.0",
4
4
  "keywords": [
5
5
  "effect",
6
6
  "orpc",
package/src/contract.ts CHANGED
@@ -34,8 +34,11 @@ import { enhanceEffectRouter } from "./effect-enhance-router";
34
34
  import { EffectDecoratedProcedure } from "./effect-procedure";
35
35
  import { createEffectProcedureHandler } from "./effect-runtime";
36
36
  import { effectContractSymbol, getEffectContractErrorMap } from "./eoc";
37
- import type { EffectRuntimeSource } from "./runtime-source";
38
- import { toManagedRuntime } from "./runtime-source";
37
+ import type {
38
+ EffectRuntimeRunner,
39
+ EffectRuntimeSource,
40
+ } from "./runtime-source";
41
+ import { makeEffectRuntimeRunner } from "./runtime-source";
39
42
  import type { EffectErrorMap } from "./tagged-error";
40
43
  import { effectErrorMapToErrorMap } from "./tagged-error";
41
44
  import type { EffectErrorMapToErrorMap, EffectProcedureHandler } from "./types";
@@ -316,13 +319,13 @@ const CONTRACT_HIDDEN_METHODS = new Set([
316
319
  ]);
317
320
 
318
321
  function makeEnhanceOptions<TRequirementsProvided, TRuntimeError>(
319
- runtime: ManagedRuntime.ManagedRuntime<TRequirementsProvided, TRuntimeError>,
322
+ runner: EffectRuntimeRunner<TRequirementsProvided, TRuntimeError>,
320
323
  ) {
321
324
  return {
322
325
  middlewares: [],
323
326
  errorMap: {},
324
327
  dedupeLeadingMiddlewares: true,
325
- runtime,
328
+ runner,
326
329
  } as const;
327
330
  }
328
331
 
@@ -333,7 +336,7 @@ function wrapContractNode<
333
336
  >(
334
337
  contract: TContract,
335
338
  target: any,
336
- runtime: ManagedRuntime.ManagedRuntime<TRequirementsProvided, TRuntimeError>,
339
+ runner: EffectRuntimeRunner<TRequirementsProvided, TRuntimeError>,
337
340
  ): EffectImplementerInternal<
338
341
  TContract,
339
342
  Context,
@@ -362,9 +365,9 @@ function wrapContractNode<
362
365
  ...currentTarget["~orpc"],
363
366
  errorMap: effectErrorMapToErrorMap(effectErrorMap),
364
367
  effectErrorMap,
365
- runtime,
368
+ runner,
366
369
  handler: createEffectProcedureHandler({
367
- runtime,
370
+ runner,
368
371
  effectErrorMap,
369
372
  effectFn,
370
373
  defaultCaptureStackTrace: addSpanStackTrace(),
@@ -385,7 +388,7 @@ function wrapContractNode<
385
388
  currentTarget,
386
389
  args,
387
390
  ),
388
- runtime,
391
+ runner,
389
392
  );
390
393
 
391
394
  cache.set(prop, use);
@@ -405,7 +408,7 @@ function wrapContractNode<
405
408
  currentTarget,
406
409
  args,
407
410
  ),
408
- runtime,
411
+ runner,
409
412
  );
410
413
 
411
414
  cache.set(prop, wrappedMethod);
@@ -420,7 +423,7 @@ function wrapContractNode<
420
423
  currentTarget,
421
424
  args,
422
425
  ) as any,
423
- makeEnhanceOptions(runtime),
426
+ makeEnhanceOptions(runner),
424
427
  );
425
428
 
426
429
  cache.set(prop, wrappedMethod);
@@ -431,7 +434,7 @@ function wrapContractNode<
431
434
  const child = wrapContractNode(
432
435
  (contract as Record<string, AnyContractRouter>)[prop]!,
433
436
  Reflect.get(currentTarget, prop, receiver),
434
- runtime,
437
+ runner,
435
438
  );
436
439
 
437
440
  cache.set(prop, child);
@@ -512,7 +515,7 @@ export function implementEffect<
512
515
  return wrapContractNode(
513
516
  contract,
514
517
  implement(contract),
515
- toManagedRuntime(source),
518
+ makeEffectRuntimeRunner(source),
516
519
  ) as EffectImplementer<
517
520
  TContract,
518
521
  Record<never, never>,
@@ -12,8 +12,10 @@ import { Layer, ManagedRuntime } from "effect";
12
12
  import { enhanceEffectRouter } from "./effect-enhance-router";
13
13
  import { EffectDecoratedProcedure } from "./effect-procedure";
14
14
  import {
15
+ createEffectOrORPCMiddleware,
15
16
  createEffectPipelineMiddleware,
16
17
  createEffectProcedureHandler,
18
+ isDecoratedMiddleware,
17
19
  isEffectMiddleware,
18
20
  } from "./effect-runtime";
19
21
  import {
@@ -28,7 +30,7 @@ import {
28
30
  type EffectProxyTarget,
29
31
  } from "./extension/state";
30
32
  import type { EffectRuntimeSource } from "./runtime-source";
31
- import { toManagedRuntime } from "./runtime-source";
33
+ import { makeEffectRuntimeRunner } from "./runtime-source";
32
34
  import type { EffectErrorMap, MergedEffectErrorMap } from "./tagged-error";
33
35
  import { effectErrorMapToErrorMap } from "./tagged-error";
34
36
  import type {
@@ -106,7 +108,7 @@ function getEffectBuilderDef(
106
108
  return {
107
109
  ...context.upstream["~orpc"],
108
110
  effectErrorMap: context.state.effectErrorMap,
109
- runtime: context.state.runtime,
111
+ runner: context.state.runner,
110
112
  spanConfig: context.state.spanConfig,
111
113
  effectSteps: context.state.effectSteps,
112
114
  effectHandler: context.state.effectHandler,
@@ -121,7 +123,7 @@ function wrapBuilderLike(
121
123
  {
122
124
  ...builder["~orpc"],
123
125
  effectErrorMap: state.effectErrorMap,
124
- runtime: state.runtime,
126
+ runner: state.runner,
125
127
  spanConfig: state.spanConfig,
126
128
  effectSteps: state.effectSteps,
127
129
  effectHandler: state.effectHandler,
@@ -158,7 +160,7 @@ function flushEffectSteps(
158
160
 
159
161
  const middleware = createEffectPipelineMiddleware({
160
162
  effectErrorMap: state.effectErrorMap,
161
- runtime: state.runtime,
163
+ runner: state.runner,
162
164
  steps: state.effectSteps,
163
165
  });
164
166
  return {
@@ -238,7 +240,7 @@ function createEffectBuilderProxy(
238
240
  effectErrorMap: state.effectErrorMap,
239
241
  effectFn,
240
242
  effectSteps: state.effectSteps,
241
- runtime: state.runtime,
243
+ runner: state.runner,
242
244
  spanConfig: state.spanConfig,
243
245
  })(opts as any);
244
246
  },
@@ -248,27 +250,25 @@ function createEffectBuilderProxy(
248
250
  case "middleware":
249
251
  return getOrCreateVirtualMethod(context, prop, () => {
250
252
  return (middleware: any) => {
251
- if (isEffectMiddleware(middleware)) {
252
- const effectMiddleware = createEffectPipelineMiddleware({
253
- effectErrorMap: state.effectErrorMap,
254
- runtime: state.runtime,
255
- steps: [
256
- ...(state.effectSteps ?? []),
257
- { _tag: "middleware" as const, middleware },
258
- ],
259
- });
260
-
261
- return Reflect.apply(
262
- Reflect.get(source, "middleware", source),
263
- source,
264
- [effectMiddleware],
265
- );
266
- }
253
+ const effectMiddleware = isEffectMiddleware(middleware)
254
+ ? createEffectPipelineMiddleware({
255
+ effectErrorMap: state.effectErrorMap,
256
+ runner: state.runner,
257
+ steps: [
258
+ ...(state.effectSteps ?? []),
259
+ { _tag: "middleware" as const, middleware },
260
+ ],
261
+ })
262
+ : createEffectOrORPCMiddleware({
263
+ effectErrorMap: state.effectErrorMap,
264
+ middleware,
265
+ runner: state.runner,
266
+ });
267
267
 
268
268
  return Reflect.apply(
269
269
  Reflect.get(source, "middleware", source),
270
270
  source,
271
- [middleware],
271
+ [effectMiddleware],
272
272
  );
273
273
  };
274
274
  });
@@ -302,7 +302,7 @@ function createEffectBuilderProxy(
302
302
  case "use":
303
303
  return getOrCreateVirtualMethod(context, prop, () => {
304
304
  return (middleware: any, ...rest: unknown[]) => {
305
- if (isEffectMiddleware(middleware) && rest.length === 0) {
305
+ if (rest.length === 0 && isEffectMiddleware(middleware)) {
306
306
  return wrapBuilderLike(
307
307
  source,
308
308
  appendEffectStep(state, {
@@ -316,7 +316,16 @@ function createEffectBuilderProxy(
316
316
  const nextBuilder: AnyBuilderLike = Reflect.apply(
317
317
  Reflect.get(flushed.builder, "use", flushed.builder),
318
318
  flushed.builder,
319
- [middleware, ...rest],
319
+ [
320
+ rest.length === 0 && !isDecoratedMiddleware(middleware)
321
+ ? createEffectOrORPCMiddleware({
322
+ effectErrorMap: flushed.state.effectErrorMap,
323
+ middleware,
324
+ runner: flushed.state.runner,
325
+ })
326
+ : middleware,
327
+ ...rest,
328
+ ],
320
329
  );
321
330
  return wrapBuilderLike(nextBuilder, flushed.state);
322
331
  };
@@ -347,11 +356,16 @@ function createEffectBuilderProxy(
347
356
  any
348
357
  >["handler"]
349
358
  >[0],
350
- ) =>
351
- new EffectDecoratedProcedure({
352
- ...effectDef,
359
+ ) => {
360
+ const flushed = flushEffectSteps(source, state);
361
+ return new EffectDecoratedProcedure({
362
+ ...flushed.builder["~orpc"],
363
+ effectErrorMap: flushed.state.effectErrorMap,
364
+ runner: flushed.state.runner,
365
+ effectSteps: flushed.state.effectSteps,
353
366
  handler,
354
367
  });
368
+ };
355
369
  });
356
370
  case "router":
357
371
  return getOrCreateVirtualMethod(context, prop, () => {
@@ -698,7 +712,7 @@ export class EffectBuilder<
698
712
  *
699
713
  * @example
700
714
  * ```ts
701
- * const getUser = effectOs
715
+ * const getUser = effectProcedure
702
716
  * .input(z.object({ id: z.string() }))
703
717
  * .traced('users.getUser')
704
718
  * .effect(function* ({ input }) {
@@ -734,7 +748,7 @@ export class EffectBuilder<
734
748
  >["handler"];
735
749
  /**
736
750
  * Defines the handler of the procedure using an Effect.
737
- * The Effect is executed using the ManagedRuntime provided during builder creation.
751
+ * The Effect is executed using the configured Effect runtime source.
738
752
  * The effect is automatically wrapped with `Effect.withSpan`.
739
753
  *
740
754
  * @see {@link https://orpc.dev/docs/procedure Procedure Docs}
@@ -826,14 +840,13 @@ export class EffectBuilder<
826
840
  >,
827
841
  builder?: AnyBuilderLike,
828
842
  ) {
829
- const { runtime, spanConfig, effectErrorMap, effectSteps, ...orpcDef } =
830
- def;
843
+ const { runner, spanConfig, effectErrorMap, effectSteps, ...orpcDef } = def;
831
844
 
832
845
  attachEffectState(this, builder ?? new Builder(orpcDef), {
833
846
  effectSteps,
834
847
  effectHandler: def.effectHandler,
835
848
  effectErrorMap,
836
- runtime,
849
+ runner,
837
850
  spanConfig,
838
851
  });
839
852
 
@@ -853,9 +866,9 @@ export class EffectBuilder<
853
866
  * import { makeEffectORPC } from '@orpc/effect'
854
867
  * import { Effect, Layer } from 'effect'
855
868
  *
856
- * const effectOs = makeEffectORPC(Layer.empty)
869
+ * const effectProcedure = makeEffectORPC(Layer.empty)
857
870
  *
858
- * const hello = effectOs.effect(() => Effect.succeed('Hello!'))
871
+ * const hello = effectProcedure.effect(() => Effect.succeed('Hello!'))
859
872
  * ```
860
873
  */
861
874
  export function makeEffectORPC(): EffectBuilder<
@@ -943,9 +956,9 @@ export function makeEffectORPC<TRequirementsProvided, TRuntimeError>(
943
956
  * const authedOs = os.use(authMiddleware)
944
957
  *
945
958
  * // Wrap it with Effect support
946
- * const effectOs = makeEffectORPC(UserServiceLive, authedOs)
959
+ * const effectProcedure = makeEffectORPC(UserServiceLive, authedOs)
947
960
  *
948
- * const getUser = effectOs
961
+ * const getUser = effectProcedure
949
962
  * .input(z.object({ id: z.string() }))
950
963
  * .effect(
951
964
  * Effect.fn(function* ({ input }) {
@@ -1025,15 +1038,16 @@ export function makeEffectORPC(
1025
1038
  ? source
1026
1039
  : (builder ?? emptyBuilder());
1027
1040
  const effectErrorMap = getEffectErrorMap(resolvedBuilder);
1028
- const runtime = toManagedRuntime(
1029
- sourceIsBuilder || source === undefined ? Layer.empty : source,
1030
- );
1041
+ const runner =
1042
+ sourceIsBuilder || source === undefined
1043
+ ? makeEffectRuntimeRunner()
1044
+ : makeEffectRuntimeRunner(source);
1031
1045
  return new EffectBuilder(
1032
1046
  {
1033
1047
  ...resolvedBuilder["~orpc"],
1034
1048
  effectErrorMap: effectErrorMap,
1035
1049
  errorMap: effectErrorMapToErrorMap(effectErrorMap),
1036
- runtime,
1050
+ runner,
1037
1051
  },
1038
1052
  unwrapEffectUpstream(resolvedBuilder),
1039
1053
  );
@@ -1051,3 +1065,12 @@ function emptyBuilder(): AnyBuilderLike {
1051
1065
  route: {},
1052
1066
  });
1053
1067
  }
1068
+
1069
+ /**
1070
+ * Runtime-less Effect oRPC builder, analogous to oRPC's `os` export.
1071
+ *
1072
+ * Provide application services with `.provide(layer)` or use
1073
+ * `makeEffectORPC(runtime)` when you need explicit ManagedRuntime lifecycle
1074
+ * control.
1075
+ */
1076
+ export const eos = makeEffectORPC();
@@ -16,10 +16,10 @@ import {
16
16
  type Context,
17
17
  type Lazyable,
18
18
  } from "@orpc/server";
19
- import type { ManagedRuntime } from "effect/ManagedRuntime";
20
19
 
21
20
  import { EffectProcedure } from "./effect-procedure";
22
21
  import { getEffectErrorMap, unwrapEffectUpstream } from "./extension/state";
22
+ import type { EffectRuntimeRunner } from "./runtime-source";
23
23
  import { effectErrorMapToErrorMap, type EffectErrorMap } from "./tagged-error";
24
24
  import type { EffectErrorMapToErrorMap, EnhancedEffectRouter } from "./types";
25
25
 
@@ -31,7 +31,7 @@ interface EnhanceEffectRouterOptions<
31
31
  middlewares: readonly AnyMiddleware[];
32
32
  errorMap: TEffectErrorMap;
33
33
  dedupeLeadingMiddlewares: boolean;
34
- runtime: ManagedRuntime<TRequirementsProvided, TRuntimeError>;
34
+ runner: EffectRuntimeRunner<TRequirementsProvided, TRuntimeError>;
35
35
  }
36
36
 
37
37
  export function enhanceEffectRouter<
@@ -97,7 +97,7 @@ export function enhanceEffectRouter<
97
97
  source["~orpc"].inputValidationIndex + newMiddlewareAdded,
98
98
  outputValidationIndex:
99
99
  source["~orpc"].outputValidationIndex + newMiddlewareAdded,
100
- runtime: options.runtime,
100
+ runner: options.runner,
101
101
  }) as any;
102
102
  }
103
103
 
@@ -20,9 +20,11 @@ import { Layer } from "effect";
20
20
 
21
21
  import {
22
22
  createEffectOptionalProviderMiddleware,
23
+ createEffectOrORPCMiddleware,
23
24
  createEffectPipelineMiddleware,
24
25
  createEffectProcedureHandler,
25
26
  createEffectProviderMiddleware,
27
+ isDecoratedMiddleware,
26
28
  isEffectMiddleware,
27
29
  } from "./effect-runtime";
28
30
  import { composeSurfaceProxy } from "./extension/compose-surfaces";
@@ -120,7 +122,7 @@ function getEffectProcedureDef(
120
122
  effectSteps: context.state.effectSteps,
121
123
  effectHandler: context.state.effectHandler,
122
124
  effectErrorMap: context.state.effectErrorMap,
123
- runtime: context.state.runtime,
125
+ runner: context.state.runner,
124
126
  };
125
127
  }
126
128
 
@@ -136,7 +138,7 @@ function makeEffectProcedureHandler(
136
138
  effectErrorMap: def.effectErrorMap,
137
139
  effectFn: def.effectHandler.effectFn,
138
140
  effectSteps: def.effectSteps,
139
- runtime: def.runtime,
141
+ runner: def.runner,
140
142
  spanConfig: def.effectHandler.spanConfig,
141
143
  });
142
144
  }
@@ -169,7 +171,7 @@ function flushEffectSteps(
169
171
 
170
172
  const middleware = createEffectPipelineMiddleware({
171
173
  effectErrorMap: def.effectErrorMap,
172
- runtime: def.runtime,
174
+ runner: def.runner,
173
175
  steps: def.effectSteps,
174
176
  });
175
177
 
@@ -255,7 +257,7 @@ function createEffectProcedureProxy<
255
257
  def.middlewares,
256
258
  createEffectPipelineMiddleware({
257
259
  effectErrorMap: state.effectErrorMap,
258
- runtime: state.runtime,
260
+ runner: state.runner,
259
261
  steps: [step],
260
262
  }),
261
263
  ),
@@ -279,7 +281,7 @@ function createEffectProcedureProxy<
279
281
  createEffectProviderMiddleware({
280
282
  effectErrorMap: state.effectErrorMap,
281
283
  provider,
282
- runtime: state.runtime,
284
+ runner: state.runner,
283
285
  tag: tagOrLayer,
284
286
  }),
285
287
  ),
@@ -308,7 +310,7 @@ function createEffectProcedureProxy<
308
310
  createEffectOptionalProviderMiddleware({
309
311
  effectErrorMap: state.effectErrorMap,
310
312
  provider,
311
- runtime: state.runtime,
313
+ runner: state.runner,
312
314
  tag,
313
315
  }),
314
316
  ),
@@ -323,18 +325,40 @@ function createEffectProcedureProxy<
323
325
  ) => {
324
326
  const def = getEffectProcedureDef(context);
325
327
  if (!mapInput && isEffectMiddleware(middleware)) {
326
- return new EffectDecoratedProcedure(
327
- appendEffectStep(def, {
328
- _tag: "middleware",
329
- middleware,
330
- }),
331
- );
328
+ const step: EffectPipelineStep = {
329
+ _tag: "middleware" as const,
330
+ middleware,
331
+ };
332
+
333
+ if (def.effectHandler) {
334
+ return new EffectDecoratedProcedure(
335
+ appendEffectStep(def, step),
336
+ );
337
+ }
338
+
339
+ return new EffectDecoratedProcedure({
340
+ ...def,
341
+ middlewares: addMiddleware(
342
+ def.middlewares,
343
+ createEffectPipelineMiddleware({
344
+ effectErrorMap: state.effectErrorMap,
345
+ runner: state.runner,
346
+ steps: [step],
347
+ }),
348
+ ),
349
+ });
332
350
  }
333
351
 
334
352
  const flushedDef = flushEffectSteps(def);
335
353
  const mapped = mapInput
336
354
  ? decorateMiddleware(middleware).mapInput(mapInput)
337
- : middleware;
355
+ : isDecoratedMiddleware(middleware)
356
+ ? middleware
357
+ : createEffectOrORPCMiddleware({
358
+ effectErrorMap: state.effectErrorMap,
359
+ middleware: middleware as any,
360
+ runner: state.runner,
361
+ });
338
362
 
339
363
  return new EffectDecoratedProcedure({
340
364
  ...flushedDef,
@@ -473,7 +497,7 @@ export class EffectProcedure<
473
497
  effectSteps,
474
498
  effectHandler,
475
499
  effectErrorMap: def.effectErrorMap,
476
- runtime: def.runtime,
500
+ runner: def.runner,
477
501
  });
478
502
 
479
503
  if (new.target === EffectProcedure) {