ts-procedures 5.15.0 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (164) hide show
  1. package/README.md +2 -0
  2. package/agent_config/claude-code/agents/ts-procedures-architect.md +13 -6
  3. package/agent_config/claude-code/skills/ts-procedures/SKILL.md +26 -4
  4. package/agent_config/claude-code/skills/ts-procedures/anti-patterns.md +85 -17
  5. package/agent_config/claude-code/skills/ts-procedures/api-reference.md +220 -9
  6. package/agent_config/claude-code/skills/ts-procedures/patterns.md +271 -16
  7. package/agent_config/claude-code/skills/ts-procedures-review/SKILL.md +1 -1
  8. package/agent_config/claude-code/skills/ts-procedures-review/checklist.md +20 -12
  9. package/agent_config/claude-code/skills/ts-procedures-scaffold/SKILL.md +2 -1
  10. package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/client.md +53 -18
  11. package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/express-rpc.md +20 -17
  12. package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/hono-api.md +20 -16
  13. package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/hono-rpc.md +20 -17
  14. package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/hono-stream.md +16 -3
  15. package/agent_config/copilot/copilot-instructions.md +132 -19
  16. package/agent_config/cursor/cursorrules +132 -19
  17. package/build/client/call.d.ts +19 -9
  18. package/build/client/call.js +33 -19
  19. package/build/client/call.js.map +1 -1
  20. package/build/client/call.test.js +167 -17
  21. package/build/client/call.test.js.map +1 -1
  22. package/build/client/error-dispatch.d.ts +13 -0
  23. package/build/client/error-dispatch.js +26 -0
  24. package/build/client/error-dispatch.js.map +1 -0
  25. package/build/client/error-dispatch.test.d.ts +1 -0
  26. package/build/client/error-dispatch.test.js +56 -0
  27. package/build/client/error-dispatch.test.js.map +1 -0
  28. package/build/client/fetch-adapter.js +10 -4
  29. package/build/client/fetch-adapter.js.map +1 -1
  30. package/build/client/index.d.ts +2 -1
  31. package/build/client/index.js +22 -3
  32. package/build/client/index.js.map +1 -1
  33. package/build/client/index.test.js +104 -0
  34. package/build/client/index.test.js.map +1 -1
  35. package/build/client/resolve-options.d.ts +45 -0
  36. package/build/client/resolve-options.js +82 -0
  37. package/build/client/resolve-options.js.map +1 -0
  38. package/build/client/resolve-options.test.d.ts +1 -0
  39. package/build/client/resolve-options.test.js +158 -0
  40. package/build/client/resolve-options.test.js.map +1 -0
  41. package/build/client/stream.d.ts +19 -9
  42. package/build/client/stream.js +36 -21
  43. package/build/client/stream.js.map +1 -1
  44. package/build/client/stream.test.js +102 -46
  45. package/build/client/stream.test.js.map +1 -1
  46. package/build/client/typed-error-dispatch.test.d.ts +1 -0
  47. package/build/client/typed-error-dispatch.test.js +168 -0
  48. package/build/client/typed-error-dispatch.test.js.map +1 -0
  49. package/build/client/types.d.ts +105 -1
  50. package/build/client/types.js +1 -1
  51. package/build/codegen/e2e.test.js +150 -4
  52. package/build/codegen/e2e.test.js.map +1 -1
  53. package/build/codegen/emit-client-runtime.js +7 -0
  54. package/build/codegen/emit-client-runtime.js.map +1 -1
  55. package/build/codegen/emit-errors.d.ts +17 -6
  56. package/build/codegen/emit-errors.integration.test.d.ts +1 -0
  57. package/build/codegen/emit-errors.integration.test.js +162 -0
  58. package/build/codegen/emit-errors.integration.test.js.map +1 -0
  59. package/build/codegen/emit-errors.js +50 -39
  60. package/build/codegen/emit-errors.js.map +1 -1
  61. package/build/codegen/emit-errors.test.js +75 -78
  62. package/build/codegen/emit-errors.test.js.map +1 -1
  63. package/build/codegen/emit-index.d.ts +7 -0
  64. package/build/codegen/emit-index.js +26 -4
  65. package/build/codegen/emit-index.js.map +1 -1
  66. package/build/codegen/emit-index.test.js +55 -23
  67. package/build/codegen/emit-index.test.js.map +1 -1
  68. package/build/codegen/emit-scope.d.ts +8 -0
  69. package/build/codegen/emit-scope.js +82 -7
  70. package/build/codegen/emit-scope.js.map +1 -1
  71. package/build/codegen/pipeline.js +22 -2
  72. package/build/codegen/pipeline.js.map +1 -1
  73. package/build/implementations/http/doc-registry.d.ts +21 -0
  74. package/build/implementations/http/doc-registry.js +51 -78
  75. package/build/implementations/http/doc-registry.js.map +1 -1
  76. package/build/implementations/http/doc-registry.test.js +8 -6
  77. package/build/implementations/http/doc-registry.test.js.map +1 -1
  78. package/build/implementations/http/error-taxonomy.d.ts +240 -0
  79. package/build/implementations/http/error-taxonomy.js +230 -0
  80. package/build/implementations/http/error-taxonomy.js.map +1 -0
  81. package/build/implementations/http/error-taxonomy.test.d.ts +1 -0
  82. package/build/implementations/http/error-taxonomy.test.js +399 -0
  83. package/build/implementations/http/error-taxonomy.test.js.map +1 -0
  84. package/build/implementations/http/express-rpc/error-taxonomy.test.d.ts +1 -0
  85. package/build/implementations/http/express-rpc/error-taxonomy.test.js +83 -0
  86. package/build/implementations/http/express-rpc/error-taxonomy.test.js.map +1 -0
  87. package/build/implementations/http/express-rpc/index.d.ts +39 -8
  88. package/build/implementations/http/express-rpc/index.js +39 -8
  89. package/build/implementations/http/express-rpc/index.js.map +1 -1
  90. package/build/implementations/http/hono-api/error-taxonomy.test.d.ts +1 -0
  91. package/build/implementations/http/hono-api/error-taxonomy.test.js +137 -0
  92. package/build/implementations/http/hono-api/error-taxonomy.test.js.map +1 -0
  93. package/build/implementations/http/hono-api/index.d.ts +38 -1
  94. package/build/implementations/http/hono-api/index.js +32 -0
  95. package/build/implementations/http/hono-api/index.js.map +1 -1
  96. package/build/implementations/http/hono-rpc/error-taxonomy.test.d.ts +1 -0
  97. package/build/implementations/http/hono-rpc/error-taxonomy.test.js +64 -0
  98. package/build/implementations/http/hono-rpc/error-taxonomy.test.js.map +1 -0
  99. package/build/implementations/http/hono-rpc/index.d.ts +34 -7
  100. package/build/implementations/http/hono-rpc/index.js +31 -4
  101. package/build/implementations/http/hono-rpc/index.js.map +1 -1
  102. package/build/implementations/http/hono-stream/error-taxonomy.test.d.ts +1 -0
  103. package/build/implementations/http/hono-stream/error-taxonomy.test.js +87 -0
  104. package/build/implementations/http/hono-stream/error-taxonomy.test.js.map +1 -0
  105. package/build/implementations/http/hono-stream/index.d.ts +40 -3
  106. package/build/implementations/http/hono-stream/index.js +37 -10
  107. package/build/implementations/http/hono-stream/index.js.map +1 -1
  108. package/build/implementations/http/hono-stream/index.test.js +45 -18
  109. package/build/implementations/http/hono-stream/index.test.js.map +1 -1
  110. package/build/implementations/http/on-request-error.test.d.ts +1 -0
  111. package/build/implementations/http/on-request-error.test.js +173 -0
  112. package/build/implementations/http/on-request-error.test.js.map +1 -0
  113. package/build/implementations/http/route-errors.test.d.ts +1 -0
  114. package/build/implementations/http/route-errors.test.js +140 -0
  115. package/build/implementations/http/route-errors.test.js.map +1 -0
  116. package/build/implementations/types.d.ts +30 -2
  117. package/docs/client-and-codegen.md +228 -14
  118. package/docs/core.md +14 -5
  119. package/docs/http-integrations.md +135 -4
  120. package/docs/streaming.md +3 -1
  121. package/package.json +7 -2
  122. package/src/client/call.test.ts +202 -29
  123. package/src/client/call.ts +50 -28
  124. package/src/client/error-dispatch.test.ts +72 -0
  125. package/src/client/error-dispatch.ts +27 -0
  126. package/src/client/fetch-adapter.ts +11 -5
  127. package/src/client/index.test.ts +117 -0
  128. package/src/client/index.ts +34 -8
  129. package/src/client/resolve-options.test.ts +205 -0
  130. package/src/client/resolve-options.ts +113 -0
  131. package/src/client/stream.test.ts +132 -107
  132. package/src/client/stream.ts +53 -27
  133. package/src/client/typed-error-dispatch.test.ts +211 -0
  134. package/src/client/types.ts +116 -2
  135. package/src/codegen/e2e.test.ts +160 -4
  136. package/src/codegen/emit-client-runtime.ts +7 -0
  137. package/src/codegen/emit-errors.integration.test.ts +183 -0
  138. package/src/codegen/emit-errors.test.ts +91 -87
  139. package/src/codegen/emit-errors.ts +123 -41
  140. package/src/codegen/emit-index.test.ts +68 -24
  141. package/src/codegen/emit-index.ts +66 -4
  142. package/src/codegen/emit-scope.ts +124 -7
  143. package/src/codegen/pipeline.ts +25 -2
  144. package/src/implementations/http/README.md +28 -5
  145. package/src/implementations/http/doc-registry.test.ts +10 -6
  146. package/src/implementations/http/doc-registry.ts +63 -80
  147. package/src/implementations/http/error-taxonomy.test.ts +438 -0
  148. package/src/implementations/http/error-taxonomy.ts +337 -0
  149. package/src/implementations/http/express-rpc/README.md +21 -22
  150. package/src/implementations/http/express-rpc/error-taxonomy.test.ts +103 -0
  151. package/src/implementations/http/express-rpc/index.ts +75 -14
  152. package/src/implementations/http/hono-api/README.md +284 -0
  153. package/src/implementations/http/hono-api/error-taxonomy.test.ts +179 -0
  154. package/src/implementations/http/hono-api/index.ts +76 -1
  155. package/src/implementations/http/hono-rpc/README.md +18 -19
  156. package/src/implementations/http/hono-rpc/error-taxonomy.test.ts +82 -0
  157. package/src/implementations/http/hono-rpc/index.ts +65 -9
  158. package/src/implementations/http/hono-stream/README.md +44 -25
  159. package/src/implementations/http/hono-stream/error-taxonomy.test.ts +98 -0
  160. package/src/implementations/http/hono-stream/index.test.ts +54 -18
  161. package/src/implementations/http/hono-stream/index.ts +83 -13
  162. package/src/implementations/http/on-request-error.test.ts +201 -0
  163. package/src/implementations/http/route-errors.test.ts +177 -0
  164. package/src/implementations/types.ts +30 -2
@@ -3,8 +3,8 @@
3
3
  ## Implementation — `{{Name}}.rpc.ts`
4
4
 
5
5
  ```typescript
6
- import { Procedures, ProcedureError, ProcedureValidationError } from 'ts-procedures'
7
- import { HonoRPCAppBuilder } from 'ts-procedures/hono-rpc'
6
+ import { Procedures } from 'ts-procedures'
7
+ import { HonoRPCAppBuilder, defineErrorTaxonomy } from 'ts-procedures/hono-rpc'
8
8
  import type { RPCConfig } from 'ts-procedures/http'
9
9
  import { Type } from 'typebox'
10
10
 
@@ -60,25 +60,28 @@ export const { ListItems } = RPC.Create(
60
60
  }
61
61
  )
62
62
 
63
+ // ─── Error Taxonomy ───────────────────────────────────────
64
+ // Declare the error classes this service throws. Framework errors
65
+ // (ProcedureValidationError → 400, ctx.error() → 500) are caught by the
66
+ // default taxonomy automatically. Add your own classes here — handlers just
67
+ // `throw` them and the builder serializes via this map. See
68
+ // docs/http-integrations.md#error-handling for the full contract.
69
+
70
+ const {{name}}Errors = defineErrorTaxonomy({
71
+ // Example — replace with your app's error classes:
72
+ // NotFoundError: { class: NotFoundError, statusCode: 404 },
73
+ // AuthError: { class: AuthError, statusCode: 401 },
74
+ })
75
+
63
76
  // ─── Hono App Builder ─────────────────────────────────────
64
77
 
65
78
  export const {{name}}App = new HonoRPCAppBuilder({
66
79
  pathPrefix: '/api',
67
- onError: (procedure, c, error) => {
68
- if (error instanceof ProcedureValidationError) {
69
- return c.json({
70
- error: error.message,
71
- details: error.errors,
72
- procedure: error.procedureName,
73
- }, 400)
74
- } else if (error instanceof ProcedureError) {
75
- return c.json({
76
- error: error.message,
77
- meta: error.meta,
78
- procedure: error.procedureName,
79
- }, 422)
80
- }
81
- return c.json({ error: 'Internal server error' }, 500)
80
+ errors: {{name}}Errors,
81
+ unknownError: {
82
+ statusCode: 500,
83
+ toResponse: () => ({ name: 'InternalServerError', message: 'Unexpected error' }),
84
+ onCatch: (err, { procedure }) => console.error(`[${procedure.name}]`, err),
82
85
  },
83
86
  })
84
87
  .register(RPC, (c) => ({
@@ -4,7 +4,7 @@
4
4
 
5
5
  ```typescript
6
6
  import { Procedures } from 'ts-procedures'
7
- import { HonoStreamAppBuilder, sse } from 'ts-procedures/hono-stream'
7
+ import { HonoStreamAppBuilder, sse, defineErrorTaxonomy } from 'ts-procedures/hono-stream'
8
8
  import type { RPCConfig } from 'ts-procedures/http'
9
9
  import { Type } from 'typebox'
10
10
 
@@ -60,13 +60,26 @@ async function pollForEvents(channel: string, opts?: { signal?: AbortSignal }) {
60
60
  return { type: 'update', data: { value: Math.random() } }
61
61
  }
62
62
 
63
+ // ─── Error Taxonomy (pre-stream) ──────────────────────────
64
+ // Pre-stream errors (validation, context resolution) flow through the
65
+ // taxonomy — framework errors are caught by the default taxonomy automatically.
66
+ // Mid-stream errors (thrown after the first yield) still go through
67
+ // onMidStreamError since the HTTP status is already committed.
68
+
69
+ const {{name}}Errors = defineErrorTaxonomy({
70
+ // Example — replace with your app's error classes:
71
+ // AuthError: { class: AuthError, statusCode: 401 },
72
+ })
73
+
63
74
  // ─── Hono Stream App Builder ──────────────────────────────
64
75
 
65
76
  export const {{name}}StreamApp = new HonoStreamAppBuilder({
66
77
  pathPrefix: '/api',
67
78
  defaultStreamMode: 'sse', // or 'text' for newline-delimited JSON
68
- onPreStreamError: (procedure, c, error) => {
69
- return c.json({ error: error.message }, 400)
79
+ errors: {{name}}Errors,
80
+ unknownError: {
81
+ statusCode: 500,
82
+ toResponse: () => ({ name: 'InternalServerError', message: 'Unexpected error' }),
70
83
  },
71
84
  onMidStreamError: (procedure, c, error) => ({
72
85
  data: { type: 'error', payload: { message: error.message }, timestamp: Date.now() },
@@ -196,24 +196,89 @@ const app = new HonoAPIAppBuilder({ pathPrefix: '/api' })
196
196
 
197
197
  | Error Class | Trigger | HTTP Status |
198
198
  |-------------|---------|-------------|
199
- | `ProcedureValidationError` | Schema params validation failure | 400 |
200
- | `ProcedureError` | `ctx.error()` or unhandled handler exception | 422/500 |
199
+ | `ProcedureValidationError` | Schema params validation failure | 400 (auto — default taxonomy) |
200
+ | `ProcedureError` | `ctx.error()` or unhandled handler exception | 500 (auto — default taxonomy) |
201
201
  | `ProcedureYieldValidationError` | Yield validation failure (validateYields: true) | N/A (mid-stream) |
202
202
  | `ProcedureRegistrationError` | Invalid schema or duplicate name at registration | N/A (startup) |
203
203
 
204
+ ### Error Taxonomy (required for custom errors)
205
+
206
+ `defineErrorTaxonomy` maps error classes to HTTP responses declaratively. Works with every HTTP builder (`hono-api`, `hono-rpc`, `express-rpc`, `hono-stream` pre-stream).
207
+
208
+ Do NOT write `onError` `instanceof` ladders — that's anti-pattern #20. Use the taxonomy instead:
209
+
204
210
  ```typescript
205
- // In HTTP builder
206
- onError: (procedure, req, res, error) => {
207
- if (error instanceof ProcedureValidationError) {
208
- res.status(400).json({ error: error.message, details: error.errors })
209
- } else if (error instanceof ProcedureError) {
210
- res.status(422).json({ error: error.message, meta: error.meta })
211
- } else {
212
- res.status(500).json({ error: 'Internal server error' })
211
+ import { defineErrorTaxonomy } from 'ts-procedures/http-errors'
212
+
213
+ class UseCaseError extends Error {
214
+ constructor(readonly externalMsg: string, readonly internalMsg: string) {
215
+ super(externalMsg); this.name = 'UseCaseError'
216
+ Object.setPrototypeOf(this, UseCaseError.prototype)
213
217
  }
214
218
  }
219
+
220
+ const appErrors = defineErrorTaxonomy({
221
+ // First-match wins — declare subclasses before base classes
222
+ UseCaseError: {
223
+ class: UseCaseError,
224
+ statusCode: 422,
225
+ toResponse: (err) => ({ name: 'UseCaseError', message: err.externalMsg }),
226
+ onCatch: (err) => logger.error(err.internalMsg), // internal logs only
227
+ },
228
+ MongoDuplicateKey: {
229
+ match: (err): err is Error => err instanceof Error && (err as any).code === 11000,
230
+ statusCode: 409,
231
+ toResponse: () => ({ name: 'Conflict', message: 'Resource already exists' }),
232
+ },
233
+ })
234
+
235
+ new HonoAPIAppBuilder({
236
+ errors: appErrors,
237
+ unknownError: { statusCode: 500, toResponse: () => ({ name: 'InternalServerError' }) },
238
+ })
215
239
  ```
216
240
 
241
+ Handlers throw the error classes directly — the builder auto-serializes. `onError` is the first-class imperative peer when you want to handle errors in one callback instead (both modes coexist). In v6 `HonoStreamAppBuilder.onPreStreamError` was renamed to `onError`. Cross-cutting `onRequestError` observer fires for every caught error.
242
+
243
+ ### Per-route errors (typed)
244
+
245
+ `APIConfig` and `RPCConfig` are generic over `TErrorKey extends string = string`. Narrow to your taxonomy for compile-time typo protection and route-level error doc entries:
246
+
247
+ ```typescript
248
+ import { APIConfig } from 'ts-procedures/http'
249
+ import { DocRegistry } from 'ts-procedures/http-docs'
250
+
251
+ type MyAPIConfig = APIConfig<keyof typeof appErrors & string>
252
+ const API = Procedures<Ctx, MyAPIConfig>()
253
+
254
+ API.Create('GetUser', { path: '/users/:id', method: 'get', errors: ['UseCaseError'], /* ... */ }, ...)
255
+
256
+ // Seed envelope errors from the taxonomy + framework defaults in one call
257
+ const envelope = DocRegistry.fromTaxonomy(appErrors, { basePath: '/api' }).from(apiApp).toJSON()
258
+ ```
259
+
260
+ ### Client-side typed catch blocks (via codegen)
261
+
262
+ The generated `_errors.ts` emits real runtime classes extending a shared `${Service}ProcedureError` base. The generated `createApiClient` wires an error registry so non-2xx responses arrive as typed class instances:
263
+
264
+ ```typescript
265
+ import { createApiClient, ApiErrors, createFetchAdapter } from './generated'
266
+
267
+ const api = createApiClient({ adapter: createFetchAdapter({ /* ... */ }), basePath: '...' })
268
+
269
+ try {
270
+ await api.users.getUser({ pathParams: { id: 'x' } })
271
+ } catch (err) {
272
+ if (err instanceof ApiErrors.UseCaseError) {
273
+ // err.body typed; err.status, err.procedureName, err.scope available
274
+ } else if (err instanceof ApiErrors.ApiProcedureError) {
275
+ // Catch-all for any service error
276
+ }
277
+ }
278
+ ```
279
+
280
+ Per-route error unions: routes with `errors: [...]` get an `Errors` type in their namespace (e.g. `Users.GetUser.Errors = ApiErrors.UseCaseError | ApiErrors.AuthError`).
281
+
217
282
  ## Lifecycle Hook Order
218
283
 
219
284
  ### Standard RPC
@@ -225,7 +290,7 @@ onRequestStart → factoryContext() → handler() → onSuccess → onRequestEnd
225
290
  ### Streaming
226
291
  ```
227
292
  onRequestStart → factoryContext() → validation → onStreamStart → handler yields → onStreamEnd → onRequestEnd
228
- onPreStreamError → onRequestEnd
293
+ onError (or taxonomy) → onRequestEnd
229
294
  → onMidStreamError → onStreamEnd → onRequestEnd
230
295
  ```
231
296
 
@@ -320,7 +385,7 @@ npx ts-procedures-codegen --url http://localhost:3000/docs --out ./src/generated
320
385
  # --clean-out-dir # recursively wipe --out before writing (prunes stale scope files)
321
386
  ```
322
387
 
323
- Generates one `.ts` file per scope plus a root `index.ts` that imports each scope as a namespace and exports a `create${ServiceName}Bindings` factory (defaults to `createApiBindings`; pass `--service-name <Name>` to rename). When namespace mode is on (the default), `index.ts` also wraps every scope namespace in an outer `export namespace ${ServiceName} { ... }` block so types are reachable as `Api.Users.GetUser.Params`, `Api.Errors.ProcedureError`, etc. The errors file (`_errors.ts`) emits `${ServiceName}Errors` and `${ServiceName}ProcedureErrorUnion` (defaults: `ApiErrors`, `ApiProcedureErrorUnion`).
388
+ Generates one `.ts` file per scope plus a root `index.ts` that imports each scope as a namespace and exports a `create${ServiceName}Bindings(client)` factory AND a `create${ServiceName}Client(config)` convenience factory that pre-wires the error registry (defaults to `createApiBindings` / `createApiClient`; pass `--service-name <Name>` to rename). When namespace mode is on (the default), `index.ts` also wraps every scope namespace in an outer `export namespace ${ServiceName} { ... }` block so types are reachable as `Api.Users.GetUser.Params`, `Api.Errors.UseCaseError`, etc. The errors file (`_errors.ts`) emits runtime error classes extending a shared `${ServiceName}ProcedureError` base, each with `static fromResponse(body, meta)`, plus `${ServiceName}ErrorRegistry` (runtime dispatch map) and `${ServiceName}ProcedureErrorUnion` (type union). Defaults: `ApiErrors`, `ApiProcedureError`, `ApiErrorRegistry`, `ApiProcedureErrorUnion`.
324
389
  By default, types are wrapped in nested TS namespaces (`Scope.Route.Params`), JSDoc comments are emitted, and output is self-contained (no runtime dependency on `ts-procedures`). Use `--no-namespace-types` to revert to flat type names (`RouteParams`); this also disables the outer service namespace in `index.ts` and skips importing `_errors` from there.
325
390
  Note: ajsc formatting options (`--enum-style enum`, `--depluralize`, etc.) only take effect in namespace mode (the default). They are ignored with `--no-namespace-types`.
326
391
  Supports config file: `ts-procedures-codegen.config.json` (auto-loaded from CWD) or `--config <path>`.
@@ -336,6 +401,7 @@ const client = createClient({
336
401
  adapter: createFetchAdapter(),
337
402
  basePath: 'http://localhost:3000',
338
403
  scopes: createApiBindings,
404
+ defaults: { timeout: 30_000, headers: { 'X-Client-Version': '1.0.0' } },
339
405
  hooks: {
340
406
  onBeforeRequest(ctx) {
341
407
  ctx.request.headers = { ...ctx.request.headers, Authorization: `Bearer ${getToken()}` }
@@ -350,20 +416,67 @@ const client = createClient({
350
416
  // Fully typed — params and response inferred from server schemas; type aliases live under Api.<Scope>.<Route>.
351
417
  const user = await client.users.GetUser({ pathParams: { id: '123' } })
352
418
 
353
- // Per-call hook override
354
- await client.users.GetUser({ pathParams: { id: '123' } }, {
355
- onAfterResponse(ctx) {
356
- console.log(ctx.response.headers['x-rate-limit-remaining'])
419
+ // Per-call options — timeout, signal, headers, basePath, and hooks all share one bag
420
+ await client.users.GetUser(
421
+ { pathParams: { id: '123' } },
422
+ {
423
+ timeout: 5000,
424
+ headers: { 'X-Request-Id': crypto.randomUUID() },
425
+ onAfterResponse(ctx) {
426
+ console.log(ctx.response.headers['x-rate-limit-remaining'])
427
+ },
357
428
  },
358
- })
429
+ )
430
+ ```
431
+
432
+ ### Per-Call Options & Defaults
433
+
434
+ ```typescript
435
+ interface ProcedureCallDefaults {
436
+ signal?: AbortSignal // cancellation (combined with per-call via AbortSignal.any)
437
+ timeout?: number // ms — per-call timeout:0 disables inherited default
438
+ headers?: Record<string, string> // merged, per-call keys win
439
+ basePath?: string // per-call > default > config.basePath
440
+ meta?: RequestMeta // typed per-request metadata (declaration-mergeable)
441
+ }
442
+
443
+ interface ProcedureCallOptions extends ProcedureCallDefaults, ClientHooks {}
444
+ ```
445
+
446
+ - Defaults are set via `config.defaults`; per-call options are passed as the 2nd argument to any generated callable.
447
+ - Header precedence (low → high): adapter config < `defaults.headers` < per-call `headers` < route-declared `schema.input.headers` < `onBeforeRequest` mutations.
448
+
449
+ ### Typed RequestMeta (Declaration Merging)
450
+
451
+ Augment `RequestMeta` to type `meta` end-to-end (options, hooks, adapter):
452
+
453
+ ```typescript
454
+ // For code-generated self-contained clients
455
+ declare module './generated/_types' {
456
+ interface RequestMeta {
457
+ traceId: string
458
+ priority?: 'high' | 'low'
459
+ }
460
+ }
461
+
462
+ // Or for direct ts-procedures/client usage
463
+ declare module 'ts-procedures/client' {
464
+ interface RequestMeta { traceId: string }
465
+ }
466
+
467
+ await client.users.GetUser(
468
+ { pathParams: { id: '1' } },
469
+ { meta: { traceId: 'req-abc', priority: 'high' } }, // fully typed
470
+ )
359
471
  ```
360
472
 
361
473
  ### Hook Types
362
474
 
363
475
  ```typescript
364
476
  interface ClientHooks {
365
- onBeforeRequest?: (ctx: { request: ClientRequest }) => { request: ClientRequest } | void
366
- onAfterResponse?: (ctx: { request: ClientRequest; response: ClientResponse }) => void
477
+ onBeforeRequest?: (ctx: BeforeRequestContext) => BeforeRequestContext | Promise<BeforeRequestContext>
478
+ onAfterResponse?: (ctx: AfterResponseContext) => void | Promise<void>
479
+ onError?: (ctx: ErrorContext) => void | Promise<void>
367
480
  }
368
481
  ```
369
482
 
@@ -196,24 +196,89 @@ const app = new HonoAPIAppBuilder({ pathPrefix: '/api' })
196
196
 
197
197
  | Error Class | Trigger | HTTP Status |
198
198
  |-------------|---------|-------------|
199
- | `ProcedureValidationError` | Schema params validation failure | 400 |
200
- | `ProcedureError` | `ctx.error()` or unhandled handler exception | 422/500 |
199
+ | `ProcedureValidationError` | Schema params validation failure | 400 (auto — default taxonomy) |
200
+ | `ProcedureError` | `ctx.error()` or unhandled handler exception | 500 (auto — default taxonomy) |
201
201
  | `ProcedureYieldValidationError` | Yield validation failure (validateYields: true) | N/A (mid-stream) |
202
202
  | `ProcedureRegistrationError` | Invalid schema or duplicate name at registration | N/A (startup) |
203
203
 
204
+ ### Error Taxonomy (required for custom errors)
205
+
206
+ `defineErrorTaxonomy` maps error classes to HTTP responses declaratively. Works with every HTTP builder (`hono-api`, `hono-rpc`, `express-rpc`, `hono-stream` pre-stream).
207
+
208
+ Do NOT write `onError` `instanceof` ladders — that's anti-pattern #20. Use the taxonomy instead:
209
+
204
210
  ```typescript
205
- // In HTTP builder
206
- onError: (procedure, req, res, error) => {
207
- if (error instanceof ProcedureValidationError) {
208
- res.status(400).json({ error: error.message, details: error.errors })
209
- } else if (error instanceof ProcedureError) {
210
- res.status(422).json({ error: error.message, meta: error.meta })
211
- } else {
212
- res.status(500).json({ error: 'Internal server error' })
211
+ import { defineErrorTaxonomy } from 'ts-procedures/http-errors'
212
+
213
+ class UseCaseError extends Error {
214
+ constructor(readonly externalMsg: string, readonly internalMsg: string) {
215
+ super(externalMsg); this.name = 'UseCaseError'
216
+ Object.setPrototypeOf(this, UseCaseError.prototype)
213
217
  }
214
218
  }
219
+
220
+ const appErrors = defineErrorTaxonomy({
221
+ // First-match wins — declare subclasses before base classes
222
+ UseCaseError: {
223
+ class: UseCaseError,
224
+ statusCode: 422,
225
+ toResponse: (err) => ({ name: 'UseCaseError', message: err.externalMsg }),
226
+ onCatch: (err) => logger.error(err.internalMsg), // internal logs only
227
+ },
228
+ MongoDuplicateKey: {
229
+ match: (err): err is Error => err instanceof Error && (err as any).code === 11000,
230
+ statusCode: 409,
231
+ toResponse: () => ({ name: 'Conflict', message: 'Resource already exists' }),
232
+ },
233
+ })
234
+
235
+ new HonoAPIAppBuilder({
236
+ errors: appErrors,
237
+ unknownError: { statusCode: 500, toResponse: () => ({ name: 'InternalServerError' }) },
238
+ })
215
239
  ```
216
240
 
241
+ Handlers throw the error classes directly — the builder auto-serializes. `onError` is the first-class imperative peer when you want to handle errors in one callback instead (both modes coexist). In v6 `HonoStreamAppBuilder.onPreStreamError` was renamed to `onError`. Cross-cutting `onRequestError` observer fires for every caught error.
242
+
243
+ ### Per-route errors (typed)
244
+
245
+ `APIConfig` and `RPCConfig` are generic over `TErrorKey extends string = string`. Narrow to your taxonomy for compile-time typo protection and route-level error doc entries:
246
+
247
+ ```typescript
248
+ import { APIConfig } from 'ts-procedures/http'
249
+ import { DocRegistry } from 'ts-procedures/http-docs'
250
+
251
+ type MyAPIConfig = APIConfig<keyof typeof appErrors & string>
252
+ const API = Procedures<Ctx, MyAPIConfig>()
253
+
254
+ API.Create('GetUser', { path: '/users/:id', method: 'get', errors: ['UseCaseError'], /* ... */ }, ...)
255
+
256
+ // Seed envelope errors from the taxonomy + framework defaults in one call
257
+ const envelope = DocRegistry.fromTaxonomy(appErrors, { basePath: '/api' }).from(apiApp).toJSON()
258
+ ```
259
+
260
+ ### Client-side typed catch blocks (via codegen)
261
+
262
+ The generated `_errors.ts` emits real runtime classes extending a shared `${Service}ProcedureError` base. The generated `createApiClient` wires an error registry so non-2xx responses arrive as typed class instances:
263
+
264
+ ```typescript
265
+ import { createApiClient, ApiErrors, createFetchAdapter } from './generated'
266
+
267
+ const api = createApiClient({ adapter: createFetchAdapter({ /* ... */ }), basePath: '...' })
268
+
269
+ try {
270
+ await api.users.getUser({ pathParams: { id: 'x' } })
271
+ } catch (err) {
272
+ if (err instanceof ApiErrors.UseCaseError) {
273
+ // err.body typed; err.status, err.procedureName, err.scope available
274
+ } else if (err instanceof ApiErrors.ApiProcedureError) {
275
+ // Catch-all for any service error
276
+ }
277
+ }
278
+ ```
279
+
280
+ Per-route error unions: routes with `errors: [...]` get an `Errors` type in their namespace (e.g. `Users.GetUser.Errors = ApiErrors.UseCaseError | ApiErrors.AuthError`).
281
+
217
282
  ## Lifecycle Hook Order
218
283
 
219
284
  ### Standard RPC
@@ -225,7 +290,7 @@ onRequestStart → factoryContext() → handler() → onSuccess → onRequestEnd
225
290
  ### Streaming
226
291
  ```
227
292
  onRequestStart → factoryContext() → validation → onStreamStart → handler yields → onStreamEnd → onRequestEnd
228
- onPreStreamError → onRequestEnd
293
+ onError (or taxonomy) → onRequestEnd
229
294
  → onMidStreamError → onStreamEnd → onRequestEnd
230
295
  ```
231
296
 
@@ -320,7 +385,7 @@ npx ts-procedures-codegen --url http://localhost:3000/docs --out ./src/generated
320
385
  # --clean-out-dir # recursively wipe --out before writing (prunes stale scope files)
321
386
  ```
322
387
 
323
- Generates one `.ts` file per scope plus a root `index.ts` that imports each scope as a namespace and exports a `create${ServiceName}Bindings` factory (defaults to `createApiBindings`; pass `--service-name <Name>` to rename). When namespace mode is on (the default), `index.ts` also wraps every scope namespace in an outer `export namespace ${ServiceName} { ... }` block so types are reachable as `Api.Users.GetUser.Params`, `Api.Errors.ProcedureError`, etc. The errors file (`_errors.ts`) emits `${ServiceName}Errors` and `${ServiceName}ProcedureErrorUnion` (defaults: `ApiErrors`, `ApiProcedureErrorUnion`).
388
+ Generates one `.ts` file per scope plus a root `index.ts` that imports each scope as a namespace and exports a `create${ServiceName}Bindings(client)` factory AND a `create${ServiceName}Client(config)` convenience factory that pre-wires the error registry (defaults to `createApiBindings` / `createApiClient`; pass `--service-name <Name>` to rename). When namespace mode is on (the default), `index.ts` also wraps every scope namespace in an outer `export namespace ${ServiceName} { ... }` block so types are reachable as `Api.Users.GetUser.Params`, `Api.Errors.UseCaseError`, etc. The errors file (`_errors.ts`) emits runtime error classes extending a shared `${ServiceName}ProcedureError` base, each with `static fromResponse(body, meta)`, plus `${ServiceName}ErrorRegistry` (runtime dispatch map) and `${ServiceName}ProcedureErrorUnion` (type union). Defaults: `ApiErrors`, `ApiProcedureError`, `ApiErrorRegistry`, `ApiProcedureErrorUnion`.
324
389
  By default, types are wrapped in nested TS namespaces (`Scope.Route.Params`), JSDoc comments are emitted, and output is self-contained (no runtime dependency on `ts-procedures`). Use `--no-namespace-types` to revert to flat type names (`RouteParams`); this also disables the outer service namespace in `index.ts` and skips importing `_errors` from there.
325
390
  Note: ajsc formatting options (`--enum-style enum`, `--depluralize`, etc.) only take effect in namespace mode (the default). They are ignored with `--no-namespace-types`.
326
391
  Supports config file: `ts-procedures-codegen.config.json` (auto-loaded from CWD) or `--config <path>`.
@@ -336,6 +401,7 @@ const client = createClient({
336
401
  adapter: createFetchAdapter(),
337
402
  basePath: 'http://localhost:3000',
338
403
  scopes: createApiBindings,
404
+ defaults: { timeout: 30_000, headers: { 'X-Client-Version': '1.0.0' } },
339
405
  hooks: {
340
406
  onBeforeRequest(ctx) {
341
407
  ctx.request.headers = { ...ctx.request.headers, Authorization: `Bearer ${getToken()}` }
@@ -350,20 +416,67 @@ const client = createClient({
350
416
  // Fully typed — params and response inferred from server schemas; type aliases live under Api.<Scope>.<Route>.
351
417
  const user = await client.users.GetUser({ pathParams: { id: '123' } })
352
418
 
353
- // Per-call hook override
354
- await client.users.GetUser({ pathParams: { id: '123' } }, {
355
- onAfterResponse(ctx) {
356
- console.log(ctx.response.headers['x-rate-limit-remaining'])
419
+ // Per-call options — timeout, signal, headers, basePath, and hooks all share one bag
420
+ await client.users.GetUser(
421
+ { pathParams: { id: '123' } },
422
+ {
423
+ timeout: 5000,
424
+ headers: { 'X-Request-Id': crypto.randomUUID() },
425
+ onAfterResponse(ctx) {
426
+ console.log(ctx.response.headers['x-rate-limit-remaining'])
427
+ },
357
428
  },
358
- })
429
+ )
430
+ ```
431
+
432
+ ### Per-Call Options & Defaults
433
+
434
+ ```typescript
435
+ interface ProcedureCallDefaults {
436
+ signal?: AbortSignal // cancellation (combined with per-call via AbortSignal.any)
437
+ timeout?: number // ms — per-call timeout:0 disables inherited default
438
+ headers?: Record<string, string> // merged, per-call keys win
439
+ basePath?: string // per-call > default > config.basePath
440
+ meta?: RequestMeta // typed per-request metadata (declaration-mergeable)
441
+ }
442
+
443
+ interface ProcedureCallOptions extends ProcedureCallDefaults, ClientHooks {}
444
+ ```
445
+
446
+ - Defaults are set via `config.defaults`; per-call options are passed as the 2nd argument to any generated callable.
447
+ - Header precedence (low → high): adapter config < `defaults.headers` < per-call `headers` < route-declared `schema.input.headers` < `onBeforeRequest` mutations.
448
+
449
+ ### Typed RequestMeta (Declaration Merging)
450
+
451
+ Augment `RequestMeta` to type `meta` end-to-end (options, hooks, adapter):
452
+
453
+ ```typescript
454
+ // For code-generated self-contained clients
455
+ declare module './generated/_types' {
456
+ interface RequestMeta {
457
+ traceId: string
458
+ priority?: 'high' | 'low'
459
+ }
460
+ }
461
+
462
+ // Or for direct ts-procedures/client usage
463
+ declare module 'ts-procedures/client' {
464
+ interface RequestMeta { traceId: string }
465
+ }
466
+
467
+ await client.users.GetUser(
468
+ { pathParams: { id: '1' } },
469
+ { meta: { traceId: 'req-abc', priority: 'high' } }, // fully typed
470
+ )
359
471
  ```
360
472
 
361
473
  ### Hook Types
362
474
 
363
475
  ```typescript
364
476
  interface ClientHooks {
365
- onBeforeRequest?: (ctx: { request: ClientRequest }) => { request: ClientRequest } | void
366
- onAfterResponse?: (ctx: { request: ClientRequest; response: ClientResponse }) => void
477
+ onBeforeRequest?: (ctx: BeforeRequestContext) => BeforeRequestContext | Promise<BeforeRequestContext>
478
+ onAfterResponse?: (ctx: AfterResponseContext) => void | Promise<void>
479
+ onError?: (ctx: ErrorContext) => void | Promise<void>
367
480
  }
368
481
  ```
369
482
 
@@ -1,14 +1,24 @@
1
- import type { ClientAdapter, ClientHooks, CallDescriptor } from './types.js';
1
+ import type { ClientAdapter, ClientHooks, CallDescriptor, ErrorRegistry, ProcedureCallDefaults, ProcedureCallOptions } from './types.js';
2
+ export interface ExecuteCallConfig {
3
+ descriptor: CallDescriptor;
4
+ basePath: string;
5
+ adapter: ClientAdapter;
6
+ hooks: ClientHooks;
7
+ defaults?: ProcedureCallDefaults;
8
+ options?: ProcedureCallOptions;
9
+ errorRegistry?: ErrorRegistry;
10
+ }
2
11
  /**
3
12
  * Executes a single procedure call through the adapter.
4
13
  *
5
14
  * Flow:
6
- * 1. Build AdapterRequest from descriptor
7
- * 2. Run onBeforeRequest hooks (global then local)
8
- * 3. Call adapter.request()
9
- * 4. On adapter error: run onError hooks, re-throw
10
- * 5. Run onAfterResponse hooks (hooks may mutate response.status)
11
- * 6. If response status is non-2xx: throw ClientRequestError
12
- * 7. Return response.body as TResponse
15
+ * 1. Resolve base path (per-call > defaults > config) and build AdapterRequest
16
+ * 2. Apply request options (headers, signal, timeout, meta) from defaults + per-call
17
+ * 3. Run onBeforeRequest hooks (global then local) — may further mutate request
18
+ * 4. Call adapter.request()
19
+ * 5. On adapter error: run onError hooks, re-throw
20
+ * 6. Run onAfterResponse hooks (may mutate response.status to swallow errors)
21
+ * 7. If response status is non-2xx: throw ClientRequestError
22
+ * 8. Return response.body as TResponse
13
23
  */
14
- export declare function executeCall<TResponse>(descriptor: CallDescriptor, basePath: string, adapter: ClientAdapter, globalHooks: ClientHooks, localHooks: ClientHooks | undefined): Promise<TResponse>;
24
+ export declare function executeCall<TResponse>(config: ExecuteCallConfig): Promise<TResponse>;
@@ -1,38 +1,52 @@
1
1
  import { buildAdapterRequest } from './request-builder.js';
2
2
  import { runBeforeRequest, runAfterResponse, runOnError } from './hooks.js';
3
+ import { applyRequestOptions, resolveBasePath } from './resolve-options.js';
3
4
  import { ClientRequestError } from './errors.js';
5
+ import { dispatchTypedError } from './error-dispatch.js';
4
6
  /**
5
7
  * Executes a single procedure call through the adapter.
6
8
  *
7
9
  * Flow:
8
- * 1. Build AdapterRequest from descriptor
9
- * 2. Run onBeforeRequest hooks (global then local)
10
- * 3. Call adapter.request()
11
- * 4. On adapter error: run onError hooks, re-throw
12
- * 5. Run onAfterResponse hooks (hooks may mutate response.status)
13
- * 6. If response status is non-2xx: throw ClientRequestError
14
- * 7. Return response.body as TResponse
10
+ * 1. Resolve base path (per-call > defaults > config) and build AdapterRequest
11
+ * 2. Apply request options (headers, signal, timeout, meta) from defaults + per-call
12
+ * 3. Run onBeforeRequest hooks (global then local) — may further mutate request
13
+ * 4. Call adapter.request()
14
+ * 5. On adapter error: run onError hooks, re-throw
15
+ * 6. Run onAfterResponse hooks (may mutate response.status to swallow errors)
16
+ * 7. If response status is non-2xx: throw ClientRequestError
17
+ * 8. Return response.body as TResponse
15
18
  */
16
- export async function executeCall(descriptor, basePath, adapter, globalHooks, localHooks) {
17
- // 1. Build the initial request
18
- let request = buildAdapterRequest(descriptor, basePath);
19
- // 2. Run before-request hooks — they may mutate the request
20
- const beforeCtx = await runBeforeRequest({ procedureName: descriptor.name, scope: descriptor.scope, request }, globalHooks, localHooks);
19
+ export async function executeCall(config) {
20
+ const { descriptor, basePath, adapter, hooks, defaults, options, errorRegistry } = config;
21
+ // 1. Build the initial request (path/query/body from descriptor)
22
+ const resolvedBasePath = resolveBasePath(defaults, options, basePath);
23
+ let request = buildAdapterRequest(descriptor, resolvedBasePath);
24
+ // 2. Apply request-level options (headers, signal, timeout, meta)
25
+ request = applyRequestOptions(request, defaults, options);
26
+ // 3. Run before-request hooks — they may further mutate the request
27
+ const beforeCtx = await runBeforeRequest({ procedureName: descriptor.name, scope: descriptor.scope, request }, hooks, options);
21
28
  request = beforeCtx.request;
22
- // 3. Call the adapter
29
+ // 4. Call the adapter
23
30
  let response;
24
31
  try {
25
32
  response = await adapter.request(request);
26
33
  }
27
34
  catch (err) {
28
- // 4. On adapter error: run error hooks, re-throw
29
- await runOnError({ procedureName: descriptor.name, scope: descriptor.scope, request, error: err }, globalHooks, localHooks);
35
+ // 5. On adapter error: run error hooks, re-throw
36
+ await runOnError({ procedureName: descriptor.name, scope: descriptor.scope, request, error: err }, hooks, options);
30
37
  throw err;
31
38
  }
32
- // 5. Run after-response hooks — they may mutate response.status to swallow errors
33
- await runAfterResponse({ procedureName: descriptor.name, scope: descriptor.scope, request, response }, globalHooks, localHooks);
34
- // 6. Check status AFTER hooks (hooks may have swallowed the error by mutating status)
39
+ // 6. Run after-response hooks — they may mutate response.status to swallow errors
40
+ await runAfterResponse({ procedureName: descriptor.name, scope: descriptor.scope, request, response }, hooks, options);
41
+ // 7. Check status AFTER hooks (hooks may have swallowed the error by mutating status)
35
42
  if (response.status < 200 || response.status >= 300) {
43
+ const typed = dispatchTypedError(errorRegistry, response.body, {
44
+ status: response.status,
45
+ procedureName: descriptor.name,
46
+ scope: descriptor.scope,
47
+ });
48
+ if (typed)
49
+ throw typed;
36
50
  throw new ClientRequestError({
37
51
  status: response.status,
38
52
  headers: response.headers,
@@ -41,7 +55,7 @@ export async function executeCall(descriptor, basePath, adapter, globalHooks, lo
41
55
  scope: descriptor.scope,
42
56
  });
43
57
  }
44
- // 7. Return the body
58
+ // 8. Return the body
45
59
  return response.body;
46
60
  }
47
61
  //# sourceMappingURL=call.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"call.js","sourceRoot":"","sources":["../../src/client/call.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAOhD;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,UAA0B,EAC1B,QAAgB,EAChB,OAAsB,EACtB,WAAwB,EACxB,UAAmC;IAEnC,+BAA+B;IAC/B,IAAI,OAAO,GAAG,mBAAmB,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAA;IAEvD,4DAA4D;IAC5D,MAAM,SAAS,GAAG,MAAM,gBAAgB,CACtC,EAAE,aAAa,EAAE,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,EACpE,WAAW,EACX,UAAU,CACX,CAAA;IACD,OAAO,GAAG,SAAS,CAAC,OAAO,CAAA;IAE3B,sBAAsB;IACtB,IAAI,QAAQ,CAAA;IACZ,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;IAC3C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,iDAAiD;QACjD,MAAM,UAAU,CACd,EAAE,aAAa,EAAE,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,EAChF,WAAW,EACX,UAAU,CACX,CAAA;QACD,MAAM,GAAG,CAAA;IACX,CAAC;IAED,kFAAkF;IAClF,MAAM,gBAAgB,CACpB,EAAE,aAAa,EAAE,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,EAC9E,WAAW,EACX,UAAU,CACX,CAAA;IAED,sFAAsF;IACtF,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;QACpD,MAAM,IAAI,kBAAkB,CAAC;YAC3B,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,OAAO,EAAE,QAAQ,CAAC,OAAO;YACzB,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,aAAa,EAAE,UAAU,CAAC,IAAI;YAC9B,KAAK,EAAE,UAAU,CAAC,KAAK;SACxB,CAAC,CAAA;IACJ,CAAC;IAED,qBAAqB;IACrB,OAAO,QAAQ,CAAC,IAAiB,CAAA;AACnC,CAAC"}
1
+ {"version":3,"file":"call.js","sourceRoot":"","sources":["../../src/client/call.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAC3E,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAA;AAC3E,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAA;AAoBxD;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAY,MAAyB;IACpE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,MAAM,CAAA;IAEzF,iEAAiE;IACjE,MAAM,gBAAgB,GAAG,eAAe,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAA;IACrE,IAAI,OAAO,GAAG,mBAAmB,CAAC,UAAU,EAAE,gBAAgB,CAAC,CAAA;IAE/D,kEAAkE;IAClE,OAAO,GAAG,mBAAmB,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAA;IAEzD,oEAAoE;IACpE,MAAM,SAAS,GAAG,MAAM,gBAAgB,CACtC,EAAE,aAAa,EAAE,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,EACpE,KAAK,EACL,OAAO,CACR,CAAA;IACD,OAAO,GAAG,SAAS,CAAC,OAAO,CAAA;IAE3B,sBAAsB;IACtB,IAAI,QAAQ,CAAA;IACZ,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;IAC3C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,iDAAiD;QACjD,MAAM,UAAU,CACd,EAAE,aAAa,EAAE,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,EAChF,KAAK,EACL,OAAO,CACR,CAAA;QACD,MAAM,GAAG,CAAA;IACX,CAAC;IAED,kFAAkF;IAClF,MAAM,gBAAgB,CACpB,EAAE,aAAa,EAAE,UAAU,CAAC,IAAI,EAAE,KAAK,EAAE,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,EAC9E,KAAK,EACL,OAAO,CACR,CAAA;IAED,sFAAsF;IACtF,IAAI,QAAQ,CAAC,MAAM,GAAG,GAAG,IAAI,QAAQ,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;QACpD,MAAM,KAAK,GAAG,kBAAkB,CAAC,aAAa,EAAE,QAAQ,CAAC,IAAI,EAAE;YAC7D,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,aAAa,EAAE,UAAU,CAAC,IAAI;YAC9B,KAAK,EAAE,UAAU,CAAC,KAAK;SACxB,CAAC,CAAA;QACF,IAAI,KAAK;YAAE,MAAM,KAAK,CAAA;QACtB,MAAM,IAAI,kBAAkB,CAAC;YAC3B,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,OAAO,EAAE,QAAQ,CAAC,OAAO;YACzB,IAAI,EAAE,QAAQ,CAAC,IAAI;YACnB,aAAa,EAAE,UAAU,CAAC,IAAI;YAC9B,KAAK,EAAE,UAAU,CAAC,KAAK;SACxB,CAAC,CAAA;IACJ,CAAC;IAED,qBAAqB;IACrB,OAAO,QAAQ,CAAC,IAAiB,CAAA;AACnC,CAAC"}