ts-procedures 5.14.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.
- package/agent_config/claude-code/skills/ts-procedures/SKILL.md +1 -1
- package/agent_config/claude-code/skills/ts-procedures/api-reference.md +59 -4
- package/agent_config/claude-code/skills/ts-procedures/patterns.md +103 -3
- package/agent_config/claude-code/skills/ts-procedures-scaffold/templates/client.md +36 -5
- package/agent_config/copilot/copilot-instructions.md +56 -7
- package/agent_config/cursor/cursorrules +56 -7
- package/build/client/call.d.ts +18 -9
- package/build/client/call.js +25 -19
- package/build/client/call.js.map +1 -1
- package/build/client/call.test.js +167 -17
- package/build/client/call.test.js.map +1 -1
- package/build/client/index.d.ts +1 -1
- package/build/client/index.js +18 -3
- package/build/client/index.js.map +1 -1
- package/build/client/index.test.js +104 -0
- package/build/client/index.test.js.map +1 -1
- package/build/client/resolve-options.d.ts +45 -0
- package/build/client/resolve-options.js +82 -0
- package/build/client/resolve-options.js.map +1 -0
- package/build/client/resolve-options.test.js +158 -0
- package/build/client/resolve-options.test.js.map +1 -0
- package/build/client/stream.d.ts +18 -9
- package/build/client/stream.js +24 -19
- package/build/client/stream.js.map +1 -1
- package/build/client/stream.test.js +102 -46
- package/build/client/stream.test.js.map +1 -1
- package/build/client/types.d.ts +68 -1
- package/build/client/types.js +1 -1
- package/build/codegen/bin/cli.d.ts +2 -0
- package/build/codegen/bin/cli.js +11 -0
- package/build/codegen/bin/cli.js.map +1 -1
- package/build/codegen/bin/cli.test.js +30 -0
- package/build/codegen/bin/cli.test.js.map +1 -1
- package/build/codegen/e2e.test.js +141 -0
- package/build/codegen/e2e.test.js.map +1 -1
- package/build/codegen/emit-client-runtime.js +3 -0
- package/build/codegen/emit-client-runtime.js.map +1 -1
- package/build/codegen/index.d.ts +1 -0
- package/build/codegen/index.js +1 -0
- package/build/codegen/index.js.map +1 -1
- package/build/codegen/pipeline.d.ts +1 -0
- package/build/codegen/pipeline.js +8 -2
- package/build/codegen/pipeline.js.map +1 -1
- package/build/codegen/pipeline.test.js +34 -1
- package/build/codegen/pipeline.test.js.map +1 -1
- package/docs/client-and-codegen.md +125 -2
- package/package.json +1 -1
- package/src/client/call.test.ts +202 -29
- package/src/client/call.ts +41 -28
- package/src/client/index.test.ts +117 -0
- package/src/client/index.ts +25 -8
- package/src/client/resolve-options.test.ts +205 -0
- package/src/client/resolve-options.ts +113 -0
- package/src/client/stream.test.ts +132 -107
- package/src/client/stream.ts +40 -25
- package/src/client/types.ts +74 -2
- package/src/codegen/bin/cli.test.ts +35 -0
- package/src/codegen/bin/cli.ts +11 -0
- package/src/codegen/e2e.test.ts +151 -0
- package/src/codegen/emit-client-runtime.ts +3 -0
- package/src/codegen/index.ts +2 -0
- package/src/codegen/pipeline.test.ts +46 -1
- package/src/codegen/pipeline.ts +9 -2
- package/src/implementations/http/README.md +11 -1
- package/build/src/client/call.d.ts +0 -14
- package/build/src/client/call.js +0 -47
- package/build/src/client/call.js.map +0 -1
- package/build/src/client/call.test.js +0 -124
- package/build/src/client/call.test.js.map +0 -1
- package/build/src/client/errors.d.ts +0 -25
- package/build/src/client/errors.js +0 -33
- package/build/src/client/errors.js.map +0 -1
- package/build/src/client/errors.test.d.ts +0 -1
- package/build/src/client/errors.test.js +0 -41
- package/build/src/client/errors.test.js.map +0 -1
- package/build/src/client/fetch-adapter.d.ts +0 -12
- package/build/src/client/fetch-adapter.js +0 -156
- package/build/src/client/fetch-adapter.js.map +0 -1
- package/build/src/client/fetch-adapter.test.d.ts +0 -1
- package/build/src/client/fetch-adapter.test.js +0 -271
- package/build/src/client/fetch-adapter.test.js.map +0 -1
- package/build/src/client/hooks.d.ts +0 -17
- package/build/src/client/hooks.js +0 -40
- package/build/src/client/hooks.js.map +0 -1
- package/build/src/client/hooks.test.d.ts +0 -1
- package/build/src/client/hooks.test.js +0 -163
- package/build/src/client/hooks.test.js.map +0 -1
- package/build/src/client/index.d.ts +0 -22
- package/build/src/client/index.js +0 -67
- package/build/src/client/index.js.map +0 -1
- package/build/src/client/index.test.d.ts +0 -1
- package/build/src/client/index.test.js +0 -231
- package/build/src/client/index.test.js.map +0 -1
- package/build/src/client/request-builder.d.ts +0 -13
- package/build/src/client/request-builder.js +0 -53
- package/build/src/client/request-builder.js.map +0 -1
- package/build/src/client/request-builder.test.d.ts +0 -1
- package/build/src/client/request-builder.test.js +0 -160
- package/build/src/client/request-builder.test.js.map +0 -1
- package/build/src/client/stream.d.ts +0 -27
- package/build/src/client/stream.js +0 -118
- package/build/src/client/stream.js.map +0 -1
- package/build/src/client/stream.test.d.ts +0 -1
- package/build/src/client/stream.test.js +0 -228
- package/build/src/client/stream.test.js.map +0 -1
- package/build/src/client/types.d.ts +0 -78
- package/build/src/client/types.js +0 -3
- package/build/src/client/types.js.map +0 -1
- package/build/src/codegen/bin/cli.d.ts +0 -45
- package/build/src/codegen/bin/cli.js +0 -246
- package/build/src/codegen/bin/cli.js.map +0 -1
- package/build/src/codegen/bin/cli.test.d.ts +0 -1
- package/build/src/codegen/bin/cli.test.js +0 -220
- package/build/src/codegen/bin/cli.test.js.map +0 -1
- package/build/src/codegen/constants.d.ts +0 -1
- package/build/src/codegen/constants.js +0 -2
- package/build/src/codegen/constants.js.map +0 -1
- package/build/src/codegen/e2e.test.d.ts +0 -1
- package/build/src/codegen/e2e.test.js +0 -464
- package/build/src/codegen/e2e.test.js.map +0 -1
- package/build/src/codegen/emit-client-runtime.d.ts +0 -9
- package/build/src/codegen/emit-client-runtime.js +0 -99
- package/build/src/codegen/emit-client-runtime.js.map +0 -1
- package/build/src/codegen/emit-client-runtime.test.d.ts +0 -1
- package/build/src/codegen/emit-client-runtime.test.js +0 -78
- package/build/src/codegen/emit-client-runtime.test.js.map +0 -1
- package/build/src/codegen/emit-client-types.d.ts +0 -8
- package/build/src/codegen/emit-client-types.js +0 -25
- package/build/src/codegen/emit-client-types.js.map +0 -1
- package/build/src/codegen/emit-client-types.test.d.ts +0 -1
- package/build/src/codegen/emit-client-types.test.js +0 -33
- package/build/src/codegen/emit-client-types.test.js.map +0 -1
- package/build/src/codegen/emit-errors.d.ts +0 -19
- package/build/src/codegen/emit-errors.js +0 -59
- package/build/src/codegen/emit-errors.js.map +0 -1
- package/build/src/codegen/emit-errors.test.d.ts +0 -1
- package/build/src/codegen/emit-errors.test.js +0 -175
- package/build/src/codegen/emit-errors.test.js.map +0 -1
- package/build/src/codegen/emit-index.d.ts +0 -12
- package/build/src/codegen/emit-index.js +0 -41
- package/build/src/codegen/emit-index.js.map +0 -1
- package/build/src/codegen/emit-index.test.d.ts +0 -1
- package/build/src/codegen/emit-index.test.js +0 -106
- package/build/src/codegen/emit-index.test.js.map +0 -1
- package/build/src/codegen/emit-scope.d.ts +0 -15
- package/build/src/codegen/emit-scope.js +0 -299
- package/build/src/codegen/emit-scope.js.map +0 -1
- package/build/src/codegen/emit-scope.test.d.ts +0 -1
- package/build/src/codegen/emit-scope.test.js +0 -559
- package/build/src/codegen/emit-scope.test.js.map +0 -1
- package/build/src/codegen/emit-types.d.ts +0 -43
- package/build/src/codegen/emit-types.js +0 -111
- package/build/src/codegen/emit-types.js.map +0 -1
- package/build/src/codegen/emit-types.test.d.ts +0 -1
- package/build/src/codegen/emit-types.test.js +0 -184
- package/build/src/codegen/emit-types.test.js.map +0 -1
- package/build/src/codegen/group-routes.d.ts +0 -23
- package/build/src/codegen/group-routes.js +0 -46
- package/build/src/codegen/group-routes.js.map +0 -1
- package/build/src/codegen/group-routes.test.d.ts +0 -1
- package/build/src/codegen/group-routes.test.js +0 -131
- package/build/src/codegen/group-routes.test.js.map +0 -1
- package/build/src/codegen/index.d.ts +0 -15
- package/build/src/codegen/index.js +0 -16
- package/build/src/codegen/index.js.map +0 -1
- package/build/src/codegen/naming.d.ts +0 -7
- package/build/src/codegen/naming.js +0 -21
- package/build/src/codegen/naming.js.map +0 -1
- package/build/src/codegen/naming.test.d.ts +0 -1
- package/build/src/codegen/naming.test.js +0 -40
- package/build/src/codegen/naming.test.js.map +0 -1
- package/build/src/codegen/pipeline.d.ts +0 -17
- package/build/src/codegen/pipeline.js +0 -78
- package/build/src/codegen/pipeline.js.map +0 -1
- package/build/src/codegen/pipeline.test.d.ts +0 -1
- package/build/src/codegen/pipeline.test.js +0 -269
- package/build/src/codegen/pipeline.test.js.map +0 -1
- package/build/src/codegen/resolve-envelope.d.ts +0 -7
- package/build/src/codegen/resolve-envelope.js +0 -46
- package/build/src/codegen/resolve-envelope.js.map +0 -1
- package/build/src/codegen/resolve-envelope.test.d.ts +0 -1
- package/build/src/codegen/resolve-envelope.test.js +0 -69
- package/build/src/codegen/resolve-envelope.test.js.map +0 -1
- package/build/src/errors.d.ts +0 -33
- package/build/src/errors.js +0 -91
- package/build/src/errors.js.map +0 -1
- package/build/src/errors.test.d.ts +0 -1
- package/build/src/errors.test.js +0 -122
- package/build/src/errors.test.js.map +0 -1
- package/build/src/exports.d.ts +0 -7
- package/build/src/exports.js +0 -8
- package/build/src/exports.js.map +0 -1
- package/build/src/implementations/http/doc-registry.d.ts +0 -12
- package/build/src/implementations/http/doc-registry.js +0 -114
- package/build/src/implementations/http/doc-registry.js.map +0 -1
- package/build/src/implementations/http/doc-registry.test.d.ts +0 -1
- package/build/src/implementations/http/doc-registry.test.js +0 -347
- package/build/src/implementations/http/doc-registry.test.js.map +0 -1
- package/build/src/implementations/http/express-rpc/index.d.ts +0 -94
- package/build/src/implementations/http/express-rpc/index.js +0 -185
- package/build/src/implementations/http/express-rpc/index.js.map +0 -1
- package/build/src/implementations/http/express-rpc/index.test.d.ts +0 -1
- package/build/src/implementations/http/express-rpc/index.test.js +0 -684
- package/build/src/implementations/http/express-rpc/index.test.js.map +0 -1
- package/build/src/implementations/http/express-rpc/types.d.ts +0 -11
- package/build/src/implementations/http/express-rpc/types.js +0 -2
- package/build/src/implementations/http/express-rpc/types.js.map +0 -1
- package/build/src/implementations/http/hono-api/index.d.ts +0 -102
- package/build/src/implementations/http/hono-api/index.js +0 -341
- package/build/src/implementations/http/hono-api/index.js.map +0 -1
- package/build/src/implementations/http/hono-api/index.test.d.ts +0 -1
- package/build/src/implementations/http/hono-api/index.test.js +0 -992
- package/build/src/implementations/http/hono-api/index.test.js.map +0 -1
- package/build/src/implementations/http/hono-api/types.d.ts +0 -13
- package/build/src/implementations/http/hono-api/types.js +0 -2
- package/build/src/implementations/http/hono-api/types.js.map +0 -1
- package/build/src/implementations/http/hono-rpc/index.d.ts +0 -92
- package/build/src/implementations/http/hono-rpc/index.js +0 -161
- package/build/src/implementations/http/hono-rpc/index.js.map +0 -1
- package/build/src/implementations/http/hono-rpc/index.test.d.ts +0 -1
- package/build/src/implementations/http/hono-rpc/index.test.js +0 -803
- package/build/src/implementations/http/hono-rpc/index.test.js.map +0 -1
- package/build/src/implementations/http/hono-rpc/types.d.ts +0 -11
- package/build/src/implementations/http/hono-rpc/types.js +0 -2
- package/build/src/implementations/http/hono-rpc/types.js.map +0 -1
- package/build/src/implementations/http/hono-stream/index.d.ts +0 -120
- package/build/src/implementations/http/hono-stream/index.js +0 -309
- package/build/src/implementations/http/hono-stream/index.js.map +0 -1
- package/build/src/implementations/http/hono-stream/index.test.d.ts +0 -1
- package/build/src/implementations/http/hono-stream/index.test.js +0 -1356
- package/build/src/implementations/http/hono-stream/index.test.js.map +0 -1
- package/build/src/implementations/http/hono-stream/types.d.ts +0 -15
- package/build/src/implementations/http/hono-stream/types.js +0 -2
- package/build/src/implementations/http/hono-stream/types.js.map +0 -1
- package/build/src/implementations/types.d.ts +0 -142
- package/build/src/implementations/types.js +0 -2
- package/build/src/implementations/types.js.map +0 -1
- package/build/src/index.d.ts +0 -165
- package/build/src/index.js +0 -253
- package/build/src/index.js.map +0 -1
- package/build/src/index.test.d.ts +0 -1
- package/build/src/index.test.js +0 -890
- package/build/src/index.test.js.map +0 -1
- package/build/src/schema/compute-schema.d.ts +0 -35
- package/build/src/schema/compute-schema.js +0 -41
- package/build/src/schema/compute-schema.js.map +0 -1
- package/build/src/schema/compute-schema.test.d.ts +0 -1
- package/build/src/schema/compute-schema.test.js +0 -107
- package/build/src/schema/compute-schema.test.js.map +0 -1
- package/build/src/schema/extract-json-schema.d.ts +0 -2
- package/build/src/schema/extract-json-schema.js +0 -12
- package/build/src/schema/extract-json-schema.js.map +0 -1
- package/build/src/schema/extract-json-schema.test.d.ts +0 -1
- package/build/src/schema/extract-json-schema.test.js +0 -23
- package/build/src/schema/extract-json-schema.test.js.map +0 -1
- package/build/src/schema/parser.d.ts +0 -28
- package/build/src/schema/parser.js +0 -170
- package/build/src/schema/parser.js.map +0 -1
- package/build/src/schema/parser.test.d.ts +0 -1
- package/build/src/schema/parser.test.js +0 -120
- package/build/src/schema/parser.test.js.map +0 -1
- package/build/src/schema/resolve-schema-lib.d.ts +0 -12
- package/build/src/schema/resolve-schema-lib.js +0 -11
- package/build/src/schema/resolve-schema-lib.js.map +0 -1
- package/build/src/schema/resolve-schema-lib.test.d.ts +0 -1
- package/build/src/schema/resolve-schema-lib.test.js +0 -17
- package/build/src/schema/resolve-schema-lib.test.js.map +0 -1
- package/build/src/schema/types.d.ts +0 -8
- package/build/src/schema/types.js +0 -2
- package/build/src/schema/types.js.map +0 -1
- package/build/src/stack-utils.d.ts +0 -25
- package/build/src/stack-utils.js +0 -95
- package/build/src/stack-utils.js.map +0 -1
- package/build/src/stack-utils.test.d.ts +0 -1
- package/build/src/stack-utils.test.js +0 -80
- package/build/src/stack-utils.test.js.map +0 -1
- /package/build/{src/client/call.test.d.ts → client/resolve-options.test.d.ts} +0 -0
package/src/client/call.ts
CHANGED
|
@@ -1,64 +1,77 @@
|
|
|
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
|
import type {
|
|
5
6
|
ClientAdapter,
|
|
6
7
|
ClientHooks,
|
|
7
8
|
CallDescriptor,
|
|
9
|
+
ProcedureCallDefaults,
|
|
10
|
+
ProcedureCallOptions,
|
|
8
11
|
} from './types.js'
|
|
9
12
|
|
|
13
|
+
export interface ExecuteCallConfig {
|
|
14
|
+
descriptor: CallDescriptor
|
|
15
|
+
basePath: string
|
|
16
|
+
adapter: ClientAdapter
|
|
17
|
+
hooks: ClientHooks
|
|
18
|
+
defaults?: ProcedureCallDefaults
|
|
19
|
+
options?: ProcedureCallOptions
|
|
20
|
+
}
|
|
21
|
+
|
|
10
22
|
/**
|
|
11
23
|
* Executes a single procedure call through the adapter.
|
|
12
24
|
*
|
|
13
25
|
* Flow:
|
|
14
|
-
* 1.
|
|
15
|
-
* 2.
|
|
16
|
-
* 3.
|
|
17
|
-
* 4.
|
|
18
|
-
* 5.
|
|
19
|
-
* 6.
|
|
20
|
-
* 7.
|
|
26
|
+
* 1. Resolve base path (per-call > defaults > config) and build AdapterRequest
|
|
27
|
+
* 2. Apply request options (headers, signal, timeout, meta) from defaults + per-call
|
|
28
|
+
* 3. Run onBeforeRequest hooks (global then local) — may further mutate request
|
|
29
|
+
* 4. Call adapter.request()
|
|
30
|
+
* 5. On adapter error: run onError hooks, re-throw
|
|
31
|
+
* 6. Run onAfterResponse hooks (may mutate response.status to swallow errors)
|
|
32
|
+
* 7. If response status is non-2xx: throw ClientRequestError
|
|
33
|
+
* 8. Return response.body as TResponse
|
|
21
34
|
*/
|
|
22
|
-
export async function executeCall<TResponse>(
|
|
23
|
-
descriptor
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
//
|
|
30
|
-
|
|
35
|
+
export async function executeCall<TResponse>(config: ExecuteCallConfig): Promise<TResponse> {
|
|
36
|
+
const { descriptor, basePath, adapter, hooks, defaults, options } = config
|
|
37
|
+
|
|
38
|
+
// 1. Build the initial request (path/query/body from descriptor)
|
|
39
|
+
const resolvedBasePath = resolveBasePath(defaults, options, basePath)
|
|
40
|
+
let request = buildAdapterRequest(descriptor, resolvedBasePath)
|
|
41
|
+
|
|
42
|
+
// 2. Apply request-level options (headers, signal, timeout, meta)
|
|
43
|
+
request = applyRequestOptions(request, defaults, options)
|
|
31
44
|
|
|
32
|
-
//
|
|
45
|
+
// 3. Run before-request hooks — they may further mutate the request
|
|
33
46
|
const beforeCtx = await runBeforeRequest(
|
|
34
47
|
{ procedureName: descriptor.name, scope: descriptor.scope, request },
|
|
35
|
-
|
|
36
|
-
|
|
48
|
+
hooks,
|
|
49
|
+
options,
|
|
37
50
|
)
|
|
38
51
|
request = beforeCtx.request
|
|
39
52
|
|
|
40
|
-
//
|
|
53
|
+
// 4. Call the adapter
|
|
41
54
|
let response
|
|
42
55
|
try {
|
|
43
56
|
response = await adapter.request(request)
|
|
44
57
|
} catch (err) {
|
|
45
|
-
//
|
|
58
|
+
// 5. On adapter error: run error hooks, re-throw
|
|
46
59
|
await runOnError(
|
|
47
60
|
{ procedureName: descriptor.name, scope: descriptor.scope, request, error: err },
|
|
48
|
-
|
|
49
|
-
|
|
61
|
+
hooks,
|
|
62
|
+
options,
|
|
50
63
|
)
|
|
51
64
|
throw err
|
|
52
65
|
}
|
|
53
66
|
|
|
54
|
-
//
|
|
67
|
+
// 6. Run after-response hooks — they may mutate response.status to swallow errors
|
|
55
68
|
await runAfterResponse(
|
|
56
69
|
{ procedureName: descriptor.name, scope: descriptor.scope, request, response },
|
|
57
|
-
|
|
58
|
-
|
|
70
|
+
hooks,
|
|
71
|
+
options,
|
|
59
72
|
)
|
|
60
73
|
|
|
61
|
-
//
|
|
74
|
+
// 7. Check status AFTER hooks (hooks may have swallowed the error by mutating status)
|
|
62
75
|
if (response.status < 200 || response.status >= 300) {
|
|
63
76
|
throw new ClientRequestError({
|
|
64
77
|
status: response.status,
|
|
@@ -69,6 +82,6 @@ export async function executeCall<TResponse>(
|
|
|
69
82
|
})
|
|
70
83
|
}
|
|
71
84
|
|
|
72
|
-
//
|
|
85
|
+
// 8. Return the body
|
|
73
86
|
return response.body as TResponse
|
|
74
87
|
}
|
package/src/client/index.test.ts
CHANGED
|
@@ -287,4 +287,121 @@ describe('createClient', () => {
|
|
|
287
287
|
const { ClientRequestError: CRE } = await import('./index.js')
|
|
288
288
|
expect(CRE).toBeDefined()
|
|
289
289
|
})
|
|
290
|
+
|
|
291
|
+
// ── defaults + per-call options ───────────────────────────
|
|
292
|
+
|
|
293
|
+
it('applies config.defaults to every call', async () => {
|
|
294
|
+
const capturedHeaders: Record<string, string>[] = []
|
|
295
|
+
const adapter: ClientAdapter = {
|
|
296
|
+
request: vi.fn(async (req: AdapterRequest): Promise<AdapterResponse> => {
|
|
297
|
+
capturedHeaders.push(req.headers ?? {})
|
|
298
|
+
return { status: 200, headers: {}, body: {} }
|
|
299
|
+
}),
|
|
300
|
+
stream: vi.fn(async (): Promise<AdapterStreamResponse> => ({
|
|
301
|
+
status: 200,
|
|
302
|
+
headers: {},
|
|
303
|
+
body: makeAsyncIterable([]),
|
|
304
|
+
})),
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const client = createClient({
|
|
308
|
+
adapter,
|
|
309
|
+
basePath: 'https://api.example.com',
|
|
310
|
+
defaults: { headers: { 'x-client': 'web' } },
|
|
311
|
+
scopes: (instance: ClientInstance) => ({
|
|
312
|
+
users: {
|
|
313
|
+
getUser: () => instance.call<unknown>(makeCallDescriptor()),
|
|
314
|
+
},
|
|
315
|
+
}),
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
await client.users.getUser()
|
|
319
|
+
expect(capturedHeaders[0]?.['x-client']).toBe('web')
|
|
320
|
+
})
|
|
321
|
+
|
|
322
|
+
it('per-call options override defaults — headers merge, per-call wins', async () => {
|
|
323
|
+
const capturedHeaders: Record<string, string>[] = []
|
|
324
|
+
const adapter: ClientAdapter = {
|
|
325
|
+
request: vi.fn(async (req: AdapterRequest): Promise<AdapterResponse> => {
|
|
326
|
+
capturedHeaders.push(req.headers ?? {})
|
|
327
|
+
return { status: 200, headers: {}, body: {} }
|
|
328
|
+
}),
|
|
329
|
+
stream: vi.fn(async (): Promise<AdapterStreamResponse> => ({
|
|
330
|
+
status: 200,
|
|
331
|
+
headers: {},
|
|
332
|
+
body: makeAsyncIterable([]),
|
|
333
|
+
})),
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
const client = createClient({
|
|
337
|
+
adapter,
|
|
338
|
+
basePath: 'https://api.example.com',
|
|
339
|
+
defaults: { headers: { 'x-client': 'web', 'x-trace': 'default' } },
|
|
340
|
+
scopes: (instance: ClientInstance) => ({
|
|
341
|
+
users: {
|
|
342
|
+
getUser: (options?: { headers?: Record<string, string> }) =>
|
|
343
|
+
instance.call<unknown>(makeCallDescriptor(), options),
|
|
344
|
+
},
|
|
345
|
+
}),
|
|
346
|
+
})
|
|
347
|
+
|
|
348
|
+
await client.users.getUser({ headers: { 'x-trace': 'override', 'x-extra': 'yes' } })
|
|
349
|
+
expect(capturedHeaders[0]).toMatchObject({
|
|
350
|
+
'x-client': 'web',
|
|
351
|
+
'x-trace': 'override',
|
|
352
|
+
'x-extra': 'yes',
|
|
353
|
+
})
|
|
354
|
+
})
|
|
355
|
+
|
|
356
|
+
it('per-call timeout attaches an AbortSignal.timeout signal', async () => {
|
|
357
|
+
const spy = vi.spyOn(AbortSignal, 'timeout')
|
|
358
|
+
try {
|
|
359
|
+
let observedSignal: AbortSignal | undefined
|
|
360
|
+
const adapter: ClientAdapter = {
|
|
361
|
+
request: vi.fn(async (req: AdapterRequest): Promise<AdapterResponse> => {
|
|
362
|
+
observedSignal = req.signal
|
|
363
|
+
return { status: 200, headers: {}, body: {} }
|
|
364
|
+
}),
|
|
365
|
+
stream: vi.fn(async (): Promise<AdapterStreamResponse> => ({
|
|
366
|
+
status: 200,
|
|
367
|
+
headers: {},
|
|
368
|
+
body: makeAsyncIterable([]),
|
|
369
|
+
})),
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const client = createClient({
|
|
373
|
+
adapter,
|
|
374
|
+
basePath: 'https://api.example.com',
|
|
375
|
+
scopes: (instance: ClientInstance) => ({
|
|
376
|
+
users: {
|
|
377
|
+
call: () => instance.call<unknown>(makeCallDescriptor(), { timeout: 5000 }),
|
|
378
|
+
},
|
|
379
|
+
}),
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
await client.users.call()
|
|
383
|
+
expect(spy).toHaveBeenCalledWith(5000)
|
|
384
|
+
expect(observedSignal).toBeDefined()
|
|
385
|
+
} finally {
|
|
386
|
+
spy.mockRestore()
|
|
387
|
+
}
|
|
388
|
+
})
|
|
389
|
+
|
|
390
|
+
it('ClientInstance exposes defaults', () => {
|
|
391
|
+
const adapter = makeAdapter()
|
|
392
|
+
const defaults = { timeout: 5000, headers: { 'x-client': 'test' } }
|
|
393
|
+
|
|
394
|
+
let capturedInstance: ClientInstance | undefined
|
|
395
|
+
createClient({
|
|
396
|
+
adapter,
|
|
397
|
+
basePath: 'https://api.example.com',
|
|
398
|
+
defaults,
|
|
399
|
+
scopes: (instance: ClientInstance) => {
|
|
400
|
+
capturedInstance = instance
|
|
401
|
+
return {}
|
|
402
|
+
},
|
|
403
|
+
})
|
|
404
|
+
|
|
405
|
+
expect(capturedInstance?.defaults).toEqual(defaults)
|
|
406
|
+
})
|
|
290
407
|
})
|
package/src/client/index.ts
CHANGED
|
@@ -25,23 +25,37 @@ import type {
|
|
|
25
25
|
* - The outer `.result` is wired up to the inner stream's `.result`.
|
|
26
26
|
*/
|
|
27
27
|
export function createClient<TScopes>(config: CreateClientConfig<TScopes>): TScopes {
|
|
28
|
-
const {
|
|
28
|
+
const {
|
|
29
|
+
adapter,
|
|
30
|
+
basePath,
|
|
31
|
+
hooks: globalHooks = {},
|
|
32
|
+
defaults: globalDefaults = {},
|
|
33
|
+
scopes,
|
|
34
|
+
} = config
|
|
29
35
|
|
|
30
36
|
const instance: ClientInstance = {
|
|
31
37
|
basePath,
|
|
32
38
|
adapter,
|
|
33
39
|
hooks: globalHooks,
|
|
40
|
+
defaults: globalDefaults,
|
|
34
41
|
|
|
35
42
|
call<TResponse>(
|
|
36
43
|
descriptor: CallDescriptor,
|
|
37
|
-
options?: ProcedureCallOptions
|
|
44
|
+
options?: ProcedureCallOptions,
|
|
38
45
|
): Promise<TResponse> {
|
|
39
|
-
return executeCall<TResponse>(
|
|
46
|
+
return executeCall<TResponse>({
|
|
47
|
+
descriptor,
|
|
48
|
+
basePath,
|
|
49
|
+
adapter,
|
|
50
|
+
hooks: globalHooks,
|
|
51
|
+
defaults: globalDefaults,
|
|
52
|
+
options,
|
|
53
|
+
})
|
|
40
54
|
},
|
|
41
55
|
|
|
42
56
|
stream<TYield, TReturn>(
|
|
43
57
|
descriptor: StreamDescriptor,
|
|
44
|
-
options?: ProcedureCallOptions
|
|
58
|
+
options?: ProcedureCallOptions,
|
|
45
59
|
): TypedStream<TYield, TReturn> {
|
|
46
60
|
// executeStream is async but stream() must be synchronous.
|
|
47
61
|
// Create a deferred TypedStream that wraps the async executeStream call.
|
|
@@ -58,13 +72,14 @@ export function createClient<TScopes>(config: CreateClientConfig<TScopes>): TSco
|
|
|
58
72
|
async function* deferredGenerator(): AsyncGenerator<TYield> {
|
|
59
73
|
let innerStream: TypedStream<TYield, TReturn>
|
|
60
74
|
try {
|
|
61
|
-
innerStream = await executeStream<TYield, TReturn>(
|
|
75
|
+
innerStream = await executeStream<TYield, TReturn>({
|
|
62
76
|
descriptor,
|
|
63
77
|
basePath,
|
|
64
78
|
adapter,
|
|
65
|
-
globalHooks,
|
|
66
|
-
|
|
67
|
-
|
|
79
|
+
hooks: globalHooks,
|
|
80
|
+
defaults: globalDefaults,
|
|
81
|
+
options,
|
|
82
|
+
})
|
|
68
83
|
} catch (err) {
|
|
69
84
|
rejectResult(err)
|
|
70
85
|
throw err
|
|
@@ -107,8 +122,10 @@ export type {
|
|
|
107
122
|
StreamDescriptor,
|
|
108
123
|
TypedStream,
|
|
109
124
|
ClientInstance,
|
|
125
|
+
ProcedureCallDefaults,
|
|
110
126
|
ProcedureCallOptions,
|
|
111
127
|
CreateClientConfig,
|
|
128
|
+
RequestMeta,
|
|
112
129
|
} from './types.js'
|
|
113
130
|
|
|
114
131
|
export { ClientRequestError, ClientPathParamError, ClientStreamError } from './errors.js'
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest'
|
|
2
|
+
import {
|
|
3
|
+
applyRequestOptions,
|
|
4
|
+
resolveBasePath,
|
|
5
|
+
resolveHeaders,
|
|
6
|
+
resolveMeta,
|
|
7
|
+
resolveSignal,
|
|
8
|
+
} from './resolve-options.js'
|
|
9
|
+
import type { AdapterRequest, ProcedureCallDefaults, ProcedureCallOptions } from './types.js'
|
|
10
|
+
|
|
11
|
+
// ── resolveBasePath ───────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
describe('resolveBasePath', () => {
|
|
14
|
+
it('uses fallback when nothing is set', () => {
|
|
15
|
+
expect(resolveBasePath(undefined, undefined, 'https://api.example.com')).toBe(
|
|
16
|
+
'https://api.example.com',
|
|
17
|
+
)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('uses default when only defaults.basePath is set', () => {
|
|
21
|
+
expect(
|
|
22
|
+
resolveBasePath({ basePath: 'https://default.example.com' }, undefined, 'https://fallback'),
|
|
23
|
+
).toBe('https://default.example.com')
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('per-call basePath overrides default', () => {
|
|
27
|
+
expect(
|
|
28
|
+
resolveBasePath(
|
|
29
|
+
{ basePath: 'https://default.example.com' },
|
|
30
|
+
{ basePath: 'https://percall.example.com' },
|
|
31
|
+
'https://fallback',
|
|
32
|
+
),
|
|
33
|
+
).toBe('https://percall.example.com')
|
|
34
|
+
})
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
// ── resolveHeaders ────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
describe('resolveHeaders', () => {
|
|
40
|
+
it('returns undefined when neither side sets headers', () => {
|
|
41
|
+
expect(resolveHeaders(undefined, undefined)).toBeUndefined()
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('returns default headers when only defaults set', () => {
|
|
45
|
+
expect(resolveHeaders({ headers: { 'x-a': '1' } }, undefined)).toEqual({ 'x-a': '1' })
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('per-call keys override default keys', () => {
|
|
49
|
+
const defaults: ProcedureCallDefaults = { headers: { 'x-a': 'default', 'x-b': 'keep' } }
|
|
50
|
+
const options: ProcedureCallOptions = { headers: { 'x-a': 'override' } }
|
|
51
|
+
expect(resolveHeaders(defaults, options)).toEqual({
|
|
52
|
+
'x-a': 'override',
|
|
53
|
+
'x-b': 'keep',
|
|
54
|
+
})
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
// ── resolveMeta ───────────────────────────────────────────
|
|
59
|
+
|
|
60
|
+
describe('resolveMeta', () => {
|
|
61
|
+
it('returns undefined when neither side sets meta', () => {
|
|
62
|
+
expect(resolveMeta(undefined, undefined)).toBeUndefined()
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
it('merges default + per-call meta (shallow), per-call keys win', () => {
|
|
66
|
+
const defaults: ProcedureCallDefaults = { meta: { a: 1, b: 2 } as never }
|
|
67
|
+
const options: ProcedureCallOptions = { meta: { b: 99, c: 3 } as never }
|
|
68
|
+
expect(resolveMeta(defaults, options)).toEqual({ a: 1, b: 99, c: 3 })
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
// ── resolveSignal ─────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
describe('resolveSignal', () => {
|
|
75
|
+
it('returns undefined when nothing is set', () => {
|
|
76
|
+
expect(resolveSignal(undefined, undefined)).toBeUndefined()
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it('returns the default signal when no timeout and no per-call signal', () => {
|
|
80
|
+
const controller = new AbortController()
|
|
81
|
+
expect(resolveSignal({ signal: controller.signal }, undefined)).toBe(controller.signal)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('returns the per-call signal when no default and no timeout', () => {
|
|
85
|
+
const controller = new AbortController()
|
|
86
|
+
expect(resolveSignal(undefined, { signal: controller.signal })).toBe(controller.signal)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('combines default + per-call signals — default aborts combined signal', () => {
|
|
90
|
+
const defaultCtrl = new AbortController()
|
|
91
|
+
const callCtrl = new AbortController()
|
|
92
|
+
const signal = resolveSignal({ signal: defaultCtrl.signal }, { signal: callCtrl.signal })!
|
|
93
|
+
|
|
94
|
+
expect(signal.aborted).toBe(false)
|
|
95
|
+
defaultCtrl.abort(new Error('default-abort'))
|
|
96
|
+
expect(signal.aborted).toBe(true)
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
it('combines default + per-call signals — per-call aborts combined signal', () => {
|
|
100
|
+
const defaultCtrl = new AbortController()
|
|
101
|
+
const callCtrl = new AbortController()
|
|
102
|
+
const signal = resolveSignal({ signal: defaultCtrl.signal }, { signal: callCtrl.signal })!
|
|
103
|
+
|
|
104
|
+
callCtrl.abort(new Error('call-abort'))
|
|
105
|
+
expect(signal.aborted).toBe(true)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('applies timeout via AbortSignal.timeout', () => {
|
|
109
|
+
const spy = vi.spyOn(AbortSignal, 'timeout')
|
|
110
|
+
try {
|
|
111
|
+
const signal = resolveSignal(undefined, { timeout: 100 })
|
|
112
|
+
expect(spy).toHaveBeenCalledWith(100)
|
|
113
|
+
expect(signal).toBeDefined()
|
|
114
|
+
} finally {
|
|
115
|
+
spy.mockRestore()
|
|
116
|
+
}
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it('per-call timeout overrides default timeout', () => {
|
|
120
|
+
const spy = vi.spyOn(AbortSignal, 'timeout')
|
|
121
|
+
try {
|
|
122
|
+
resolveSignal({ timeout: 10_000 }, { timeout: 100 })
|
|
123
|
+
expect(spy).toHaveBeenCalledWith(100)
|
|
124
|
+
expect(spy).not.toHaveBeenCalledWith(10_000)
|
|
125
|
+
} finally {
|
|
126
|
+
spy.mockRestore()
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
it('combines signal + timeout — signal aborts first', () => {
|
|
131
|
+
const controller = new AbortController()
|
|
132
|
+
const signal = resolveSignal(undefined, { signal: controller.signal, timeout: 10_000 })!
|
|
133
|
+
expect(signal.aborted).toBe(false)
|
|
134
|
+
controller.abort()
|
|
135
|
+
expect(signal.aborted).toBe(true)
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
it('per-call timeout: 0 disables default timeout', () => {
|
|
139
|
+
const signal = resolveSignal({ timeout: 1000 }, { timeout: 0 })
|
|
140
|
+
expect(signal).toBeUndefined()
|
|
141
|
+
})
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
// ── applyRequestOptions ───────────────────────────────────
|
|
145
|
+
|
|
146
|
+
describe('applyRequestOptions', () => {
|
|
147
|
+
const baseRequest: AdapterRequest = {
|
|
148
|
+
url: 'https://api.example.com/foo',
|
|
149
|
+
method: 'POST',
|
|
150
|
+
body: { hello: 'world' },
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
it('returns the request unchanged when nothing is provided', () => {
|
|
154
|
+
const result = applyRequestOptions(baseRequest, undefined, undefined)
|
|
155
|
+
expect(result.url).toBe(baseRequest.url)
|
|
156
|
+
expect(result.body).toEqual({ hello: 'world' })
|
|
157
|
+
expect(result.headers).toBeUndefined()
|
|
158
|
+
expect(result.signal).toBeUndefined()
|
|
159
|
+
expect(result.meta).toBeUndefined()
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it('merges default + per-call headers, preserving route-declared headers', () => {
|
|
163
|
+
const reqWithHeaders: AdapterRequest = {
|
|
164
|
+
...baseRequest,
|
|
165
|
+
headers: { 'content-type': 'application/json', 'x-route': 'declared' },
|
|
166
|
+
}
|
|
167
|
+
const result = applyRequestOptions(
|
|
168
|
+
reqWithHeaders,
|
|
169
|
+
{ headers: { 'x-default': 'd', 'x-route': 'from-default' } },
|
|
170
|
+
{ headers: { 'x-call': 'c', 'x-route': 'from-call' } },
|
|
171
|
+
)
|
|
172
|
+
expect(result.headers).toEqual({
|
|
173
|
+
'x-default': 'd',
|
|
174
|
+
'x-call': 'c',
|
|
175
|
+
// Route-declared headers WIN over resolved options (typed contract)
|
|
176
|
+
'content-type': 'application/json',
|
|
177
|
+
'x-route': 'declared',
|
|
178
|
+
})
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
it('attaches meta to the request when provided', () => {
|
|
182
|
+
const result = applyRequestOptions(baseRequest, undefined, {
|
|
183
|
+
meta: { traceId: 'abc' } as never,
|
|
184
|
+
})
|
|
185
|
+
expect(result.meta).toEqual({ traceId: 'abc' })
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
it('passes per-call signal through', () => {
|
|
189
|
+
const controller = new AbortController()
|
|
190
|
+
const result = applyRequestOptions(baseRequest, undefined, { signal: controller.signal })
|
|
191
|
+
expect(result.signal).toBe(controller.signal)
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
it('attaches a signal when per-call timeout is set', () => {
|
|
195
|
+
const spy = vi.spyOn(AbortSignal, 'timeout')
|
|
196
|
+
try {
|
|
197
|
+
const result = applyRequestOptions(baseRequest, undefined, { timeout: 100 })
|
|
198
|
+
expect(spy).toHaveBeenCalledWith(100)
|
|
199
|
+
expect(result.signal).toBeDefined()
|
|
200
|
+
expect(result.signal?.aborted).toBe(false)
|
|
201
|
+
} finally {
|
|
202
|
+
spy.mockRestore()
|
|
203
|
+
}
|
|
204
|
+
})
|
|
205
|
+
})
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AdapterRequest,
|
|
3
|
+
ProcedureCallDefaults,
|
|
4
|
+
ProcedureCallOptions,
|
|
5
|
+
RequestMeta,
|
|
6
|
+
} from './types.js'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Resolves the effective base path:
|
|
10
|
+
* per-call `basePath` > default `basePath` > config `basePath` (fallback).
|
|
11
|
+
*/
|
|
12
|
+
export function resolveBasePath(
|
|
13
|
+
defaults: ProcedureCallDefaults | undefined,
|
|
14
|
+
options: ProcedureCallOptions | undefined,
|
|
15
|
+
fallback: string,
|
|
16
|
+
): string {
|
|
17
|
+
return options?.basePath ?? defaults?.basePath ?? fallback
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Resolves the effective AbortSignal by combining (via `AbortSignal.any`):
|
|
22
|
+
* - default signal (if any)
|
|
23
|
+
* - per-call signal (if any)
|
|
24
|
+
* - timeout signal (if resolved timeout > 0)
|
|
25
|
+
*
|
|
26
|
+
* Returns undefined when none apply. Per-call `timeout: 0` disables an
|
|
27
|
+
* inherited default timeout.
|
|
28
|
+
*/
|
|
29
|
+
export function resolveSignal(
|
|
30
|
+
defaults: ProcedureCallDefaults | undefined,
|
|
31
|
+
options: ProcedureCallOptions | undefined,
|
|
32
|
+
): AbortSignal | undefined {
|
|
33
|
+
const signals: AbortSignal[] = []
|
|
34
|
+
|
|
35
|
+
if (defaults?.signal) signals.push(defaults.signal)
|
|
36
|
+
if (options?.signal) signals.push(options.signal)
|
|
37
|
+
|
|
38
|
+
const timeout = options?.timeout ?? defaults?.timeout
|
|
39
|
+
if (timeout != null && timeout > 0) {
|
|
40
|
+
signals.push(AbortSignal.timeout(timeout))
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (signals.length === 0) return undefined
|
|
44
|
+
if (signals.length === 1) return signals[0]
|
|
45
|
+
return AbortSignal.any(signals)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Merges headers with precedence: default < per-call. Returns undefined if
|
|
50
|
+
* no headers would be set.
|
|
51
|
+
*/
|
|
52
|
+
export function resolveHeaders(
|
|
53
|
+
defaults: ProcedureCallDefaults | undefined,
|
|
54
|
+
options: ProcedureCallOptions | undefined,
|
|
55
|
+
): Record<string, string> | undefined {
|
|
56
|
+
const defaultHeaders = defaults?.headers
|
|
57
|
+
const callHeaders = options?.headers
|
|
58
|
+
|
|
59
|
+
if (!defaultHeaders && !callHeaders) return undefined
|
|
60
|
+
|
|
61
|
+
return { ...defaultHeaders, ...callHeaders }
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Merges meta with precedence: default < per-call. Returns undefined if
|
|
66
|
+
* no meta fields would be set.
|
|
67
|
+
*
|
|
68
|
+
* The cast is load-bearing: when a developer augments `RequestMeta` with
|
|
69
|
+
* required fields, spread of two `RequestMeta | undefined` values widens to
|
|
70
|
+
* a partial shape, which TypeScript can't prove satisfies `RequestMeta`.
|
|
71
|
+
* At runtime, the merged object carries whichever keys the caller supplied —
|
|
72
|
+
* the contract is "if you declare required fields in RequestMeta, supply them
|
|
73
|
+
* somewhere (defaults or per-call)."
|
|
74
|
+
*/
|
|
75
|
+
export function resolveMeta(
|
|
76
|
+
defaults: ProcedureCallDefaults | undefined,
|
|
77
|
+
options: ProcedureCallOptions | undefined,
|
|
78
|
+
): RequestMeta | undefined {
|
|
79
|
+
const defaultMeta = defaults?.meta
|
|
80
|
+
const callMeta = options?.meta
|
|
81
|
+
|
|
82
|
+
if (!defaultMeta && !callMeta) return undefined
|
|
83
|
+
|
|
84
|
+
return { ...defaultMeta, ...callMeta } as RequestMeta
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Applies resolved default + per-call options to an AdapterRequest.
|
|
89
|
+
*
|
|
90
|
+
* Runs before hooks, so `onBeforeRequest` observes the merged request and can
|
|
91
|
+
* still override any field.
|
|
92
|
+
*
|
|
93
|
+
* Headers produced by the request builder (e.g., `schema.input.headers` for
|
|
94
|
+
* API routes) are preserved; resolved headers merge underneath them so the
|
|
95
|
+
* route-declared headers win, matching the adapter.config → defaults → call
|
|
96
|
+
* → route-declared → hooks precedence chain documented in the types.
|
|
97
|
+
*/
|
|
98
|
+
export function applyRequestOptions(
|
|
99
|
+
request: AdapterRequest,
|
|
100
|
+
defaults: ProcedureCallDefaults | undefined,
|
|
101
|
+
options: ProcedureCallOptions | undefined,
|
|
102
|
+
): AdapterRequest {
|
|
103
|
+
const signal = resolveSignal(defaults, options)
|
|
104
|
+
const resolvedHeaders = resolveHeaders(defaults, options)
|
|
105
|
+
const meta = resolveMeta(defaults, options)
|
|
106
|
+
|
|
107
|
+
const headers =
|
|
108
|
+
resolvedHeaders || request.headers
|
|
109
|
+
? { ...resolvedHeaders, ...request.headers }
|
|
110
|
+
: undefined
|
|
111
|
+
|
|
112
|
+
return { ...request, headers, signal, meta }
|
|
113
|
+
}
|