ts-procedures 10.3.0 → 11.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 (83) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/agent_config/skills/ts-procedures/api-reference.md +4 -4
  3. package/build/client/call.d.ts +2 -1
  4. package/build/client/call.js +20 -9
  5. package/build/client/call.js.map +1 -1
  6. package/build/client/call.test.js +72 -9
  7. package/build/client/call.test.js.map +1 -1
  8. package/build/client/errors.d.ts +0 -4
  9. package/build/client/errors.js +0 -2
  10. package/build/client/errors.js.map +1 -1
  11. package/build/client/errors.test.js +1 -16
  12. package/build/client/errors.test.js.map +1 -1
  13. package/build/client/freeze.test.js +22 -10
  14. package/build/client/freeze.test.js.map +1 -1
  15. package/build/client/index.d.ts +1 -1
  16. package/build/client/index.js +1 -1
  17. package/build/client/index.js.map +1 -1
  18. package/build/client/index.test.js +6 -6
  19. package/build/client/index.test.js.map +1 -1
  20. package/build/client/resolve-options.d.ts +1 -14
  21. package/build/client/resolve-options.js +0 -15
  22. package/build/client/resolve-options.js.map +1 -1
  23. package/build/client/resolve-options.test.js +1 -63
  24. package/build/client/resolve-options.test.js.map +1 -1
  25. package/build/client/stream.test.js +3 -3
  26. package/build/client/stream.test.js.map +1 -1
  27. package/build/client/typed-error-dispatch.test.js +2 -2
  28. package/build/client/typed-error-dispatch.test.js.map +1 -1
  29. package/build/codegen/e2e.test.js +2 -2
  30. package/build/codegen/e2e.test.js.map +1 -1
  31. package/build/codegen/emit-client-runtime.test.js +3 -3
  32. package/build/codegen/emit-client-runtime.test.js.map +1 -1
  33. package/build/codegen/emit-index.js +17 -0
  34. package/build/codegen/emit-index.js.map +1 -1
  35. package/build/codegen/goldens.test.js +18 -0
  36. package/build/codegen/goldens.test.js.map +1 -1
  37. package/build/exports.d.ts +1 -1
  38. package/build/schema/json-schema.d.ts +0 -2
  39. package/docs/client-and-codegen.md +2 -2
  40. package/docs/client-error-handling.md +3 -3
  41. package/docs/core.md +1 -2
  42. package/docs/migration-v8-to-v9.md +2 -2
  43. package/package.json +1 -1
  44. package/src/client/call.test.ts +80 -8
  45. package/src/client/call.ts +23 -9
  46. package/src/client/errors.test.ts +0 -18
  47. package/src/client/errors.ts +0 -7
  48. package/src/client/freeze.test.ts +22 -10
  49. package/src/client/index.test.ts +6 -6
  50. package/src/client/index.ts +0 -1
  51. package/src/client/resolve-options.test.ts +0 -73
  52. package/src/client/resolve-options.ts +1 -20
  53. package/src/client/stream.test.ts +3 -3
  54. package/src/client/typed-error-dispatch.test.ts +2 -2
  55. package/src/codegen/__goldens__/ts-default--models/_client.ts +24 -36
  56. package/src/codegen/__goldens__/ts-default--models/_types.ts +0 -7
  57. package/src/codegen/__goldens__/ts-default--models/index.ts +10 -0
  58. package/src/codegen/__goldens__/ts-default--users/_client.ts +24 -36
  59. package/src/codegen/__goldens__/ts-default--users/_types.ts +0 -7
  60. package/src/codegen/__goldens__/ts-default--users/index.ts +10 -0
  61. package/src/codegen/__goldens__/ts-external-runtime--models/index.ts +10 -0
  62. package/src/codegen/__goldens__/ts-external-runtime--users/index.ts +10 -0
  63. package/src/codegen/__goldens__/ts-flat--models/_client.ts +24 -36
  64. package/src/codegen/__goldens__/ts-flat--models/_types.ts +0 -7
  65. package/src/codegen/__goldens__/ts-flat--models/index.ts +10 -0
  66. package/src/codegen/__goldens__/ts-flat--users/_client.ts +24 -36
  67. package/src/codegen/__goldens__/ts-flat--users/_types.ts +0 -7
  68. package/src/codegen/__goldens__/ts-flat--users/index.ts +10 -0
  69. package/src/codegen/__goldens__/ts-no-share-models--models/_client.ts +24 -36
  70. package/src/codegen/__goldens__/ts-no-share-models--models/_types.ts +0 -7
  71. package/src/codegen/__goldens__/ts-no-share-models--models/index.ts +10 -0
  72. package/src/codegen/__goldens__/ts-no-share-models--users/_client.ts +24 -36
  73. package/src/codegen/__goldens__/ts-no-share-models--users/_types.ts +0 -7
  74. package/src/codegen/__goldens__/ts-no-share-models--users/index.ts +10 -0
  75. package/src/codegen/__goldens__/ts-shared-models-module--models/_client.ts +24 -36
  76. package/src/codegen/__goldens__/ts-shared-models-module--models/_types.ts +0 -7
  77. package/src/codegen/__goldens__/ts-shared-models-module--models/index.ts +10 -0
  78. package/src/codegen/e2e.test.ts +2 -2
  79. package/src/codegen/emit-client-runtime.test.ts +3 -3
  80. package/src/codegen/emit-index.ts +19 -0
  81. package/src/codegen/goldens.test.ts +18 -0
  82. package/src/exports.ts +1 -1
  83. package/src/schema/json-schema.ts +0 -3
@@ -27,7 +27,7 @@ export function resolveBasePath(
27
27
  * - `timeoutMs`: the resolved timeout value (may be 0 if per-call explicitly disables)
28
28
  * - `combined`: `AbortSignal.any([...])` of all active signals, or undefined if none
29
29
  *
30
- * Per-call `timeout: 0` disables an inherited default timeout (same as `resolveSignal`).
30
+ * Per-call `timeout: 0` disables an inherited default timeout.
31
31
  * Both `defaults.signal` and `options.signal` are included in `combined` when present.
32
32
  */
33
33
  export interface SignalSources {
@@ -60,25 +60,6 @@ export function resolveSignalSources(
60
60
  return { combined, timeoutSignal, userSignal, timeoutMs }
61
61
  }
62
62
 
63
- /**
64
- * Resolves the effective AbortSignal by combining (via `AbortSignal.any`):
65
- * - default signal (if any)
66
- * - per-call signal (if any)
67
- * - timeout signal (if resolved timeout > 0)
68
- *
69
- * Returns undefined when none apply. Per-call `timeout: 0` disables an
70
- * inherited default timeout.
71
- *
72
- * @deprecated Prefer `resolveSignalSources` when you need access to individual
73
- * signal references (e.g. for abort-cause classification).
74
- */
75
- export function resolveSignal(
76
- defaults: ProcedureCallDefaults | undefined,
77
- options: ProcedureCallOptions | undefined,
78
- ): AbortSignal | undefined {
79
- return resolveSignalSources(defaults, options).combined
80
- }
81
-
82
63
  /**
83
64
  * Resolves a `ClientHeadersInit` value: a static record passes through, a
84
65
  * function is invoked and (if async) awaited — re-evaluated on every call so
@@ -1,6 +1,6 @@
1
1
  import { describe, it, expect, vi } from 'vitest'
2
2
  import { createTypedStream, executeStream } from './stream.js'
3
- import { ClientRequestError, ClientNetworkError, ClientStreamError } from './errors.js'
3
+ import { ClientHttpError, ClientNetworkError, ClientStreamError } from './errors.js'
4
4
  import type {
5
5
  ClientAdapter,
6
6
  AdapterRequest,
@@ -298,7 +298,7 @@ describe('executeStream', () => {
298
298
  expect(order).toEqual(['adapter', 'afterResponse'])
299
299
  })
300
300
 
301
- it('throws ClientRequestError on non-2xx status', async () => {
301
+ it('throws ClientHttpError on non-2xx status', async () => {
302
302
  const adapter: ClientAdapter = {
303
303
  request: vi.fn(async (): Promise<never> => { throw new Error('not expected') }),
304
304
  stream: vi.fn(async (): Promise<AdapterStreamResponse> => ({
@@ -307,7 +307,7 @@ describe('executeStream', () => {
307
307
  body: makeAsyncIterable([]),
308
308
  })),
309
309
  }
310
- await expect(run({ adapter })).rejects.toThrow(ClientRequestError)
310
+ await expect(run({ adapter })).rejects.toThrow(ClientHttpError)
311
311
  })
312
312
 
313
313
  it('runs onError on adapter failure and re-throws', async () => {
@@ -4,7 +4,7 @@ import { Type } from 'typebox'
4
4
  import { Procedures } from '../core/procedures.js'
5
5
  import { HonoAppBuilder, defineErrorTaxonomy } from '../adapters/hono/index.js'
6
6
  import { createClient } from './index.js'
7
- import { ClientRequestError } from './errors.js'
7
+ import { ClientHttpError } from './errors.js'
8
8
  import type { ClientAdapter, ErrorRegistry } from './types.js'
9
9
 
10
10
  // ---------------------------------------------------------------------------
@@ -113,7 +113,7 @@ describe('typed error dispatch — end-to-end', () => {
113
113
  test.todo('server-thrown UseCaseError arrives on client as a typed class instance')
114
114
 
115
115
  // migrated to schema.req in Phase 3+ (CreateHttp)
116
- test.todo('unregistered error body falls back to ClientRequestError')
116
+ test.todo('unregistered error body falls back to ClientHttpError')
117
117
 
118
118
  // migrated to schema.req in Phase 3+ (CreateHttp)
119
119
  test.todo('success responses are not disturbed by dispatch logic')
@@ -59,13 +59,6 @@ export class ClientHttpError extends Error {
59
59
  }
60
60
  }
61
61
 
62
- /** @deprecated Renamed to `ClientHttpError`. The alias is retained for one minor release after the 7.0.0 major bump and will be removed in a subsequent minor (e.g. 7.1.0). Migrate to `ClientHttpError` now. */
63
-
64
- export const ClientRequestError = ClientHttpError
65
- /** @deprecated Renamed to `ClientHttpError`. The alias is retained for one minor release after the 7.0.0 major bump and will be removed in a subsequent minor (e.g. 7.1.0). Migrate to `ClientHttpError` now. */
66
- // eslint-disable-next-line no-redeclare
67
- export type ClientRequestError = ClientHttpError
68
-
69
62
  export class ClientPathParamError extends Error {
70
63
  readonly name = 'ClientPathParamError'
71
64
 
@@ -305,7 +298,7 @@ export function resolveBasePath(
305
298
  * - `timeoutMs`: the resolved timeout value (may be 0 if per-call explicitly disables)
306
299
  * - `combined`: `AbortSignal.any([...])` of all active signals, or undefined if none
307
300
  *
308
- * Per-call `timeout: 0` disables an inherited default timeout (same as `resolveSignal`).
301
+ * Per-call `timeout: 0` disables an inherited default timeout.
309
302
  * Both `defaults.signal` and `options.signal` are included in `combined` when present.
310
303
  */
311
304
  export interface SignalSources {
@@ -338,25 +331,6 @@ export function resolveSignalSources(
338
331
  return { combined, timeoutSignal, userSignal, timeoutMs }
339
332
  }
340
333
 
341
- /**
342
- * Resolves the effective AbortSignal by combining (via `AbortSignal.any`):
343
- * - default signal (if any)
344
- * - per-call signal (if any)
345
- * - timeout signal (if resolved timeout > 0)
346
- *
347
- * Returns undefined when none apply. Per-call `timeout: 0` disables an
348
- * inherited default timeout.
349
- *
350
- * @deprecated Prefer `resolveSignalSources` when you need access to individual
351
- * signal references (e.g. for abort-cause classification).
352
- */
353
- export function resolveSignal(
354
- defaults: ProcedureCallDefaults | undefined,
355
- options: ProcedureCallOptions | undefined,
356
- ): AbortSignal | undefined {
357
- return resolveSignalSources(defaults, options).combined
358
- }
359
-
360
334
  /**
361
335
  * Resolves a `ClientHeadersInit` value: a static record passes through, a
362
336
  * function is invoked and (if async) awaited — re-evaluated on every call so
@@ -599,19 +573,32 @@ export async function executeCall<TResponse>(config: ExecuteCallConfig): Promise
599
573
  procedureName: descriptor.name,
600
574
  scope: descriptor.scope,
601
575
  })
576
+ let httpError: Error
602
577
  if (typed) {
603
578
  // Tag so executeSafeCall can distinguish typed registry errors from plain
604
579
  // ClientHttpError without re-inspecting the registry.
605
580
  ;(typed as unknown as { __tsProceduresTyped?: boolean }).__tsProceduresTyped = true
606
- throw typed
581
+ httpError = typed
582
+ } else {
583
+ httpError = new ClientHttpError({
584
+ status: response.status,
585
+ headers: response.headers,
586
+ body: response.body,
587
+ procedureName: descriptor.name,
588
+ scope: descriptor.scope,
589
+ })
607
590
  }
608
- throw new ClientHttpError({
609
- status: response.status,
610
- headers: response.headers,
611
- body: response.body,
612
- procedureName: descriptor.name,
613
- scope: descriptor.scope,
614
- })
591
+ // A non-2xx response is not a thrown adapter error (fetch resolves it), so
592
+ // it bypasses the catch block above. Fire onError here too so the hook is a
593
+ // uniform cross-cutting failure signal across adapter throws AND HTTP error
594
+ // statuses (401/403/5xx). Runs after onAfterResponse, which had its chance
595
+ // to swallow the error by mutating status back into the 2xx range.
596
+ await runOnError(
597
+ { procedureName: descriptor.name, scope: descriptor.scope, request, error: httpError },
598
+ hooks,
599
+ options,
600
+ )
601
+ throw httpError
615
602
  }
616
603
 
617
604
  // 8. Return the body (or { body, headers } when the route declares res.headers)
@@ -629,7 +616,8 @@ export async function executeCall<TResponse>(config: ExecuteCallConfig): Promise
629
616
  * 2. Adapter throw, classified → `kind: 'network' | 'timeout' | 'aborted' | <custom> | 'unknown'`
630
617
  * 3. Adapter returns non-2xx → `kind: 'typed'` (registry match) or `kind: 'http'`
631
618
  *
632
- * `onError` hook fires on path 2 and 3 (cross-cutting telemetry); NOT on
619
+ * `onError` hook fires on path 2 and 3 (cross-cutting telemetry) including
620
+ * plain non-2xx `ClientHttpError` and typed registry errors — but NOT on
633
621
  * path 1 (usage errors bypass the classifier and onError entirely).
634
622
  */
635
623
  export async function executeSafeCall<TResponse, ETyped = never>(
@@ -29,13 +29,6 @@ export class ClientHttpError extends Error {
29
29
  }
30
30
  }
31
31
 
32
- /** @deprecated Renamed to `ClientHttpError`. The alias is retained for one minor release after the 7.0.0 major bump and will be removed in a subsequent minor (e.g. 7.1.0). Migrate to `ClientHttpError` now. */
33
-
34
- export const ClientRequestError = ClientHttpError
35
- /** @deprecated Renamed to `ClientHttpError`. The alias is retained for one minor release after the 7.0.0 major bump and will be removed in a subsequent minor (e.g. 7.1.0). Migrate to `ClientHttpError` now. */
36
- // eslint-disable-next-line no-redeclare
37
- export type ClientRequestError = ClientHttpError
38
-
39
32
  export class ClientPathParamError extends Error {
40
33
  readonly name = 'ClientPathParamError'
41
34
 
@@ -7,6 +7,16 @@ import * as _billing from './billing'
7
7
  import * as _billingReports from './billing-reports'
8
8
  import * as _errorsModule from './_errors'
9
9
 
10
+ export {
11
+ ClientHttpError,
12
+ ClientPathParamError,
13
+ ClientStreamError,
14
+ ClientNetworkError,
15
+ ClientTimeoutError,
16
+ ClientAbortError,
17
+ ClientParseError,
18
+ } from './_client'
19
+
10
20
  export namespace Api {
11
21
  export import Orders = _orders.Orders
12
22
  export import Billing = _billing.Billing
@@ -59,13 +59,6 @@ export class ClientHttpError extends Error {
59
59
  }
60
60
  }
61
61
 
62
- /** @deprecated Renamed to `ClientHttpError`. The alias is retained for one minor release after the 7.0.0 major bump and will be removed in a subsequent minor (e.g. 7.1.0). Migrate to `ClientHttpError` now. */
63
-
64
- export const ClientRequestError = ClientHttpError
65
- /** @deprecated Renamed to `ClientHttpError`. The alias is retained for one minor release after the 7.0.0 major bump and will be removed in a subsequent minor (e.g. 7.1.0). Migrate to `ClientHttpError` now. */
66
- // eslint-disable-next-line no-redeclare
67
- export type ClientRequestError = ClientHttpError
68
-
69
62
  export class ClientPathParamError extends Error {
70
63
  readonly name = 'ClientPathParamError'
71
64
 
@@ -305,7 +298,7 @@ export function resolveBasePath(
305
298
  * - `timeoutMs`: the resolved timeout value (may be 0 if per-call explicitly disables)
306
299
  * - `combined`: `AbortSignal.any([...])` of all active signals, or undefined if none
307
300
  *
308
- * Per-call `timeout: 0` disables an inherited default timeout (same as `resolveSignal`).
301
+ * Per-call `timeout: 0` disables an inherited default timeout.
309
302
  * Both `defaults.signal` and `options.signal` are included in `combined` when present.
310
303
  */
311
304
  export interface SignalSources {
@@ -338,25 +331,6 @@ export function resolveSignalSources(
338
331
  return { combined, timeoutSignal, userSignal, timeoutMs }
339
332
  }
340
333
 
341
- /**
342
- * Resolves the effective AbortSignal by combining (via `AbortSignal.any`):
343
- * - default signal (if any)
344
- * - per-call signal (if any)
345
- * - timeout signal (if resolved timeout > 0)
346
- *
347
- * Returns undefined when none apply. Per-call `timeout: 0` disables an
348
- * inherited default timeout.
349
- *
350
- * @deprecated Prefer `resolveSignalSources` when you need access to individual
351
- * signal references (e.g. for abort-cause classification).
352
- */
353
- export function resolveSignal(
354
- defaults: ProcedureCallDefaults | undefined,
355
- options: ProcedureCallOptions | undefined,
356
- ): AbortSignal | undefined {
357
- return resolveSignalSources(defaults, options).combined
358
- }
359
-
360
334
  /**
361
335
  * Resolves a `ClientHeadersInit` value: a static record passes through, a
362
336
  * function is invoked and (if async) awaited — re-evaluated on every call so
@@ -599,19 +573,32 @@ export async function executeCall<TResponse>(config: ExecuteCallConfig): Promise
599
573
  procedureName: descriptor.name,
600
574
  scope: descriptor.scope,
601
575
  })
576
+ let httpError: Error
602
577
  if (typed) {
603
578
  // Tag so executeSafeCall can distinguish typed registry errors from plain
604
579
  // ClientHttpError without re-inspecting the registry.
605
580
  ;(typed as unknown as { __tsProceduresTyped?: boolean }).__tsProceduresTyped = true
606
- throw typed
581
+ httpError = typed
582
+ } else {
583
+ httpError = new ClientHttpError({
584
+ status: response.status,
585
+ headers: response.headers,
586
+ body: response.body,
587
+ procedureName: descriptor.name,
588
+ scope: descriptor.scope,
589
+ })
607
590
  }
608
- throw new ClientHttpError({
609
- status: response.status,
610
- headers: response.headers,
611
- body: response.body,
612
- procedureName: descriptor.name,
613
- scope: descriptor.scope,
614
- })
591
+ // A non-2xx response is not a thrown adapter error (fetch resolves it), so
592
+ // it bypasses the catch block above. Fire onError here too so the hook is a
593
+ // uniform cross-cutting failure signal across adapter throws AND HTTP error
594
+ // statuses (401/403/5xx). Runs after onAfterResponse, which had its chance
595
+ // to swallow the error by mutating status back into the 2xx range.
596
+ await runOnError(
597
+ { procedureName: descriptor.name, scope: descriptor.scope, request, error: httpError },
598
+ hooks,
599
+ options,
600
+ )
601
+ throw httpError
615
602
  }
616
603
 
617
604
  // 8. Return the body (or { body, headers } when the route declares res.headers)
@@ -629,7 +616,8 @@ export async function executeCall<TResponse>(config: ExecuteCallConfig): Promise
629
616
  * 2. Adapter throw, classified → `kind: 'network' | 'timeout' | 'aborted' | <custom> | 'unknown'`
630
617
  * 3. Adapter returns non-2xx → `kind: 'typed'` (registry match) or `kind: 'http'`
631
618
  *
632
- * `onError` hook fires on path 2 and 3 (cross-cutting telemetry); NOT on
619
+ * `onError` hook fires on path 2 and 3 (cross-cutting telemetry) including
620
+ * plain non-2xx `ClientHttpError` and typed registry errors — but NOT on
633
621
  * path 1 (usage errors bypass the classifier and onError entirely).
634
622
  */
635
623
  export async function executeSafeCall<TResponse, ETyped = never>(
@@ -29,13 +29,6 @@ export class ClientHttpError extends Error {
29
29
  }
30
30
  }
31
31
 
32
- /** @deprecated Renamed to `ClientHttpError`. The alias is retained for one minor release after the 7.0.0 major bump and will be removed in a subsequent minor (e.g. 7.1.0). Migrate to `ClientHttpError` now. */
33
-
34
- export const ClientRequestError = ClientHttpError
35
- /** @deprecated Renamed to `ClientHttpError`. The alias is retained for one minor release after the 7.0.0 major bump and will be removed in a subsequent minor (e.g. 7.1.0). Migrate to `ClientHttpError` now. */
36
- // eslint-disable-next-line no-redeclare
37
- export type ClientRequestError = ClientHttpError
38
-
39
32
  export class ClientPathParamError extends Error {
40
33
  readonly name = 'ClientPathParamError'
41
34
 
@@ -5,6 +5,16 @@ import type { ClientInstance, CreateClientConfig } from './_types'
5
5
  import * as _users from './users'
6
6
  import * as _errorsModule from './_errors'
7
7
 
8
+ export {
9
+ ClientHttpError,
10
+ ClientPathParamError,
11
+ ClientStreamError,
12
+ ClientNetworkError,
13
+ ClientTimeoutError,
14
+ ClientAbortError,
15
+ ClientParseError,
16
+ } from './_client'
17
+
8
18
  export namespace Api {
9
19
  export import Users = _users.Users
10
20
  export import Errors = _errorsModule.ApiErrors
@@ -7,6 +7,16 @@ import * as _billing from './billing'
7
7
  import * as _billingReports from './billing-reports'
8
8
  import * as _errorsModule from './_errors'
9
9
 
10
+ export {
11
+ ClientHttpError,
12
+ ClientPathParamError,
13
+ ClientStreamError,
14
+ ClientNetworkError,
15
+ ClientTimeoutError,
16
+ ClientAbortError,
17
+ ClientParseError,
18
+ } from 'ts-procedures/client'
19
+
10
20
  export namespace Api {
11
21
  export import Orders = _orders.Orders
12
22
  export import Billing = _billing.Billing
@@ -5,6 +5,16 @@ import type { ClientInstance, CreateClientConfig } from 'ts-procedures/client'
5
5
  import * as _users from './users'
6
6
  import * as _errorsModule from './_errors'
7
7
 
8
+ export {
9
+ ClientHttpError,
10
+ ClientPathParamError,
11
+ ClientStreamError,
12
+ ClientNetworkError,
13
+ ClientTimeoutError,
14
+ ClientAbortError,
15
+ ClientParseError,
16
+ } from 'ts-procedures/client'
17
+
8
18
  export namespace Api {
9
19
  export import Users = _users.Users
10
20
  export import Errors = _errorsModule.ApiErrors
@@ -59,13 +59,6 @@ export class ClientHttpError extends Error {
59
59
  }
60
60
  }
61
61
 
62
- /** @deprecated Renamed to `ClientHttpError`. The alias is retained for one minor release after the 7.0.0 major bump and will be removed in a subsequent minor (e.g. 7.1.0). Migrate to `ClientHttpError` now. */
63
-
64
- export const ClientRequestError = ClientHttpError
65
- /** @deprecated Renamed to `ClientHttpError`. The alias is retained for one minor release after the 7.0.0 major bump and will be removed in a subsequent minor (e.g. 7.1.0). Migrate to `ClientHttpError` now. */
66
- // eslint-disable-next-line no-redeclare
67
- export type ClientRequestError = ClientHttpError
68
-
69
62
  export class ClientPathParamError extends Error {
70
63
  readonly name = 'ClientPathParamError'
71
64
 
@@ -305,7 +298,7 @@ export function resolveBasePath(
305
298
  * - `timeoutMs`: the resolved timeout value (may be 0 if per-call explicitly disables)
306
299
  * - `combined`: `AbortSignal.any([...])` of all active signals, or undefined if none
307
300
  *
308
- * Per-call `timeout: 0` disables an inherited default timeout (same as `resolveSignal`).
301
+ * Per-call `timeout: 0` disables an inherited default timeout.
309
302
  * Both `defaults.signal` and `options.signal` are included in `combined` when present.
310
303
  */
311
304
  export interface SignalSources {
@@ -338,25 +331,6 @@ export function resolveSignalSources(
338
331
  return { combined, timeoutSignal, userSignal, timeoutMs }
339
332
  }
340
333
 
341
- /**
342
- * Resolves the effective AbortSignal by combining (via `AbortSignal.any`):
343
- * - default signal (if any)
344
- * - per-call signal (if any)
345
- * - timeout signal (if resolved timeout > 0)
346
- *
347
- * Returns undefined when none apply. Per-call `timeout: 0` disables an
348
- * inherited default timeout.
349
- *
350
- * @deprecated Prefer `resolveSignalSources` when you need access to individual
351
- * signal references (e.g. for abort-cause classification).
352
- */
353
- export function resolveSignal(
354
- defaults: ProcedureCallDefaults | undefined,
355
- options: ProcedureCallOptions | undefined,
356
- ): AbortSignal | undefined {
357
- return resolveSignalSources(defaults, options).combined
358
- }
359
-
360
334
  /**
361
335
  * Resolves a `ClientHeadersInit` value: a static record passes through, a
362
336
  * function is invoked and (if async) awaited — re-evaluated on every call so
@@ -599,19 +573,32 @@ export async function executeCall<TResponse>(config: ExecuteCallConfig): Promise
599
573
  procedureName: descriptor.name,
600
574
  scope: descriptor.scope,
601
575
  })
576
+ let httpError: Error
602
577
  if (typed) {
603
578
  // Tag so executeSafeCall can distinguish typed registry errors from plain
604
579
  // ClientHttpError without re-inspecting the registry.
605
580
  ;(typed as unknown as { __tsProceduresTyped?: boolean }).__tsProceduresTyped = true
606
- throw typed
581
+ httpError = typed
582
+ } else {
583
+ httpError = new ClientHttpError({
584
+ status: response.status,
585
+ headers: response.headers,
586
+ body: response.body,
587
+ procedureName: descriptor.name,
588
+ scope: descriptor.scope,
589
+ })
607
590
  }
608
- throw new ClientHttpError({
609
- status: response.status,
610
- headers: response.headers,
611
- body: response.body,
612
- procedureName: descriptor.name,
613
- scope: descriptor.scope,
614
- })
591
+ // A non-2xx response is not a thrown adapter error (fetch resolves it), so
592
+ // it bypasses the catch block above. Fire onError here too so the hook is a
593
+ // uniform cross-cutting failure signal across adapter throws AND HTTP error
594
+ // statuses (401/403/5xx). Runs after onAfterResponse, which had its chance
595
+ // to swallow the error by mutating status back into the 2xx range.
596
+ await runOnError(
597
+ { procedureName: descriptor.name, scope: descriptor.scope, request, error: httpError },
598
+ hooks,
599
+ options,
600
+ )
601
+ throw httpError
615
602
  }
616
603
 
617
604
  // 8. Return the body (or { body, headers } when the route declares res.headers)
@@ -629,7 +616,8 @@ export async function executeCall<TResponse>(config: ExecuteCallConfig): Promise
629
616
  * 2. Adapter throw, classified → `kind: 'network' | 'timeout' | 'aborted' | <custom> | 'unknown'`
630
617
  * 3. Adapter returns non-2xx → `kind: 'typed'` (registry match) or `kind: 'http'`
631
618
  *
632
- * `onError` hook fires on path 2 and 3 (cross-cutting telemetry); NOT on
619
+ * `onError` hook fires on path 2 and 3 (cross-cutting telemetry) including
620
+ * plain non-2xx `ClientHttpError` and typed registry errors — but NOT on
633
621
  * path 1 (usage errors bypass the classifier and onError entirely).
634
622
  */
635
623
  export async function executeSafeCall<TResponse, ETyped = never>(
@@ -29,13 +29,6 @@ export class ClientHttpError extends Error {
29
29
  }
30
30
  }
31
31
 
32
- /** @deprecated Renamed to `ClientHttpError`. The alias is retained for one minor release after the 7.0.0 major bump and will be removed in a subsequent minor (e.g. 7.1.0). Migrate to `ClientHttpError` now. */
33
-
34
- export const ClientRequestError = ClientHttpError
35
- /** @deprecated Renamed to `ClientHttpError`. The alias is retained for one minor release after the 7.0.0 major bump and will be removed in a subsequent minor (e.g. 7.1.0). Migrate to `ClientHttpError` now. */
36
- // eslint-disable-next-line no-redeclare
37
- export type ClientRequestError = ClientHttpError
38
-
39
32
  export class ClientPathParamError extends Error {
40
33
  readonly name = 'ClientPathParamError'
41
34
 
@@ -7,6 +7,16 @@ import * as _billing from './billing'
7
7
  import * as _billingReports from './billing-reports'
8
8
  import * as _errorsModule from './_errors'
9
9
 
10
+ export {
11
+ ClientHttpError,
12
+ ClientPathParamError,
13
+ ClientStreamError,
14
+ ClientNetworkError,
15
+ ClientTimeoutError,
16
+ ClientAbortError,
17
+ ClientParseError,
18
+ } from './_client'
19
+
10
20
 
11
21
  export function createApiBindings(client: ClientInstance) {
12
22
  return {
@@ -59,13 +59,6 @@ export class ClientHttpError extends Error {
59
59
  }
60
60
  }
61
61
 
62
- /** @deprecated Renamed to `ClientHttpError`. The alias is retained for one minor release after the 7.0.0 major bump and will be removed in a subsequent minor (e.g. 7.1.0). Migrate to `ClientHttpError` now. */
63
-
64
- export const ClientRequestError = ClientHttpError
65
- /** @deprecated Renamed to `ClientHttpError`. The alias is retained for one minor release after the 7.0.0 major bump and will be removed in a subsequent minor (e.g. 7.1.0). Migrate to `ClientHttpError` now. */
66
- // eslint-disable-next-line no-redeclare
67
- export type ClientRequestError = ClientHttpError
68
-
69
62
  export class ClientPathParamError extends Error {
70
63
  readonly name = 'ClientPathParamError'
71
64
 
@@ -305,7 +298,7 @@ export function resolveBasePath(
305
298
  * - `timeoutMs`: the resolved timeout value (may be 0 if per-call explicitly disables)
306
299
  * - `combined`: `AbortSignal.any([...])` of all active signals, or undefined if none
307
300
  *
308
- * Per-call `timeout: 0` disables an inherited default timeout (same as `resolveSignal`).
301
+ * Per-call `timeout: 0` disables an inherited default timeout.
309
302
  * Both `defaults.signal` and `options.signal` are included in `combined` when present.
310
303
  */
311
304
  export interface SignalSources {
@@ -338,25 +331,6 @@ export function resolveSignalSources(
338
331
  return { combined, timeoutSignal, userSignal, timeoutMs }
339
332
  }
340
333
 
341
- /**
342
- * Resolves the effective AbortSignal by combining (via `AbortSignal.any`):
343
- * - default signal (if any)
344
- * - per-call signal (if any)
345
- * - timeout signal (if resolved timeout > 0)
346
- *
347
- * Returns undefined when none apply. Per-call `timeout: 0` disables an
348
- * inherited default timeout.
349
- *
350
- * @deprecated Prefer `resolveSignalSources` when you need access to individual
351
- * signal references (e.g. for abort-cause classification).
352
- */
353
- export function resolveSignal(
354
- defaults: ProcedureCallDefaults | undefined,
355
- options: ProcedureCallOptions | undefined,
356
- ): AbortSignal | undefined {
357
- return resolveSignalSources(defaults, options).combined
358
- }
359
-
360
334
  /**
361
335
  * Resolves a `ClientHeadersInit` value: a static record passes through, a
362
336
  * function is invoked and (if async) awaited — re-evaluated on every call so
@@ -599,19 +573,32 @@ export async function executeCall<TResponse>(config: ExecuteCallConfig): Promise
599
573
  procedureName: descriptor.name,
600
574
  scope: descriptor.scope,
601
575
  })
576
+ let httpError: Error
602
577
  if (typed) {
603
578
  // Tag so executeSafeCall can distinguish typed registry errors from plain
604
579
  // ClientHttpError without re-inspecting the registry.
605
580
  ;(typed as unknown as { __tsProceduresTyped?: boolean }).__tsProceduresTyped = true
606
- throw typed
581
+ httpError = typed
582
+ } else {
583
+ httpError = new ClientHttpError({
584
+ status: response.status,
585
+ headers: response.headers,
586
+ body: response.body,
587
+ procedureName: descriptor.name,
588
+ scope: descriptor.scope,
589
+ })
607
590
  }
608
- throw new ClientHttpError({
609
- status: response.status,
610
- headers: response.headers,
611
- body: response.body,
612
- procedureName: descriptor.name,
613
- scope: descriptor.scope,
614
- })
591
+ // A non-2xx response is not a thrown adapter error (fetch resolves it), so
592
+ // it bypasses the catch block above. Fire onError here too so the hook is a
593
+ // uniform cross-cutting failure signal across adapter throws AND HTTP error
594
+ // statuses (401/403/5xx). Runs after onAfterResponse, which had its chance
595
+ // to swallow the error by mutating status back into the 2xx range.
596
+ await runOnError(
597
+ { procedureName: descriptor.name, scope: descriptor.scope, request, error: httpError },
598
+ hooks,
599
+ options,
600
+ )
601
+ throw httpError
615
602
  }
616
603
 
617
604
  // 8. Return the body (or { body, headers } when the route declares res.headers)
@@ -629,7 +616,8 @@ export async function executeCall<TResponse>(config: ExecuteCallConfig): Promise
629
616
  * 2. Adapter throw, classified → `kind: 'network' | 'timeout' | 'aborted' | <custom> | 'unknown'`
630
617
  * 3. Adapter returns non-2xx → `kind: 'typed'` (registry match) or `kind: 'http'`
631
618
  *
632
- * `onError` hook fires on path 2 and 3 (cross-cutting telemetry); NOT on
619
+ * `onError` hook fires on path 2 and 3 (cross-cutting telemetry) including
620
+ * plain non-2xx `ClientHttpError` and typed registry errors — but NOT on
633
621
  * path 1 (usage errors bypass the classifier and onError entirely).
634
622
  */
635
623
  export async function executeSafeCall<TResponse, ETyped = never>(
@@ -29,13 +29,6 @@ export class ClientHttpError extends Error {
29
29
  }
30
30
  }
31
31
 
32
- /** @deprecated Renamed to `ClientHttpError`. The alias is retained for one minor release after the 7.0.0 major bump and will be removed in a subsequent minor (e.g. 7.1.0). Migrate to `ClientHttpError` now. */
33
-
34
- export const ClientRequestError = ClientHttpError
35
- /** @deprecated Renamed to `ClientHttpError`. The alias is retained for one minor release after the 7.0.0 major bump and will be removed in a subsequent minor (e.g. 7.1.0). Migrate to `ClientHttpError` now. */
36
- // eslint-disable-next-line no-redeclare
37
- export type ClientRequestError = ClientHttpError
38
-
39
32
  export class ClientPathParamError extends Error {
40
33
  readonly name = 'ClientPathParamError'
41
34
 
@@ -5,6 +5,16 @@ import type { ClientInstance, CreateClientConfig } from './_types'
5
5
  import * as _users from './users'
6
6
  import * as _errorsModule from './_errors'
7
7
 
8
+ export {
9
+ ClientHttpError,
10
+ ClientPathParamError,
11
+ ClientStreamError,
12
+ ClientNetworkError,
13
+ ClientTimeoutError,
14
+ ClientAbortError,
15
+ ClientParseError,
16
+ } from './_client'
17
+
8
18
 
9
19
  export function createApiBindings(client: ClientInstance) {
10
20
  return {