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/README.md +133 -146
- package/dist/index.js +421 -133
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/contract.ts +15 -12
- package/src/effect-builder.ts +63 -40
- package/src/effect-enhance-router.ts +3 -3
- package/src/effect-procedure.ts +38 -14
- package/src/effect-runtime.ts +647 -115
- package/src/extension/state.ts +3 -5
- package/src/index.ts +1 -0
- package/src/runtime-source.ts +70 -12
- package/src/tagged-error.ts +1 -1
- package/src/tests/contract.test.ts +5 -8
- package/src/tests/effect-builder.proxy.test.ts +15 -17
- package/src/tests/effect-builder.test.ts +352 -161
- package/src/tests/effect-callback-shapes.test.ts +410 -0
- package/src/tests/effect-error-map.test.ts +12 -14
- package/src/tests/effect-procedure.test.ts +53 -11
- package/src/tests/parity-shared.ts +2 -2
- package/src/types/effect-builder-surface.ts +1 -1
- package/src/types/index.ts +76 -51
- package/src/types/variants.ts +5 -5
package/README.md
CHANGED
|
@@ -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,26 +56,27 @@ class UserNotFoundError extends ORPCTaggedError("UserNotFoundError", {
|
|
|
65
56
|
status: 404,
|
|
66
57
|
}) {}
|
|
67
58
|
|
|
68
|
-
// Create an Effect-aware oRPC builder with your service layer,
|
|
69
|
-
//
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
})
|
|
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
|
+
});
|
|
73
69
|
|
|
74
|
-
//
|
|
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:
|
|
75
72
|
// const runtime = ManagedRuntime.make(UsersRepo.Default);
|
|
76
|
-
// const
|
|
77
|
-
|
|
78
|
-
// Or start with only the builder and provide the layer later:
|
|
79
|
-
// const effectOs = makeEffectORPC(authedOs)
|
|
80
|
-
// .provide(UsersRepo.Default)
|
|
81
|
-
// .errors({ UserNotFoundError });
|
|
73
|
+
// const effectProcedure = makeEffectORPC(runtime).errors({ UserNotFoundError });
|
|
82
74
|
|
|
83
75
|
// Create the router with mixed procedures
|
|
84
76
|
export const router = {
|
|
85
77
|
health: os.handler(() => "ok"),
|
|
86
78
|
users: {
|
|
87
|
-
me:
|
|
79
|
+
me: effectProcedure.effect(function* ({ context: { userId } }) {
|
|
88
80
|
const user = yield* UsersRepo.get(userId);
|
|
89
81
|
if (!user) {
|
|
90
82
|
return yield* new UserNotFoundError();
|
|
@@ -103,7 +95,7 @@ The wrapper enforces that Effect procedures only use services provided by `.prov
|
|
|
103
95
|
|
|
104
96
|
```ts
|
|
105
97
|
import { Context, Effect, Layer } from "effect";
|
|
106
|
-
import {
|
|
98
|
+
import { eos } from "effect-orpc";
|
|
107
99
|
|
|
108
100
|
class ProvidedService extends Context.Tag("ProvidedService")<
|
|
109
101
|
ProvidedService,
|
|
@@ -119,16 +111,16 @@ const AppLive = Layer.succeed(ProvidedService, {
|
|
|
119
111
|
doSomething: () => Effect.succeed("ok"),
|
|
120
112
|
});
|
|
121
113
|
|
|
122
|
-
const
|
|
114
|
+
const effectProcedure = eos.provide(AppLive);
|
|
123
115
|
|
|
124
116
|
// ✅ This compiles - ProvidedService is provided by AppLive
|
|
125
|
-
const works =
|
|
117
|
+
const works = effectProcedure.effect(function* () {
|
|
126
118
|
const service = yield* ProvidedService;
|
|
127
119
|
return yield* service.doSomething();
|
|
128
120
|
});
|
|
129
121
|
|
|
130
122
|
// ❌ This fails to compile - MissingService is not provided
|
|
131
|
-
const fails =
|
|
123
|
+
const fails = effectProcedure.effect(function* () {
|
|
132
124
|
const service = yield* MissingService; // Type error!
|
|
133
125
|
return yield* service.doSomething();
|
|
134
126
|
});
|
|
@@ -145,7 +137,7 @@ const fails = effectOs.effect(function* () {
|
|
|
145
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.
|
|
146
138
|
|
|
147
139
|
```ts
|
|
148
|
-
const getUser =
|
|
140
|
+
const getUser = effectProcedure
|
|
149
141
|
// Mixed error maps
|
|
150
142
|
.errors({
|
|
151
143
|
// Regular oRPC error
|
|
@@ -214,19 +206,19 @@ All Effect procedures are automatically traced with `Effect.withSpan`. By defaul
|
|
|
214
206
|
const router = {
|
|
215
207
|
users: {
|
|
216
208
|
// Span name: "users.get"
|
|
217
|
-
get:
|
|
209
|
+
get: effectProcedure.input(z.object({ id: z.string() })).effect(function* ({
|
|
218
210
|
input,
|
|
219
211
|
}) {
|
|
220
212
|
const userService = yield* UserService;
|
|
221
213
|
return yield* userService.findById(input.id);
|
|
222
214
|
}),
|
|
223
215
|
// Span name: "users.create"
|
|
224
|
-
create:
|
|
225
|
-
input
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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
|
+
}),
|
|
230
222
|
},
|
|
231
223
|
};
|
|
232
224
|
```
|
|
@@ -234,7 +226,7 @@ const router = {
|
|
|
234
226
|
Use `.traced()` to override the default span name:
|
|
235
227
|
|
|
236
228
|
```ts
|
|
237
|
-
const getUser =
|
|
229
|
+
const getUser = effectProcedure
|
|
238
230
|
.input(z.object({ id: z.string() }))
|
|
239
231
|
.traced("custom.span.name") // Override the default path-based name
|
|
240
232
|
.effect(function* ({ input }) {
|
|
@@ -261,7 +253,7 @@ const TracingLive = NodeSdk.layer(
|
|
|
261
253
|
|
|
262
254
|
const AppLive = Layer.mergeAll(UserServiceLive, TracingLive);
|
|
263
255
|
|
|
264
|
-
const
|
|
256
|
+
const effectProcedure = eos.provide(AppLive);
|
|
265
257
|
```
|
|
266
258
|
|
|
267
259
|
### Error Stack Traces
|
|
@@ -276,14 +268,14 @@ MyCustomError: Something went wrong
|
|
|
276
268
|
|
|
277
269
|
## Effect middleware
|
|
278
270
|
|
|
279
|
-
`.use(...)` accepts generator-based Effect middleware in addition
|
|
280
|
-
middleware. Two patterns are supported:
|
|
271
|
+
`.use(...)` accepts generator-based and Effect-returning middleware in addition
|
|
272
|
+
to native oRPC middleware. Two patterns are supported:
|
|
281
273
|
|
|
282
274
|
**Gate** — run auth or validation side effects, then let the pipeline continue
|
|
283
275
|
automatically (no need to call `next`):
|
|
284
276
|
|
|
285
277
|
```ts
|
|
286
|
-
|
|
278
|
+
effectProcedure.use(function* () {
|
|
287
279
|
const user = yield* CurrentUser;
|
|
288
280
|
yield* requireActiveUser(user);
|
|
289
281
|
});
|
|
@@ -293,7 +285,7 @@ effectOs.use(function* () {
|
|
|
293
285
|
middleware that uses `return next(...)`, use `return yield* next(...)`:
|
|
294
286
|
|
|
295
287
|
```ts
|
|
296
|
-
|
|
288
|
+
effectProcedure.use(function* ({ next }) {
|
|
297
289
|
const user = yield* CurrentUser;
|
|
298
290
|
yield* requireActiveUser(user);
|
|
299
291
|
|
|
@@ -306,25 +298,45 @@ effectOs.use(function* ({ next }) {
|
|
|
306
298
|
To transform the downstream output, capture `next()` and pass through `output`:
|
|
307
299
|
|
|
308
300
|
```ts
|
|
309
|
-
|
|
301
|
+
effectProcedure.use(function* ({ next }, _input, output) {
|
|
310
302
|
const result = yield* next();
|
|
311
303
|
return yield* output(`${result.output}-wrapped`);
|
|
312
304
|
});
|
|
313
305
|
```
|
|
314
306
|
|
|
315
|
-
Calling `yield* next()` without returning its result still runs the handler once,
|
|
316
|
-
|
|
317
|
-
|
|
307
|
+
Calling `yield* next()` without returning its result still runs the handler once, but prefer `return yield* next(...)` so the pipeline receives your middleware result explicitly.
|
|
308
|
+
|
|
309
|
+
Effect-returning middleware is also supported, including `Effect.fn(...)` and
|
|
310
|
+
`() => Effect.gen(...)`:
|
|
311
|
+
|
|
312
|
+
```ts
|
|
313
|
+
effectProcedure.use(
|
|
314
|
+
Effect.fn("middleware.auth")(function* ({ next }) {
|
|
315
|
+
const user = yield* CurrentUser;
|
|
316
|
+
return yield* next({ context: { userId: user.id } });
|
|
317
|
+
}),
|
|
318
|
+
);
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Request-scoped providers support the same generator or Effect-returning style:
|
|
322
|
+
|
|
323
|
+
```ts
|
|
324
|
+
effectProcedure.provide(CurrentUser, function* ({ context }) {
|
|
325
|
+
yield* Effect.logDebug("resolving current user");
|
|
326
|
+
return context.user;
|
|
327
|
+
});
|
|
328
|
+
```
|
|
318
329
|
|
|
319
330
|
### Runtime boundaries and fiber context continuity
|
|
320
331
|
|
|
321
|
-
`effect-orpc` batches contiguous Effect-native steps into one runtime boundary.
|
|
322
|
-
Effect-native steps are `.provide(...)`, `.provideOptional(...)`, generator
|
|
323
|
-
`.use(function* ...)`, and `.effect(function* ...)`.
|
|
332
|
+
`effect-orpc` batches contiguous Effect-native steps into one runtime boundary. Effect-native steps are `.provide(...)`, `.provideOptional(...)`, generator `.use(function* ...)`, and `.effect(function* ...)`. Effect-returning handlers, providers, and middleware are supported too; named `Effect.fn(...)` callbacks keep their own spans rather than being wrapped as generators.
|
|
324
333
|
|
|
325
334
|
```ts
|
|
326
|
-
|
|
327
|
-
.provide(
|
|
335
|
+
eos
|
|
336
|
+
.provide(AppLive)
|
|
337
|
+
.provide(CurrentUser, function* ({ context }) {
|
|
338
|
+
return context.user;
|
|
339
|
+
})
|
|
328
340
|
.use(function* ({ next }) {
|
|
329
341
|
const user = yield* CurrentUser;
|
|
330
342
|
return yield* next({ context: { userId: user.id } });
|
|
@@ -335,14 +347,13 @@ makeEffectORPC(AppLive)
|
|
|
335
347
|
});
|
|
336
348
|
```
|
|
337
349
|
|
|
338
|
-
The example above runs the provider, middleware, and handler inside a single
|
|
339
|
-
internal `runtime.runPromiseExit(...)` call.
|
|
350
|
+
The example above runs the provider, middleware, and handler inside a single Effect execution boundary.
|
|
340
351
|
|
|
341
|
-
A native oRPC middleware breaks the contiguous Effect pipeline. Pending Effect
|
|
342
|
-
steps are flushed into one generated oRPC middleware before the native middleware:
|
|
352
|
+
A native oRPC middleware breaks the contiguous Effect pipeline. Pending Effect steps are flushed into one generated oRPC middleware before the native middleware:
|
|
343
353
|
|
|
344
354
|
```ts
|
|
345
|
-
|
|
355
|
+
eos
|
|
356
|
+
.provide(AppLive)
|
|
346
357
|
.provide(CurrentUser, getCurrentUser) // Effect group #1
|
|
347
358
|
.use(function* ({ next }) {
|
|
348
359
|
return yield* next();
|
|
@@ -356,51 +367,41 @@ makeEffectORPC(AppLive)
|
|
|
356
367
|
});
|
|
357
368
|
```
|
|
358
369
|
|
|
359
|
-
That split still creates multiple runtime boundaries. If the Node bridge is
|
|
360
|
-
installed, however, `effect-orpc` carries the current `FiberRefs` through the
|
|
361
|
-
native oRPC continuation and merges them into the next Effect boundary:
|
|
370
|
+
That split still creates multiple runtime boundaries. If the Node bridge is installed, however, `effect-orpc` carries the current `FiberRefs` through the native oRPC continuation and merges them into the next Effect boundary:
|
|
362
371
|
|
|
363
372
|
```ts
|
|
364
373
|
import "effect-orpc/node";
|
|
365
374
|
```
|
|
366
375
|
|
|
367
|
-
Use the side-effect import when you only need continuity across internal
|
|
368
|
-
`effect-orpc` boundaries, such as Effect group #1 → native oRPC middleware →
|
|
369
|
-
Effect group #2.
|
|
376
|
+
Use the side-effect import when you only need continuity across internal `effect-orpc` boundaries, such as Effect group #1 → native oRPC middleware → Effect group #2.
|
|
370
377
|
|
|
371
|
-
Procedure-level `.provide*` after a native `.handler(...)` has no Effect handler
|
|
372
|
-
boundary to attach to, so it is installed as an oRPC middleware that runs its
|
|
373
|
-
provider Effect through the runtime:
|
|
378
|
+
Procedure-level `.provide*` after a native `.handler(...)` has no Effect handler boundary to attach to, so it is installed as an oRPC middleware that runs its provider Effect through the configured runtime source:
|
|
374
379
|
|
|
375
380
|
```ts
|
|
376
|
-
|
|
381
|
+
eos
|
|
382
|
+
.provide(AppLive)
|
|
377
383
|
.handler(() => "ok") // native oRPC handler
|
|
378
384
|
.provide(CurrentUser, getCurrentUser); // fallback provider middleware
|
|
379
385
|
```
|
|
380
386
|
|
|
381
|
-
If you want `.provide*` and Effect middleware to batch with the handler, use
|
|
382
|
-
`.effect(function* ...)` instead of `.handler(...)`.
|
|
387
|
+
If you want `.provide*` and Effect middleware to batch with the handler, use `.effect(function* ...)` instead of `.handler(...)`.
|
|
383
388
|
|
|
384
389
|
## Request-Scoped Fiber Context
|
|
385
390
|
|
|
386
|
-
The `/node` entrypoint installs a bridge backed by `AsyncLocalStorage`. It has
|
|
387
|
-
two uses:
|
|
391
|
+
The `/node` entrypoint installs a bridge backed by `AsyncLocalStorage`. It has two uses:
|
|
388
392
|
|
|
389
|
-
- `import "effect-orpc/node"` installs the bridge passively. This is enough for
|
|
390
|
-
|
|
391
|
-
- `withFiberContext(() => next())` actively seeds the bridge from an external
|
|
392
|
-
Effect scope, such as framework middleware wrapping an oRPC handler.
|
|
393
|
+
- `import "effect-orpc/node"` installs the bridge passively. This is enough for `effect-orpc` to propagate `FiberRefs` across its own split runtime boundaries.
|
|
394
|
+
- `withFiberContext(() => next())` actively seeds the bridge from an external Effect scope, such as framework middleware wrapping an oRPC handler.
|
|
393
395
|
|
|
394
|
-
Use `withFiberContext` when request-local `FiberRef` state is created outside the
|
|
395
|
-
oRPC pipeline and should be visible inside handlers:
|
|
396
|
+
Use `withFiberContext` when request-local `FiberRef` state is created outside the oRPC pipeline and should be visible inside handlers:
|
|
396
397
|
|
|
397
398
|
```ts
|
|
398
399
|
import { Hono } from "hono";
|
|
399
400
|
import { Effect } from "effect";
|
|
400
|
-
import {
|
|
401
|
+
import { eos } from "effect-orpc";
|
|
401
402
|
import { withFiberContext } from "effect-orpc/node";
|
|
402
403
|
|
|
403
|
-
const
|
|
404
|
+
const effectProcedure = eos.provide(AppLive);
|
|
404
405
|
const app = new Hono();
|
|
405
406
|
|
|
406
407
|
app.use("*", async (c, next) => {
|
|
@@ -416,24 +417,16 @@ app.use("*", async (c, next) => {
|
|
|
416
417
|
});
|
|
417
418
|
```
|
|
418
419
|
|
|
419
|
-
Importing `withFiberContext` from `effect-orpc/node` also installs the bridge, so
|
|
420
|
-
you do not need a separate side-effect import.
|
|
420
|
+
Importing `withFiberContext` from `effect-orpc/node` also installs the bridge, so you do not need a separate side-effect import.
|
|
421
421
|
|
|
422
|
-
When a captured fiber context and the application `Layer` / `ManagedRuntime`
|
|
423
|
-
|
|
424
|
-
The application layer is treated as the base layer, while the bridge preserves more specific
|
|
425
|
-
request-scoped values such as request IDs, logging annotations, tracing context,
|
|
426
|
-
or scoped overrides when crossing runtime boundaries.
|
|
422
|
+
When a captured fiber context and the application `Layer` / `ManagedRuntime` both provide the same service, `effect-orpc` prioritizes the captured context.
|
|
423
|
+
The application layer is treated as the base layer, while the bridge preserves more specific request-scoped values such as request IDs, logging annotations, tracing context, or scoped overrides when crossing runtime boundaries.
|
|
427
424
|
|
|
428
|
-
The main package stays runtime-agnostic; `/node` is separate because the bridge
|
|
429
|
-
relies on `AsyncLocalStorage` from `node:async_hooks`.
|
|
425
|
+
The main package stays runtime-agnostic; `/node` is separate because the bridge relies on `AsyncLocalStorage` from `node:async_hooks`.
|
|
430
426
|
|
|
431
427
|
## Contract-First Usage
|
|
432
428
|
|
|
433
|
-
Use `implementEffect(contract, layerOrRuntime)` when you already have an oRPC
|
|
434
|
-
contract and want to keep contract-first enforcement while adding Effect-native
|
|
435
|
-
handlers. Use `makeEffectORPC(layerOrRuntime, builder?)` when you want to build
|
|
436
|
-
procedures directly from an oRPC builder.
|
|
429
|
+
Use `implementEffect(contract, layerOrRuntime)` when you already have an oRPC contract and want to keep contract-first enforcement while adding Effect-native handlers. Use `eos.provide(layer)` when you want to build procedures directly from the default Effect-aware builder.
|
|
437
430
|
|
|
438
431
|
```ts
|
|
439
432
|
import { Effect } from "effect";
|
|
@@ -467,40 +460,35 @@ export const router = oe.router({
|
|
|
467
460
|
});
|
|
468
461
|
```
|
|
469
462
|
|
|
470
|
-
Contract leaves keep the contract-defined input, output, and error surface.
|
|
471
|
-
They add `.effect(...)` alongside existing implementer methods such as
|
|
472
|
-
`.handler(...)` and `.use(...)`, but do not expose contract-changing builder
|
|
473
|
-
methods like `.input(...)` or `.output(...)`.
|
|
463
|
+
Contract leaves keep the contract-defined input, output, and error surface. They add `.effect(...)` alongside existing implementer methods such as `.handler(...)` and `.use(...)`, but do not expose contract-changing builder methods like `.input(...)` or `.output(...)`.
|
|
474
464
|
|
|
475
|
-
If your contract declares tagged Effect error classes, prefer `eoc.errors(...)`
|
|
476
|
-
instead of raw `oc.errors(...)` so the error schema and metadata are derived
|
|
477
|
-
directly from the `ORPCTaggedError` class.
|
|
465
|
+
If your contract declares tagged Effect error classes, prefer `eoc.errors(...)` instead of raw `oc.errors(...)` so the error schema and metadata are derived directly from the `ORPCTaggedError` class.
|
|
478
466
|
|
|
479
467
|
## API Reference
|
|
480
468
|
|
|
481
|
-
### `
|
|
469
|
+
### `eos`
|
|
482
470
|
|
|
483
|
-
|
|
484
|
-
your application `Layer` up front.
|
|
471
|
+
The default Effect-aware procedure builder. Provide your application services with `.provide(layer)`:
|
|
485
472
|
|
|
486
|
-
|
|
473
|
+
```ts
|
|
474
|
+
const effectProcedure = eos.provide(AppLive);
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
Use `makeEffectORPC(runtime)` when a scoped `Layer` should be acquired once and released by your application shutdown path, such as a shared cache, database pool, HTTP client, or telemetry SDK:
|
|
487
478
|
|
|
488
479
|
```ts
|
|
489
|
-
|
|
490
|
-
|
|
480
|
+
const runtime = ManagedRuntime.make(AppLive);
|
|
481
|
+
|
|
482
|
+
const effectProcedure = makeEffectORPC(runtime);
|
|
491
483
|
|
|
492
|
-
//
|
|
493
|
-
|
|
484
|
+
// later, during app shutdown
|
|
485
|
+
await runtime.dispose();
|
|
494
486
|
```
|
|
495
487
|
|
|
496
|
-
|
|
497
|
-
`ManagedRuntime` when you need explicit runtime lifecycle control:
|
|
488
|
+
`makeEffectORPC(builder)` is also available when you need to wrap an existing oRPC builder:
|
|
498
489
|
|
|
499
490
|
```ts
|
|
500
|
-
const
|
|
501
|
-
const effectAuthedOsWithProvidedLayer =
|
|
502
|
-
makeEffectORPC(authedBuilder).provide(AppLive);
|
|
503
|
-
const effectOsFromRuntime = makeEffectORPC(runtime);
|
|
491
|
+
const effectAuthedOs = makeEffectORPC(authedBuilder).provide(AppLive);
|
|
504
492
|
```
|
|
505
493
|
|
|
506
494
|
### `implementEffect(contract, layerOrRuntime)`
|
|
@@ -508,7 +496,7 @@ const effectOsFromRuntime = makeEffectORPC(runtime);
|
|
|
508
496
|
Creates an Effect-aware contract implementer.
|
|
509
497
|
|
|
510
498
|
- `contract` - An oRPC contract router built with `oc`
|
|
511
|
-
- `layerOrRuntime` - A `Layer<R, E, never>` or `ManagedRuntime<R, E>`
|
|
499
|
+
- `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)
|
|
512
500
|
|
|
513
501
|
Returns a contract-shaped implementer tree whose leaves support `.effect(...)`.
|
|
514
502
|
|
|
@@ -528,8 +516,7 @@ const router = oe.router({
|
|
|
528
516
|
|
|
529
517
|
An Effect-aware wrapper around oRPC's `oc` contract builder.
|
|
530
518
|
|
|
531
|
-
Use it when you want contract definitions to accept `ORPCTaggedError` classes
|
|
532
|
-
directly in `.errors(...)` without duplicating the error schema.
|
|
519
|
+
Use it when you want contract definitions to accept `ORPCTaggedError` classes directly in `.errors(...)` without duplicating the error schema.
|
|
533
520
|
|
|
534
521
|
```ts
|
|
535
522
|
class UserNotFoundError extends ORPCTaggedError("UserNotFoundError", {
|
|
@@ -553,43 +540,43 @@ const contract = {
|
|
|
553
540
|
|
|
554
541
|
Wraps an oRPC Builder with Effect support. Available methods:
|
|
555
542
|
|
|
556
|
-
| Method
|
|
557
|
-
|
|
|
558
|
-
| `.$config(config)`
|
|
559
|
-
| `.$context<U>()`
|
|
560
|
-
| `.$meta(meta)`
|
|
561
|
-
| `.$route(route)`
|
|
562
|
-
| `.$input(schema)`
|
|
563
|
-
| `.errors(map)`
|
|
564
|
-
| `.meta(meta)`
|
|
565
|
-
| `.route(route)`
|
|
566
|
-
| `.input(schema)`
|
|
567
|
-
| `.output(schema)`
|
|
568
|
-
| `.provide(layer)`
|
|
569
|
-
| `.provide(tag,
|
|
570
|
-
| `.use(middleware)`
|
|
571
|
-
| `.traced(name)`
|
|
572
|
-
| `.handler(handler)`
|
|
573
|
-
| `.effect(handler)`
|
|
574
|
-
| `.prefix(prefix)`
|
|
575
|
-
| `.tag(...tags)`
|
|
576
|
-
| `.router(router)`
|
|
577
|
-
| `.lazy(loader)`
|
|
543
|
+
| Method | Description |
|
|
544
|
+
| ------------------------- | ------------------------------------------------------------------------------------ |
|
|
545
|
+
| `.$config(config)` | Set or override the builder config |
|
|
546
|
+
| `.$context<U>()` | Set or override the initial context type |
|
|
547
|
+
| `.$meta(meta)` | Set or override the initial metadata |
|
|
548
|
+
| `.$route(route)` | Set or override the initial route configuration |
|
|
549
|
+
| `.$input(schema)` | Set or override the initial input schema |
|
|
550
|
+
| `.errors(map)` | Add type-safe custom errors |
|
|
551
|
+
| `.meta(meta)` | Set procedure metadata (merged with existing) |
|
|
552
|
+
| `.route(route)` | Configure OpenAPI route (merged with existing) |
|
|
553
|
+
| `.input(schema)` | Define input validation schema |
|
|
554
|
+
| `.output(schema)` | Define output validation schema |
|
|
555
|
+
| `.provide(layer)` | Provide a base Effect layer to downstream Effect middleware and handlers |
|
|
556
|
+
| `.provide(tag, provider)` | Provide a request-scoped Effect service to downstream Effect middleware and handlers |
|
|
557
|
+
| `.use(middleware)` | Add middleware |
|
|
558
|
+
| `.traced(name)` | Add a traceable span for telemetry (optional, defaults to the procedure's path) |
|
|
559
|
+
| `.handler(handler)` | Define a non-Effect handler (standard oRPC handler) |
|
|
560
|
+
| `.effect(handler)` | Define the Effect handler |
|
|
561
|
+
| `.prefix(prefix)` | Prefix all procedures in the router (for OpenAPI) |
|
|
562
|
+
| `.tag(...tags)` | Add tags to all procedures in the router (for OpenAPI) |
|
|
563
|
+
| `.router(router)` | Apply all options to a router |
|
|
564
|
+
| `.lazy(loader)` | Create and apply options to a lazy-loaded router |
|
|
578
565
|
|
|
579
566
|
### `EffectDecoratedProcedure`
|
|
580
567
|
|
|
581
568
|
The result of calling `.effect()`. Extends standard oRPC `DecoratedProcedure` with Effect type preservation.
|
|
582
569
|
|
|
583
|
-
| Method
|
|
584
|
-
|
|
|
585
|
-
| `.errors(map)`
|
|
586
|
-
| `.meta(meta)`
|
|
587
|
-
| `.route(route)`
|
|
588
|
-
| `.provide(layer)`
|
|
589
|
-
| `.provide(tag,
|
|
590
|
-
| `.use(middleware)`
|
|
591
|
-
| `.callable(options?)`
|
|
592
|
-
| `.actionable(options?)`
|
|
570
|
+
| Method | Description |
|
|
571
|
+
| ------------------------- | --------------------------------------------- |
|
|
572
|
+
| `.errors(map)` | Add more custom errors |
|
|
573
|
+
| `.meta(meta)` | Update metadata (merged with existing) |
|
|
574
|
+
| `.route(route)` | Update route configuration (merged) |
|
|
575
|
+
| `.provide(layer)` | Provide a base Effect layer |
|
|
576
|
+
| `.provide(tag, provider)` | Provide a request-scoped Effect service |
|
|
577
|
+
| `.use(middleware)` | Add middleware |
|
|
578
|
+
| `.callable(options?)` | Make procedure directly invocable |
|
|
579
|
+
| `.actionable(options?)` | Make procedure compatible with server actions |
|
|
593
580
|
|
|
594
581
|
### `ORPCTaggedError(tag, options?)`
|
|
595
582
|
|