effect-orpc 0.0.4 → 0.0.6
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 +286 -190
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/tagged-error.ts +14 -8
- package/src/tests/effect-error-map.test.ts +7 -4
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@ Inspired by [effect-trpc](https://github.com/mikearnaldi/effect-trpc).
|
|
|
8
8
|
|
|
9
9
|
- **Effect-native procedures** - Write oRPC procedures using generators with `yield*` syntax
|
|
10
10
|
- **Type-safe service injection** - Use `ManagedRuntime<R>` to provide services to procedures with compile-time safety
|
|
11
|
+
- **Tagged errors** - Create Effect-native error classes with `ORPCTaggedError` that integrate with oRPC's error handling
|
|
11
12
|
- **Full oRPC compatibility** - Mix Effect procedures with standard oRPC procedures in the same router
|
|
12
13
|
- **Builder pattern preserved** - All oRPC builder methods (`.errors()`, `.meta()`, `.route()`, `.input()`, `.output()`, `.use()`) work seamlessly
|
|
13
14
|
- **Callable procedures** - Make procedures directly invocable while preserving Effect types
|
|
@@ -57,29 +58,22 @@ const effectOs = makeEffectORPC(runtime)
|
|
|
57
58
|
// Define your procedures
|
|
58
59
|
const getUser = effectOs
|
|
59
60
|
.input(z.object({ id: z.string() }))
|
|
60
|
-
.effect(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
})
|
|
65
|
-
)
|
|
61
|
+
.effect(function* ({ input }) {
|
|
62
|
+
const userService = yield* UserService
|
|
63
|
+
return yield* userService.findById(input.id)
|
|
64
|
+
})
|
|
66
65
|
|
|
67
|
-
const listUsers = effectOs
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
return yield* userService.findAll()
|
|
72
|
-
})
|
|
73
|
-
)
|
|
66
|
+
const listUsers = effectOs.effect(function* () {
|
|
67
|
+
const userService = yield* UserService
|
|
68
|
+
return yield* userService.findAll()
|
|
69
|
+
})
|
|
74
70
|
|
|
75
71
|
const createUser = effectOs
|
|
76
72
|
.input(z.object({ name: z.string() }))
|
|
77
|
-
.effect(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
})
|
|
82
|
-
)
|
|
73
|
+
.effect(function* ({ input }) {
|
|
74
|
+
const userService = yield* UserService
|
|
75
|
+
return yield* userService.create(input.name)
|
|
76
|
+
})
|
|
83
77
|
|
|
84
78
|
// Create router with mixed procedures
|
|
85
79
|
const router = os.router({
|
|
@@ -119,87 +113,16 @@ const runtime = ManagedRuntime.make(Layer.succeed(ProvidedService, {
|
|
|
119
113
|
const effectOs = makeEffectORPC(runtime)
|
|
120
114
|
|
|
121
115
|
// ✅ This compiles - ProvidedService is in the runtime
|
|
122
|
-
const works = effectOs
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
const svc = yield* ProvidedService
|
|
126
|
-
return yield* svc.doSomething()
|
|
127
|
-
})
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
// ❌ This fails to compile - MissingService is not in the runtime
|
|
131
|
-
const fails = effectOs
|
|
132
|
-
.effect(
|
|
133
|
-
Effect.fn(function* () {
|
|
134
|
-
const svc = yield* MissingService // Type error!
|
|
135
|
-
return yield* svc.doSomething()
|
|
136
|
-
})
|
|
137
|
-
)
|
|
138
|
-
```
|
|
139
|
-
|
|
140
|
-
## Using Services
|
|
141
|
-
|
|
142
|
-
```ts
|
|
143
|
-
import { makeEffectORPC } from 'effect-orpc'
|
|
144
|
-
import { Context, Effect, Layer, ManagedRuntime } from 'effect'
|
|
145
|
-
import { z } from 'zod'
|
|
146
|
-
|
|
147
|
-
// Define services
|
|
148
|
-
class DatabaseService extends Context.Tag('DatabaseService')<
|
|
149
|
-
DatabaseService,
|
|
150
|
-
{
|
|
151
|
-
query: <T>(sql: string) => Effect.Effect<T[]>
|
|
152
|
-
execute: (sql: string) => Effect.Effect<void>
|
|
153
|
-
}
|
|
154
|
-
>() {}
|
|
155
|
-
|
|
156
|
-
class CacheService extends Context.Tag('CacheService')<
|
|
157
|
-
CacheService,
|
|
158
|
-
{
|
|
159
|
-
get: <T>(key: string) => Effect.Effect<T | undefined>
|
|
160
|
-
set: <T>(key: string, value: T, ttl?: number) => Effect.Effect<void>
|
|
161
|
-
}
|
|
162
|
-
>() {}
|
|
163
|
-
|
|
164
|
-
// Create layers
|
|
165
|
-
const DatabaseServiceLive = Layer.succeed(DatabaseService, {
|
|
166
|
-
query: sql => Effect.succeed([]),
|
|
167
|
-
execute: sql => Effect.succeed(undefined),
|
|
116
|
+
const works = effectOs.effect(function* () {
|
|
117
|
+
const service = yield* ProvidedService
|
|
118
|
+
return yield* service.doSomething()
|
|
168
119
|
})
|
|
169
120
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
121
|
+
// ❌ This fails to compile - MissingService is not in the runtime
|
|
122
|
+
const fails = effectOs.effect(function* () {
|
|
123
|
+
const service = yield* MissingService // Type error!
|
|
124
|
+
return yield* service.doSomething()
|
|
173
125
|
})
|
|
174
|
-
|
|
175
|
-
// Compose layers
|
|
176
|
-
const AppLive = Layer.mergeAll(DatabaseServiceLive, CacheServiceLive)
|
|
177
|
-
|
|
178
|
-
// Create runtime with all services
|
|
179
|
-
const runtime = ManagedRuntime.make(AppLive)
|
|
180
|
-
const effectOs = makeEffectORPC(runtime)
|
|
181
|
-
|
|
182
|
-
// Use multiple services in a procedure
|
|
183
|
-
const getUserWithCache = effectOs
|
|
184
|
-
.input(z.object({ id: z.string() }))
|
|
185
|
-
.effect(
|
|
186
|
-
Effect.fn(function* ({ input }) {
|
|
187
|
-
const cache = yield* CacheService
|
|
188
|
-
const db = yield* DatabaseService
|
|
189
|
-
|
|
190
|
-
// Try cache first
|
|
191
|
-
const cached = yield* cache.get<User>(`user:${input.id}`)
|
|
192
|
-
if (cached)
|
|
193
|
-
return cached
|
|
194
|
-
|
|
195
|
-
// Fall back to database
|
|
196
|
-
const [user] = yield* db.query<User>(`SELECT * FROM users WHERE id = '${input.id}'`)
|
|
197
|
-
if (user) {
|
|
198
|
-
yield* cache.set(`user:${input.id}`, user, 3600)
|
|
199
|
-
}
|
|
200
|
-
return user
|
|
201
|
-
})
|
|
202
|
-
)
|
|
203
126
|
```
|
|
204
127
|
|
|
205
128
|
## Wrapping a Customized Builder
|
|
@@ -228,22 +151,17 @@ const authedOs = os
|
|
|
228
151
|
const effectAuthedOs = makeEffectORPC(runtime, authedOs)
|
|
229
152
|
|
|
230
153
|
// All procedures inherit the auth middleware and error definitions
|
|
231
|
-
const getProfile = effectAuthedOs
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
return yield* userService.findById(context.userId)
|
|
236
|
-
})
|
|
237
|
-
)
|
|
154
|
+
const getProfile = effectAuthedOs.effect(function* ({ context }) {
|
|
155
|
+
const userService = yield* UserService
|
|
156
|
+
return yield* userService.findById(context.userId)
|
|
157
|
+
})
|
|
238
158
|
|
|
239
159
|
const updateProfile = effectAuthedOs
|
|
240
160
|
.input(z.object({ name: z.string() }))
|
|
241
|
-
.effect(
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
})
|
|
246
|
-
)
|
|
161
|
+
.effect(function* ({ context, input }) {
|
|
162
|
+
const userService = yield* UserService
|
|
163
|
+
return yield* userService.update(context.userId, input)
|
|
164
|
+
})
|
|
247
165
|
```
|
|
248
166
|
|
|
249
167
|
## Chaining Builder Methods
|
|
@@ -278,23 +196,21 @@ const createPost = effectOs
|
|
|
278
196
|
createdAt: z.date(),
|
|
279
197
|
}))
|
|
280
198
|
// Define Effect handler
|
|
281
|
-
.effect(({ input, errors })
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
authorId: input.authorId,
|
|
295
|
-
})
|
|
199
|
+
.effect(function* ({ input, errors }) {
|
|
200
|
+
const userService = yield* UserService
|
|
201
|
+
const user = yield* userService.findById(input.authorId)
|
|
202
|
+
|
|
203
|
+
if (!user) {
|
|
204
|
+
return yield Effect.Fail(errors.NOT_FOUND())
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const postService = yield* PostService
|
|
208
|
+
return yield* postService.create({
|
|
209
|
+
title: input.title,
|
|
210
|
+
content: input.content,
|
|
211
|
+
authorId: input.authorId,
|
|
296
212
|
})
|
|
297
|
-
)
|
|
213
|
+
})
|
|
298
214
|
```
|
|
299
215
|
|
|
300
216
|
## Making Procedures Callable
|
|
@@ -304,7 +220,9 @@ Use `.callable()` to make procedures directly invocable:
|
|
|
304
220
|
```ts
|
|
305
221
|
const greet = effectOs
|
|
306
222
|
.input(z.object({ name: z.string() }))
|
|
307
|
-
.effect(({ input })
|
|
223
|
+
.effect(function* ({ input }) {
|
|
224
|
+
return `Hello, ${input.name}!`
|
|
225
|
+
})
|
|
308
226
|
.callable()
|
|
309
227
|
|
|
310
228
|
// Can be called directly as a function
|
|
@@ -322,12 +240,10 @@ Use `.actionable()` for framework server actions (Next.js, etc.):
|
|
|
322
240
|
```tsx
|
|
323
241
|
const createTodo = effectOs
|
|
324
242
|
.input(z.object({ title: z.string() }))
|
|
325
|
-
.effect(
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
})
|
|
330
|
-
)
|
|
243
|
+
.effect(function* ({ input }) {
|
|
244
|
+
const todoService = yield* TodoService
|
|
245
|
+
return yield* todoService.create(input.title)
|
|
246
|
+
})
|
|
331
247
|
.actionable({ context: async () => ({ user: await getSession() }) })
|
|
332
248
|
|
|
333
249
|
// Use in React Server Components
|
|
@@ -343,16 +259,9 @@ export async function TodoForm() {
|
|
|
343
259
|
|
|
344
260
|
## Error Handling
|
|
345
261
|
|
|
346
|
-
Effect errors are properly propagated through oRPC's error handling:
|
|
262
|
+
Effect errors are properly propagated through oRPC's error handling. You can use the `errors` object passed to your handler:
|
|
347
263
|
|
|
348
264
|
```ts
|
|
349
|
-
import { Effect } from 'effect'
|
|
350
|
-
|
|
351
|
-
class NotFoundError extends Effect.Tag('NotFoundError')<
|
|
352
|
-
NotFoundError,
|
|
353
|
-
{ readonly _tag: 'NotFoundError', readonly id: string }
|
|
354
|
-
>() {}
|
|
355
|
-
|
|
356
265
|
const getUser = effectOs
|
|
357
266
|
.errors({
|
|
358
267
|
NOT_FOUND: {
|
|
@@ -361,54 +270,199 @@ const getUser = effectOs
|
|
|
361
270
|
},
|
|
362
271
|
})
|
|
363
272
|
.input(z.object({ id: z.string() }))
|
|
364
|
-
.effect(({ input, errors })
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
const user = yield* userService.findById(input.id)
|
|
273
|
+
.effect(function* ({ input, errors }) {
|
|
274
|
+
const userService = yield* UserService
|
|
275
|
+
const user = yield* userService.findById(input.id)
|
|
368
276
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
277
|
+
if (!user) {
|
|
278
|
+
// Use oRPC's type-safe errors
|
|
279
|
+
yield* Effect.fail(errors.NOT_FOUND({ id: input.id }))
|
|
280
|
+
}
|
|
373
281
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
282
|
+
return user
|
|
283
|
+
})
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## Tagged Errors
|
|
287
|
+
|
|
288
|
+
`ORPCTaggedError` lets you create Effect-native error classes that integrate seamlessly with oRPC. These errors:
|
|
289
|
+
|
|
290
|
+
- Can be yielded in Effect generators (`yield* new MyError()` or `yield* Effect.fail(errors.MyError)`)
|
|
291
|
+
- Have all ORPCError properties (code, status, data, defined)
|
|
292
|
+
- Can be used in `.errors()` maps for type-safe error handling
|
|
293
|
+
- Automatically convert to ORPCError when thrown
|
|
294
|
+
|
|
295
|
+
### Creating Tagged Errors
|
|
296
|
+
|
|
297
|
+
```ts
|
|
298
|
+
import { ORPCTaggedError } from 'effect-orpc'
|
|
299
|
+
|
|
300
|
+
// Basic tagged error - code defaults to 'USER_NOT_FOUND' (CONSTANT_CASE of tag)
|
|
301
|
+
class UserNotFound extends ORPCTaggedError<UserNotFound>()('UserNotFound') {}
|
|
302
|
+
|
|
303
|
+
// With explicit code
|
|
304
|
+
class NotFound extends ORPCTaggedError<NotFound>()('NotFound', 'NOT_FOUND') {}
|
|
305
|
+
|
|
306
|
+
// With default options (code defaults to 'VALIDATION_ERROR') (CONSTANT_CASE of tag)
|
|
307
|
+
class ValidationError extends ORPCTaggedError<ValidationError>()(
|
|
308
|
+
'ValidationError',
|
|
309
|
+
{ status: 400, message: 'Validation failed' }
|
|
310
|
+
) {}
|
|
311
|
+
|
|
312
|
+
// With explicit code and options
|
|
313
|
+
class Forbidden extends ORPCTaggedError<Forbidden>()(
|
|
314
|
+
'Forbidden',
|
|
315
|
+
'FORBIDDEN',
|
|
316
|
+
{ status: 403, message: 'Access denied' }
|
|
317
|
+
) {}
|
|
318
|
+
|
|
319
|
+
// With typed data
|
|
320
|
+
class UserNotFoundWithData extends ORPCTaggedError<
|
|
321
|
+
UserNotFoundWithData,
|
|
322
|
+
{ userId: string }
|
|
323
|
+
>()('UserNotFoundWithData') {}
|
|
377
324
|
```
|
|
378
325
|
|
|
379
|
-
|
|
326
|
+
### Using Tagged Errors in Procedures
|
|
380
327
|
|
|
381
|
-
|
|
328
|
+
Tagged errors can be yielded directly in Effect generators:
|
|
382
329
|
|
|
383
330
|
```ts
|
|
384
|
-
|
|
385
|
-
|
|
331
|
+
class UserNotFound extends ORPCTaggedError<
|
|
332
|
+
UserNotFound,
|
|
333
|
+
{ userId: string }
|
|
334
|
+
>()('UserNotFound', { status: 404, message: 'User not found' })
|
|
335
|
+
|
|
336
|
+
const getUser = effectOs
|
|
386
337
|
.input(z.object({ id: z.string() }))
|
|
387
|
-
.effect(
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
338
|
+
.effect(function* ({ input }) {
|
|
339
|
+
const userService = yield* UserService
|
|
340
|
+
const user = yield* userService.findById(input.id)
|
|
341
|
+
|
|
342
|
+
if (!user) {
|
|
343
|
+
// Yield the error - it will be converted to ORPCError automatically
|
|
344
|
+
return yield* new UserNotFound({ data: { userId: input.id } })
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
return user
|
|
348
|
+
})
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Using Tagged Errors in Error Maps
|
|
352
|
+
|
|
353
|
+
Tagged error classes can be passed directly to `.errors()`:
|
|
354
|
+
|
|
355
|
+
```ts
|
|
356
|
+
class UserNotFound extends ORPCTaggedError<
|
|
357
|
+
UserNotFound,
|
|
358
|
+
{ userId: string }
|
|
359
|
+
>()('UserNotFound', 'NOT_FOUND', { status: 404, message: 'User not found' })
|
|
360
|
+
|
|
361
|
+
class InvalidInput extends ORPCTaggedError<
|
|
362
|
+
InvalidInput,
|
|
363
|
+
{ field: string }
|
|
364
|
+
>()('InvalidInput', 'BAD_REQUEST', { status: 400 })
|
|
365
|
+
|
|
366
|
+
const getUser = effectOs
|
|
367
|
+
.errors({
|
|
368
|
+
// Tagged error class - use the class directly
|
|
369
|
+
// The only difference is that the code is defined by the constant version of the tag
|
|
370
|
+
// Or when defined explicitely like in the Forbidden tagged error above
|
|
371
|
+
UserNotFound,
|
|
372
|
+
INVALID_INPUT: InvalidInput,
|
|
373
|
+
// Traditional format still works, and can be colocated
|
|
374
|
+
INTERNAL_ERROR: { status: 500, message: 'Something went wrong' },
|
|
375
|
+
})
|
|
376
|
+
.input(z.object({ id: z.string() }))
|
|
377
|
+
.effect(function* ({ input, errors }) {
|
|
378
|
+
if (!input.id) {
|
|
379
|
+
// errors.BAD_REQUEST is the InvalidInput class
|
|
380
|
+
return yield* new errors.INVALID_INPUT({ data: { field: 'id' } })
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const userService = yield* UserService
|
|
384
|
+
const user = yield* userService.findById(input.id)
|
|
385
|
+
|
|
386
|
+
if (!user) {
|
|
387
|
+
// errors.UserNotFound is the UserNotFound class
|
|
388
|
+
// with the code USER_NOT_FOUND (defined at the class level)
|
|
389
|
+
return yield* new errors.UserNotFound({ data: { userId: input.id } })
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
return user
|
|
393
|
+
})
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
### Converting Tagged Errors
|
|
397
|
+
|
|
398
|
+
Use `toORPCError` to convert a tagged error to a plain ORPCError:
|
|
399
|
+
|
|
400
|
+
```ts
|
|
401
|
+
import { toORPCError, ORPCTaggedError } from 'effect-orpc'
|
|
402
|
+
import { Effect } from 'effect'
|
|
403
|
+
|
|
404
|
+
class MyError extends ORPCTaggedError<MyError>()('MyError', 'BAD_REQUEST') {}
|
|
405
|
+
|
|
406
|
+
const procedure = effectOs.effect(function* () {
|
|
407
|
+
const result = yield* someOperation.pipe(
|
|
408
|
+
Effect.catchTag('MyError', (e) =>
|
|
409
|
+
// Convert to plain ORPCError if needed
|
|
410
|
+
Effect.fail(toORPCError(e))
|
|
411
|
+
)
|
|
392
412
|
)
|
|
413
|
+
return result
|
|
414
|
+
})
|
|
415
|
+
```
|
|
393
416
|
|
|
394
|
-
|
|
417
|
+
### Tagged Error Properties
|
|
418
|
+
|
|
419
|
+
Tagged errors have all the properties of ORPCError plus Effect integration:
|
|
420
|
+
|
|
421
|
+
```ts
|
|
422
|
+
const error = new UserNotFound({
|
|
423
|
+
data: { userId: '123' },
|
|
424
|
+
message: 'Custom message', // Override default message
|
|
425
|
+
cause: originalError, // Attach cause for debugging
|
|
426
|
+
})
|
|
427
|
+
|
|
428
|
+
error._tag // 'UserNotFound' - for Effect's catchTag
|
|
429
|
+
error.code // 'USER_NOT_FOUND' - ORPCError code
|
|
430
|
+
error.status // 404 - HTTP status
|
|
431
|
+
error.data // { userId: '123' } - typed data
|
|
432
|
+
error.message // 'Custom message'
|
|
433
|
+
error.defined // true - whether error is defined in error map
|
|
434
|
+
|
|
435
|
+
// Convert to plain ORPCError
|
|
436
|
+
const orpcError = error.toORPCError()
|
|
437
|
+
|
|
438
|
+
// Serialize to JSON
|
|
439
|
+
const json = error.toJSON()
|
|
440
|
+
// { _tag: 'UserNotFound', code: 'USER_NOT_FOUND', status: 404, ... }
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
## Generator Syntax
|
|
444
|
+
|
|
445
|
+
Pass a generator function directly to `.effect()` — no need to wrap it with `Effect.fn()` or `Effect.gen()`:
|
|
446
|
+
|
|
447
|
+
```ts
|
|
448
|
+
// Recommended: Pass generator function directly
|
|
395
449
|
const procedureWithGen = effectOs
|
|
396
450
|
.input(z.object({ id: z.string() }))
|
|
397
|
-
.effect(({ input })
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
})
|
|
402
|
-
)
|
|
451
|
+
.effect(function* ({ input }) {
|
|
452
|
+
const service = yield* MyService
|
|
453
|
+
return yield* service.doSomething(input.id)
|
|
454
|
+
})
|
|
403
455
|
|
|
404
|
-
// Simple
|
|
456
|
+
// Simple procedures without yield*
|
|
405
457
|
const simpleProcedure = effectOs
|
|
406
458
|
.input(z.object({ name: z.string() }))
|
|
407
|
-
.effect(({ input })
|
|
408
|
-
|
|
409
|
-
)
|
|
459
|
+
.effect(function* ({ input }) {
|
|
460
|
+
return `Hello, ${input.name}!`
|
|
461
|
+
})
|
|
410
462
|
```
|
|
411
463
|
|
|
464
|
+
The handler receives `{ context, input, path, procedure, signal, lastEventId, errors }` as its argument, giving you full access to the oRPC procedure context.
|
|
465
|
+
|
|
412
466
|
## Traceable Spans
|
|
413
467
|
|
|
414
468
|
All Effect procedures are automatically traced with `Effect.withSpan`. By default, the span name is the procedure path (e.g., `users.getUser`):
|
|
@@ -420,21 +474,17 @@ const router = os.router({
|
|
|
420
474
|
// Span name: "users.get"
|
|
421
475
|
get: effectOs
|
|
422
476
|
.input(z.object({ id: z.string() }))
|
|
423
|
-
.effect(
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
})
|
|
428
|
-
),
|
|
477
|
+
.effect(function* ({ input }) {
|
|
478
|
+
const userService = yield* UserService
|
|
479
|
+
return yield* userService.findById(input.id)
|
|
480
|
+
}),
|
|
429
481
|
// Span name: "users.create"
|
|
430
482
|
create: effectOs
|
|
431
483
|
.input(z.object({ name: z.string() }))
|
|
432
|
-
.effect(
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
})
|
|
437
|
-
),
|
|
484
|
+
.effect(function* ({ input }) {
|
|
485
|
+
const userService = yield* UserService
|
|
486
|
+
return yield* userService.create(input.name)
|
|
487
|
+
}),
|
|
438
488
|
})
|
|
439
489
|
})
|
|
440
490
|
```
|
|
@@ -445,12 +495,10 @@ Use `.traced()` to override the default span name:
|
|
|
445
495
|
const getUser = effectOs
|
|
446
496
|
.input(z.object({ id: z.string() }))
|
|
447
497
|
.traced('custom.span.name') // Override the default path-based name
|
|
448
|
-
.effect(
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
})
|
|
453
|
-
)
|
|
498
|
+
.effect(function* ({ input }) {
|
|
499
|
+
const userService = yield* UserService
|
|
500
|
+
return yield* userService.findById(input.id)
|
|
501
|
+
})
|
|
454
502
|
```
|
|
455
503
|
|
|
456
504
|
### Enabling OpenTelemetry
|
|
@@ -533,6 +581,54 @@ The result of calling `.effect()`. Extends standard oRPC `DecoratedProcedure` wi
|
|
|
533
581
|
| `.callable(options?)` | Make procedure directly invocable |
|
|
534
582
|
| `.actionable(options?)` | Make procedure compatible with server actions |
|
|
535
583
|
|
|
584
|
+
### `ORPCTaggedError<Self, TData>()(tag, codeOrOptions?, defaultOptions?)`
|
|
585
|
+
|
|
586
|
+
Factory function to create Effect-native tagged error classes.
|
|
587
|
+
|
|
588
|
+
- `Self` - The class type itself (for proper typing)
|
|
589
|
+
- `TData` - Optional type for the error's data payload
|
|
590
|
+
- `tag` - Unique tag for discriminated unions (used by Effect's `catchTag`)
|
|
591
|
+
- `codeOrOptions` - Either an ORPCErrorCode string or `{ status?, message? }` options
|
|
592
|
+
- `defaultOptions` - Default `{ status?, message? }` when code is provided explicitly
|
|
593
|
+
|
|
594
|
+
If no code is provided, it defaults to CONSTANT_CASE of the tag (e.g., `UserNotFound` → `USER_NOT_FOUND`).
|
|
595
|
+
|
|
596
|
+
```ts
|
|
597
|
+
// Tag only - code defaults to 'MY_ERROR'
|
|
598
|
+
class MyError extends ORPCTaggedError<MyError>()('MyError') {}
|
|
599
|
+
|
|
600
|
+
// With options - code defaults to 'MY_ERROR'
|
|
601
|
+
class MyError extends ORPCTaggedError<MyError>()(
|
|
602
|
+
'MyError',
|
|
603
|
+
{ status: 400, message: 'Bad request' }
|
|
604
|
+
) {}
|
|
605
|
+
|
|
606
|
+
// With explicit code
|
|
607
|
+
class MyError extends ORPCTaggedError<MyError>()(
|
|
608
|
+
'MyError',
|
|
609
|
+
'CUSTOM_CODE',
|
|
610
|
+
{ status: 400 }
|
|
611
|
+
) {}
|
|
612
|
+
|
|
613
|
+
// With typed data
|
|
614
|
+
class MyError extends ORPCTaggedError<MyError, { field: string }>()(
|
|
615
|
+
'MyError',
|
|
616
|
+
'BAD_REQUEST'
|
|
617
|
+
) {}
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
### `toORPCError(taggedError)`
|
|
621
|
+
|
|
622
|
+
Converts an `ORPCTaggedError` instance to a plain `ORPCError`.
|
|
623
|
+
|
|
624
|
+
```ts
|
|
625
|
+
import { toORPCError } from 'effect-orpc'
|
|
626
|
+
|
|
627
|
+
const taggedError = new UserNotFound({ data: { userId: '123' } })
|
|
628
|
+
const orpcError = toORPCError(taggedError)
|
|
629
|
+
// => ORPCError { code: 'USER_NOT_FOUND', status: 404, data: { userId: '123' } }
|
|
630
|
+
```
|
|
631
|
+
|
|
536
632
|
## License
|
|
537
633
|
|
|
538
634
|
MIT
|
package/dist/index.js
CHANGED
|
@@ -117,7 +117,7 @@ function createEffectErrorConstructorMap(errors) {
|
|
|
117
117
|
}
|
|
118
118
|
const config = target[code];
|
|
119
119
|
if (isORPCTaggedErrorClass(config)) {
|
|
120
|
-
return config;
|
|
120
|
+
return (...opts) => new config(...opts);
|
|
121
121
|
}
|
|
122
122
|
return (...rest) => {
|
|
123
123
|
const options = resolveMaybeOptionalOptions(rest);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/effect-builder.ts","../src/effect-procedure.ts","../src/tagged-error.ts"],"sourcesContent":["import type { ORPCErrorCode } from \"@orpc/client\";\nimport type {\n AnySchema,\n ErrorMap,\n InferSchemaOutput,\n Meta,\n Route,\n Schema,\n} from \"@orpc/contract\";\nimport type {\n AnyMiddleware,\n BuilderDef,\n Context,\n MapInputMiddleware,\n MergedCurrentContext,\n MergedInitialContext,\n Middleware,\n ORPCErrorConstructorMap,\n ProcedureHandlerOptions,\n} from \"@orpc/server\";\nimport type { IntersectPick } from \"@orpc/shared\";\nimport type { ManagedRuntime } from \"effect\";\nimport type { YieldWrap } from \"effect/Utils\";\n\nimport {\n mergeErrorMap,\n mergeMeta,\n mergeRoute,\n ORPCError,\n} from \"@orpc/contract\";\nimport {\n addMiddleware,\n Builder,\n decorateMiddleware,\n fallbackConfig,\n} from \"@orpc/server\";\nimport { Cause, Effect, Exit } from \"effect\";\n\nimport type {\n EffectErrorConstructorMap,\n EffectErrorMap,\n EffectErrorMapToUnion,\n MergedEffectErrorMap,\n} from \"./tagged-error\";\n\nimport { EffectDecoratedProcedure } from \"./effect-procedure\";\nimport {\n createEffectErrorConstructorMap,\n effectErrorMapToErrorMap,\n isORPCTaggedError,\n} from \"./tagged-error\";\n\n/**\n * Captures the stack trace at the call site for better error reporting in spans.\n * This is called at procedure definition time to capture where the procedure was defined.\n *\n * @returns A function that lazily extracts the relevant stack frame\n */\nexport function addSpanStackTrace(): () => string | undefined {\n const ErrorConstructor = Error as typeof Error & {\n stackTraceLimit?: number;\n };\n const limit = ErrorConstructor.stackTraceLimit;\n ErrorConstructor.stackTraceLimit = 3;\n const traceError = new Error();\n ErrorConstructor.stackTraceLimit = limit;\n let cache: false | string = false;\n return () => {\n if (cache !== false) {\n return cache;\n }\n if (traceError.stack !== undefined) {\n const stack = traceError.stack.split(\"\\n\");\n if (stack[3] !== undefined) {\n cache = stack[3].trim();\n return cache;\n }\n }\n };\n}\n\n/**\n * Configuration for Effect span tracing.\n */\nexport interface EffectSpanConfig {\n /**\n * The name of the span for telemetry.\n */\n name: string;\n /**\n * Function to lazily capture the stack trace at definition time.\n */\n captureStackTrace: () => string | undefined;\n}\n\n/**\n * Options passed to the Effect procedure handler.\n */\nexport interface EffectProcedureHandlerOptions<\n TCurrentContext extends Context,\n TInput,\n TEffectErrorMap extends EffectErrorMap,\n TMeta extends Meta,\n> extends Omit<\n ProcedureHandlerOptions<\n TCurrentContext,\n TInput,\n ORPCErrorConstructorMap<any>,\n TMeta\n >,\n \"errors\"\n> {\n errors: EffectErrorConstructorMap<TEffectErrorMap>;\n}\n\n/**\n * Handler type for Effect procedures.\n * The handler receives procedure options and returns an Effect.\n */\nexport type EffectProcedureHandler<\n TCurrentContext extends Context,\n TInput,\n THandlerOutput,\n TEffectErrorMap extends EffectErrorMap,\n TRequirementsProvided,\n TMeta extends Meta,\n> = (\n opt: EffectProcedureHandlerOptions<\n TCurrentContext,\n TInput,\n TEffectErrorMap,\n TMeta\n >,\n) => Generator<\n YieldWrap<\n Effect.Effect<\n any,\n | EffectErrorMapToUnion<TEffectErrorMap>\n | ORPCError<ORPCErrorCode, unknown>,\n TRequirementsProvided\n >\n >,\n THandlerOutput,\n never\n>;\n\n/**\n * Extended builder definition that includes the Effect ManagedRuntime.\n */\nexport interface EffectBuilderDef<\n TInputSchema extends AnySchema,\n TOutputSchema extends AnySchema,\n TEffectErrorMap extends EffectErrorMap,\n TMeta extends Meta,\n TRequirementsProvided,\n TRuntimeError,\n> extends BuilderDef<TInputSchema, TOutputSchema, ErrorMap, TMeta> {\n runtime: ManagedRuntime.ManagedRuntime<TRequirementsProvided, TRuntimeError>;\n /**\n * Optional span configuration for Effect tracing.\n */\n spanConfig?: EffectSpanConfig;\n /**\n * Effect-extended error map that supports both traditional errors and tagged errors.\n */\n effectErrorMap: TEffectErrorMap;\n}\n\n/**\n * Effect-native procedure builder that wraps an oRPC Builder instance\n * and adds Effect-specific capabilities while preserving Effect error\n * and requirements types.\n */\nexport class EffectBuilder<\n TInitialContext extends Context,\n TCurrentContext extends Context,\n TInputSchema extends AnySchema,\n TOutputSchema extends AnySchema,\n TEffectErrorMap extends EffectErrorMap,\n TMeta extends Meta,\n TRequirementsProvided,\n TRuntimeError,\n> {\n \"~orpc\": EffectBuilderDef<\n TInputSchema,\n TOutputSchema,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n >;\n\n constructor(\n def: EffectBuilderDef<\n TInputSchema,\n TOutputSchema,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n >,\n ) {\n this[\"~orpc\"] = def;\n }\n\n /**\n * Adds type-safe custom errors.\n * Supports both traditional oRPC error definitions and ORPCTaggedError classes.\n *\n * @example\n * ```ts\n * // Traditional format\n * builder.errors({ BAD_REQUEST: { status: 400, message: 'Bad request' } })\n *\n * // Tagged error class\n * builder.errors({ USER_NOT_FOUND: UserNotFoundError })\n *\n * // Mixed\n * builder.errors({\n * BAD_REQUEST: { status: 400 },\n * USER_NOT_FOUND: UserNotFoundError,\n * })\n * ```\n *\n * @see {@link https://orpc.dev/docs/error-handling#type%E2%80%90safe-error-handling Type-Safe Error Handling Docs}\n */\n errors<U extends EffectErrorMap>(\n errors: U,\n ): EffectBuilder<\n TInitialContext,\n TCurrentContext,\n TInputSchema,\n TOutputSchema,\n MergedEffectErrorMap<TEffectErrorMap, U>,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n > {\n return new EffectBuilder({\n ...this[\"~orpc\"],\n errorMap: mergeErrorMap(\n this[\"~orpc\"].errorMap,\n effectErrorMapToErrorMap(errors),\n ),\n effectErrorMap: {\n ...this[\"~orpc\"].effectErrorMap,\n ...errors,\n },\n });\n }\n\n /**\n * Uses a middleware to modify the context or improve the pipeline.\n *\n * @info Supports both normal middleware and inline middleware implementations.\n * @note The current context must be satisfy middleware dependent-context\n * @see {@link https://orpc.dev/docs/middleware Middleware Docs}\n */\n use<\n UOutContext extends IntersectPick<TCurrentContext, UOutContext>,\n UInContext extends Context = TCurrentContext,\n >(\n middleware: Middleware<\n UInContext | TCurrentContext,\n UOutContext,\n unknown,\n unknown,\n ORPCErrorConstructorMap<ErrorMap>,\n TMeta\n >,\n ): EffectBuilder<\n MergedInitialContext<TInitialContext, UInContext, TCurrentContext>,\n MergedCurrentContext<TCurrentContext, UOutContext>,\n TInputSchema,\n TOutputSchema,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n >;\n\n use(\n middleware: AnyMiddleware,\n mapInput?: MapInputMiddleware<any, any>,\n ): EffectBuilder<any, any, any, any, any, any, any, any> {\n const mapped = mapInput\n ? decorateMiddleware(middleware).mapInput(mapInput)\n : middleware;\n\n return new EffectBuilder({\n ...this[\"~orpc\"],\n middlewares: addMiddleware(this[\"~orpc\"].middlewares, mapped),\n });\n }\n\n /**\n * Sets or updates the metadata.\n * The provided metadata is spared-merged with any existing metadata.\n *\n * @see {@link https://orpc.dev/docs/metadata Metadata Docs}\n */\n meta(\n meta: TMeta,\n ): EffectBuilder<\n TInitialContext,\n TCurrentContext,\n TInputSchema,\n TOutputSchema,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n > {\n return new EffectBuilder({\n ...this[\"~orpc\"],\n meta: mergeMeta(this[\"~orpc\"].meta, meta),\n });\n }\n\n /**\n * Sets or updates the route definition.\n * The provided route is spared-merged with any existing route.\n * This option is typically relevant when integrating with OpenAPI.\n *\n * @see {@link https://orpc.dev/docs/openapi/routing OpenAPI Routing Docs}\n * @see {@link https://orpc.dev/docs/openapi/input-output-structure OpenAPI Input/Output Structure Docs}\n */\n route(\n route: Route,\n ): EffectBuilder<\n TInitialContext,\n TCurrentContext,\n TInputSchema,\n TOutputSchema,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n > {\n return new EffectBuilder({\n ...this[\"~orpc\"],\n route: mergeRoute(this[\"~orpc\"].route, route),\n });\n }\n\n /**\n * Defines the input validation schema.\n *\n * @see {@link https://orpc.dev/docs/procedure#input-output-validation Input Validation Docs}\n */\n input<USchema extends AnySchema>(\n schema: USchema,\n ): EffectBuilder<\n TInitialContext,\n TCurrentContext,\n USchema,\n TOutputSchema,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n > {\n return new EffectBuilder({\n ...this[\"~orpc\"],\n inputSchema: schema,\n inputValidationIndex:\n fallbackConfig(\n \"initialInputValidationIndex\",\n this[\"~orpc\"].config.initialInputValidationIndex,\n ) + this[\"~orpc\"].middlewares.length,\n });\n }\n\n /**\n * Defines the output validation schema.\n *\n * @see {@link https://orpc.dev/docs/procedure#input-output-validation Output Validation Docs}\n */\n output<USchema extends AnySchema>(\n schema: USchema,\n ): EffectBuilder<\n TInitialContext,\n TCurrentContext,\n TInputSchema,\n USchema,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n > {\n return new EffectBuilder({\n ...this[\"~orpc\"],\n outputSchema: schema,\n outputValidationIndex:\n fallbackConfig(\n \"initialOutputValidationIndex\",\n this[\"~orpc\"].config.initialOutputValidationIndex,\n ) + this[\"~orpc\"].middlewares.length,\n });\n }\n\n /**\n * Adds a traceable span to the procedure for telemetry.\n * The span name is used for Effect tracing via `Effect.withSpan`.\n * Stack trace is captured at the call site for better error reporting.\n *\n * @param spanName - The name of the span for telemetry (e.g., 'users.getUser')\n * @returns An EffectBuilder with span tracing configured\n *\n * @example\n * ```ts\n * const getUser = effectOs\n * .input(z.object({ id: z.string() }))\n * .traced('users.getUser')\n * .effect(function* ({ input }) {\n * const userService = yield* UserService\n * return yield* userService.findById(input.id)\n * })\n * ```\n */\n traced(\n spanName: string,\n ): EffectBuilder<\n TInitialContext,\n TCurrentContext,\n TInputSchema,\n TOutputSchema,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n > {\n return new EffectBuilder({\n ...this[\"~orpc\"],\n spanConfig: {\n name: spanName,\n captureStackTrace: addSpanStackTrace(),\n },\n });\n }\n\n /**\n * Defines the handler of the procedure using an Effect.\n * The Effect is executed using the ManagedRuntime provided during builder creation.\n * The effect is automatically wrapped with `Effect.withSpan`.\n *\n * @see {@link https://orpc.dev/docs/procedure Procedure Docs}\n */\n effect<UFuncOutput>(\n effectFn: EffectProcedureHandler<\n TCurrentContext,\n InferSchemaOutput<TInputSchema>,\n UFuncOutput,\n TEffectErrorMap,\n TRequirementsProvided,\n TMeta\n >,\n ): EffectDecoratedProcedure<\n TInitialContext,\n TCurrentContext,\n TInputSchema,\n Schema<UFuncOutput, UFuncOutput>,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n > {\n const { runtime, spanConfig } = this[\"~orpc\"];\n // Capture stack trace at definition time for default tracing\n const defaultCaptureStackTrace = addSpanStackTrace();\n return new EffectDecoratedProcedure({\n ...this[\"~orpc\"],\n handler: async (opts) => {\n const effectOpts: EffectProcedureHandlerOptions<\n TCurrentContext,\n InferSchemaOutput<TInputSchema>,\n TEffectErrorMap,\n TMeta\n > = {\n context: opts.context,\n input: opts.input,\n path: opts.path,\n procedure: opts.procedure,\n signal: opts.signal,\n lastEventId: opts.lastEventId,\n errors: createEffectErrorConstructorMap(this[\"~orpc\"].effectErrorMap),\n };\n const spanName = spanConfig?.name ?? opts.path.join(\".\");\n const captureStackTrace =\n spanConfig?.captureStackTrace ?? defaultCaptureStackTrace;\n const resolver = Effect.fnUntraced(effectFn);\n const tracedEffect = Effect.withSpan(resolver(effectOpts), spanName, {\n captureStackTrace,\n });\n const exit = await runtime.runPromiseExit(tracedEffect, {\n signal: opts.signal,\n });\n\n if (Exit.isFailure(exit)) {\n throw Cause.match(exit.cause, {\n onDie(defect) {\n return new ORPCError(\"INTERNAL_SERVER_ERROR\", {\n cause: defect,\n });\n },\n onFail(error) {\n if (isORPCTaggedError(error)) {\n return error.toORPCError();\n }\n if (error instanceof ORPCError) {\n return error;\n }\n return new ORPCError(\"INTERNAL_SERVER_ERROR\", {\n cause: error,\n });\n },\n onInterrupt(fiberId) {\n return new ORPCError(\"INTERNAL_SERVER_ERROR\", {\n cause: new Error(`${fiberId} Interrupted`),\n });\n },\n onSequential(left) {\n return left;\n },\n onEmpty: new ORPCError(\"INTERNAL_SERVER_ERROR\", {\n cause: new Error(\"Unknown error\"),\n }),\n onParallel(left) {\n return left;\n },\n });\n }\n\n return exit.value;\n },\n });\n }\n}\n\n/**\n * Any oRPC builder-like object that has the `~orpc` definition property.\n * This includes Builder, BuilderWithMiddlewares, ProcedureBuilder, etc.\n */\nexport interface AnyBuilderLike<\n TInputSchema extends AnySchema,\n TOutputSchema extends AnySchema,\n TErrorMap extends ErrorMap,\n TMeta extends Meta,\n> {\n \"~orpc\": BuilderDef<TInputSchema, TOutputSchema, TErrorMap, TMeta>;\n}\n\n/**\n * Creates an Effect-aware procedure builder with the specified ManagedRuntime.\n * Uses the default `os` builder from `@orpc/server`.\n *\n * @param runtime - The ManagedRuntime that provides services for Effect procedures\n * @returns An EffectBuilder instance for creating Effect-native procedures\n *\n * @example\n * ```ts\n * import { makeEffectORPC } from '@orpc/effect'\n * import { Effect, Layer, ManagedRuntime } from 'effect'\n *\n * const runtime = ManagedRuntime.make(Layer.empty)\n * const effectOs = makeEffectORPC(runtime)\n *\n * const hello = effectOs.effect(() => Effect.succeed('Hello!'))\n * ```\n */\nexport function makeEffectORPC<TRequirementsProvided, TRuntimeError>(\n runtime: ManagedRuntime.ManagedRuntime<TRequirementsProvided, TRuntimeError>,\n): EffectBuilder<\n Context,\n Context,\n Schema<unknown, unknown>,\n Schema<unknown, unknown>,\n Record<never, never>,\n Record<never, never>,\n TRequirementsProvided,\n TRuntimeError\n>;\n\n/**\n * Creates an Effect-aware procedure builder by wrapping an existing oRPC Builder\n * with the specified ManagedRuntime.\n *\n * @param runtime - The ManagedRuntime that provides services for Effect procedures\n * @param builder - The oRPC Builder instance to wrap (e.g., a customized `os`)\n * @returns An EffectBuilder instance that extends the original builder with Effect support\n *\n * @example\n * ```ts\n * import { makeEffectORPC } from '@orpc/effect'\n * import { os } from '@orpc/server'\n * import { Effect, Layer, ManagedRuntime } from 'effect'\n *\n * // Create a customized builder\n * const authedOs = os.use(authMiddleware)\n *\n * // Wrap it with Effect support\n * const runtime = ManagedRuntime.make(UserServiceLive)\n * const effectOs = makeEffectORPC(runtime, authedOs)\n *\n * const getUser = effectOs\n * .input(z.object({ id: z.string() }))\n * .effect(\n * Effect.fn(function* ({ input }) {\n * const userService = yield* UserService\n * return yield* userService.findById(input.id)\n * })\n * )\n * ```\n */\nexport function makeEffectORPC<\n TInputSchema extends AnySchema,\n TOutputSchema extends AnySchema,\n TErrorMap extends ErrorMap,\n TMeta extends Meta,\n TRequirementsProvided,\n TRuntimeError,\n>(\n runtime: ManagedRuntime.ManagedRuntime<TRequirementsProvided, TRuntimeError>,\n builder: AnyBuilderLike<TInputSchema, TOutputSchema, TErrorMap, TMeta>,\n): EffectBuilder<\n Context,\n Context,\n TInputSchema,\n TOutputSchema,\n TErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n>;\n\nexport function makeEffectORPC<\n TInputSchema extends AnySchema,\n TOutputSchema extends AnySchema,\n TErrorMap extends ErrorMap,\n TMeta extends Meta,\n TRequirementsProvided,\n TRuntimeError,\n>(\n runtime: ManagedRuntime.ManagedRuntime<TRequirementsProvided, TRuntimeError>,\n builder?: AnyBuilderLike<TInputSchema, TOutputSchema, TErrorMap, TMeta>,\n): EffectBuilder<\n Context,\n Context,\n TInputSchema,\n TOutputSchema,\n TErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n> {\n const resolvedBuilder =\n builder ?? emptyBuilder<TInputSchema, TOutputSchema, TErrorMap, TMeta>();\n return new EffectBuilder({\n ...resolvedBuilder[\"~orpc\"],\n effectErrorMap: resolvedBuilder[\"~orpc\"].errorMap,\n runtime,\n });\n}\n\nfunction emptyBuilder<\n TInputSchema extends AnySchema,\n TOutputSchema extends AnySchema,\n TErrorMap extends ErrorMap,\n TMeta extends Meta,\n>() {\n return new Builder<\n Record<never, never>,\n Record<never, never>,\n TInputSchema,\n TOutputSchema,\n TErrorMap,\n TMeta\n >({\n config: {},\n route: {},\n meta: {} as TMeta,\n errorMap: {} as TErrorMap,\n inputValidationIndex: fallbackConfig(\"initialInputValidationIndex\"),\n outputValidationIndex: fallbackConfig(\"initialOutputValidationIndex\"),\n middlewares: [],\n dedupeLeadingMiddlewares: true,\n });\n}\n","import type { ClientContext } from \"@orpc/client\";\nimport type {\n AnySchema,\n ErrorMap,\n InferSchemaInput,\n InferSchemaOutput,\n Meta,\n Route,\n} from \"@orpc/contract\";\nimport type {\n AnyMiddleware,\n Context,\n CreateProcedureClientOptions,\n MapInputMiddleware,\n MergedCurrentContext,\n MergedInitialContext,\n Middleware,\n ORPCErrorConstructorMap,\n ProcedureActionableClient,\n ProcedureClient,\n ProcedureDef,\n} from \"@orpc/server\";\nimport type { IntersectPick, MaybeOptionalOptions } from \"@orpc/shared\";\nimport type { ManagedRuntime } from \"effect\";\n\nimport { mergeMeta, mergeRoute } from \"@orpc/contract\";\nimport {\n addMiddleware,\n createActionableClient,\n createProcedureClient,\n decorateMiddleware,\n Procedure,\n} from \"@orpc/server\";\n\nimport type { EffectErrorMap, MergedEffectErrorMap } from \"./tagged-error\";\n\nimport { effectErrorMapToErrorMap } from \"./tagged-error\";\n\n/**\n * Extended procedure definition that includes the Effect ManagedRuntime.\n */\nexport interface EffectProcedureDef<\n TInitialContext extends Context,\n TCurrentContext extends Context,\n TInputSchema extends AnySchema,\n TOutputSchema extends AnySchema,\n TEffectErrorMap extends EffectErrorMap,\n TMeta extends Meta,\n TRequirementsProvided,\n TRuntimeError,\n> extends ProcedureDef<\n TInitialContext,\n TCurrentContext,\n TInputSchema,\n TOutputSchema,\n ErrorMap,\n TMeta\n> {\n runtime: ManagedRuntime.ManagedRuntime<TRequirementsProvided, TRuntimeError>;\n effectErrorMap: TEffectErrorMap;\n}\n\n/**\n * An Effect-native decorated procedure that preserves Effect error and requirements types.\n *\n * This class extends Procedure with additional type parameters for Effect-specific\n * type information, allowing full type inference of Effect errors and requirements.\n */\nexport class EffectDecoratedProcedure<\n TInitialContext extends Context,\n TCurrentContext extends Context,\n TInputSchema extends AnySchema,\n TOutputSchema extends AnySchema,\n TEffectErrorMap extends EffectErrorMap,\n TMeta extends Meta,\n TRequirementsProvided,\n TRuntimeError,\n> extends Procedure<\n TInitialContext,\n TCurrentContext,\n TInputSchema,\n TOutputSchema,\n ErrorMap,\n TMeta\n> {\n declare \"~orpc\": EffectProcedureDef<\n TInitialContext,\n TCurrentContext,\n TInputSchema,\n TOutputSchema,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n >;\n\n constructor(\n def: EffectProcedureDef<\n TInitialContext,\n TCurrentContext,\n TInputSchema,\n TOutputSchema,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n >,\n ) {\n super(def);\n }\n\n /**\n * Adds type-safe custom errors.\n * Supports both traditional oRPC error definitions and ORPCTaggedError classes.\n *\n * @see {@link https://orpc.dev/docs/error-handling#type%E2%80%90safe-error-handling Type-Safe Error Handling Docs}\n */\n errors<U extends EffectErrorMap>(\n errors: U,\n ): EffectDecoratedProcedure<\n TInitialContext,\n TCurrentContext,\n TInputSchema,\n TOutputSchema,\n MergedEffectErrorMap<TEffectErrorMap, U>,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n > {\n const newEffectErrorMap = { ...this[\"~orpc\"].effectErrorMap, ...errors };\n return new EffectDecoratedProcedure({\n ...this[\"~orpc\"],\n effectErrorMap: newEffectErrorMap,\n errorMap: effectErrorMapToErrorMap(newEffectErrorMap),\n });\n }\n\n /**\n * Sets or updates the metadata.\n * The provided metadata is spared-merged with any existing metadata.\n *\n * @see {@link https://orpc.dev/docs/metadata Metadata Docs}\n */\n meta(\n meta: TMeta,\n ): EffectDecoratedProcedure<\n TInitialContext,\n TCurrentContext,\n TInputSchema,\n TOutputSchema,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n > {\n return new EffectDecoratedProcedure({\n ...this[\"~orpc\"],\n meta: mergeMeta(this[\"~orpc\"].meta, meta),\n });\n }\n\n /**\n * Sets or updates the route definition.\n * The provided route is spared-merged with any existing route.\n * This option is typically relevant when integrating with OpenAPI.\n *\n * @see {@link https://orpc.dev/docs/openapi/routing OpenAPI Routing Docs}\n * @see {@link https://orpc.dev/docs/openapi/input-output-structure OpenAPI Input/Output Structure Docs}\n */\n route(\n route: Route,\n ): EffectDecoratedProcedure<\n TInitialContext,\n TCurrentContext,\n TInputSchema,\n TOutputSchema,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n > {\n return new EffectDecoratedProcedure({\n ...this[\"~orpc\"],\n route: mergeRoute(this[\"~orpc\"].route, route),\n });\n }\n\n /**\n * Uses a middleware to modify the context or improve the pipeline.\n *\n * @info Supports both normal middleware and inline middleware implementations.\n * @info Pass second argument to map the input.\n * @note The current context must be satisfy middleware dependent-context\n * @see {@link https://orpc.dev/docs/middleware Middleware Docs}\n */\n use<\n UOutContext extends IntersectPick<TCurrentContext, UOutContext>,\n UInContext extends Context = TCurrentContext,\n >(\n middleware: Middleware<\n UInContext | TCurrentContext,\n UOutContext,\n InferSchemaOutput<TInputSchema>,\n InferSchemaInput<TOutputSchema>,\n ORPCErrorConstructorMap<ErrorMap>,\n TMeta\n >,\n ): EffectDecoratedProcedure<\n MergedInitialContext<TInitialContext, UInContext, TCurrentContext>,\n MergedCurrentContext<TCurrentContext, UOutContext>,\n TInputSchema,\n TOutputSchema,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n >;\n\n /**\n * Uses a middleware to modify the context or improve the pipeline.\n *\n * @info Supports both normal middleware and inline middleware implementations.\n * @info Pass second argument to map the input.\n * @note The current context must be satisfy middleware dependent-context\n * @see {@link https://orpc.dev/docs/middleware Middleware Docs}\n */\n use<\n UOutContext extends IntersectPick<TCurrentContext, UOutContext>,\n UInput,\n UInContext extends Context = TCurrentContext,\n >(\n middleware: Middleware<\n UInContext | TCurrentContext,\n UOutContext,\n UInput,\n InferSchemaInput<TOutputSchema>,\n ORPCErrorConstructorMap<ErrorMap>,\n TMeta\n >,\n mapInput: MapInputMiddleware<InferSchemaOutput<TInputSchema>, UInput>,\n ): EffectDecoratedProcedure<\n MergedInitialContext<TInitialContext, UInContext, TCurrentContext>,\n MergedCurrentContext<TCurrentContext, UOutContext>,\n TInputSchema,\n TOutputSchema,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n >;\n\n use(\n middleware: AnyMiddleware,\n mapInput?: MapInputMiddleware<any, any>,\n ): EffectDecoratedProcedure<any, any, any, any, any, any, any, any> {\n const mapped = mapInput\n ? decorateMiddleware(middleware).mapInput(mapInput)\n : middleware;\n\n return new EffectDecoratedProcedure({\n ...this[\"~orpc\"],\n middlewares: addMiddleware(this[\"~orpc\"].middlewares, mapped),\n });\n }\n\n /**\n * Make this procedure callable (works like a function while still being a procedure).\n *\n * @see {@link https://orpc.dev/docs/client/server-side Server-side Client Docs}\n */\n callable<TClientContext extends ClientContext>(\n ...rest: MaybeOptionalOptions<\n CreateProcedureClientOptions<\n TInitialContext,\n TOutputSchema,\n ErrorMap,\n TMeta,\n TClientContext\n >\n >\n ): EffectDecoratedProcedure<\n TInitialContext,\n TCurrentContext,\n TInputSchema,\n TOutputSchema,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n > &\n ProcedureClient<TClientContext, TInputSchema, TOutputSchema, ErrorMap> {\n const client: ProcedureClient<\n TClientContext,\n TInputSchema,\n TOutputSchema,\n ErrorMap\n > = createProcedureClient(this, ...rest);\n\n return new Proxy(client, {\n get: (target, key) => {\n return Reflect.has(this, key)\n ? Reflect.get(this, key)\n : Reflect.get(target, key);\n },\n has: (target, key) => {\n return Reflect.has(this, key) || Reflect.has(target, key);\n },\n }) as any;\n }\n\n /**\n * Make this procedure compatible with server action.\n *\n * @see {@link https://orpc.dev/docs/server-action Server Action Docs}\n */\n actionable(\n ...rest: MaybeOptionalOptions<\n CreateProcedureClientOptions<\n TInitialContext,\n TOutputSchema,\n ErrorMap,\n TMeta,\n Record<never, never>\n >\n >\n ): EffectDecoratedProcedure<\n TInitialContext,\n TCurrentContext,\n TInputSchema,\n TOutputSchema,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n > &\n ProcedureActionableClient<TInputSchema, TOutputSchema, ErrorMap> {\n const action: ProcedureActionableClient<\n TInputSchema,\n TOutputSchema,\n ErrorMap\n > = createActionableClient(createProcedureClient(this, ...rest));\n\n return new Proxy(action, {\n get: (target, key) => {\n return Reflect.has(this, key)\n ? Reflect.get(this, key)\n : Reflect.get(target, key);\n },\n has: (target, key) => {\n return Reflect.has(this, key) || Reflect.has(target, key);\n },\n }) as any;\n }\n}\n","import type {\n ORPCErrorCode,\n ORPCErrorJSON,\n ORPCErrorOptions,\n} from \"@orpc/client\";\nimport type { AnySchema, ErrorMap, ErrorMapItem } from \"@orpc/contract\";\nimport type { ORPCErrorConstructorMapItemOptions } from \"@orpc/server\";\nimport type { MaybeOptionalOptions } from \"@orpc/shared\";\nimport type { Pipeable, Types } from \"effect\";\nimport type * as Cause from \"effect/Cause\";\nimport type * as Effect from \"effect/Effect\";\n\nimport {\n fallbackORPCErrorMessage,\n fallbackORPCErrorStatus,\n isORPCErrorStatus,\n ORPCError,\n} from \"@orpc/client\";\nimport { resolveMaybeOptionalOptions } from \"@orpc/shared\";\nimport * as Data from \"effect/Data\";\n\n/**\n * Symbol to access the underlying ORPCError instance\n */\nexport const ORPCErrorSymbol: unique symbol = Symbol.for(\n \"@orpc/effect/ORPCTaggedError\",\n);\n\n/**\n * Instance type for ORPCTaggedError that combines YieldableError with ORPCError properties\n */\nexport interface ORPCTaggedErrorInstance<\n TTag extends string,\n TCode extends ORPCErrorCode,\n TData,\n>\n extends Cause.YieldableError, Pipeable.Pipeable {\n readonly _tag: TTag;\n readonly code: TCode;\n readonly status: number;\n readonly data: TData;\n readonly defined: boolean;\n readonly [ORPCErrorSymbol]: ORPCError<TCode, TData>;\n\n toJSON(): ORPCErrorJSON<TCode, TData> & { _tag: TTag };\n toORPCError(): ORPCError<TCode, TData>;\n commit(): Effect.Effect<never, this, never>;\n}\n\n/**\n * Options for creating an ORPCTaggedError\n */\nexport type ORPCTaggedErrorOptions<TData> = Omit<\n ORPCErrorOptions<TData>,\n \"defined\"\n> & { defined?: boolean };\n\n/**\n * Constructor type for ORPCTaggedError classes\n */\nexport interface ORPCTaggedErrorClass<\n TTag extends string,\n TCode extends ORPCErrorCode,\n TData,\n> {\n readonly _tag: TTag;\n readonly code: TCode;\n new (\n ...args: MaybeOptionalOptions<ORPCTaggedErrorOptions<TData>>\n ): ORPCTaggedErrorInstance<TTag, TCode, TData>;\n}\n\n/**\n * Type helper to infer the ORPCError type from an ORPCTaggedError\n */\nexport type InferORPCError<T> =\n T extends ORPCTaggedErrorInstance<string, infer TCode, infer TData>\n ? ORPCError<TCode, TData>\n : never;\n\n/**\n * Any ORPCTaggedErrorClass\n */\nexport type AnyORPCTaggedErrorClass = ORPCTaggedErrorClass<\n string,\n ORPCErrorCode,\n any\n>;\n\n/**\n * Check if a value is an ORPCTaggedErrorClass (constructor)\n */\nexport function isORPCTaggedErrorClass(\n value: unknown,\n): value is AnyORPCTaggedErrorClass {\n return (\n typeof value === \"function\" &&\n \"_tag\" in value &&\n \"code\" in value &&\n typeof value._tag === \"string\" &&\n typeof value.code === \"string\"\n );\n}\n\n/**\n * Check if a value is an ORPCTaggedError instance\n */\nexport function isORPCTaggedError(\n value: unknown,\n): value is ORPCTaggedErrorInstance<string, ORPCErrorCode, unknown> {\n return (\n typeof value === \"object\" && value !== null && ORPCErrorSymbol in value\n );\n}\n\n/**\n * Converts a PascalCase or camelCase string to CONSTANT_CASE.\n * e.g., \"UserNotFoundError\" -> \"USER_NOT_FOUND_ERROR\"\n */\nfunction toConstantCase(str: string): string {\n return str\n .replace(/([a-z])([A-Z])/g, \"$1_$2\")\n .replace(/([A-Z])([A-Z][a-z])/g, \"$1_$2\")\n .toUpperCase();\n}\n\n// Type-level conversion: split on capital letters and join with underscore\ntype SplitOnCapital<\n S extends string,\n Acc extends string = \"\",\n> = S extends `${infer Head}${infer Tail}`\n ? Head extends Uppercase<Head>\n ? Head extends Lowercase<Head>\n ? SplitOnCapital<Tail, `${Acc}${Head}`>\n : Acc extends \"\"\n ? SplitOnCapital<Tail, Head>\n : `${Acc}_${SplitOnCapital<Tail, Head>}`\n : SplitOnCapital<Tail, `${Acc}${Uppercase<Head>}`>\n : Acc;\n\n/**\n * Converts a tag name to an error code in CONSTANT_CASE.\n */\nexport type TagToCode<TTag extends string> = SplitOnCapital<TTag>;\n\n/**\n * Creates a tagged error class that combines Effect's YieldableError with ORPCError.\n *\n * This allows you to create errors that:\n * - Can be yielded in Effect generators (`yield* myError`)\n * - Have all ORPCError properties (code, status, data, defined)\n * - Can be converted to a plain ORPCError for oRPC handlers\n *\n * The returned factory function takes:\n * - `tag` - The unique tag for this error type (used for discriminated unions)\n * - `codeOrOptions` - Optional ORPC error code or options. If omitted, code defaults to CONSTANT_CASE of tag\n * - `defaultOptions` - Optional default options for status and message (when code is provided)\n *\n * @example\n * ```ts\n * import { ORPCTaggedError } from '@orpc/effect'\n * import { Effect } from 'effect'\n *\n * // Define a custom error (code defaults to 'USER_NOT_FOUND_ERROR')\n * class UserNotFoundError extends ORPCTaggedError<UserNotFoundError>()('UserNotFoundError') {}\n *\n * // With explicit code\n * class NotFoundError extends ORPCTaggedError<NotFoundError>()('NotFoundError', 'NOT_FOUND') {}\n *\n * // Use in an Effect\n * const getUser = (id: string) => Effect.gen(function* () {\n * const user = yield* findUser(id)\n * if (!user) {\n * return yield* new UserNotFoundError({ data: { userId: id } })\n * }\n * return user\n * })\n *\n * // With custom data type\n * class ValidationError extends ORPCTaggedError<ValidationError, { fields: string[] }>()('ValidationError', 'BAD_REQUEST') {}\n *\n * // With options only (code defaults to 'VALIDATION_ERROR')\n * class ValidationError2 extends ORPCTaggedError<ValidationError2, { fields: string[] }>()(\n * 'ValidationError2',\n * { message: 'Validation failed' }\n * ) {}\n * ```\n */\n/**\n * Return type for the factory function with overloads\n */\ninterface ORPCTaggedErrorFactory<Self, TData> {\n // Overload 1: tag only (code defaults to CONSTANT_CASE of tag)\n <TTag extends string>(\n tag: TTag,\n ): Types.Equals<Self, unknown> extends true\n ? `Missing \\`Self\\` generic - use \\`class MyError extends ORPCTaggedError<MyError>()(tag) {}\\``\n : ORPCTaggedErrorClass<TTag, TagToCode<TTag>, TData>;\n\n // Overload 2: tag + options (code defaults to CONSTANT_CASE of tag)\n <TTag extends string>(\n tag: TTag,\n options: { status?: number; message?: string },\n ): Types.Equals<Self, unknown> extends true\n ? `Missing \\`Self\\` generic - use \\`class MyError extends ORPCTaggedError<MyError>()(tag, options) {}\\``\n : ORPCTaggedErrorClass<TTag, TagToCode<TTag>, TData>;\n\n // Overload 3: tag + explicit code\n <TTag extends string, TCode extends ORPCErrorCode>(\n tag: TTag,\n code: TCode,\n defaultOptions?: { status?: number; message?: string },\n ): Types.Equals<Self, unknown> extends true\n ? `Missing \\`Self\\` generic - use \\`class MyError extends ORPCTaggedError<MyError>()(tag, code) {}\\``\n : ORPCTaggedErrorClass<TTag, TCode, TData>;\n}\n\nexport function ORPCTaggedError<\n Self,\n TData = undefined,\n>(): ORPCTaggedErrorFactory<Self, TData> {\n const factory = <TTag extends string, TCode extends ORPCErrorCode>(\n tag: TTag,\n codeOrOptions?: TCode | { status?: number; message?: string },\n defaultOptions?: { status?: number; message?: string },\n ): ORPCTaggedErrorClass<TTag, TCode, TData> => {\n // Determine if second arg is code or options\n const isCodeProvided = typeof codeOrOptions === \"string\";\n const code = (\n isCodeProvided ? codeOrOptions : toConstantCase(tag)\n ) as TCode;\n const options = isCodeProvided ? defaultOptions : codeOrOptions;\n\n const defaultStatus = options?.status;\n const defaultMessage = options?.message;\n\n // Use Effect's TaggedError as the base - this handles all Effect internals\n // (YieldableError, type symbols, commit(), Symbol.iterator, pipe(), etc.)\n const BaseTaggedError = Data.TaggedError(tag) as unknown as new (args: {\n message?: string;\n cause?: unknown;\n code: TCode;\n status: number;\n data: TData;\n defined: boolean;\n }) => Cause.YieldableError & {\n readonly _tag: TTag;\n readonly code: TCode;\n readonly status: number;\n readonly data: TData;\n readonly defined: boolean;\n };\n\n class ORPCTaggedErrorBase extends BaseTaggedError {\n static readonly _tag = tag;\n static readonly code = code;\n\n readonly [ORPCErrorSymbol]: ORPCError<TCode, TData>;\n\n constructor(\n ...rest: MaybeOptionalOptions<ORPCTaggedErrorOptions<TData>>\n ) {\n const opts = resolveMaybeOptionalOptions(rest);\n const status = opts.status ?? defaultStatus;\n const inputMessage = opts.message ?? defaultMessage;\n\n if (status !== undefined && !isORPCErrorStatus(status)) {\n throw new globalThis.Error(\n \"[ORPCTaggedError] Invalid error status code.\",\n );\n }\n\n const finalStatus = fallbackORPCErrorStatus(code, status);\n const finalMessage = fallbackORPCErrorMessage(code, inputMessage);\n\n // Pass to Effect's TaggedError - it spreads these onto the instance\n super({\n message: finalMessage,\n cause: opts.cause,\n code,\n status: finalStatus,\n data: opts.data as TData,\n defined: opts.defined ?? true,\n });\n\n // Create the underlying ORPCError for interop\n this[ORPCErrorSymbol] = new ORPCError(code, {\n status: finalStatus,\n message: finalMessage,\n data: opts.data as TData,\n defined: this.defined,\n cause: opts.cause,\n });\n }\n\n /**\n * Converts this error to a plain ORPCError.\n * Useful when you need to return from an oRPC handler.\n */\n toORPCError(): ORPCError<TCode, TData> {\n return this[ORPCErrorSymbol];\n }\n\n override toJSON(): ORPCErrorJSON<TCode, TData> & { _tag: TTag } {\n return {\n _tag: this._tag,\n defined: this.defined,\n code: this.code,\n status: this.status,\n message: this.message,\n data: this.data,\n };\n }\n }\n\n return ORPCTaggedErrorBase as any;\n };\n\n return factory as ORPCTaggedErrorFactory<Self, TData>;\n}\n\n/**\n * Converts an ORPCTaggedError to a plain ORPCError.\n * Useful in handlers that need to throw ORPCError.\n *\n * @example\n * ```ts\n * const handler = effectOs.effect(function* () {\n * const result = yield* someOperation.pipe(\n * Effect.catchTag('UserNotFoundError', (e) =>\n * Effect.fail(toORPCError(e))\n * )\n * )\n * return result\n * })\n * ```\n */\nexport function toORPCError<TCode extends ORPCErrorCode, TData>(\n error: ORPCTaggedErrorInstance<string, TCode, TData>,\n): ORPCError<TCode, TData> {\n return error[ORPCErrorSymbol];\n}\n\n// ============================================================================\n// Extended Error Map Types for Effect\n// ============================================================================\n\n/**\n * An item in the EffectErrorMap - can be either a traditional ErrorMapItem or an ORPCTaggedErrorClass\n */\nexport type EffectErrorMapItem =\n | ErrorMapItem<AnySchema>\n | AnyORPCTaggedErrorClass;\n\n/**\n * Extended error map that supports both traditional oRPC errors and ORPCTaggedError classes.\n *\n * @example\n * ```ts\n * const errorMap = {\n * // Traditional format\n * BAD_REQUEST: { status: 400, message: 'Bad request' },\n *\n * // Tagged error class reference\n * USER_NOT_FOUND: UserNotFoundError,\n * } satisfies EffectErrorMap\n * ```\n */\nexport type EffectErrorMap = {\n [key in ORPCErrorCode]?: EffectErrorMapItem;\n};\n\n/**\n * Merges two EffectErrorMaps, with the second map taking precedence.\n */\nexport type MergedEffectErrorMap<\n T1 extends EffectErrorMap,\n T2 extends EffectErrorMap,\n> = T1 & T2;\n\n/**\n * Extracts the instance type from an EffectErrorMapItem\n */\nexport type EffectErrorMapItemToInstance<\n TCode extends ORPCErrorCode,\n T extends EffectErrorMapItem,\n> = T extends AnyORPCTaggedErrorClass\n ? InstanceType<T>\n : T extends { data?: infer TData }\n ? ORPCError<TCode, TData>\n : ORPCError<TCode, unknown>;\n\n/**\n * Converts an EffectErrorMap to a union of error instances.\n */\nexport type EffectErrorMapToUnion<T extends EffectErrorMap> = {\n [K in keyof T]: K extends ORPCErrorCode\n ? T[K] extends EffectErrorMapItem\n ? EffectErrorMapItemToInstance<K, T[K]>\n : never\n : never;\n}[keyof T];\n\n/**\n * Type for the error constructors available in Effect handlers.\n * For tagged errors, it's the class constructor itself.\n * For traditional errors, it's a function that creates ORPCError.\n */\nexport type EffectErrorConstructorMapItem<\n TCode extends ORPCErrorCode,\n T extends EffectErrorMapItem,\n> =\n T extends ORPCTaggedErrorClass<infer _TTag, TCode, infer TData>\n ? ORPCTaggedErrorClass<_TTag, TCode, TData>\n : T extends { data?: infer TData }\n ? (\n ...args: MaybeOptionalOptions<\n ORPCErrorConstructorMapItemOptions<TData>\n >\n ) => ORPCError<TCode, TData>\n : (\n ...args: MaybeOptionalOptions<\n ORPCErrorConstructorMapItemOptions<unknown>\n >\n ) => ORPCError<TCode, unknown>;\n\n/**\n * Constructor map for EffectErrorMap - provides typed error constructors for handlers.\n */\nexport type EffectErrorConstructorMap<T extends EffectErrorMap> = {\n [K in keyof T]: K extends ORPCErrorCode\n ? T[K] extends EffectErrorMapItem\n ? EffectErrorConstructorMapItem<K, T[K]>\n : never\n : never;\n};\n\n/**\n * Creates an error constructor map from an EffectErrorMap.\n * Tagged error classes are passed through directly.\n * Traditional error items become ORPCError factory functions.\n */\nexport function createEffectErrorConstructorMap<T extends EffectErrorMap>(\n errors: T | undefined,\n): EffectErrorConstructorMap<T> {\n const target = errors ?? ({} as T);\n const proxy = new Proxy(target, {\n get(proxyTarget, code) {\n if (typeof code !== \"string\") {\n return Reflect.get(proxyTarget, code);\n }\n\n const config = target[code];\n\n // If it's a tagged error class, return it directly\n if (isORPCTaggedErrorClass(config)) {\n return config;\n }\n\n // Otherwise, create a factory function for ORPCError\n return (\n ...rest: MaybeOptionalOptions<\n Omit<ORPCErrorOptions<unknown>, \"defined\" | \"status\">\n >\n ) => {\n const options = resolveMaybeOptionalOptions(rest);\n return new ORPCError(code, {\n defined: Boolean(config),\n status: config?.status,\n message: options.message ?? config?.message,\n data: options.data,\n cause: options.cause,\n });\n };\n },\n });\n\n return proxy as EffectErrorConstructorMap<T>;\n}\n\n/**\n * Converts an EffectErrorMap to a standard oRPC ErrorMap for interop.\n * Tagged error classes are converted to their equivalent ErrorMapItem format.\n */\nexport function effectErrorMapToErrorMap<T extends EffectErrorMap>(\n errorMap: T | undefined,\n): ErrorMap {\n const result: ErrorMap = {};\n\n if (!errorMap) {\n return result;\n }\n\n for (const [code, ClassOrErrorItem] of Object.entries(errorMap)) {\n if (!ClassOrErrorItem) {\n continue;\n }\n\n if (isORPCTaggedErrorClass(ClassOrErrorItem)) {\n const error = new ClassOrErrorItem().toORPCError();\n\n // For tagged errors, we create a minimal entry\n // The actual validation will be handled by the tagged error class\n result[code] = {\n status: error.status,\n message: error.message,\n data: error.data,\n };\n } else {\n result[code] = ClassOrErrorItem;\n }\n }\n\n return result;\n}\n"],"mappings":";AAwBA;AAAA,EACE;AAAA,EACA,aAAAA;AAAA,EACA,cAAAC;AAAA,EACA,aAAAC;AAAA,OACK;AACP;AAAA,EACE,iBAAAC;AAAA,EACA;AAAA,EACA,sBAAAC;AAAA,EACA;AAAA,OACK;AACP,SAAS,OAAO,QAAQ,YAAY;;;ACXpC,SAAS,WAAW,kBAAkB;AACtC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;;;ACpBP;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,mCAAmC;AAC5C,YAAY,UAAU;AAKf,IAAM,kBAAiC,uBAAO;AAAA,EACnD;AACF;AAkEO,SAAS,uBACd,OACkC;AAClC,SACE,OAAO,UAAU,cACjB,UAAU,SACV,UAAU,SACV,OAAO,MAAM,SAAS,YACtB,OAAO,MAAM,SAAS;AAE1B;AAKO,SAAS,kBACd,OACkE;AAClE,SACE,OAAO,UAAU,YAAY,UAAU,QAAQ,mBAAmB;AAEtE;AAMA,SAAS,eAAe,KAAqB;AAC3C,SAAO,IACJ,QAAQ,mBAAmB,OAAO,EAClC,QAAQ,wBAAwB,OAAO,EACvC,YAAY;AACjB;AA6FO,SAAS,kBAGyB;AACvC,QAAM,UAAU,CACd,KACA,eACA,mBAC6C;AAE7C,UAAM,iBAAiB,OAAO,kBAAkB;AAChD,UAAM,OACJ,iBAAiB,gBAAgB,eAAe,GAAG;AAErD,UAAM,UAAU,iBAAiB,iBAAiB;AAElD,UAAM,gBAAgB,SAAS;AAC/B,UAAM,iBAAiB,SAAS;AAIhC,UAAM,kBAAuB,iBAAY,GAAG;AAAA,IAe5C,MAAM,4BAA4B,gBAAgB;AAAA,MAChD,OAAgB,OAAO;AAAA,MACvB,OAAgB,OAAO;AAAA,MAEvB,CAAU,eAAe;AAAA,MAEzB,eACK,MACH;AACA,cAAM,OAAO,4BAA4B,IAAI;AAC7C,cAAM,SAAS,KAAK,UAAU;AAC9B,cAAM,eAAe,KAAK,WAAW;AAErC,YAAI,WAAW,UAAa,CAAC,kBAAkB,MAAM,GAAG;AACtD,gBAAM,IAAI,WAAW;AAAA,YACnB;AAAA,UACF;AAAA,QACF;AAEA,cAAM,cAAc,wBAAwB,MAAM,MAAM;AACxD,cAAM,eAAe,yBAAyB,MAAM,YAAY;AAGhE,cAAM;AAAA,UACJ,SAAS;AAAA,UACT,OAAO,KAAK;AAAA,UACZ;AAAA,UACA,QAAQ;AAAA,UACR,MAAM,KAAK;AAAA,UACX,SAAS,KAAK,WAAW;AAAA,QAC3B,CAAC;AAGD,aAAK,eAAe,IAAI,IAAI,UAAU,MAAM;AAAA,UAC1C,QAAQ;AAAA,UACR,SAAS;AAAA,UACT,MAAM,KAAK;AAAA,UACX,SAAS,KAAK;AAAA,UACd,OAAO,KAAK;AAAA,QACd,CAAC;AAAA,MACH;AAAA;AAAA;AAAA;AAAA;AAAA,MAMA,cAAuC;AACrC,eAAO,KAAK,eAAe;AAAA,MAC7B;AAAA,MAES,SAAuD;AAC9D,eAAO;AAAA,UACL,MAAM,KAAK;AAAA,UACX,SAAS,KAAK;AAAA,UACd,MAAM,KAAK;AAAA,UACX,QAAQ,KAAK;AAAA,UACb,SAAS,KAAK;AAAA,UACd,MAAM,KAAK;AAAA,QACb;AAAA,MACF;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAkBO,SAAS,YACd,OACyB;AACzB,SAAO,MAAM,eAAe;AAC9B;AAqGO,SAAS,gCACd,QAC8B;AAC9B,QAAM,SAAS,UAAW,CAAC;AAC3B,QAAM,QAAQ,IAAI,MAAM,QAAQ;AAAA,IAC9B,IAAI,aAAa,MAAM;AACrB,UAAI,OAAO,SAAS,UAAU;AAC5B,eAAO,QAAQ,IAAI,aAAa,IAAI;AAAA,MACtC;AAEA,YAAM,SAAS,OAAO,IAAI;AAG1B,UAAI,uBAAuB,MAAM,GAAG;AAClC,eAAO;AAAA,MACT;AAGA,aAAO,IACF,SAGA;AACH,cAAM,UAAU,4BAA4B,IAAI;AAChD,eAAO,IAAI,UAAU,MAAM;AAAA,UACzB,SAAS,QAAQ,MAAM;AAAA,UACvB,QAAQ,QAAQ;AAAA,UAChB,SAAS,QAAQ,WAAW,QAAQ;AAAA,UACpC,MAAM,QAAQ;AAAA,UACd,OAAO,QAAQ;AAAA,QACjB,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF,CAAC;AAED,SAAO;AACT;AAMO,SAAS,yBACd,UACU;AACV,QAAM,SAAmB,CAAC;AAE1B,MAAI,CAAC,UAAU;AACb,WAAO;AAAA,EACT;AAEA,aAAW,CAAC,MAAM,gBAAgB,KAAK,OAAO,QAAQ,QAAQ,GAAG;AAC/D,QAAI,CAAC,kBAAkB;AACrB;AAAA,IACF;AAEA,QAAI,uBAAuB,gBAAgB,GAAG;AAC5C,YAAM,QAAQ,IAAI,iBAAiB,EAAE,YAAY;AAIjD,aAAO,IAAI,IAAI;AAAA,QACb,QAAQ,MAAM;AAAA,QACd,SAAS,MAAM;AAAA,QACf,MAAM,MAAM;AAAA,MACd;AAAA,IACF,OAAO;AACL,aAAO,IAAI,IAAI;AAAA,IACjB;AAAA,EACF;AAEA,SAAO;AACT;;;AD9bO,IAAM,2BAAN,MAAM,kCASH,UAOR;AAAA,EAYA,YACE,KAUA;AACA,UAAM,GAAG;AAAA,EACX;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OACE,QAUA;AACA,UAAM,oBAAoB,EAAE,GAAG,KAAK,OAAO,EAAE,gBAAgB,GAAG,OAAO;AACvE,WAAO,IAAI,0BAAyB;AAAA,MAClC,GAAG,KAAK,OAAO;AAAA,MACf,gBAAgB;AAAA,MAChB,UAAU,yBAAyB,iBAAiB;AAAA,IACtD,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,KACE,MAUA;AACA,WAAO,IAAI,0BAAyB;AAAA,MAClC,GAAG,KAAK,OAAO;AAAA,MACf,MAAM,UAAU,KAAK,OAAO,EAAE,MAAM,IAAI;AAAA,IAC1C,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MACE,OAUA;AACA,WAAO,IAAI,0BAAyB;AAAA,MAClC,GAAG,KAAK,OAAO;AAAA,MACf,OAAO,WAAW,KAAK,OAAO,EAAE,OAAO,KAAK;AAAA,IAC9C,CAAC;AAAA,EACH;AAAA,EAkEA,IACE,YACA,UACkE;AAClE,UAAM,SAAS,WACX,mBAAmB,UAAU,EAAE,SAAS,QAAQ,IAChD;AAEJ,WAAO,IAAI,0BAAyB;AAAA,MAClC,GAAG,KAAK,OAAO;AAAA,MACf,aAAa,cAAc,KAAK,OAAO,EAAE,aAAa,MAAM;AAAA,IAC9D,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,YACK,MAmBoE;AACvE,UAAM,SAKF,sBAAsB,MAAM,GAAG,IAAI;AAEvC,WAAO,IAAI,MAAM,QAAQ;AAAA,MACvB,KAAK,CAAC,QAAQ,QAAQ;AACpB,eAAO,QAAQ,IAAI,MAAM,GAAG,IACxB,QAAQ,IAAI,MAAM,GAAG,IACrB,QAAQ,IAAI,QAAQ,GAAG;AAAA,MAC7B;AAAA,MACA,KAAK,CAAC,QAAQ,QAAQ;AACpB,eAAO,QAAQ,IAAI,MAAM,GAAG,KAAK,QAAQ,IAAI,QAAQ,GAAG;AAAA,MAC1D;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,cACK,MAmB8D;AACjE,UAAM,SAIF,uBAAuB,sBAAsB,MAAM,GAAG,IAAI,CAAC;AAE/D,WAAO,IAAI,MAAM,QAAQ;AAAA,MACvB,KAAK,CAAC,QAAQ,QAAQ;AACpB,eAAO,QAAQ,IAAI,MAAM,GAAG,IACxB,QAAQ,IAAI,MAAM,GAAG,IACrB,QAAQ,IAAI,QAAQ,GAAG;AAAA,MAC7B;AAAA,MACA,KAAK,CAAC,QAAQ,QAAQ;AACpB,eAAO,QAAQ,IAAI,MAAM,GAAG,KAAK,QAAQ,IAAI,QAAQ,GAAG;AAAA,MAC1D;AAAA,IACF,CAAC;AAAA,EACH;AACF;;;ADvSO,SAAS,oBAA8C;AAC5D,QAAM,mBAAmB;AAGzB,QAAM,QAAQ,iBAAiB;AAC/B,mBAAiB,kBAAkB;AACnC,QAAM,aAAa,IAAI,MAAM;AAC7B,mBAAiB,kBAAkB;AACnC,MAAI,QAAwB;AAC5B,SAAO,MAAM;AACX,QAAI,UAAU,OAAO;AACnB,aAAO;AAAA,IACT;AACA,QAAI,WAAW,UAAU,QAAW;AAClC,YAAM,QAAQ,WAAW,MAAM,MAAM,IAAI;AACzC,UAAI,MAAM,CAAC,MAAM,QAAW;AAC1B,gBAAQ,MAAM,CAAC,EAAE,KAAK;AACtB,eAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;AA8FO,IAAM,gBAAN,MAAM,eASX;AAAA,EACA;AAAA,EASA,YACE,KAQA;AACA,SAAK,OAAO,IAAI;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuBA,OACE,QAUA;AACA,WAAO,IAAI,eAAc;AAAA,MACvB,GAAG,KAAK,OAAO;AAAA,MACf,UAAU;AAAA,QACR,KAAK,OAAO,EAAE;AAAA,QACd,yBAAyB,MAAM;AAAA,MACjC;AAAA,MACA,gBAAgB;AAAA,QACd,GAAG,KAAK,OAAO,EAAE;AAAA,QACjB,GAAG;AAAA,MACL;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAgCA,IACE,YACA,UACuD;AACvD,UAAM,SAAS,WACXC,oBAAmB,UAAU,EAAE,SAAS,QAAQ,IAChD;AAEJ,WAAO,IAAI,eAAc;AAAA,MACvB,GAAG,KAAK,OAAO;AAAA,MACf,aAAaC,eAAc,KAAK,OAAO,EAAE,aAAa,MAAM;AAAA,IAC9D,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,KACE,MAUA;AACA,WAAO,IAAI,eAAc;AAAA,MACvB,GAAG,KAAK,OAAO;AAAA,MACf,MAAMC,WAAU,KAAK,OAAO,EAAE,MAAM,IAAI;AAAA,IAC1C,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MACE,OAUA;AACA,WAAO,IAAI,eAAc;AAAA,MACvB,GAAG,KAAK,OAAO;AAAA,MACf,OAAOC,YAAW,KAAK,OAAO,EAAE,OAAO,KAAK;AAAA,IAC9C,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MACE,QAUA;AACA,WAAO,IAAI,eAAc;AAAA,MACvB,GAAG,KAAK,OAAO;AAAA,MACf,aAAa;AAAA,MACb,sBACE;AAAA,QACE;AAAA,QACA,KAAK,OAAO,EAAE,OAAO;AAAA,MACvB,IAAI,KAAK,OAAO,EAAE,YAAY;AAAA,IAClC,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OACE,QAUA;AACA,WAAO,IAAI,eAAc;AAAA,MACvB,GAAG,KAAK,OAAO;AAAA,MACf,cAAc;AAAA,MACd,uBACE;AAAA,QACE;AAAA,QACA,KAAK,OAAO,EAAE,OAAO;AAAA,MACvB,IAAI,KAAK,OAAO,EAAE,YAAY;AAAA,IAClC,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAqBA,OACE,UAUA;AACA,WAAO,IAAI,eAAc;AAAA,MACvB,GAAG,KAAK,OAAO;AAAA,MACf,YAAY;AAAA,QACV,MAAM;AAAA,QACN,mBAAmB,kBAAkB;AAAA,MACvC;AAAA,IACF,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,OACE,UAiBA;AACA,UAAM,EAAE,SAAS,WAAW,IAAI,KAAK,OAAO;AAE5C,UAAM,2BAA2B,kBAAkB;AACnD,WAAO,IAAI,yBAAyB;AAAA,MAClC,GAAG,KAAK,OAAO;AAAA,MACf,SAAS,OAAO,SAAS;AACvB,cAAM,aAKF;AAAA,UACF,SAAS,KAAK;AAAA,UACd,OAAO,KAAK;AAAA,UACZ,MAAM,KAAK;AAAA,UACX,WAAW,KAAK;AAAA,UAChB,QAAQ,KAAK;AAAA,UACb,aAAa,KAAK;AAAA,UAClB,QAAQ,gCAAgC,KAAK,OAAO,EAAE,cAAc;AAAA,QACtE;AACA,cAAM,WAAW,YAAY,QAAQ,KAAK,KAAK,KAAK,GAAG;AACvD,cAAM,oBACJ,YAAY,qBAAqB;AACnC,cAAM,WAAW,OAAO,WAAW,QAAQ;AAC3C,cAAM,eAAe,OAAO,SAAS,SAAS,UAAU,GAAG,UAAU;AAAA,UACnE;AAAA,QACF,CAAC;AACD,cAAM,OAAO,MAAM,QAAQ,eAAe,cAAc;AAAA,UACtD,QAAQ,KAAK;AAAA,QACf,CAAC;AAED,YAAI,KAAK,UAAU,IAAI,GAAG;AACxB,gBAAM,MAAM,MAAM,KAAK,OAAO;AAAA,YAC5B,MAAM,QAAQ;AACZ,qBAAO,IAAIC,WAAU,yBAAyB;AAAA,gBAC5C,OAAO;AAAA,cACT,CAAC;AAAA,YACH;AAAA,YACA,OAAO,OAAO;AACZ,kBAAI,kBAAkB,KAAK,GAAG;AAC5B,uBAAO,MAAM,YAAY;AAAA,cAC3B;AACA,kBAAI,iBAAiBA,YAAW;AAC9B,uBAAO;AAAA,cACT;AACA,qBAAO,IAAIA,WAAU,yBAAyB;AAAA,gBAC5C,OAAO;AAAA,cACT,CAAC;AAAA,YACH;AAAA,YACA,YAAY,SAAS;AACnB,qBAAO,IAAIA,WAAU,yBAAyB;AAAA,gBAC5C,OAAO,IAAI,MAAM,GAAG,OAAO,cAAc;AAAA,cAC3C,CAAC;AAAA,YACH;AAAA,YACA,aAAa,MAAM;AACjB,qBAAO;AAAA,YACT;AAAA,YACA,SAAS,IAAIA,WAAU,yBAAyB;AAAA,cAC9C,OAAO,IAAI,MAAM,eAAe;AAAA,YAClC,CAAC;AAAA,YACD,WAAW,MAAM;AACf,qBAAO;AAAA,YACT;AAAA,UACF,CAAC;AAAA,QACH;AAEA,eAAO,KAAK;AAAA,MACd;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAkGO,SAAS,eAQd,SACA,SAUA;AACA,QAAM,kBACJ,WAAW,aAA4D;AACzE,SAAO,IAAI,cAAc;AAAA,IACvB,GAAG,gBAAgB,OAAO;AAAA,IAC1B,gBAAgB,gBAAgB,OAAO,EAAE;AAAA,IACzC;AAAA,EACF,CAAC;AACH;AAEA,SAAS,eAKL;AACF,SAAO,IAAI,QAOT;AAAA,IACA,QAAQ,CAAC;AAAA,IACT,OAAO,CAAC;AAAA,IACR,MAAM,CAAC;AAAA,IACP,UAAU,CAAC;AAAA,IACX,sBAAsB,eAAe,6BAA6B;AAAA,IAClE,uBAAuB,eAAe,8BAA8B;AAAA,IACpE,aAAa,CAAC;AAAA,IACd,0BAA0B;AAAA,EAC5B,CAAC;AACH;","names":["mergeMeta","mergeRoute","ORPCError","addMiddleware","decorateMiddleware","decorateMiddleware","addMiddleware","mergeMeta","mergeRoute","ORPCError"]}
|
|
1
|
+
{"version":3,"sources":["../src/effect-builder.ts","../src/effect-procedure.ts","../src/tagged-error.ts"],"sourcesContent":["import type { ORPCErrorCode } from \"@orpc/client\";\nimport type {\n AnySchema,\n ErrorMap,\n InferSchemaOutput,\n Meta,\n Route,\n Schema,\n} from \"@orpc/contract\";\nimport type {\n AnyMiddleware,\n BuilderDef,\n Context,\n MapInputMiddleware,\n MergedCurrentContext,\n MergedInitialContext,\n Middleware,\n ORPCErrorConstructorMap,\n ProcedureHandlerOptions,\n} from \"@orpc/server\";\nimport type { IntersectPick } from \"@orpc/shared\";\nimport type { ManagedRuntime } from \"effect\";\nimport type { YieldWrap } from \"effect/Utils\";\n\nimport {\n mergeErrorMap,\n mergeMeta,\n mergeRoute,\n ORPCError,\n} from \"@orpc/contract\";\nimport {\n addMiddleware,\n Builder,\n decorateMiddleware,\n fallbackConfig,\n} from \"@orpc/server\";\nimport { Cause, Effect, Exit } from \"effect\";\n\nimport type {\n EffectErrorConstructorMap,\n EffectErrorMap,\n EffectErrorMapToUnion,\n MergedEffectErrorMap,\n} from \"./tagged-error\";\n\nimport { EffectDecoratedProcedure } from \"./effect-procedure\";\nimport {\n createEffectErrorConstructorMap,\n effectErrorMapToErrorMap,\n isORPCTaggedError,\n} from \"./tagged-error\";\n\n/**\n * Captures the stack trace at the call site for better error reporting in spans.\n * This is called at procedure definition time to capture where the procedure was defined.\n *\n * @returns A function that lazily extracts the relevant stack frame\n */\nexport function addSpanStackTrace(): () => string | undefined {\n const ErrorConstructor = Error as typeof Error & {\n stackTraceLimit?: number;\n };\n const limit = ErrorConstructor.stackTraceLimit;\n ErrorConstructor.stackTraceLimit = 3;\n const traceError = new Error();\n ErrorConstructor.stackTraceLimit = limit;\n let cache: false | string = false;\n return () => {\n if (cache !== false) {\n return cache;\n }\n if (traceError.stack !== undefined) {\n const stack = traceError.stack.split(\"\\n\");\n if (stack[3] !== undefined) {\n cache = stack[3].trim();\n return cache;\n }\n }\n };\n}\n\n/**\n * Configuration for Effect span tracing.\n */\nexport interface EffectSpanConfig {\n /**\n * The name of the span for telemetry.\n */\n name: string;\n /**\n * Function to lazily capture the stack trace at definition time.\n */\n captureStackTrace: () => string | undefined;\n}\n\n/**\n * Options passed to the Effect procedure handler.\n */\nexport interface EffectProcedureHandlerOptions<\n TCurrentContext extends Context,\n TInput,\n TEffectErrorMap extends EffectErrorMap,\n TMeta extends Meta,\n> extends Omit<\n ProcedureHandlerOptions<\n TCurrentContext,\n TInput,\n ORPCErrorConstructorMap<any>,\n TMeta\n >,\n \"errors\"\n> {\n errors: EffectErrorConstructorMap<TEffectErrorMap>;\n}\n\n/**\n * Handler type for Effect procedures.\n * The handler receives procedure options and returns an Effect.\n */\nexport type EffectProcedureHandler<\n TCurrentContext extends Context,\n TInput,\n THandlerOutput,\n TEffectErrorMap extends EffectErrorMap,\n TRequirementsProvided,\n TMeta extends Meta,\n> = (\n opt: EffectProcedureHandlerOptions<\n TCurrentContext,\n TInput,\n TEffectErrorMap,\n TMeta\n >,\n) => Generator<\n YieldWrap<\n Effect.Effect<\n any,\n | EffectErrorMapToUnion<TEffectErrorMap>\n | ORPCError<ORPCErrorCode, unknown>,\n TRequirementsProvided\n >\n >,\n THandlerOutput,\n never\n>;\n\n/**\n * Extended builder definition that includes the Effect ManagedRuntime.\n */\nexport interface EffectBuilderDef<\n TInputSchema extends AnySchema,\n TOutputSchema extends AnySchema,\n TEffectErrorMap extends EffectErrorMap,\n TMeta extends Meta,\n TRequirementsProvided,\n TRuntimeError,\n> extends BuilderDef<TInputSchema, TOutputSchema, ErrorMap, TMeta> {\n runtime: ManagedRuntime.ManagedRuntime<TRequirementsProvided, TRuntimeError>;\n /**\n * Optional span configuration for Effect tracing.\n */\n spanConfig?: EffectSpanConfig;\n /**\n * Effect-extended error map that supports both traditional errors and tagged errors.\n */\n effectErrorMap: TEffectErrorMap;\n}\n\n/**\n * Effect-native procedure builder that wraps an oRPC Builder instance\n * and adds Effect-specific capabilities while preserving Effect error\n * and requirements types.\n */\nexport class EffectBuilder<\n TInitialContext extends Context,\n TCurrentContext extends Context,\n TInputSchema extends AnySchema,\n TOutputSchema extends AnySchema,\n TEffectErrorMap extends EffectErrorMap,\n TMeta extends Meta,\n TRequirementsProvided,\n TRuntimeError,\n> {\n \"~orpc\": EffectBuilderDef<\n TInputSchema,\n TOutputSchema,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n >;\n\n constructor(\n def: EffectBuilderDef<\n TInputSchema,\n TOutputSchema,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n >,\n ) {\n this[\"~orpc\"] = def;\n }\n\n /**\n * Adds type-safe custom errors.\n * Supports both traditional oRPC error definitions and ORPCTaggedError classes.\n *\n * @example\n * ```ts\n * // Traditional format\n * builder.errors({ BAD_REQUEST: { status: 400, message: 'Bad request' } })\n *\n * // Tagged error class\n * builder.errors({ USER_NOT_FOUND: UserNotFoundError })\n *\n * // Mixed\n * builder.errors({\n * BAD_REQUEST: { status: 400 },\n * USER_NOT_FOUND: UserNotFoundError,\n * })\n * ```\n *\n * @see {@link https://orpc.dev/docs/error-handling#type%E2%80%90safe-error-handling Type-Safe Error Handling Docs}\n */\n errors<U extends EffectErrorMap>(\n errors: U,\n ): EffectBuilder<\n TInitialContext,\n TCurrentContext,\n TInputSchema,\n TOutputSchema,\n MergedEffectErrorMap<TEffectErrorMap, U>,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n > {\n return new EffectBuilder({\n ...this[\"~orpc\"],\n errorMap: mergeErrorMap(\n this[\"~orpc\"].errorMap,\n effectErrorMapToErrorMap(errors),\n ),\n effectErrorMap: {\n ...this[\"~orpc\"].effectErrorMap,\n ...errors,\n },\n });\n }\n\n /**\n * Uses a middleware to modify the context or improve the pipeline.\n *\n * @info Supports both normal middleware and inline middleware implementations.\n * @note The current context must be satisfy middleware dependent-context\n * @see {@link https://orpc.dev/docs/middleware Middleware Docs}\n */\n use<\n UOutContext extends IntersectPick<TCurrentContext, UOutContext>,\n UInContext extends Context = TCurrentContext,\n >(\n middleware: Middleware<\n UInContext | TCurrentContext,\n UOutContext,\n unknown,\n unknown,\n ORPCErrorConstructorMap<ErrorMap>,\n TMeta\n >,\n ): EffectBuilder<\n MergedInitialContext<TInitialContext, UInContext, TCurrentContext>,\n MergedCurrentContext<TCurrentContext, UOutContext>,\n TInputSchema,\n TOutputSchema,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n >;\n\n use(\n middleware: AnyMiddleware,\n mapInput?: MapInputMiddleware<any, any>,\n ): EffectBuilder<any, any, any, any, any, any, any, any> {\n const mapped = mapInput\n ? decorateMiddleware(middleware).mapInput(mapInput)\n : middleware;\n\n return new EffectBuilder({\n ...this[\"~orpc\"],\n middlewares: addMiddleware(this[\"~orpc\"].middlewares, mapped),\n });\n }\n\n /**\n * Sets or updates the metadata.\n * The provided metadata is spared-merged with any existing metadata.\n *\n * @see {@link https://orpc.dev/docs/metadata Metadata Docs}\n */\n meta(\n meta: TMeta,\n ): EffectBuilder<\n TInitialContext,\n TCurrentContext,\n TInputSchema,\n TOutputSchema,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n > {\n return new EffectBuilder({\n ...this[\"~orpc\"],\n meta: mergeMeta(this[\"~orpc\"].meta, meta),\n });\n }\n\n /**\n * Sets or updates the route definition.\n * The provided route is spared-merged with any existing route.\n * This option is typically relevant when integrating with OpenAPI.\n *\n * @see {@link https://orpc.dev/docs/openapi/routing OpenAPI Routing Docs}\n * @see {@link https://orpc.dev/docs/openapi/input-output-structure OpenAPI Input/Output Structure Docs}\n */\n route(\n route: Route,\n ): EffectBuilder<\n TInitialContext,\n TCurrentContext,\n TInputSchema,\n TOutputSchema,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n > {\n return new EffectBuilder({\n ...this[\"~orpc\"],\n route: mergeRoute(this[\"~orpc\"].route, route),\n });\n }\n\n /**\n * Defines the input validation schema.\n *\n * @see {@link https://orpc.dev/docs/procedure#input-output-validation Input Validation Docs}\n */\n input<USchema extends AnySchema>(\n schema: USchema,\n ): EffectBuilder<\n TInitialContext,\n TCurrentContext,\n USchema,\n TOutputSchema,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n > {\n return new EffectBuilder({\n ...this[\"~orpc\"],\n inputSchema: schema,\n inputValidationIndex:\n fallbackConfig(\n \"initialInputValidationIndex\",\n this[\"~orpc\"].config.initialInputValidationIndex,\n ) + this[\"~orpc\"].middlewares.length,\n });\n }\n\n /**\n * Defines the output validation schema.\n *\n * @see {@link https://orpc.dev/docs/procedure#input-output-validation Output Validation Docs}\n */\n output<USchema extends AnySchema>(\n schema: USchema,\n ): EffectBuilder<\n TInitialContext,\n TCurrentContext,\n TInputSchema,\n USchema,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n > {\n return new EffectBuilder({\n ...this[\"~orpc\"],\n outputSchema: schema,\n outputValidationIndex:\n fallbackConfig(\n \"initialOutputValidationIndex\",\n this[\"~orpc\"].config.initialOutputValidationIndex,\n ) + this[\"~orpc\"].middlewares.length,\n });\n }\n\n /**\n * Adds a traceable span to the procedure for telemetry.\n * The span name is used for Effect tracing via `Effect.withSpan`.\n * Stack trace is captured at the call site for better error reporting.\n *\n * @param spanName - The name of the span for telemetry (e.g., 'users.getUser')\n * @returns An EffectBuilder with span tracing configured\n *\n * @example\n * ```ts\n * const getUser = effectOs\n * .input(z.object({ id: z.string() }))\n * .traced('users.getUser')\n * .effect(function* ({ input }) {\n * const userService = yield* UserService\n * return yield* userService.findById(input.id)\n * })\n * ```\n */\n traced(\n spanName: string,\n ): EffectBuilder<\n TInitialContext,\n TCurrentContext,\n TInputSchema,\n TOutputSchema,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n > {\n return new EffectBuilder({\n ...this[\"~orpc\"],\n spanConfig: {\n name: spanName,\n captureStackTrace: addSpanStackTrace(),\n },\n });\n }\n\n /**\n * Defines the handler of the procedure using an Effect.\n * The Effect is executed using the ManagedRuntime provided during builder creation.\n * The effect is automatically wrapped with `Effect.withSpan`.\n *\n * @see {@link https://orpc.dev/docs/procedure Procedure Docs}\n */\n effect<UFuncOutput>(\n effectFn: EffectProcedureHandler<\n TCurrentContext,\n InferSchemaOutput<TInputSchema>,\n UFuncOutput,\n TEffectErrorMap,\n TRequirementsProvided,\n TMeta\n >,\n ): EffectDecoratedProcedure<\n TInitialContext,\n TCurrentContext,\n TInputSchema,\n Schema<UFuncOutput, UFuncOutput>,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n > {\n const { runtime, spanConfig } = this[\"~orpc\"];\n // Capture stack trace at definition time for default tracing\n const defaultCaptureStackTrace = addSpanStackTrace();\n return new EffectDecoratedProcedure({\n ...this[\"~orpc\"],\n handler: async (opts) => {\n const effectOpts: EffectProcedureHandlerOptions<\n TCurrentContext,\n InferSchemaOutput<TInputSchema>,\n TEffectErrorMap,\n TMeta\n > = {\n context: opts.context,\n input: opts.input,\n path: opts.path,\n procedure: opts.procedure,\n signal: opts.signal,\n lastEventId: opts.lastEventId,\n errors: createEffectErrorConstructorMap(this[\"~orpc\"].effectErrorMap),\n };\n const spanName = spanConfig?.name ?? opts.path.join(\".\");\n const captureStackTrace =\n spanConfig?.captureStackTrace ?? defaultCaptureStackTrace;\n const resolver = Effect.fnUntraced(effectFn);\n const tracedEffect = Effect.withSpan(resolver(effectOpts), spanName, {\n captureStackTrace,\n });\n const exit = await runtime.runPromiseExit(tracedEffect, {\n signal: opts.signal,\n });\n\n if (Exit.isFailure(exit)) {\n throw Cause.match(exit.cause, {\n onDie(defect) {\n return new ORPCError(\"INTERNAL_SERVER_ERROR\", {\n cause: defect,\n });\n },\n onFail(error) {\n if (isORPCTaggedError(error)) {\n return error.toORPCError();\n }\n if (error instanceof ORPCError) {\n return error;\n }\n return new ORPCError(\"INTERNAL_SERVER_ERROR\", {\n cause: error,\n });\n },\n onInterrupt(fiberId) {\n return new ORPCError(\"INTERNAL_SERVER_ERROR\", {\n cause: new Error(`${fiberId} Interrupted`),\n });\n },\n onSequential(left) {\n return left;\n },\n onEmpty: new ORPCError(\"INTERNAL_SERVER_ERROR\", {\n cause: new Error(\"Unknown error\"),\n }),\n onParallel(left) {\n return left;\n },\n });\n }\n\n return exit.value;\n },\n });\n }\n}\n\n/**\n * Any oRPC builder-like object that has the `~orpc` definition property.\n * This includes Builder, BuilderWithMiddlewares, ProcedureBuilder, etc.\n */\nexport interface AnyBuilderLike<\n TInputSchema extends AnySchema,\n TOutputSchema extends AnySchema,\n TErrorMap extends ErrorMap,\n TMeta extends Meta,\n> {\n \"~orpc\": BuilderDef<TInputSchema, TOutputSchema, TErrorMap, TMeta>;\n}\n\n/**\n * Creates an Effect-aware procedure builder with the specified ManagedRuntime.\n * Uses the default `os` builder from `@orpc/server`.\n *\n * @param runtime - The ManagedRuntime that provides services for Effect procedures\n * @returns An EffectBuilder instance for creating Effect-native procedures\n *\n * @example\n * ```ts\n * import { makeEffectORPC } from '@orpc/effect'\n * import { Effect, Layer, ManagedRuntime } from 'effect'\n *\n * const runtime = ManagedRuntime.make(Layer.empty)\n * const effectOs = makeEffectORPC(runtime)\n *\n * const hello = effectOs.effect(() => Effect.succeed('Hello!'))\n * ```\n */\nexport function makeEffectORPC<TRequirementsProvided, TRuntimeError>(\n runtime: ManagedRuntime.ManagedRuntime<TRequirementsProvided, TRuntimeError>,\n): EffectBuilder<\n Context,\n Context,\n Schema<unknown, unknown>,\n Schema<unknown, unknown>,\n Record<never, never>,\n Record<never, never>,\n TRequirementsProvided,\n TRuntimeError\n>;\n\n/**\n * Creates an Effect-aware procedure builder by wrapping an existing oRPC Builder\n * with the specified ManagedRuntime.\n *\n * @param runtime - The ManagedRuntime that provides services for Effect procedures\n * @param builder - The oRPC Builder instance to wrap (e.g., a customized `os`)\n * @returns An EffectBuilder instance that extends the original builder with Effect support\n *\n * @example\n * ```ts\n * import { makeEffectORPC } from '@orpc/effect'\n * import { os } from '@orpc/server'\n * import { Effect, Layer, ManagedRuntime } from 'effect'\n *\n * // Create a customized builder\n * const authedOs = os.use(authMiddleware)\n *\n * // Wrap it with Effect support\n * const runtime = ManagedRuntime.make(UserServiceLive)\n * const effectOs = makeEffectORPC(runtime, authedOs)\n *\n * const getUser = effectOs\n * .input(z.object({ id: z.string() }))\n * .effect(\n * Effect.fn(function* ({ input }) {\n * const userService = yield* UserService\n * return yield* userService.findById(input.id)\n * })\n * )\n * ```\n */\nexport function makeEffectORPC<\n TInputSchema extends AnySchema,\n TOutputSchema extends AnySchema,\n TErrorMap extends ErrorMap,\n TMeta extends Meta,\n TRequirementsProvided,\n TRuntimeError,\n>(\n runtime: ManagedRuntime.ManagedRuntime<TRequirementsProvided, TRuntimeError>,\n builder: AnyBuilderLike<TInputSchema, TOutputSchema, TErrorMap, TMeta>,\n): EffectBuilder<\n Context,\n Context,\n TInputSchema,\n TOutputSchema,\n TErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n>;\n\nexport function makeEffectORPC<\n TInputSchema extends AnySchema,\n TOutputSchema extends AnySchema,\n TErrorMap extends ErrorMap,\n TMeta extends Meta,\n TRequirementsProvided,\n TRuntimeError,\n>(\n runtime: ManagedRuntime.ManagedRuntime<TRequirementsProvided, TRuntimeError>,\n builder?: AnyBuilderLike<TInputSchema, TOutputSchema, TErrorMap, TMeta>,\n): EffectBuilder<\n Context,\n Context,\n TInputSchema,\n TOutputSchema,\n TErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n> {\n const resolvedBuilder =\n builder ?? emptyBuilder<TInputSchema, TOutputSchema, TErrorMap, TMeta>();\n return new EffectBuilder({\n ...resolvedBuilder[\"~orpc\"],\n effectErrorMap: resolvedBuilder[\"~orpc\"].errorMap,\n runtime,\n });\n}\n\nfunction emptyBuilder<\n TInputSchema extends AnySchema,\n TOutputSchema extends AnySchema,\n TErrorMap extends ErrorMap,\n TMeta extends Meta,\n>() {\n return new Builder<\n Record<never, never>,\n Record<never, never>,\n TInputSchema,\n TOutputSchema,\n TErrorMap,\n TMeta\n >({\n config: {},\n route: {},\n meta: {} as TMeta,\n errorMap: {} as TErrorMap,\n inputValidationIndex: fallbackConfig(\"initialInputValidationIndex\"),\n outputValidationIndex: fallbackConfig(\"initialOutputValidationIndex\"),\n middlewares: [],\n dedupeLeadingMiddlewares: true,\n });\n}\n","import type { ClientContext } from \"@orpc/client\";\nimport type {\n AnySchema,\n ErrorMap,\n InferSchemaInput,\n InferSchemaOutput,\n Meta,\n Route,\n} from \"@orpc/contract\";\nimport type {\n AnyMiddleware,\n Context,\n CreateProcedureClientOptions,\n MapInputMiddleware,\n MergedCurrentContext,\n MergedInitialContext,\n Middleware,\n ORPCErrorConstructorMap,\n ProcedureActionableClient,\n ProcedureClient,\n ProcedureDef,\n} from \"@orpc/server\";\nimport type { IntersectPick, MaybeOptionalOptions } from \"@orpc/shared\";\nimport type { ManagedRuntime } from \"effect\";\n\nimport { mergeMeta, mergeRoute } from \"@orpc/contract\";\nimport {\n addMiddleware,\n createActionableClient,\n createProcedureClient,\n decorateMiddleware,\n Procedure,\n} from \"@orpc/server\";\n\nimport type { EffectErrorMap, MergedEffectErrorMap } from \"./tagged-error\";\n\nimport { effectErrorMapToErrorMap } from \"./tagged-error\";\n\n/**\n * Extended procedure definition that includes the Effect ManagedRuntime.\n */\nexport interface EffectProcedureDef<\n TInitialContext extends Context,\n TCurrentContext extends Context,\n TInputSchema extends AnySchema,\n TOutputSchema extends AnySchema,\n TEffectErrorMap extends EffectErrorMap,\n TMeta extends Meta,\n TRequirementsProvided,\n TRuntimeError,\n> extends ProcedureDef<\n TInitialContext,\n TCurrentContext,\n TInputSchema,\n TOutputSchema,\n ErrorMap,\n TMeta\n> {\n runtime: ManagedRuntime.ManagedRuntime<TRequirementsProvided, TRuntimeError>;\n effectErrorMap: TEffectErrorMap;\n}\n\n/**\n * An Effect-native decorated procedure that preserves Effect error and requirements types.\n *\n * This class extends Procedure with additional type parameters for Effect-specific\n * type information, allowing full type inference of Effect errors and requirements.\n */\nexport class EffectDecoratedProcedure<\n TInitialContext extends Context,\n TCurrentContext extends Context,\n TInputSchema extends AnySchema,\n TOutputSchema extends AnySchema,\n TEffectErrorMap extends EffectErrorMap,\n TMeta extends Meta,\n TRequirementsProvided,\n TRuntimeError,\n> extends Procedure<\n TInitialContext,\n TCurrentContext,\n TInputSchema,\n TOutputSchema,\n ErrorMap,\n TMeta\n> {\n declare \"~orpc\": EffectProcedureDef<\n TInitialContext,\n TCurrentContext,\n TInputSchema,\n TOutputSchema,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n >;\n\n constructor(\n def: EffectProcedureDef<\n TInitialContext,\n TCurrentContext,\n TInputSchema,\n TOutputSchema,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n >,\n ) {\n super(def);\n }\n\n /**\n * Adds type-safe custom errors.\n * Supports both traditional oRPC error definitions and ORPCTaggedError classes.\n *\n * @see {@link https://orpc.dev/docs/error-handling#type%E2%80%90safe-error-handling Type-Safe Error Handling Docs}\n */\n errors<U extends EffectErrorMap>(\n errors: U,\n ): EffectDecoratedProcedure<\n TInitialContext,\n TCurrentContext,\n TInputSchema,\n TOutputSchema,\n MergedEffectErrorMap<TEffectErrorMap, U>,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n > {\n const newEffectErrorMap = { ...this[\"~orpc\"].effectErrorMap, ...errors };\n return new EffectDecoratedProcedure({\n ...this[\"~orpc\"],\n effectErrorMap: newEffectErrorMap,\n errorMap: effectErrorMapToErrorMap(newEffectErrorMap),\n });\n }\n\n /**\n * Sets or updates the metadata.\n * The provided metadata is spared-merged with any existing metadata.\n *\n * @see {@link https://orpc.dev/docs/metadata Metadata Docs}\n */\n meta(\n meta: TMeta,\n ): EffectDecoratedProcedure<\n TInitialContext,\n TCurrentContext,\n TInputSchema,\n TOutputSchema,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n > {\n return new EffectDecoratedProcedure({\n ...this[\"~orpc\"],\n meta: mergeMeta(this[\"~orpc\"].meta, meta),\n });\n }\n\n /**\n * Sets or updates the route definition.\n * The provided route is spared-merged with any existing route.\n * This option is typically relevant when integrating with OpenAPI.\n *\n * @see {@link https://orpc.dev/docs/openapi/routing OpenAPI Routing Docs}\n * @see {@link https://orpc.dev/docs/openapi/input-output-structure OpenAPI Input/Output Structure Docs}\n */\n route(\n route: Route,\n ): EffectDecoratedProcedure<\n TInitialContext,\n TCurrentContext,\n TInputSchema,\n TOutputSchema,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n > {\n return new EffectDecoratedProcedure({\n ...this[\"~orpc\"],\n route: mergeRoute(this[\"~orpc\"].route, route),\n });\n }\n\n /**\n * Uses a middleware to modify the context or improve the pipeline.\n *\n * @info Supports both normal middleware and inline middleware implementations.\n * @info Pass second argument to map the input.\n * @note The current context must be satisfy middleware dependent-context\n * @see {@link https://orpc.dev/docs/middleware Middleware Docs}\n */\n use<\n UOutContext extends IntersectPick<TCurrentContext, UOutContext>,\n UInContext extends Context = TCurrentContext,\n >(\n middleware: Middleware<\n UInContext | TCurrentContext,\n UOutContext,\n InferSchemaOutput<TInputSchema>,\n InferSchemaInput<TOutputSchema>,\n ORPCErrorConstructorMap<ErrorMap>,\n TMeta\n >,\n ): EffectDecoratedProcedure<\n MergedInitialContext<TInitialContext, UInContext, TCurrentContext>,\n MergedCurrentContext<TCurrentContext, UOutContext>,\n TInputSchema,\n TOutputSchema,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n >;\n\n /**\n * Uses a middleware to modify the context or improve the pipeline.\n *\n * @info Supports both normal middleware and inline middleware implementations.\n * @info Pass second argument to map the input.\n * @note The current context must be satisfy middleware dependent-context\n * @see {@link https://orpc.dev/docs/middleware Middleware Docs}\n */\n use<\n UOutContext extends IntersectPick<TCurrentContext, UOutContext>,\n UInput,\n UInContext extends Context = TCurrentContext,\n >(\n middleware: Middleware<\n UInContext | TCurrentContext,\n UOutContext,\n UInput,\n InferSchemaInput<TOutputSchema>,\n ORPCErrorConstructorMap<ErrorMap>,\n TMeta\n >,\n mapInput: MapInputMiddleware<InferSchemaOutput<TInputSchema>, UInput>,\n ): EffectDecoratedProcedure<\n MergedInitialContext<TInitialContext, UInContext, TCurrentContext>,\n MergedCurrentContext<TCurrentContext, UOutContext>,\n TInputSchema,\n TOutputSchema,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n >;\n\n use(\n middleware: AnyMiddleware,\n mapInput?: MapInputMiddleware<any, any>,\n ): EffectDecoratedProcedure<any, any, any, any, any, any, any, any> {\n const mapped = mapInput\n ? decorateMiddleware(middleware).mapInput(mapInput)\n : middleware;\n\n return new EffectDecoratedProcedure({\n ...this[\"~orpc\"],\n middlewares: addMiddleware(this[\"~orpc\"].middlewares, mapped),\n });\n }\n\n /**\n * Make this procedure callable (works like a function while still being a procedure).\n *\n * @see {@link https://orpc.dev/docs/client/server-side Server-side Client Docs}\n */\n callable<TClientContext extends ClientContext>(\n ...rest: MaybeOptionalOptions<\n CreateProcedureClientOptions<\n TInitialContext,\n TOutputSchema,\n ErrorMap,\n TMeta,\n TClientContext\n >\n >\n ): EffectDecoratedProcedure<\n TInitialContext,\n TCurrentContext,\n TInputSchema,\n TOutputSchema,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n > &\n ProcedureClient<TClientContext, TInputSchema, TOutputSchema, ErrorMap> {\n const client: ProcedureClient<\n TClientContext,\n TInputSchema,\n TOutputSchema,\n ErrorMap\n > = createProcedureClient(this, ...rest);\n\n return new Proxy(client, {\n get: (target, key) => {\n return Reflect.has(this, key)\n ? Reflect.get(this, key)\n : Reflect.get(target, key);\n },\n has: (target, key) => {\n return Reflect.has(this, key) || Reflect.has(target, key);\n },\n }) as any;\n }\n\n /**\n * Make this procedure compatible with server action.\n *\n * @see {@link https://orpc.dev/docs/server-action Server Action Docs}\n */\n actionable(\n ...rest: MaybeOptionalOptions<\n CreateProcedureClientOptions<\n TInitialContext,\n TOutputSchema,\n ErrorMap,\n TMeta,\n Record<never, never>\n >\n >\n ): EffectDecoratedProcedure<\n TInitialContext,\n TCurrentContext,\n TInputSchema,\n TOutputSchema,\n TEffectErrorMap,\n TMeta,\n TRequirementsProvided,\n TRuntimeError\n > &\n ProcedureActionableClient<TInputSchema, TOutputSchema, ErrorMap> {\n const action: ProcedureActionableClient<\n TInputSchema,\n TOutputSchema,\n ErrorMap\n > = createActionableClient(createProcedureClient(this, ...rest));\n\n return new Proxy(action, {\n get: (target, key) => {\n return Reflect.has(this, key)\n ? Reflect.get(this, key)\n : Reflect.get(target, key);\n },\n has: (target, key) => {\n return Reflect.has(this, key) || Reflect.has(target, key);\n },\n }) as any;\n }\n}\n","import type {\n ORPCErrorCode,\n ORPCErrorJSON,\n ORPCErrorOptions,\n} from \"@orpc/client\";\nimport type { AnySchema, ErrorMap, ErrorMapItem } from \"@orpc/contract\";\nimport type { ORPCErrorConstructorMapItemOptions } from \"@orpc/server\";\nimport type { MaybeOptionalOptions } from \"@orpc/shared\";\nimport type { Pipeable, Types } from \"effect\";\nimport type * as Cause from \"effect/Cause\";\nimport type * as Effect from \"effect/Effect\";\n\nimport {\n fallbackORPCErrorMessage,\n fallbackORPCErrorStatus,\n isORPCErrorStatus,\n ORPCError,\n} from \"@orpc/client\";\nimport { resolveMaybeOptionalOptions } from \"@orpc/shared\";\nimport * as Data from \"effect/Data\";\n\n/**\n * Symbol to access the underlying ORPCError instance\n */\nexport const ORPCErrorSymbol: unique symbol = Symbol.for(\n \"@orpc/effect/ORPCTaggedError\",\n);\n\n/**\n * Instance type for ORPCTaggedError that combines YieldableError with ORPCError properties\n */\nexport interface ORPCTaggedErrorInstance<\n TTag extends string,\n TCode extends ORPCErrorCode,\n TData,\n>\n extends Cause.YieldableError, Pipeable.Pipeable {\n readonly _tag: TTag;\n readonly code: TCode;\n readonly status: number;\n readonly data: TData;\n readonly defined: boolean;\n readonly [ORPCErrorSymbol]: ORPCError<TCode, TData>;\n\n toJSON(): ORPCErrorJSON<TCode, TData> & { _tag: TTag };\n toORPCError(): ORPCError<TCode, TData>;\n commit(): Effect.Effect<never, this, never>;\n}\n\n/**\n * Options for creating an ORPCTaggedError\n */\nexport type ORPCTaggedErrorOptions<TData> = Omit<\n ORPCErrorOptions<TData>,\n \"defined\"\n> & { defined?: boolean };\n\n/**\n * Constructor type for ORPCTaggedError classes\n */\nexport interface ORPCTaggedErrorClass<\n TTag extends string,\n TCode extends ORPCErrorCode,\n TData,\n> {\n readonly _tag: TTag;\n readonly code: TCode;\n new (\n ...args: MaybeOptionalOptions<ORPCTaggedErrorOptions<TData>>\n ): ORPCTaggedErrorInstance<TTag, TCode, TData>;\n}\n\n/**\n * Type helper to infer the ORPCError type from an ORPCTaggedError\n */\nexport type InferORPCError<T> =\n T extends ORPCTaggedErrorInstance<string, infer TCode, infer TData>\n ? ORPCError<TCode, TData>\n : never;\n\n/**\n * Any ORPCTaggedErrorClass\n * Uses `...args: any[]` for the constructor to accept any tagged error class,\n * regardless of whether TData requires options to be provided.\n */\nexport type AnyORPCTaggedErrorClass = {\n readonly _tag: string;\n readonly code: ORPCErrorCode;\n new (...args: any[]): ORPCTaggedErrorInstance<string, ORPCErrorCode, any>;\n};\n\n/**\n * Check if a value is an ORPCTaggedErrorClass (constructor)\n */\nexport function isORPCTaggedErrorClass(\n value: unknown,\n): value is AnyORPCTaggedErrorClass {\n return (\n typeof value === \"function\" &&\n \"_tag\" in value &&\n \"code\" in value &&\n typeof value._tag === \"string\" &&\n typeof value.code === \"string\"\n );\n}\n\n/**\n * Check if a value is an ORPCTaggedError instance\n */\nexport function isORPCTaggedError(\n value: unknown,\n): value is ORPCTaggedErrorInstance<string, ORPCErrorCode, unknown> {\n return (\n typeof value === \"object\" && value !== null && ORPCErrorSymbol in value\n );\n}\n\n/**\n * Converts a PascalCase or camelCase string to CONSTANT_CASE.\n * e.g., \"UserNotFoundError\" -> \"USER_NOT_FOUND_ERROR\"\n */\nfunction toConstantCase(str: string): string {\n return str\n .replace(/([a-z])([A-Z])/g, \"$1_$2\")\n .replace(/([A-Z])([A-Z][a-z])/g, \"$1_$2\")\n .toUpperCase();\n}\n\n// Type-level conversion: split on capital letters and join with underscore\ntype SplitOnCapital<\n S extends string,\n Acc extends string = \"\",\n> = S extends `${infer Head}${infer Tail}`\n ? Head extends Uppercase<Head>\n ? Head extends Lowercase<Head>\n ? SplitOnCapital<Tail, `${Acc}${Head}`>\n : Acc extends \"\"\n ? SplitOnCapital<Tail, Head>\n : `${Acc}_${SplitOnCapital<Tail, Head>}`\n : SplitOnCapital<Tail, `${Acc}${Uppercase<Head>}`>\n : Acc;\n\n/**\n * Converts a tag name to an error code in CONSTANT_CASE.\n */\nexport type TagToCode<TTag extends string> = SplitOnCapital<TTag>;\n\n/**\n * Creates a tagged error class that combines Effect's YieldableError with ORPCError.\n *\n * This allows you to create errors that:\n * - Can be yielded in Effect generators (`yield* myError`)\n * - Have all ORPCError properties (code, status, data, defined)\n * - Can be converted to a plain ORPCError for oRPC handlers\n *\n * The returned factory function takes:\n * - `tag` - The unique tag for this error type (used for discriminated unions)\n * - `codeOrOptions` - Optional ORPC error code or options. If omitted, code defaults to CONSTANT_CASE of tag\n * - `defaultOptions` - Optional default options for status and message (when code is provided)\n *\n * @example\n * ```ts\n * import { ORPCTaggedError } from '@orpc/effect'\n * import { Effect } from 'effect'\n *\n * // Define a custom error (code defaults to 'USER_NOT_FOUND_ERROR')\n * class UserNotFoundError extends ORPCTaggedError<UserNotFoundError>()('UserNotFoundError') {}\n *\n * // With explicit code\n * class NotFoundError extends ORPCTaggedError<NotFoundError>()('NotFoundError', 'NOT_FOUND') {}\n *\n * // Use in an Effect\n * const getUser = (id: string) => Effect.gen(function* () {\n * const user = yield* findUser(id)\n * if (!user) {\n * return yield* new UserNotFoundError({ data: { userId: id } })\n * }\n * return user\n * })\n *\n * // With custom data type\n * class ValidationError extends ORPCTaggedError<ValidationError, { fields: string[] }>()('ValidationError', 'BAD_REQUEST') {}\n *\n * // With options only (code defaults to 'VALIDATION_ERROR')\n * class ValidationError2 extends ORPCTaggedError<ValidationError2, { fields: string[] }>()(\n * 'ValidationError2',\n * { message: 'Validation failed' }\n * ) {}\n * ```\n */\n/**\n * Return type for the factory function with overloads\n */\ninterface ORPCTaggedErrorFactory<Self, TData> {\n // Overload 1: tag only (code defaults to CONSTANT_CASE of tag)\n <TTag extends string>(\n tag: TTag,\n ): Types.Equals<Self, unknown> extends true\n ? `Missing \\`Self\\` generic - use \\`class MyError extends ORPCTaggedError<MyError>()(tag) {}\\``\n : ORPCTaggedErrorClass<TTag, TagToCode<TTag>, TData>;\n\n // Overload 2: tag + options (code defaults to CONSTANT_CASE of tag)\n <TTag extends string>(\n tag: TTag,\n options: { status?: number; message?: string },\n ): Types.Equals<Self, unknown> extends true\n ? `Missing \\`Self\\` generic - use \\`class MyError extends ORPCTaggedError<MyError>()(tag, options) {}\\``\n : ORPCTaggedErrorClass<TTag, TagToCode<TTag>, TData>;\n\n // Overload 3: tag + explicit code\n <TTag extends string, TCode extends ORPCErrorCode>(\n tag: TTag,\n code: TCode,\n defaultOptions?: { status?: number; message?: string },\n ): Types.Equals<Self, unknown> extends true\n ? `Missing \\`Self\\` generic - use \\`class MyError extends ORPCTaggedError<MyError>()(tag, code) {}\\``\n : ORPCTaggedErrorClass<TTag, TCode, TData>;\n}\n\nexport function ORPCTaggedError<\n Self,\n TData = undefined,\n>(): ORPCTaggedErrorFactory<Self, TData> {\n const factory = <TTag extends string, TCode extends ORPCErrorCode>(\n tag: TTag,\n codeOrOptions?: TCode | { status?: number; message?: string },\n defaultOptions?: { status?: number; message?: string },\n ): ORPCTaggedErrorClass<TTag, TCode, TData> => {\n // Determine if second arg is code or options\n const isCodeProvided = typeof codeOrOptions === \"string\";\n const code = (\n isCodeProvided ? codeOrOptions : toConstantCase(tag)\n ) as TCode;\n const options = isCodeProvided ? defaultOptions : codeOrOptions;\n\n const defaultStatus = options?.status;\n const defaultMessage = options?.message;\n\n // Use Effect's TaggedError as the base - this handles all Effect internals\n // (YieldableError, type symbols, commit(), Symbol.iterator, pipe(), etc.)\n const BaseTaggedError = Data.TaggedError(tag) as unknown as new (args: {\n message?: string;\n cause?: unknown;\n code: TCode;\n status: number;\n data: TData;\n defined: boolean;\n }) => Cause.YieldableError & {\n readonly _tag: TTag;\n readonly code: TCode;\n readonly status: number;\n readonly data: TData;\n readonly defined: boolean;\n };\n\n class ORPCTaggedErrorBase extends BaseTaggedError {\n static readonly _tag = tag;\n static readonly code = code;\n\n readonly [ORPCErrorSymbol]: ORPCError<TCode, TData>;\n\n constructor(\n ...rest: MaybeOptionalOptions<ORPCTaggedErrorOptions<TData>>\n ) {\n const opts = resolveMaybeOptionalOptions(rest);\n const status = opts.status ?? defaultStatus;\n const inputMessage = opts.message ?? defaultMessage;\n\n if (status !== undefined && !isORPCErrorStatus(status)) {\n throw new globalThis.Error(\n \"[ORPCTaggedError] Invalid error status code.\",\n );\n }\n\n const finalStatus = fallbackORPCErrorStatus(code, status);\n const finalMessage = fallbackORPCErrorMessage(code, inputMessage);\n\n // Pass to Effect's TaggedError - it spreads these onto the instance\n super({\n message: finalMessage,\n cause: opts.cause,\n code,\n status: finalStatus,\n data: opts.data as TData,\n defined: opts.defined ?? true,\n });\n\n // Create the underlying ORPCError for interop\n this[ORPCErrorSymbol] = new ORPCError(code, {\n status: finalStatus,\n message: finalMessage,\n data: opts.data as TData,\n defined: this.defined,\n cause: opts.cause,\n });\n }\n\n /**\n * Converts this error to a plain ORPCError.\n * Useful when you need to return from an oRPC handler.\n */\n toORPCError(): ORPCError<TCode, TData> {\n return this[ORPCErrorSymbol];\n }\n\n override toJSON(): ORPCErrorJSON<TCode, TData> & { _tag: TTag } {\n return {\n _tag: this._tag,\n defined: this.defined,\n code: this.code,\n status: this.status,\n message: this.message,\n data: this.data,\n };\n }\n }\n\n return ORPCTaggedErrorBase as any;\n };\n\n return factory as ORPCTaggedErrorFactory<Self, TData>;\n}\n\n/**\n * Converts an ORPCTaggedError to a plain ORPCError.\n * Useful in handlers that need to throw ORPCError.\n *\n * @example\n * ```ts\n * const handler = effectOs.effect(function* () {\n * const result = yield* someOperation.pipe(\n * Effect.catchTag('UserNotFoundError', (e) =>\n * Effect.fail(toORPCError(e))\n * )\n * )\n * return result\n * })\n * ```\n */\nexport function toORPCError<TCode extends ORPCErrorCode, TData>(\n error: ORPCTaggedErrorInstance<string, TCode, TData>,\n): ORPCError<TCode, TData> {\n return error[ORPCErrorSymbol];\n}\n\n// ============================================================================\n// Extended Error Map Types for Effect\n// ============================================================================\n\n/**\n * An item in the EffectErrorMap - can be either a traditional ErrorMapItem or an ORPCTaggedErrorClass\n */\nexport type EffectErrorMapItem =\n | ErrorMapItem<AnySchema>\n | AnyORPCTaggedErrorClass;\n\n/**\n * Extended error map that supports both traditional oRPC errors and ORPCTaggedError classes.\n *\n * @example\n * ```ts\n * const errorMap = {\n * // Traditional format\n * BAD_REQUEST: { status: 400, message: 'Bad request' },\n *\n * // Tagged error class reference\n * USER_NOT_FOUND: UserNotFoundError,\n * } satisfies EffectErrorMap\n * ```\n */\nexport type EffectErrorMap = {\n [key in ORPCErrorCode]?: EffectErrorMapItem;\n};\n\n/**\n * Merges two EffectErrorMaps, with the second map taking precedence.\n */\nexport type MergedEffectErrorMap<\n T1 extends EffectErrorMap,\n T2 extends EffectErrorMap,\n> = T1 & T2;\n\n/**\n * Extracts the instance type from an EffectErrorMapItem\n */\nexport type EffectErrorMapItemToInstance<\n TCode extends ORPCErrorCode,\n T extends EffectErrorMapItem,\n> = T extends AnyORPCTaggedErrorClass\n ? InstanceType<T>\n : T extends { data?: infer TData }\n ? ORPCError<TCode, TData>\n : ORPCError<TCode, unknown>;\n\n/**\n * Converts an EffectErrorMap to a union of error instances.\n */\nexport type EffectErrorMapToUnion<T extends EffectErrorMap> = {\n [K in keyof T]: K extends ORPCErrorCode\n ? T[K] extends EffectErrorMapItem\n ? EffectErrorMapItemToInstance<K, T[K]>\n : never\n : never;\n}[keyof T];\n\n/**\n * Type for the error constructors available in Effect handlers.\n * For tagged errors, it's the class constructor itself.\n * For traditional errors, it's a function that creates ORPCError.\n */\nexport type EffectErrorConstructorMapItem<\n TCode extends ORPCErrorCode,\n T extends EffectErrorMapItem,\n> =\n T extends ORPCTaggedErrorClass<infer _TTag, TCode, infer TData>\n ? (\n ...args: MaybeOptionalOptions<ORPCTaggedErrorOptions<TData>>\n ) => ORPCTaggedErrorInstance<_TTag, TCode, TData>\n : T extends { data?: infer TData }\n ? (\n ...args: MaybeOptionalOptions<\n ORPCErrorConstructorMapItemOptions<TData>\n >\n ) => ORPCError<TCode, TData>\n : (\n ...args: MaybeOptionalOptions<\n ORPCErrorConstructorMapItemOptions<unknown>\n >\n ) => ORPCError<TCode, unknown>;\n\n/**\n * Constructor map for EffectErrorMap - provides typed error constructors for handlers.\n */\nexport type EffectErrorConstructorMap<T extends EffectErrorMap> = {\n [K in keyof T]: K extends ORPCErrorCode\n ? T[K] extends EffectErrorMapItem\n ? EffectErrorConstructorMapItem<K, T[K]>\n : never\n : never;\n};\n\n/**\n * Creates an error constructor map from an EffectErrorMap.\n * Tagged error classes are passed through directly.\n * Traditional error items become ORPCError factory functions.\n */\nexport function createEffectErrorConstructorMap<T extends EffectErrorMap>(\n errors: T | undefined,\n): EffectErrorConstructorMap<T> {\n const target = errors ?? ({} as T);\n const proxy = new Proxy(target, {\n get(proxyTarget, code) {\n if (typeof code !== \"string\") {\n return Reflect.get(proxyTarget, code);\n }\n\n const config = target[code];\n\n // If it's a tagged error class, create a class constructor function\n if (isORPCTaggedErrorClass(config)) {\n return (\n ...opts: MaybeOptionalOptions<ORPCTaggedErrorOptions<unknown>>\n ) => new config(...opts);\n }\n\n // Otherwise, create a factory function for ORPCError\n return (\n ...rest: MaybeOptionalOptions<\n Omit<ORPCErrorOptions<unknown>, \"defined\" | \"status\">\n >\n ) => {\n const options = resolveMaybeOptionalOptions(rest);\n return new ORPCError(code, {\n defined: Boolean(config),\n status: config?.status,\n message: options.message ?? config?.message,\n data: options.data,\n cause: options.cause,\n });\n };\n },\n });\n\n return proxy as EffectErrorConstructorMap<T>;\n}\n\n/**\n * Converts an EffectErrorMap to a standard oRPC ErrorMap for interop.\n * Tagged error classes are converted to their equivalent ErrorMapItem format.\n */\nexport function effectErrorMapToErrorMap<T extends EffectErrorMap>(\n errorMap: T | undefined,\n): ErrorMap {\n const result: ErrorMap = {};\n\n if (!errorMap) {\n return result;\n }\n\n for (const [code, ClassOrErrorItem] of Object.entries(errorMap)) {\n if (!ClassOrErrorItem) {\n continue;\n }\n\n if (isORPCTaggedErrorClass(ClassOrErrorItem)) {\n const error = new ClassOrErrorItem().toORPCError();\n\n // For tagged errors, we create a minimal entry\n // The actual validation will be handled by the tagged error class\n result[code] = {\n status: error.status,\n message: error.message,\n data: error.data,\n };\n } else {\n result[code] = ClassOrErrorItem;\n }\n }\n\n return result;\n}\n"],"mappings":"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","names":["mergeMeta","mergeRoute","ORPCError","addMiddleware","decorateMiddleware","decorateMiddleware","addMiddleware","mergeMeta","mergeRoute","ORPCError"]}
|
package/package.json
CHANGED
package/src/tagged-error.ts
CHANGED
|
@@ -80,12 +80,14 @@ export type InferORPCError<T> =
|
|
|
80
80
|
|
|
81
81
|
/**
|
|
82
82
|
* Any ORPCTaggedErrorClass
|
|
83
|
+
* Uses `...args: any[]` for the constructor to accept any tagged error class,
|
|
84
|
+
* regardless of whether TData requires options to be provided.
|
|
83
85
|
*/
|
|
84
|
-
export type AnyORPCTaggedErrorClass =
|
|
85
|
-
string
|
|
86
|
-
ORPCErrorCode
|
|
87
|
-
any
|
|
88
|
-
|
|
86
|
+
export type AnyORPCTaggedErrorClass = {
|
|
87
|
+
readonly _tag: string;
|
|
88
|
+
readonly code: ORPCErrorCode;
|
|
89
|
+
new (...args: any[]): ORPCTaggedErrorInstance<string, ORPCErrorCode, any>;
|
|
90
|
+
};
|
|
89
91
|
|
|
90
92
|
/**
|
|
91
93
|
* Check if a value is an ORPCTaggedErrorClass (constructor)
|
|
@@ -411,7 +413,9 @@ export type EffectErrorConstructorMapItem<
|
|
|
411
413
|
T extends EffectErrorMapItem,
|
|
412
414
|
> =
|
|
413
415
|
T extends ORPCTaggedErrorClass<infer _TTag, TCode, infer TData>
|
|
414
|
-
?
|
|
416
|
+
? (
|
|
417
|
+
...args: MaybeOptionalOptions<ORPCTaggedErrorOptions<TData>>
|
|
418
|
+
) => ORPCTaggedErrorInstance<_TTag, TCode, TData>
|
|
415
419
|
: T extends { data?: infer TData }
|
|
416
420
|
? (
|
|
417
421
|
...args: MaybeOptionalOptions<
|
|
@@ -452,9 +456,11 @@ export function createEffectErrorConstructorMap<T extends EffectErrorMap>(
|
|
|
452
456
|
|
|
453
457
|
const config = target[code];
|
|
454
458
|
|
|
455
|
-
// If it's a tagged error class,
|
|
459
|
+
// If it's a tagged error class, create a class constructor function
|
|
456
460
|
if (isORPCTaggedErrorClass(config)) {
|
|
457
|
-
return
|
|
461
|
+
return (
|
|
462
|
+
...opts: MaybeOptionalOptions<ORPCTaggedErrorOptions<unknown>>
|
|
463
|
+
) => new config(...opts);
|
|
458
464
|
}
|
|
459
465
|
|
|
460
466
|
// Otherwise, create a factory function for ORPCError
|
|
@@ -96,8 +96,11 @@ describe("createEffectErrorConstructorMap", () => {
|
|
|
96
96
|
|
|
97
97
|
const constructorMap = createEffectErrorConstructorMap(errorMap);
|
|
98
98
|
|
|
99
|
-
|
|
100
|
-
expect(
|
|
99
|
+
const userNotFoundError = constructorMap.USER_NOT_FOUND_ERROR();
|
|
100
|
+
expect(userNotFoundError).toBeInstanceOf(UserNotFoundError);
|
|
101
|
+
|
|
102
|
+
const forbiddenError = constructorMap.FORBIDDEN();
|
|
103
|
+
expect(forbiddenError).toBeInstanceOf(PermissionDenied);
|
|
101
104
|
});
|
|
102
105
|
|
|
103
106
|
it("should create ORPCError factory for traditional items", () => {
|
|
@@ -135,7 +138,7 @@ describe("createEffectErrorConstructorMap", () => {
|
|
|
135
138
|
expect(badRequestError.message).toBe("Invalid input");
|
|
136
139
|
|
|
137
140
|
// Tagged error class is passed through
|
|
138
|
-
const userNotFoundError =
|
|
141
|
+
const userNotFoundError = constructorMap.USER_NOT_FOUND_ERROR();
|
|
139
142
|
expect(isORPCTaggedError(userNotFoundError)).toBe(true);
|
|
140
143
|
expect(userNotFoundError._tag).toBe("UserNotFoundError");
|
|
141
144
|
});
|
|
@@ -253,7 +256,7 @@ describe("effectBuilder with EffectErrorMap", () => {
|
|
|
253
256
|
// oxlint-disable-next-line require-yield
|
|
254
257
|
.effect(function* ({ input, errors }) {
|
|
255
258
|
if (input.id === "not-found") {
|
|
256
|
-
return yield* Effect.fail(
|
|
259
|
+
return yield* Effect.fail(errors.USER_NOT_FOUND_ERROR());
|
|
257
260
|
}
|
|
258
261
|
return yield* Effect.succeed({ id: input.id, name: "Test User" });
|
|
259
262
|
});
|