effect-orpc 0.2.2 → 0.4.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.
@@ -0,0 +1,76 @@
1
+ import { Effect, Exit, FiberRefs, Layer, ManagedRuntime } from "effect";
2
+
3
+ import { getCurrentFiberRefs } from "./fiber-context-bridge";
4
+
5
+ export type EffectRuntimeSource<TRequirementsProvided, TRuntimeError> =
6
+ | ManagedRuntime.ManagedRuntime<TRequirementsProvided, TRuntimeError>
7
+ | Layer.Layer<TRequirementsProvided, TRuntimeError, never>;
8
+
9
+ export interface EffectRuntimeRunner<TRequirementsProvided, TRuntimeError> {
10
+ readonly runtime?: ManagedRuntime.ManagedRuntime<
11
+ TRequirementsProvided,
12
+ TRuntimeError
13
+ >;
14
+ readonly runPromiseExit: <A, E>(
15
+ effect: Effect.Effect<A, E, TRequirementsProvided>,
16
+ options?: { readonly signal?: AbortSignal },
17
+ ) => Promise<Exit.Exit<A, E | TRuntimeError>>;
18
+ }
19
+
20
+ export function makeEffectRuntimeRunner<
21
+ TRequirementsProvided = never,
22
+ TRuntimeError = never,
23
+ >(
24
+ source?: EffectRuntimeSource<TRequirementsProvided, TRuntimeError>,
25
+ ): EffectRuntimeRunner<TRequirementsProvided, TRuntimeError> {
26
+ if (source === undefined) {
27
+ return {
28
+ runPromiseExit: (effect, options) =>
29
+ Effect.runPromiseExit(withParentFiberRefs(effect as any), {
30
+ signal: options?.signal,
31
+ }) as Promise<Exit.Exit<any, any>>,
32
+ };
33
+ }
34
+
35
+ if (ManagedRuntime.isManagedRuntime(source)) {
36
+ const runtime = source as ManagedRuntime.ManagedRuntime<
37
+ TRequirementsProvided,
38
+ TRuntimeError
39
+ >;
40
+ return {
41
+ runtime,
42
+ runPromiseExit: (effect, options) =>
43
+ runtime.runPromiseExit(withParentFiberRefs(effect), {
44
+ signal: options?.signal,
45
+ }),
46
+ };
47
+ }
48
+
49
+ const layer = source as Layer.Layer<
50
+ TRequirementsProvided,
51
+ TRuntimeError,
52
+ never
53
+ >;
54
+ return {
55
+ runPromiseExit: (effect, options) =>
56
+ Effect.runPromiseExit(
57
+ withParentFiberRefs(Effect.provide(effect as any, layer)),
58
+ { signal: options?.signal },
59
+ ) as Promise<Exit.Exit<any, any>>,
60
+ };
61
+ }
62
+
63
+ function withParentFiberRefs<A, E, R>(
64
+ effect: Effect.Effect<A, E, R>,
65
+ ): Effect.Effect<A, E, R> {
66
+ const parentFiberRefs = getCurrentFiberRefs();
67
+ return parentFiberRefs
68
+ ? Effect.fiberIdWith((fiberId) =>
69
+ Effect.flatMap(Effect.getFiberRefs, (fiberRefs) =>
70
+ Effect.setFiberRefs(
71
+ FiberRefs.joinAs(fiberRefs, fiberId, parentFiberRefs),
72
+ ).pipe(Effect.andThen(effect)),
73
+ ),
74
+ )
75
+ : effect;
76
+ }
@@ -341,7 +341,7 @@ export function ORPCTaggedError<
341
341
  *
342
342
  * @example
343
343
  * ```ts
344
- * const handler = effectOs.effect(function* () {
344
+ * const handler = effectProcedure.effect(function* () {
345
345
  * const result = yield* someOperation.pipe(
346
346
  * Effect.catchTag('UserNotFoundError', (e) =>
347
347
  * Effect.fail(toORPCError(e))
@@ -371,15 +371,6 @@ export type EffectErrorMapItem =
371
371
  | ErrorMapItem<AnySchema>
372
372
  | AnyORPCTaggedErrorClass;
373
373
 
374
- export type ORPCTaggedErrorClassToErrorMapItem<T> =
375
- T extends ORPCTaggedErrorClass<any, any, infer TData>
376
- ? {
377
- status?: number;
378
- message?: string;
379
- data?: TData;
380
- }
381
- : never;
382
-
383
374
  /**
384
375
  * Extended error map that supports both traditional oRPC errors and ORPCTaggedError classes.
385
376
  *
@@ -62,6 +62,27 @@ describe("implementEffect", () => {
62
62
  });
63
63
  });
64
64
 
65
+ it("can implement a contract directly from a Layer", async () => {
66
+ const CounterLive = Layer.succeed(Counter, {
67
+ increment: (n: number) => Effect.succeed(n + 1),
68
+ });
69
+ const oe = implementEffect(contract, CounterLive);
70
+ const procedure = oe.users.list.effect(function* ({ input }) {
71
+ const counter = yield* Counter;
72
+
73
+ return {
74
+ next: yield* counter.increment(input.amount),
75
+ requestId: "layer",
76
+ };
77
+ });
78
+
79
+ expect(procedure["~effect"].runner.runtime).toBeUndefined();
80
+ await expect(call(procedure, { amount: 2 })).resolves.toEqual({
81
+ next: 3,
82
+ requestId: "layer",
83
+ });
84
+ });
85
+
65
86
  it("preserves contract enforcement at the root router", async () => {
66
87
  const oe = implementEffect(contract, runtime);
67
88
 
@@ -121,7 +121,7 @@ describe("effectBuilder proxy compatibility", () => {
121
121
  ({ input }: { input: { id: string } }) => input,
122
122
  );
123
123
  expect(procedure).toBeInstanceOf(EffectDecoratedProcedure);
124
- expect(procedure["~effect"].runtime).toBe(runtime);
124
+ expect(procedure["~effect"].runner.runtime).toBe(runtime);
125
125
  });
126
126
 
127
127
  it("preserves tagged class support in errors()", () => {
@@ -155,11 +155,11 @@ describe("effectBuilder proxy compatibility", () => {
155
155
 
156
156
  expect(handled).toBeInstanceOf(EffectDecoratedProcedure);
157
157
  expect(effected).toBeInstanceOf(EffectDecoratedProcedure);
158
- expect(routed.ping["~effect"].runtime).toBe(runtime);
158
+ expect(routed.ping["~effect"].runner.runtime).toBe(runtime);
159
159
  expect(isLazy(lazied)).toBe(true);
160
160
 
161
161
  const { default: resolved } = await unlazy(lazied as any);
162
- expect(resolved.ping["~effect"].runtime).toBe(runtime);
162
+ expect(resolved.ping["~effect"].runner.runtime).toBe(runtime);
163
163
  });
164
164
 
165
165
  it("preserves decorated procedure proxy reflection and extracted methods", () => {
@@ -214,7 +214,7 @@ describe("effectBuilder proxy compatibility", () => {
214
214
 
215
215
  await expect(callable("hello")).resolves.toEqual({ echoed: "hello" });
216
216
  expect(callable).toSatisfy(isProcedure);
217
- expect(callable["~effect"].runtime).toBe(runtime);
217
+ expect(callable["~effect"].runner.runtime).toBe(runtime);
218
218
  expect(callable.route({ path: "/echo" })).toBeInstanceOf(
219
219
  EffectDecoratedProcedure,
220
220
  );