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.
- package/README.md +225 -105
- package/dist/{chunk-VOWRLWZZ.js → chunk-IJP6L2XR.js} +6 -2
- package/dist/chunk-IJP6L2XR.js.map +1 -0
- package/dist/index.js +770 -282
- package/dist/index.js.map +1 -1
- package/dist/node.js +4 -3
- package/dist/node.js.map +1 -1
- package/package.json +1 -1
- package/src/contract.ts +46 -11
- package/src/effect-builder.ts +295 -33
- package/src/effect-enhance-router.ts +3 -3
- package/src/effect-procedure.ts +205 -11
- package/src/effect-runtime.ts +445 -23
- package/src/extension/create-node-proxy.ts +17 -1
- package/src/extension/state.ts +16 -20
- package/src/fiber-context-bridge.ts +13 -0
- package/src/index.ts +1 -0
- package/src/node.ts +2 -1
- package/src/runtime-source.ts +76 -0
- package/src/tagged-error.ts +1 -10
- package/src/tests/contract.test.ts +21 -0
- package/src/tests/effect-builder.proxy.test.ts +4 -4
- package/src/tests/effect-builder.test.ts +544 -7
- package/src/tests/effect-error-map.test.ts +10 -10
- package/src/tests/effect-procedure.test.ts +9 -6
- package/src/tests/node-side-effect.test.ts +80 -0
- package/src/tests/parity.effect-builder.test.ts +10 -3
- package/src/tests/parity.effect-procedure.test.ts +24 -8
- package/src/tests/shared.ts +1 -25
- package/src/types/effect-builder-surface.ts +117 -1
- package/src/types/effect-procedure-surface.ts +98 -1
- package/src/types/index.ts +291 -5
- package/src/types/variants.ts +351 -18
- package/dist/chunk-VOWRLWZZ.js.map +0 -1
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ Inspired by [effect-trpc](https://github.com/mikearnaldi/effect-trpc).
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
9
|
- **Effect-native procedures** - Write oRPC procedures using generators with `yield*` syntax
|
|
10
|
-
- **Type-safe service injection** -
|
|
10
|
+
- **Type-safe service injection** - Add base services with `.provide(layer)` or pass a `Layer` / `ManagedRuntime<R>` directly
|
|
11
11
|
- **Tagged errors** - Create Effect-native error classes with `ORPCTaggedError` that integrate with oRPC's error handling
|
|
12
12
|
- **Full oRPC compatibility** - Mix Effect procedures with standard oRPC procedures in the same router
|
|
13
13
|
- **Telemetry support with automatic tracing** - Procedures are automatically traced with OpenTelemetry-compatible spans. Customize span names with `.traced()`.
|
|
@@ -30,7 +30,7 @@ Runnable demos live in the repository's `examples/` directory.
|
|
|
30
30
|
```ts
|
|
31
31
|
import { os } from "@orpc/server";
|
|
32
32
|
import { Effect, ManagedRuntime } from "effect";
|
|
33
|
-
import { makeEffectORPC, ORPCTaggedError } from "effect-orpc";
|
|
33
|
+
import { eos, makeEffectORPC, ORPCTaggedError } from "effect-orpc";
|
|
34
34
|
|
|
35
35
|
interface User {
|
|
36
36
|
id: number;
|
|
@@ -43,15 +43,6 @@ let users: User[] = [
|
|
|
43
43
|
{ id: 3, name: "James Dane" },
|
|
44
44
|
];
|
|
45
45
|
|
|
46
|
-
// Authenticated os with initial context & errors set
|
|
47
|
-
const authedOs = os
|
|
48
|
-
.errors({ UNAUTHORIZED: { status: 401 } })
|
|
49
|
-
.$context<{ userId?: number }>()
|
|
50
|
-
.use(({ context, errors, next }) => {
|
|
51
|
-
if (context.userId === undefined) throw errors.UNAUTHORIZED();
|
|
52
|
-
return next({ context: { ...context, userId: context.userId } });
|
|
53
|
-
});
|
|
54
|
-
|
|
55
46
|
// Define your services
|
|
56
47
|
class UsersRepo extends Effect.Service<UsersRepo>()("UsersRepo", {
|
|
57
48
|
accessors: true,
|
|
@@ -65,18 +56,27 @@ class UserNotFoundError extends ORPCTaggedError("UserNotFoundError", {
|
|
|
65
56
|
status: 404,
|
|
66
57
|
}) {}
|
|
67
58
|
|
|
68
|
-
// Create
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
UserNotFoundError
|
|
73
|
-
})
|
|
59
|
+
// Create an Effect-aware oRPC builder with your service layer, context, errors,
|
|
60
|
+
// and middleware.
|
|
61
|
+
const effectProcedure = eos
|
|
62
|
+
.provide(UsersRepo.Default)
|
|
63
|
+
.errors({ UNAUTHORIZED: { status: 401 }, UserNotFoundError })
|
|
64
|
+
.$context<{ userId?: number }>()
|
|
65
|
+
.use(({ context, errors, next }) => {
|
|
66
|
+
if (context.userId === undefined) throw errors.UNAUTHORIZED();
|
|
67
|
+
return next({ context: { ...context, userId: context.userId } });
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Use ManagedRuntime only when scoped resources should be acquired once and
|
|
71
|
+
// released on shutdown, for example a shared cache, database pool, or telemetry SDK:
|
|
72
|
+
// const runtime = ManagedRuntime.make(UsersRepo.Default);
|
|
73
|
+
// const effectProcedure = makeEffectORPC(runtime).errors({ UserNotFoundError });
|
|
74
74
|
|
|
75
75
|
// Create the router with mixed procedures
|
|
76
76
|
export const router = {
|
|
77
77
|
health: os.handler(() => "ok"),
|
|
78
78
|
users: {
|
|
79
|
-
me:
|
|
79
|
+
me: effectProcedure.effect(function* ({ context: { userId } }) {
|
|
80
80
|
const user = yield* UsersRepo.get(userId);
|
|
81
81
|
if (!user) {
|
|
82
82
|
return yield* new UserNotFoundError();
|
|
@@ -91,11 +91,11 @@ export type Router = typeof router;
|
|
|
91
91
|
|
|
92
92
|
## Type Safety
|
|
93
93
|
|
|
94
|
-
The wrapper enforces that Effect procedures only use services provided by
|
|
94
|
+
The wrapper enforces that Effect procedures only use services provided by `.provide(layer)`, request-scoped `.provide(tag, provider)` calls, or an initial `Layer` / `ManagedRuntime`. If you try to use a service that isn't available, you'll get a compile-time error:
|
|
95
95
|
|
|
96
96
|
```ts
|
|
97
|
-
import { Context, Effect, Layer
|
|
98
|
-
import {
|
|
97
|
+
import { Context, Effect, Layer } from "effect";
|
|
98
|
+
import { eos } from "effect-orpc";
|
|
99
99
|
|
|
100
100
|
class ProvidedService extends Context.Tag("ProvidedService")<
|
|
101
101
|
ProvidedService,
|
|
@@ -107,22 +107,20 @@ class MissingService extends Context.Tag("MissingService")<
|
|
|
107
107
|
{ doSomething: () => Effect.Effect<string> }
|
|
108
108
|
>() {}
|
|
109
109
|
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}),
|
|
114
|
-
);
|
|
110
|
+
const AppLive = Layer.succeed(ProvidedService, {
|
|
111
|
+
doSomething: () => Effect.succeed("ok"),
|
|
112
|
+
});
|
|
115
113
|
|
|
116
|
-
const
|
|
114
|
+
const effectProcedure = eos.provide(AppLive);
|
|
117
115
|
|
|
118
|
-
// ✅ This compiles - ProvidedService is
|
|
119
|
-
const works =
|
|
116
|
+
// ✅ This compiles - ProvidedService is provided by AppLive
|
|
117
|
+
const works = effectProcedure.effect(function* () {
|
|
120
118
|
const service = yield* ProvidedService;
|
|
121
119
|
return yield* service.doSomething();
|
|
122
120
|
});
|
|
123
121
|
|
|
124
|
-
// ❌ This fails to compile - MissingService is not
|
|
125
|
-
const fails =
|
|
122
|
+
// ❌ This fails to compile - MissingService is not provided
|
|
123
|
+
const fails = effectProcedure.effect(function* () {
|
|
126
124
|
const service = yield* MissingService; // Type error!
|
|
127
125
|
return yield* service.doSomething();
|
|
128
126
|
});
|
|
@@ -139,7 +137,7 @@ const fails = effectOs.effect(function* () {
|
|
|
139
137
|
Make sure the tagged error class is passed to the effect `.errors()` to be able to yield the error class directly and make the client recognize it as defined.
|
|
140
138
|
|
|
141
139
|
```ts
|
|
142
|
-
const getUser =
|
|
140
|
+
const getUser = effectProcedure
|
|
143
141
|
// Mixed error maps
|
|
144
142
|
.errors({
|
|
145
143
|
// Regular oRPC error
|
|
@@ -208,19 +206,19 @@ All Effect procedures are automatically traced with `Effect.withSpan`. By defaul
|
|
|
208
206
|
const router = {
|
|
209
207
|
users: {
|
|
210
208
|
// Span name: "users.get"
|
|
211
|
-
get:
|
|
209
|
+
get: effectProcedure.input(z.object({ id: z.string() })).effect(function* ({
|
|
212
210
|
input,
|
|
213
211
|
}) {
|
|
214
212
|
const userService = yield* UserService;
|
|
215
213
|
return yield* userService.findById(input.id);
|
|
216
214
|
}),
|
|
217
215
|
// Span name: "users.create"
|
|
218
|
-
create:
|
|
219
|
-
input
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
216
|
+
create: effectProcedure
|
|
217
|
+
.input(z.object({ name: z.string() }))
|
|
218
|
+
.effect(function* ({ input }) {
|
|
219
|
+
const userService = yield* UserService;
|
|
220
|
+
return yield* userService.create(input.name);
|
|
221
|
+
}),
|
|
224
222
|
},
|
|
225
223
|
};
|
|
226
224
|
```
|
|
@@ -228,7 +226,7 @@ const router = {
|
|
|
228
226
|
Use `.traced()` to override the default span name:
|
|
229
227
|
|
|
230
228
|
```ts
|
|
231
|
-
const getUser =
|
|
229
|
+
const getUser = effectProcedure
|
|
232
230
|
.input(z.object({ id: z.string() }))
|
|
233
231
|
.traced("custom.span.name") // Override the default path-based name
|
|
234
232
|
.effect(function* ({ input }) {
|
|
@@ -239,7 +237,7 @@ const getUser = effectOs
|
|
|
239
237
|
|
|
240
238
|
### Enabling OpenTelemetry
|
|
241
239
|
|
|
242
|
-
To enable tracing, include the OpenTelemetry layer in your
|
|
240
|
+
To enable tracing, include the OpenTelemetry layer in your application layer:
|
|
243
241
|
|
|
244
242
|
```ts
|
|
245
243
|
import { NodeSdk } from "@effect/opentelemetry";
|
|
@@ -255,8 +253,7 @@ const TracingLive = NodeSdk.layer(
|
|
|
255
253
|
|
|
256
254
|
const AppLive = Layer.mergeAll(UserServiceLive, TracingLive);
|
|
257
255
|
|
|
258
|
-
const
|
|
259
|
-
const effectOs = makeEffectORPC(runtime);
|
|
256
|
+
const effectProcedure = eos.provide(AppLive);
|
|
260
257
|
```
|
|
261
258
|
|
|
262
259
|
### Error Stack Traces
|
|
@@ -269,24 +266,136 @@ MyCustomError: Something went wrong
|
|
|
269
266
|
at users.getById (/app/src/procedures.ts:41:35)
|
|
270
267
|
```
|
|
271
268
|
|
|
269
|
+
## Effect middleware
|
|
270
|
+
|
|
271
|
+
`.use(...)` accepts generator-based Effect middleware in addition to native oRPC
|
|
272
|
+
middleware. Two patterns are supported:
|
|
273
|
+
|
|
274
|
+
**Gate** — run auth or validation side effects, then let the pipeline continue
|
|
275
|
+
automatically (no need to call `next`):
|
|
276
|
+
|
|
277
|
+
```ts
|
|
278
|
+
effectProcedure.use(function* () {
|
|
279
|
+
const user = yield* CurrentUser;
|
|
280
|
+
yield* requireActiveUser(user);
|
|
281
|
+
});
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
**Wrap** — call downstream explicitly and return the result. When porting oRPC
|
|
285
|
+
middleware that uses `return next(...)`, use `return yield* next(...)`:
|
|
286
|
+
|
|
287
|
+
```ts
|
|
288
|
+
effectProcedure.use(function* ({ next }) {
|
|
289
|
+
const user = yield* CurrentUser;
|
|
290
|
+
yield* requireActiveUser(user);
|
|
291
|
+
|
|
292
|
+
return yield* next({
|
|
293
|
+
context: { userId: user.id },
|
|
294
|
+
});
|
|
295
|
+
});
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
To transform the downstream output, capture `next()` and pass through `output`:
|
|
299
|
+
|
|
300
|
+
```ts
|
|
301
|
+
effectProcedure.use(function* ({ next }, _input, output) {
|
|
302
|
+
const result = yield* next();
|
|
303
|
+
return yield* output(`${result.output}-wrapped`);
|
|
304
|
+
});
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Calling `yield* next()` without returning its result still runs the handler once,
|
|
308
|
+
but prefer `return yield* next(...)` so the pipeline receives your middleware
|
|
309
|
+
result explicitly.
|
|
310
|
+
|
|
311
|
+
### Runtime boundaries and fiber context continuity
|
|
312
|
+
|
|
313
|
+
`effect-orpc` batches contiguous Effect-native steps into one runtime boundary.
|
|
314
|
+
Effect-native steps are `.provide(...)`, `.provideOptional(...)`, generator
|
|
315
|
+
`.use(function* ...)`, and `.effect(function* ...)`.
|
|
316
|
+
|
|
317
|
+
```ts
|
|
318
|
+
eos
|
|
319
|
+
.provide(AppLive)
|
|
320
|
+
.provide(CurrentUser, ({ context }) => Effect.succeed(context.user))
|
|
321
|
+
.use(function* ({ next }) {
|
|
322
|
+
const user = yield* CurrentUser;
|
|
323
|
+
return yield* next({ context: { userId: user.id } });
|
|
324
|
+
})
|
|
325
|
+
.effect(function* ({ context }) {
|
|
326
|
+
const user = yield* CurrentUser;
|
|
327
|
+
return `${context.userId}:${user.id}`;
|
|
328
|
+
});
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
The example above runs the provider, middleware, and handler inside a single
|
|
332
|
+
Effect execution boundary.
|
|
333
|
+
|
|
334
|
+
A native oRPC middleware breaks the contiguous Effect pipeline. Pending Effect
|
|
335
|
+
steps are flushed into one generated oRPC middleware before the native middleware:
|
|
336
|
+
|
|
337
|
+
```ts
|
|
338
|
+
eos
|
|
339
|
+
.provide(AppLive)
|
|
340
|
+
.provide(CurrentUser, getCurrentUser) // Effect group #1
|
|
341
|
+
.use(function* ({ next }) {
|
|
342
|
+
return yield* next();
|
|
343
|
+
})
|
|
344
|
+
.use(({ next }) => next()) // native oRPC middleware; flushes group #1
|
|
345
|
+
.use(function* ({ next }) {
|
|
346
|
+
return yield* next();
|
|
347
|
+
}) // Effect group #2
|
|
348
|
+
.effect(function* () {
|
|
349
|
+
return "ok";
|
|
350
|
+
});
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
That split still creates multiple runtime boundaries. If the Node bridge is
|
|
354
|
+
installed, however, `effect-orpc` carries the current `FiberRefs` through the
|
|
355
|
+
native oRPC continuation and merges them into the next Effect boundary:
|
|
356
|
+
|
|
357
|
+
```ts
|
|
358
|
+
import "effect-orpc/node";
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
Use the side-effect import when you only need continuity across internal
|
|
362
|
+
`effect-orpc` boundaries, such as Effect group #1 → native oRPC middleware →
|
|
363
|
+
Effect group #2.
|
|
364
|
+
|
|
365
|
+
Procedure-level `.provide*` after a native `.handler(...)` has no Effect handler
|
|
366
|
+
boundary to attach to, so it is installed as an oRPC middleware that runs its
|
|
367
|
+
provider Effect through the configured runtime source:
|
|
368
|
+
|
|
369
|
+
```ts
|
|
370
|
+
eos
|
|
371
|
+
.provide(AppLive)
|
|
372
|
+
.handler(() => "ok") // native oRPC handler
|
|
373
|
+
.provide(CurrentUser, getCurrentUser); // fallback provider middleware
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
If you want `.provide*` and Effect middleware to batch with the handler, use
|
|
377
|
+
`.effect(function* ...)` instead of `.handler(...)`.
|
|
378
|
+
|
|
272
379
|
## Request-Scoped Fiber Context
|
|
273
380
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
381
|
+
The `/node` entrypoint installs a bridge backed by `AsyncLocalStorage`. It has
|
|
382
|
+
two uses:
|
|
383
|
+
|
|
384
|
+
- `import "effect-orpc/node"` installs the bridge passively. This is enough for
|
|
385
|
+
`effect-orpc` to propagate `FiberRefs` across its own split runtime boundaries.
|
|
386
|
+
- `withFiberContext(() => next())` actively seeds the bridge from an external
|
|
387
|
+
Effect scope, such as framework middleware wrapping an oRPC handler.
|
|
277
388
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
`effect-orpc/node`.
|
|
389
|
+
Use `withFiberContext` when request-local `FiberRef` state is created outside the
|
|
390
|
+
oRPC pipeline and should be visible inside handlers:
|
|
281
391
|
|
|
282
392
|
```ts
|
|
283
393
|
import { Hono } from "hono";
|
|
284
|
-
import { Effect
|
|
285
|
-
import {
|
|
394
|
+
import { Effect } from "effect";
|
|
395
|
+
import { eos } from "effect-orpc";
|
|
286
396
|
import { withFiberContext } from "effect-orpc/node";
|
|
287
397
|
|
|
288
|
-
const
|
|
289
|
-
const effectOs = makeEffectORPC(runtime);
|
|
398
|
+
const effectProcedure = eos.provide(AppLive);
|
|
290
399
|
const app = new Hono();
|
|
291
400
|
|
|
292
401
|
app.use("*", async (c, next) => {
|
|
@@ -302,31 +411,27 @@ app.use("*", async (c, next) => {
|
|
|
302
411
|
});
|
|
303
412
|
```
|
|
304
413
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
as the application-wide base layer, while `withFiberContext` preserves the
|
|
308
|
-
more specific request-scoped values from outer middleware. This prevents
|
|
309
|
-
request-local references such as request IDs, logging annotations, tracing
|
|
310
|
-
context, or scoped overrides from being replaced by runtime defaults when the
|
|
311
|
-
handler crosses the runtime boundary.
|
|
414
|
+
Importing `withFiberContext` from `effect-orpc/node` also installs the bridge, so
|
|
415
|
+
you do not need a separate side-effect import.
|
|
312
416
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
417
|
+
When a captured fiber context and the application `Layer` / `ManagedRuntime`
|
|
418
|
+
both provide the same service, `effect-orpc` prioritizes the captured context.
|
|
419
|
+
The application layer is treated as the base layer, while the bridge preserves more specific
|
|
420
|
+
request-scoped values such as request IDs, logging annotations, tracing context,
|
|
421
|
+
or scoped overrides when crossing runtime boundaries.
|
|
317
422
|
|
|
318
|
-
|
|
319
|
-
|
|
423
|
+
The main package stays runtime-agnostic; `/node` is separate because the bridge
|
|
424
|
+
relies on `AsyncLocalStorage` from `node:async_hooks`.
|
|
320
425
|
|
|
321
426
|
## Contract-First Usage
|
|
322
427
|
|
|
323
|
-
Use `implementEffect(contract,
|
|
324
|
-
and want to keep contract-first enforcement while adding Effect-native
|
|
325
|
-
Use `
|
|
326
|
-
|
|
428
|
+
Use `implementEffect(contract, layerOrRuntime)` when you already have an oRPC
|
|
429
|
+
contract and want to keep contract-first enforcement while adding Effect-native
|
|
430
|
+
handlers. Use `eos.provide(layer)` when you want to build procedures directly
|
|
431
|
+
from the default Effect-aware builder.
|
|
327
432
|
|
|
328
433
|
```ts
|
|
329
|
-
import { Effect
|
|
434
|
+
import { Effect } from "effect";
|
|
330
435
|
import { eoc, implementEffect } from "effect-orpc";
|
|
331
436
|
import z from "zod";
|
|
332
437
|
|
|
@@ -346,8 +451,7 @@ const contract = {
|
|
|
346
451
|
},
|
|
347
452
|
};
|
|
348
453
|
|
|
349
|
-
const
|
|
350
|
-
const oe = implementEffect(contract, runtime);
|
|
454
|
+
const oe = implementEffect(contract, UsersRepo.Default);
|
|
351
455
|
|
|
352
456
|
export const router = oe.router({
|
|
353
457
|
users: {
|
|
@@ -369,34 +473,46 @@ directly from the `ORPCTaggedError` class.
|
|
|
369
473
|
|
|
370
474
|
## API Reference
|
|
371
475
|
|
|
372
|
-
### `
|
|
476
|
+
### `eos`
|
|
373
477
|
|
|
374
|
-
|
|
478
|
+
The default Effect-aware procedure builder. Provide your application services
|
|
479
|
+
with `.provide(layer)`:
|
|
375
480
|
|
|
376
|
-
|
|
377
|
-
|
|
481
|
+
```ts
|
|
482
|
+
const effectProcedure = eos.provide(AppLive);
|
|
483
|
+
```
|
|
378
484
|
|
|
379
|
-
|
|
485
|
+
Use `makeEffectORPC(runtime)` when a scoped `Layer` should be acquired once and
|
|
486
|
+
released by your application shutdown path, such as a shared cache, database
|
|
487
|
+
pool, HTTP client, or telemetry SDK:
|
|
380
488
|
|
|
381
489
|
```ts
|
|
382
|
-
|
|
383
|
-
|
|
490
|
+
const runtime = ManagedRuntime.make(AppLive);
|
|
491
|
+
|
|
492
|
+
const effectProcedure = makeEffectORPC(runtime);
|
|
384
493
|
|
|
385
|
-
//
|
|
386
|
-
|
|
494
|
+
// later, during app shutdown
|
|
495
|
+
await runtime.dispose();
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
`makeEffectORPC(builder)` is also available when you need to wrap an existing
|
|
499
|
+
oRPC builder:
|
|
500
|
+
|
|
501
|
+
```ts
|
|
502
|
+
const effectAuthedOs = makeEffectORPC(authedBuilder).provide(AppLive);
|
|
387
503
|
```
|
|
388
504
|
|
|
389
|
-
### `implementEffect(contract,
|
|
505
|
+
### `implementEffect(contract, layerOrRuntime)`
|
|
390
506
|
|
|
391
507
|
Creates an Effect-aware contract implementer.
|
|
392
508
|
|
|
393
509
|
- `contract` - An oRPC contract router built with `oc`
|
|
394
|
-
- `
|
|
510
|
+
- `layerOrRuntime` - A `Layer<R, E, never>` provided per call, or a user-owned `ManagedRuntime<R, E>` when the application should control acquisition and release (for example, a shared cache, database pool, or telemetry SDK)
|
|
395
511
|
|
|
396
512
|
Returns a contract-shaped implementer tree whose leaves support `.effect(...)`.
|
|
397
513
|
|
|
398
514
|
```ts
|
|
399
|
-
const oe = implementEffect(contract,
|
|
515
|
+
const oe = implementEffect(contract, AppLive);
|
|
400
516
|
|
|
401
517
|
const router = oe.router({
|
|
402
518
|
users: {
|
|
@@ -436,26 +552,28 @@ const contract = {
|
|
|
436
552
|
|
|
437
553
|
Wraps an oRPC Builder with Effect support. Available methods:
|
|
438
554
|
|
|
439
|
-
| Method | Description
|
|
440
|
-
| ------------------- |
|
|
441
|
-
| `.$config(config)` | Set or override the builder config
|
|
442
|
-
| `.$context<U>()` | Set or override the initial context type
|
|
443
|
-
| `.$meta(meta)` | Set or override the initial metadata
|
|
444
|
-
| `.$route(route)` | Set or override the initial route configuration
|
|
445
|
-
| `.$input(schema)` | Set or override the initial input schema
|
|
446
|
-
| `.errors(map)` | Add type-safe custom errors
|
|
447
|
-
| `.meta(meta)` | Set procedure metadata (merged with existing)
|
|
448
|
-
| `.route(route)` | Configure OpenAPI route (merged with existing)
|
|
449
|
-
| `.input(schema)` | Define input validation schema
|
|
450
|
-
| `.output(schema)` | Define output validation schema
|
|
451
|
-
| `.
|
|
452
|
-
| `.
|
|
453
|
-
| `.
|
|
454
|
-
| `.
|
|
455
|
-
| `.
|
|
456
|
-
| `.
|
|
457
|
-
| `.
|
|
458
|
-
| `.
|
|
555
|
+
| Method | Description |
|
|
556
|
+
| ------------------- | ------------------------------------------------------------------------------------ |
|
|
557
|
+
| `.$config(config)` | Set or override the builder config |
|
|
558
|
+
| `.$context<U>()` | Set or override the initial context type |
|
|
559
|
+
| `.$meta(meta)` | Set or override the initial metadata |
|
|
560
|
+
| `.$route(route)` | Set or override the initial route configuration |
|
|
561
|
+
| `.$input(schema)` | Set or override the initial input schema |
|
|
562
|
+
| `.errors(map)` | Add type-safe custom errors |
|
|
563
|
+
| `.meta(meta)` | Set procedure metadata (merged with existing) |
|
|
564
|
+
| `.route(route)` | Configure OpenAPI route (merged with existing) |
|
|
565
|
+
| `.input(schema)` | Define input validation schema |
|
|
566
|
+
| `.output(schema)` | Define output validation schema |
|
|
567
|
+
| `.provide(layer)` | Provide a base Effect layer to downstream Effect middleware and handlers |
|
|
568
|
+
| `.provide(tag, fn)` | Provide a request-scoped Effect service to downstream Effect middleware and handlers |
|
|
569
|
+
| `.use(middleware)` | Add middleware |
|
|
570
|
+
| `.traced(name)` | Add a traceable span for telemetry (optional, defaults to the procedure's path) |
|
|
571
|
+
| `.handler(handler)` | Define a non-Effect handler (standard oRPC handler) |
|
|
572
|
+
| `.effect(handler)` | Define the Effect handler |
|
|
573
|
+
| `.prefix(prefix)` | Prefix all procedures in the router (for OpenAPI) |
|
|
574
|
+
| `.tag(...tags)` | Add tags to all procedures in the router (for OpenAPI) |
|
|
575
|
+
| `.router(router)` | Apply all options to a router |
|
|
576
|
+
| `.lazy(loader)` | Create and apply options to a lazy-loaded router |
|
|
459
577
|
|
|
460
578
|
### `EffectDecoratedProcedure`
|
|
461
579
|
|
|
@@ -466,6 +584,8 @@ The result of calling `.effect()`. Extends standard oRPC `DecoratedProcedure` wi
|
|
|
466
584
|
| `.errors(map)` | Add more custom errors |
|
|
467
585
|
| `.meta(meta)` | Update metadata (merged with existing) |
|
|
468
586
|
| `.route(route)` | Update route configuration (merged) |
|
|
587
|
+
| `.provide(layer)` | Provide a base Effect layer |
|
|
588
|
+
| `.provide(tag, fn)` | Provide a request-scoped Effect service |
|
|
469
589
|
| `.use(middleware)` | Add middleware |
|
|
470
590
|
| `.callable(options?)` | Make procedure directly invocable |
|
|
471
591
|
| `.actionable(options?)` | Make procedure compatible with server actions |
|
|
@@ -6,9 +6,13 @@ function installFiberContextBridge(nextBridge) {
|
|
|
6
6
|
function getCurrentFiberRefs() {
|
|
7
7
|
return bridge?.getCurrentFiberRefs();
|
|
8
8
|
}
|
|
9
|
+
function runWithFiberRefs(fiberRefs, fn) {
|
|
10
|
+
return bridge?.runWithFiberRefs ? bridge.runWithFiberRefs(fiberRefs, fn) : fn();
|
|
11
|
+
}
|
|
9
12
|
|
|
10
13
|
export {
|
|
11
14
|
installFiberContextBridge,
|
|
12
|
-
getCurrentFiberRefs
|
|
15
|
+
getCurrentFiberRefs,
|
|
16
|
+
runWithFiberRefs
|
|
13
17
|
};
|
|
14
|
-
//# sourceMappingURL=chunk-
|
|
18
|
+
//# sourceMappingURL=chunk-IJP6L2XR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/fiber-context-bridge.ts"],"sourcesContent":["import type { FiberRefs } from \"effect\";\n\nexport interface FiberContextBridge {\n readonly getCurrentFiberRefs: () => FiberRefs.FiberRefs | undefined;\n readonly runWithFiberRefs?: <T>(\n fiberRefs: FiberRefs.FiberRefs,\n fn: () => Promise<T>,\n ) => Promise<T>;\n}\n\nlet bridge: FiberContextBridge | undefined;\n\nexport function installFiberContextBridge(\n nextBridge: FiberContextBridge | undefined,\n): void {\n bridge = nextBridge;\n}\n\nexport function getCurrentFiberRefs(): FiberRefs.FiberRefs | undefined {\n return bridge?.getCurrentFiberRefs();\n}\n\nexport function runWithFiberRefs<T>(\n fiberRefs: FiberRefs.FiberRefs,\n fn: () => Promise<T>,\n): Promise<T> {\n return bridge?.runWithFiberRefs\n ? bridge.runWithFiberRefs(fiberRefs, fn)\n : fn();\n}\n"],"mappings":";AAUA,IAAI;AAEG,SAAS,0BACd,YACM;AACN,WAAS;AACX;AAEO,SAAS,sBAAuD;AACrE,SAAO,QAAQ,oBAAoB;AACrC;AAEO,SAAS,iBACd,WACA,IACY;AACZ,SAAO,QAAQ,mBACX,OAAO,iBAAiB,WAAW,EAAE,IACrC,GAAG;AACT;","names":[]}
|