ts-procedures 5.15.0 → 5.16.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 (47) hide show
  1. package/agent_config/claude-code/skills/ts-procedures/SKILL.md +1 -1
  2. package/agent_config/claude-code/skills/ts-procedures/api-reference.md +57 -4
  3. package/agent_config/claude-code/skills/ts-procedures/patterns.md +102 -3
  4. package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/client.md +33 -5
  5. package/agent_config/copilot/copilot-instructions.md +55 -7
  6. package/agent_config/cursor/cursorrules +55 -7
  7. package/build/client/call.d.ts +18 -9
  8. package/build/client/call.js +25 -19
  9. package/build/client/call.js.map +1 -1
  10. package/build/client/call.test.js +167 -17
  11. package/build/client/call.test.js.map +1 -1
  12. package/build/client/index.d.ts +1 -1
  13. package/build/client/index.js +18 -3
  14. package/build/client/index.js.map +1 -1
  15. package/build/client/index.test.js +104 -0
  16. package/build/client/index.test.js.map +1 -1
  17. package/build/client/resolve-options.d.ts +45 -0
  18. package/build/client/resolve-options.js +82 -0
  19. package/build/client/resolve-options.js.map +1 -0
  20. package/build/client/resolve-options.test.d.ts +1 -0
  21. package/build/client/resolve-options.test.js +158 -0
  22. package/build/client/resolve-options.test.js.map +1 -0
  23. package/build/client/stream.d.ts +18 -9
  24. package/build/client/stream.js +24 -19
  25. package/build/client/stream.js.map +1 -1
  26. package/build/client/stream.test.js +102 -46
  27. package/build/client/stream.test.js.map +1 -1
  28. package/build/client/types.d.ts +68 -1
  29. package/build/client/types.js +1 -1
  30. package/build/codegen/e2e.test.js +141 -0
  31. package/build/codegen/e2e.test.js.map +1 -1
  32. package/build/codegen/emit-client-runtime.js +3 -0
  33. package/build/codegen/emit-client-runtime.js.map +1 -1
  34. package/docs/client-and-codegen.md +123 -2
  35. package/package.json +1 -1
  36. package/src/client/call.test.ts +202 -29
  37. package/src/client/call.ts +41 -28
  38. package/src/client/index.test.ts +117 -0
  39. package/src/client/index.ts +25 -8
  40. package/src/client/resolve-options.test.ts +205 -0
  41. package/src/client/resolve-options.ts +113 -0
  42. package/src/client/stream.test.ts +132 -107
  43. package/src/client/stream.ts +40 -25
  44. package/src/client/types.ts +74 -2
  45. package/src/codegen/e2e.test.ts +151 -0
  46. package/src/codegen/emit-client-runtime.ts +3 -0
  47. package/src/implementations/http/README.md +9 -1
@@ -151,7 +151,7 @@ The npm package ships user-facing documentation with narrative explanations and
151
151
  | `docs/core.md` | Procedures factory, Create, CreateStream, schema.input, error handling |
152
152
  | `docs/streaming.md` | Streaming procedures, AbortSignal, SSE patterns |
153
153
  | `docs/http-integrations.md` | Express RPC, Hono RPC/Stream/API builders, DocRegistry |
154
- | `docs/client-and-codegen.md` | Client code generation, createClient, CLI options |
154
+ | `docs/client-and-codegen.md` | Client code generation, createClient, per-call options (timeout/signal/headers/basePath/meta), client-level defaults, typed RequestMeta augmentation, CLI options |
155
155
 
156
156
  ## Workflow
157
157
 
@@ -732,6 +732,7 @@ function createClient<TScopes>(config: {
732
732
  basePath: string
733
733
  scopes: (client: ClientInstance) => TScopes
734
734
  hooks?: ClientHooks
735
+ defaults?: ProcedureCallDefaults
735
736
  }): TScopes
736
737
  ```
737
738
 
@@ -740,7 +741,8 @@ function createClient<TScopes>(config: {
740
741
  - `config.adapter` — Transport adapter implementing `ClientAdapter`. Use `createFetchAdapter()` for fetch-based transport.
741
742
  - `config.basePath` — Base URL prepended to all request paths (e.g., `'http://localhost:3000'`).
742
743
  - `config.scopes` — Factory function that receives a raw `ClientInstance` and returns the typed scope object. The generated `create${ServiceName}Bindings` export (defaults to `createApiBindings`; pass `--service-name <Name>` to rename).
743
- - `config.hooks` — Optional global hooks applied to every call. Can be overridden per call.
744
+ - `config.hooks` — Optional global hooks applied to every call. Per-call hooks (passed via the options bag) run *after* global hooks — they stack, not replace.
745
+ - `config.defaults` — Optional default request options (timeout, signal, headers, basePath) applied to every call. Overridden by per-call options.
744
746
 
745
747
  ### Return Value
746
748
 
@@ -750,11 +752,51 @@ Returns the result of `config.scopes(clientInstance)` — a typed object where e
750
752
 
751
753
  ```typescript
752
754
  interface ClientHooks {
753
- onBeforeRequest?: (ctx: { request: ClientRequest }) => { request: ClientRequest } | void
754
- onAfterResponse?: (ctx: { request: ClientRequest; response: ClientResponse }) => void
755
+ onBeforeRequest?: (ctx: BeforeRequestContext) => BeforeRequestContext | Promise<BeforeRequestContext>
756
+ onAfterResponse?: (ctx: AfterResponseContext) => void | Promise<void>
757
+ onError?: (ctx: ErrorContext) => void | Promise<void>
755
758
  }
756
759
  ```
757
760
 
761
+ ### ProcedureCallDefaults / ProcedureCallOptions
762
+
763
+ `ProcedureCallDefaults` is the shape used at `config.defaults` and is a strict subset of `ProcedureCallOptions` (per-call options). `ProcedureCallOptions` additionally includes the `ClientHooks` fields so a single options bag covers both request config and hooks.
764
+
765
+ ```typescript
766
+ interface ProcedureCallDefaults {
767
+ signal?: AbortSignal // cancel signal (combined with per-call via AbortSignal.any)
768
+ timeout?: number // ms — per-call timeout:0 disables an inherited default
769
+ headers?: Record<string, string> // merged (per-call keys win)
770
+ basePath?: string // per-call > default > config.basePath
771
+ meta?: RequestMeta // typed per-request metadata (see RequestMeta below)
772
+ }
773
+
774
+ interface ProcedureCallOptions extends ProcedureCallDefaults, ClientHooks {}
775
+ ```
776
+
777
+ ### RequestMeta (typed per-request metadata)
778
+
779
+ `RequestMeta` is an empty interface designed for TypeScript declaration merging. Augment it in your project to type the `meta` field end-to-end (per-call options, hook contexts, and the adapter).
780
+
781
+ ```typescript
782
+ // Self-contained (code-generated) client
783
+ declare module './generated/_types' {
784
+ interface RequestMeta {
785
+ traceId: string
786
+ priority?: 'high' | 'low'
787
+ }
788
+ }
789
+
790
+ // Or when using ts-procedures/client directly
791
+ declare module 'ts-procedures/client' {
792
+ interface RequestMeta {
793
+ traceId: string
794
+ }
795
+ }
796
+ ```
797
+
798
+ After augmentation, `request.meta` is typed in adapters, hooks, defaults, and per-call options. If `RequestMeta` declares required fields, supply them via `defaults.meta` or per-call `options.meta` — the merged shape must satisfy them at runtime.
799
+
758
800
  ### Example
759
801
 
760
802
  ```typescript
@@ -765,6 +807,7 @@ const client = createClient({
765
807
  adapter: createFetchAdapter(),
766
808
  basePath: 'http://localhost:3000',
767
809
  scopes: createApiBindings,
810
+ defaults: { timeout: 30_000, headers: { 'X-Client-Version': '1.0.0' } },
768
811
  hooks: {
769
812
  onBeforeRequest(ctx) {
770
813
  ctx.request.headers = { ...ctx.request.headers, Authorization: `Bearer ${getToken()}` }
@@ -773,7 +816,17 @@ const client = createClient({
773
816
  },
774
817
  })
775
818
 
776
- const user = await client.users.GetUser({ pathParams: { id: '123' } })
819
+ // Per-call timeout + signal + hooks all in one options bag
820
+ const controller = new AbortController()
821
+ const user = await client.users.GetUser(
822
+ { pathParams: { id: '123' } },
823
+ {
824
+ timeout: 5000,
825
+ signal: controller.signal,
826
+ headers: { 'X-Request-Id': crypto.randomUUID() },
827
+ onAfterResponse(ctx) { log(ctx) },
828
+ },
829
+ )
777
830
 
778
831
  // Reach types via the namespace: Api.Users.GetUser.Params, Api.Errors.ProcedureError
779
832
  ```
@@ -841,10 +841,11 @@ const result = await stream.result // Typed as WatchNotificationsReturn
841
841
 
842
842
  ---
843
843
 
844
- ## Per-Procedure Hook Override
844
+ ## Per-Procedure Hooks
845
+
846
+ Per-call hooks run *after* global hooks (they don't replace them). They live in the same options bag as `timeout`, `signal`, `headers`, etc.
845
847
 
846
848
  ```typescript
847
- // Override hooks for a specific call
848
849
  await client.users.GetUser({ pathParams: { id: '123' } }, {
849
850
  onAfterResponse(ctx) {
850
851
  const rateLimit = ctx.response.headers['x-rate-limit-remaining']
@@ -855,6 +856,102 @@ await client.users.GetUser({ pathParams: { id: '123' } }, {
855
856
 
856
857
  ---
857
858
 
859
+ ## Per-Call Request Options (timeout, signal, headers, basePath)
860
+
861
+ Every generated callable takes an optional second argument for per-call config. The options bag covers both request-level config and hooks.
862
+
863
+ ```typescript
864
+ // Timeout — aborts the request after 5 seconds
865
+ const user = await client.users.GetUser({ pathParams: { id: '123' } }, { timeout: 5000 })
866
+
867
+ // Cancellation — supply your own AbortSignal
868
+ const controller = new AbortController()
869
+ const user = await client.users.GetUser(
870
+ { pathParams: { id: '123' } },
871
+ { signal: controller.signal },
872
+ )
873
+
874
+ // Extra per-call headers
875
+ await client.users.GetUser(
876
+ { pathParams: { id: '123' } },
877
+ { headers: { 'X-Request-Id': crypto.randomUUID() } },
878
+ )
879
+
880
+ // Override base path for a single call (multi-region, dev/prod switching)
881
+ await client.users.GetUser(
882
+ { pathParams: { id: '123' } },
883
+ { basePath: 'https://api-eu.example.com' },
884
+ )
885
+ ```
886
+
887
+ ---
888
+
889
+ ## Client-Level Defaults
890
+
891
+ Set defaults via `config.defaults` — applied to every call, overridden by per-call options. Headers merge (per-call keys win); `signal` combines via `AbortSignal.any` (whichever aborts first wins); per-call `timeout: 0` disables an inherited default timeout.
892
+
893
+ ```typescript
894
+ const client = createClient({
895
+ adapter: createFetchAdapter(),
896
+ basePath: 'http://localhost:3000',
897
+ scopes: createApiBindings,
898
+ defaults: {
899
+ timeout: 30_000,
900
+ headers: { 'X-Client-Version': '1.0.0' },
901
+ },
902
+ })
903
+ ```
904
+
905
+ ---
906
+
907
+ ## Typed Per-Request Meta (RequestMeta Augmentation)
908
+
909
+ `AdapterRequest.meta` is typed via the `RequestMeta` interface — declared empty so developers augment it via TypeScript declaration merging. Augmented fields are then typed end-to-end: per-call options, hook contexts, and adapter.
910
+
911
+ ```typescript
912
+ // Self-contained (code-generated) client
913
+ declare module './generated/_types' {
914
+ interface RequestMeta {
915
+ traceId: string
916
+ priority?: 'high' | 'low'
917
+ }
918
+ }
919
+
920
+ // Or, when using ts-procedures/client directly
921
+ declare module 'ts-procedures/client' {
922
+ interface RequestMeta {
923
+ traceId: string
924
+ }
925
+ }
926
+
927
+ // After augmentation, meta is typed everywhere:
928
+ await client.users.GetUser(
929
+ { pathParams: { id: '123' } },
930
+ { meta: { traceId: 'req-abc', priority: 'high' } }, // typed
931
+ )
932
+
933
+ // Typed in hooks
934
+ hooks: {
935
+ onBeforeRequest(ctx) {
936
+ const trace = ctx.request.meta?.traceId // string | undefined
937
+ return ctx
938
+ },
939
+ }
940
+
941
+ // Typed in adapters
942
+ const adapter: ClientAdapter = {
943
+ async request(req) {
944
+ const priority = req.meta?.priority // 'high' | 'low' | undefined
945
+ return { status: 200, headers: {}, body: {} }
946
+ },
947
+ async stream(req) { /* ... */ },
948
+ }
949
+ ```
950
+
951
+ If `RequestMeta` declares required fields, supply them in `defaults.meta` or per-call `options.meta` — the merged shape must contain them at runtime.
952
+
953
+ ---
954
+
858
955
  ## Custom Client Adapter (Axios)
859
956
 
860
957
  ```typescript
@@ -862,7 +959,9 @@ import type { ClientAdapter } from 'ts-procedures/client'
862
959
  import axios from 'axios'
863
960
 
864
961
  const axiosAdapter: ClientAdapter = {
865
- async request({ url, method, headers, body, signal }) {
962
+ async request({ url, method, headers, body, signal, meta }) {
963
+ // meta is typed via RequestMeta augmentation — use it for tracing,
964
+ // priority routing, custom auth, etc.
866
965
  const res = await axios({ url, method, headers, data: body, signal })
867
966
  return {
868
967
  status: res.status,
@@ -7,12 +7,28 @@ import { createClient, createFetchAdapter } from 'ts-procedures/client'
7
7
  import { createApiBindings, Api } from './generated/api'
8
8
  // With --service-name {{Name}}: import { create{{Name}}Bindings, {{Name}} } from './generated/api'
9
9
 
10
+ // --- Optional: type per-request meta end-to-end via declaration merging ---
11
+ // Self-contained clients augment './generated/_types' instead:
12
+ // declare module './generated/_types' { interface RequestMeta { traceId: string } }
13
+ declare module 'ts-procedures/client' {
14
+ interface RequestMeta {
15
+ // TODO: add your fields — typed in options.meta, ctx.request.meta, adapter req.meta
16
+ // traceId?: string
17
+ // priority?: 'high' | 'low'
18
+ }
19
+ }
20
+
10
21
  // Create the typed client
11
22
  export const {{name}}Client = createClient({
12
23
  adapter: createFetchAdapter(),
13
24
  basePath: 'http://localhost:3000', // TODO: configure base URL
14
25
  scopes: createApiBindings,
15
26
  // With --service-name {{Name}}: scopes: create{{Name}}Bindings,
27
+ defaults: {
28
+ // TODO: set client-wide defaults (overridden by per-call options)
29
+ // timeout: 30_000,
30
+ // headers: { 'X-Client-Version': '1.0.0' },
31
+ },
16
32
  hooks: {
17
33
  onBeforeRequest(ctx) {
18
34
  // TODO: add auth headers, request IDs, etc.
@@ -42,12 +58,24 @@ export const {{name}}Client = createClient({
42
58
  // }
43
59
  // const result = await stream.result // Typed from server returnType schema
44
60
 
45
- // --- Per-procedure hook override ---
46
- // const user = await {{name}}Client.users.GetUser({ id: '123' }, {
47
- // onAfterResponse(ctx) {
48
- // console.log('Rate limit:', ctx.response.headers['x-rate-limit-remaining'])
61
+ // --- Per-call options (timeout, signal, headers, basePath, meta, hooks) ---
62
+ // const user = await {{name}}Client.users.GetUser(
63
+ // { id: '123' },
64
+ // {
65
+ // timeout: 5000,
66
+ // headers: { 'X-Request-Id': crypto.randomUUID() },
67
+ // // meta is typed via RequestMeta augmentation above:
68
+ // // meta: { traceId: 'req-abc' },
69
+ // onAfterResponse(ctx) {
70
+ // console.log('Rate limit:', ctx.response.headers['x-rate-limit-remaining'])
71
+ // },
49
72
  // },
50
- // })
73
+ // )
74
+
75
+ // --- Cancellation with AbortController ---
76
+ // const controller = new AbortController()
77
+ // const promise = {{name}}Client.users.GetUser({ id: '123' }, { signal: controller.signal })
78
+ // setTimeout(() => controller.abort(), 3000)
51
79
  ```
52
80
 
53
81
  ## Test — `{{Name}}.client.test.ts`
@@ -336,6 +336,7 @@ const client = createClient({
336
336
  adapter: createFetchAdapter(),
337
337
  basePath: 'http://localhost:3000',
338
338
  scopes: createApiBindings,
339
+ defaults: { timeout: 30_000, headers: { 'X-Client-Version': '1.0.0' } },
339
340
  hooks: {
340
341
  onBeforeRequest(ctx) {
341
342
  ctx.request.headers = { ...ctx.request.headers, Authorization: `Bearer ${getToken()}` }
@@ -350,20 +351,67 @@ const client = createClient({
350
351
  // Fully typed — params and response inferred from server schemas; type aliases live under Api.<Scope>.<Route>.
351
352
  const user = await client.users.GetUser({ pathParams: { id: '123' } })
352
353
 
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'])
354
+ // Per-call options — timeout, signal, headers, basePath, and hooks all share one bag
355
+ await client.users.GetUser(
356
+ { pathParams: { id: '123' } },
357
+ {
358
+ timeout: 5000,
359
+ headers: { 'X-Request-Id': crypto.randomUUID() },
360
+ onAfterResponse(ctx) {
361
+ console.log(ctx.response.headers['x-rate-limit-remaining'])
362
+ },
357
363
  },
358
- })
364
+ )
365
+ ```
366
+
367
+ ### Per-Call Options & Defaults
368
+
369
+ ```typescript
370
+ interface ProcedureCallDefaults {
371
+ signal?: AbortSignal // cancellation (combined with per-call via AbortSignal.any)
372
+ timeout?: number // ms — per-call timeout:0 disables inherited default
373
+ headers?: Record<string, string> // merged, per-call keys win
374
+ basePath?: string // per-call > default > config.basePath
375
+ meta?: RequestMeta // typed per-request metadata (declaration-mergeable)
376
+ }
377
+
378
+ interface ProcedureCallOptions extends ProcedureCallDefaults, ClientHooks {}
379
+ ```
380
+
381
+ - Defaults are set via `config.defaults`; per-call options are passed as the 2nd argument to any generated callable.
382
+ - Header precedence (low → high): adapter config < `defaults.headers` < per-call `headers` < route-declared `schema.input.headers` < `onBeforeRequest` mutations.
383
+
384
+ ### Typed RequestMeta (Declaration Merging)
385
+
386
+ Augment `RequestMeta` to type `meta` end-to-end (options, hooks, adapter):
387
+
388
+ ```typescript
389
+ // For code-generated self-contained clients
390
+ declare module './generated/_types' {
391
+ interface RequestMeta {
392
+ traceId: string
393
+ priority?: 'high' | 'low'
394
+ }
395
+ }
396
+
397
+ // Or for direct ts-procedures/client usage
398
+ declare module 'ts-procedures/client' {
399
+ interface RequestMeta { traceId: string }
400
+ }
401
+
402
+ await client.users.GetUser(
403
+ { pathParams: { id: '1' } },
404
+ { meta: { traceId: 'req-abc', priority: 'high' } }, // fully typed
405
+ )
359
406
  ```
360
407
 
361
408
  ### Hook Types
362
409
 
363
410
  ```typescript
364
411
  interface ClientHooks {
365
- onBeforeRequest?: (ctx: { request: ClientRequest }) => { request: ClientRequest } | void
366
- onAfterResponse?: (ctx: { request: ClientRequest; response: ClientResponse }) => void
412
+ onBeforeRequest?: (ctx: BeforeRequestContext) => BeforeRequestContext | Promise<BeforeRequestContext>
413
+ onAfterResponse?: (ctx: AfterResponseContext) => void | Promise<void>
414
+ onError?: (ctx: ErrorContext) => void | Promise<void>
367
415
  }
368
416
  ```
369
417
 
@@ -336,6 +336,7 @@ const client = createClient({
336
336
  adapter: createFetchAdapter(),
337
337
  basePath: 'http://localhost:3000',
338
338
  scopes: createApiBindings,
339
+ defaults: { timeout: 30_000, headers: { 'X-Client-Version': '1.0.0' } },
339
340
  hooks: {
340
341
  onBeforeRequest(ctx) {
341
342
  ctx.request.headers = { ...ctx.request.headers, Authorization: `Bearer ${getToken()}` }
@@ -350,20 +351,67 @@ const client = createClient({
350
351
  // Fully typed — params and response inferred from server schemas; type aliases live under Api.<Scope>.<Route>.
351
352
  const user = await client.users.GetUser({ pathParams: { id: '123' } })
352
353
 
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'])
354
+ // Per-call options — timeout, signal, headers, basePath, and hooks all share one bag
355
+ await client.users.GetUser(
356
+ { pathParams: { id: '123' } },
357
+ {
358
+ timeout: 5000,
359
+ headers: { 'X-Request-Id': crypto.randomUUID() },
360
+ onAfterResponse(ctx) {
361
+ console.log(ctx.response.headers['x-rate-limit-remaining'])
362
+ },
357
363
  },
358
- })
364
+ )
365
+ ```
366
+
367
+ ### Per-Call Options & Defaults
368
+
369
+ ```typescript
370
+ interface ProcedureCallDefaults {
371
+ signal?: AbortSignal // cancellation (combined with per-call via AbortSignal.any)
372
+ timeout?: number // ms — per-call timeout:0 disables inherited default
373
+ headers?: Record<string, string> // merged, per-call keys win
374
+ basePath?: string // per-call > default > config.basePath
375
+ meta?: RequestMeta // typed per-request metadata (declaration-mergeable)
376
+ }
377
+
378
+ interface ProcedureCallOptions extends ProcedureCallDefaults, ClientHooks {}
379
+ ```
380
+
381
+ - Defaults are set via `config.defaults`; per-call options are passed as the 2nd argument to any generated callable.
382
+ - Header precedence (low → high): adapter config < `defaults.headers` < per-call `headers` < route-declared `schema.input.headers` < `onBeforeRequest` mutations.
383
+
384
+ ### Typed RequestMeta (Declaration Merging)
385
+
386
+ Augment `RequestMeta` to type `meta` end-to-end (options, hooks, adapter):
387
+
388
+ ```typescript
389
+ // For code-generated self-contained clients
390
+ declare module './generated/_types' {
391
+ interface RequestMeta {
392
+ traceId: string
393
+ priority?: 'high' | 'low'
394
+ }
395
+ }
396
+
397
+ // Or for direct ts-procedures/client usage
398
+ declare module 'ts-procedures/client' {
399
+ interface RequestMeta { traceId: string }
400
+ }
401
+
402
+ await client.users.GetUser(
403
+ { pathParams: { id: '1' } },
404
+ { meta: { traceId: 'req-abc', priority: 'high' } }, // fully typed
405
+ )
359
406
  ```
360
407
 
361
408
  ### Hook Types
362
409
 
363
410
  ```typescript
364
411
  interface ClientHooks {
365
- onBeforeRequest?: (ctx: { request: ClientRequest }) => { request: ClientRequest } | void
366
- onAfterResponse?: (ctx: { request: ClientRequest; response: ClientResponse }) => void
412
+ onBeforeRequest?: (ctx: BeforeRequestContext) => BeforeRequestContext | Promise<BeforeRequestContext>
413
+ onAfterResponse?: (ctx: AfterResponseContext) => void | Promise<void>
414
+ onError?: (ctx: ErrorContext) => void | Promise<void>
367
415
  }
368
416
  ```
369
417
 
@@ -1,14 +1,23 @@
1
- import type { ClientAdapter, ClientHooks, CallDescriptor } from './types.js';
1
+ import type { ClientAdapter, ClientHooks, CallDescriptor, 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
+ }
2
10
  /**
3
11
  * Executes a single procedure call through the adapter.
4
12
  *
5
13
  * 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
14
+ * 1. Resolve base path (per-call > defaults > config) and build AdapterRequest
15
+ * 2. Apply request options (headers, signal, timeout, meta) from defaults + per-call
16
+ * 3. Run onBeforeRequest hooks (global then local) — may further mutate request
17
+ * 4. Call adapter.request()
18
+ * 5. On adapter error: run onError hooks, re-throw
19
+ * 6. Run onAfterResponse hooks (may mutate response.status to swallow errors)
20
+ * 7. If response status is non-2xx: throw ClientRequestError
21
+ * 8. Return response.body as TResponse
13
22
  */
14
- export declare function executeCall<TResponse>(descriptor: CallDescriptor, basePath: string, adapter: ClientAdapter, globalHooks: ClientHooks, localHooks: ClientHooks | undefined): Promise<TResponse>;
23
+ export declare function executeCall<TResponse>(config: ExecuteCallConfig): Promise<TResponse>;
@@ -1,37 +1,43 @@
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';
4
5
  /**
5
6
  * Executes a single procedure call through the adapter.
6
7
  *
7
8
  * 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
9
+ * 1. Resolve base path (per-call > defaults > config) and build AdapterRequest
10
+ * 2. Apply request options (headers, signal, timeout, meta) from defaults + per-call
11
+ * 3. Run onBeforeRequest hooks (global then local) — may further mutate request
12
+ * 4. Call adapter.request()
13
+ * 5. On adapter error: run onError hooks, re-throw
14
+ * 6. Run onAfterResponse hooks (may mutate response.status to swallow errors)
15
+ * 7. If response status is non-2xx: throw ClientRequestError
16
+ * 8. Return response.body as TResponse
15
17
  */
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);
18
+ export async function executeCall(config) {
19
+ const { descriptor, basePath, adapter, hooks, defaults, options } = config;
20
+ // 1. Build the initial request (path/query/body from descriptor)
21
+ const resolvedBasePath = resolveBasePath(defaults, options, basePath);
22
+ let request = buildAdapterRequest(descriptor, resolvedBasePath);
23
+ // 2. Apply request-level options (headers, signal, timeout, meta)
24
+ request = applyRequestOptions(request, defaults, options);
25
+ // 3. Run before-request hooks — they may further mutate the request
26
+ const beforeCtx = await runBeforeRequest({ procedureName: descriptor.name, scope: descriptor.scope, request }, hooks, options);
21
27
  request = beforeCtx.request;
22
- // 3. Call the adapter
28
+ // 4. Call the adapter
23
29
  let response;
24
30
  try {
25
31
  response = await adapter.request(request);
26
32
  }
27
33
  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);
34
+ // 5. On adapter error: run error hooks, re-throw
35
+ await runOnError({ procedureName: descriptor.name, scope: descriptor.scope, request, error: err }, hooks, options);
30
36
  throw err;
31
37
  }
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)
38
+ // 6. Run after-response hooks — they may mutate response.status to swallow errors
39
+ await runAfterResponse({ procedureName: descriptor.name, scope: descriptor.scope, request, response }, hooks, options);
40
+ // 7. Check status AFTER hooks (hooks may have swallowed the error by mutating status)
35
41
  if (response.status < 200 || response.status >= 300) {
36
42
  throw new ClientRequestError({
37
43
  status: response.status,
@@ -41,7 +47,7 @@ export async function executeCall(descriptor, basePath, adapter, globalHooks, lo
41
47
  scope: descriptor.scope,
42
48
  });
43
49
  }
44
- // 7. Return the body
50
+ // 8. Return the body
45
51
  return response.body;
46
52
  }
47
53
  //# 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;AAkBhD;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAY,MAAyB;IACpE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,MAAM,CAAA;IAE1E,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,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"}