ts-procedures 5.7.0 → 5.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +185 -29
- package/agent_config/claude-code/skills/guide/SKILL.md +1 -1
- package/agent_config/claude-code/skills/guide/api-reference.md +203 -3
- package/agent_config/claude-code/skills/guide/patterns.md +108 -0
- package/agent_config/copilot/copilot-instructions.md +87 -0
- package/agent_config/cursor/cursorrules +87 -0
- package/build/implementations/http/doc-registry.test.js +27 -1
- package/build/implementations/http/doc-registry.test.js.map +1 -1
- package/build/implementations/http/express-rpc/index.js +1 -0
- package/build/implementations/http/express-rpc/index.js.map +1 -1
- package/build/implementations/http/express-rpc/index.test.js +1 -1
- package/build/implementations/http/express-rpc/index.test.js.map +1 -1
- package/build/implementations/http/hono-api/index.js +2 -0
- package/build/implementations/http/hono-api/index.js.map +1 -1
- package/build/implementations/http/hono-api/index.test.js +9 -0
- package/build/implementations/http/hono-api/index.test.js.map +1 -1
- package/build/implementations/http/hono-rpc/index.js +1 -0
- package/build/implementations/http/hono-rpc/index.js.map +1 -1
- package/build/implementations/http/hono-rpc/index.test.js +1 -1
- package/build/implementations/http/hono-rpc/index.test.js.map +1 -1
- package/build/implementations/http/hono-stream/index.js +17 -1
- package/build/implementations/http/hono-stream/index.js.map +1 -1
- package/build/implementations/http/hono-stream/index.test.js +75 -6
- package/build/implementations/http/hono-stream/index.test.js.map +1 -1
- package/build/implementations/http/hono-stream/types.d.ts +4 -13
- package/build/implementations/types.d.ts +5 -0
- package/build/index.d.ts +2 -6
- package/build/index.js +8 -1
- package/build/index.js.map +1 -1
- package/build/index.test.js +4 -10
- package/build/index.test.js.map +1 -1
- package/package.json +21 -3
- package/src/client/call.ts +74 -0
- package/src/client/errors.ts +43 -0
- package/src/client/fetch-adapter.ts +191 -0
- package/src/client/hooks.ts +65 -0
- package/src/client/index.ts +121 -0
- package/src/client/request-builder.ts +73 -0
- package/src/client/stream.ts +164 -0
- package/src/client/types.ts +103 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { executeCall } from './call.js'
|
|
2
|
+
import { executeStream, createTypedStream } from './stream.js'
|
|
3
|
+
import type {
|
|
4
|
+
CreateClientConfig,
|
|
5
|
+
ClientInstance,
|
|
6
|
+
CallDescriptor,
|
|
7
|
+
StreamDescriptor,
|
|
8
|
+
ProcedureCallOptions,
|
|
9
|
+
TypedStream,
|
|
10
|
+
} from './types.js'
|
|
11
|
+
|
|
12
|
+
// ── createClient ──────────────────────────────────────────
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Creates a typed client from a config object.
|
|
16
|
+
*
|
|
17
|
+
* The `scopes` callback receives a `ClientInstance` and returns the typed
|
|
18
|
+
* scope bindings (e.g., `{ users: { getUser, createUser }, posts: { ... } }`).
|
|
19
|
+
* The return value of `createClient` is the scopes object.
|
|
20
|
+
*
|
|
21
|
+
* `client.stream()` must return `TypedStream` synchronously even though
|
|
22
|
+
* `executeStream` is async. We achieve this by creating a deferred TypedStream:
|
|
23
|
+
* - A deferred async generator awaits `executeStream` internally, then forwards
|
|
24
|
+
* yields from the inner stream.
|
|
25
|
+
* - The outer `.result` is wired up to the inner stream's `.result`.
|
|
26
|
+
*/
|
|
27
|
+
export function createClient<TScopes>(config: CreateClientConfig<TScopes>): TScopes {
|
|
28
|
+
const { adapter, basePath, hooks: globalHooks = {}, scopes } = config
|
|
29
|
+
|
|
30
|
+
const instance: ClientInstance = {
|
|
31
|
+
basePath,
|
|
32
|
+
adapter,
|
|
33
|
+
hooks: globalHooks,
|
|
34
|
+
|
|
35
|
+
call<TResponse>(
|
|
36
|
+
descriptor: CallDescriptor,
|
|
37
|
+
options?: ProcedureCallOptions
|
|
38
|
+
): Promise<TResponse> {
|
|
39
|
+
return executeCall<TResponse>(descriptor, basePath, adapter, globalHooks, options)
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
stream<TYield, TReturn>(
|
|
43
|
+
descriptor: StreamDescriptor,
|
|
44
|
+
options?: ProcedureCallOptions
|
|
45
|
+
): TypedStream<TYield, TReturn> {
|
|
46
|
+
// executeStream is async but stream() must be synchronous.
|
|
47
|
+
// Create a deferred TypedStream that wraps the async executeStream call.
|
|
48
|
+
|
|
49
|
+
let resolveResult: (value: TReturn) => void
|
|
50
|
+
let rejectResult: (reason: unknown) => void
|
|
51
|
+
|
|
52
|
+
const resultPromise = new Promise<TReturn>((resolve, reject) => {
|
|
53
|
+
resolveResult = resolve
|
|
54
|
+
rejectResult = reject
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
// The deferred async generator: awaits executeStream, then forwards
|
|
58
|
+
async function* deferredGenerator(): AsyncGenerator<TYield> {
|
|
59
|
+
let innerStream: TypedStream<TYield, TReturn>
|
|
60
|
+
try {
|
|
61
|
+
innerStream = await executeStream<TYield, TReturn>(
|
|
62
|
+
descriptor,
|
|
63
|
+
basePath,
|
|
64
|
+
adapter,
|
|
65
|
+
globalHooks,
|
|
66
|
+
options
|
|
67
|
+
)
|
|
68
|
+
} catch (err) {
|
|
69
|
+
rejectResult(err)
|
|
70
|
+
throw err
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Wire up .result from the inner stream
|
|
74
|
+
innerStream.result.then(resolveResult, rejectResult)
|
|
75
|
+
|
|
76
|
+
for await (const item of innerStream) {
|
|
77
|
+
yield item
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const iterator = deferredGenerator()
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
[Symbol.asyncIterator]() {
|
|
85
|
+
return iterator
|
|
86
|
+
},
|
|
87
|
+
result: resultPromise,
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return scopes(instance)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ── Barrel exports ────────────────────────────────────────
|
|
96
|
+
|
|
97
|
+
export type {
|
|
98
|
+
ClientAdapter,
|
|
99
|
+
AdapterRequest,
|
|
100
|
+
AdapterResponse,
|
|
101
|
+
AdapterStreamResponse,
|
|
102
|
+
ClientHooks,
|
|
103
|
+
BeforeRequestContext,
|
|
104
|
+
AfterResponseContext,
|
|
105
|
+
ErrorContext,
|
|
106
|
+
CallDescriptor,
|
|
107
|
+
StreamDescriptor,
|
|
108
|
+
TypedStream,
|
|
109
|
+
ClientInstance,
|
|
110
|
+
ProcedureCallOptions,
|
|
111
|
+
CreateClientConfig,
|
|
112
|
+
} from './types.js'
|
|
113
|
+
|
|
114
|
+
export { ClientRequestError, ClientPathParamError, ClientStreamError } from './errors.js'
|
|
115
|
+
|
|
116
|
+
export { createTypedStream } from './stream.js'
|
|
117
|
+
export { executeCall } from './call.js'
|
|
118
|
+
export { executeStream } from './stream.js'
|
|
119
|
+
|
|
120
|
+
export { createFetchAdapter } from './fetch-adapter.js'
|
|
121
|
+
export type { FetchAdapterConfig } from './fetch-adapter.js'
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { ClientPathParamError } from './errors.js'
|
|
2
|
+
import type { AdapterRequest, CallDescriptor } from './types.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Replaces `:paramName` segments in `path` with URI-encoded values from `params`.
|
|
6
|
+
* Throws `ClientPathParamError` if a required segment is missing from `params`.
|
|
7
|
+
*/
|
|
8
|
+
export function interpolatePath(
|
|
9
|
+
path: string,
|
|
10
|
+
params: Record<string, unknown>,
|
|
11
|
+
procedureName: string
|
|
12
|
+
): string {
|
|
13
|
+
return path.replace(/:([A-Za-z_][A-Za-z0-9_]*)/g, (_match, key: string) => {
|
|
14
|
+
const value = params[key]
|
|
15
|
+
if (value === undefined || value === null) {
|
|
16
|
+
throw new ClientPathParamError(key, path, procedureName)
|
|
17
|
+
}
|
|
18
|
+
return encodeURIComponent(String(value))
|
|
19
|
+
})
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Builds an `AdapterRequest` from a `CallDescriptor` and a base URL.
|
|
24
|
+
*
|
|
25
|
+
* - `kind === 'rpc'` or `kind === 'stream'`: params are flat — sent as the JSON body.
|
|
26
|
+
* - `kind === 'api'`: params are structured channels — `pathParams`, `query`, `body`, `headers`.
|
|
27
|
+
*/
|
|
28
|
+
export function buildAdapterRequest(descriptor: CallDescriptor, basePath: string): AdapterRequest {
|
|
29
|
+
const { name, path, method, kind, params } = descriptor
|
|
30
|
+
|
|
31
|
+
if (kind === 'rpc' || kind === 'stream') {
|
|
32
|
+
return {
|
|
33
|
+
url: `${basePath}${path}`,
|
|
34
|
+
method,
|
|
35
|
+
body: params,
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// kind === 'api' — params are structured channels
|
|
40
|
+
const structured = (params ?? {}) as {
|
|
41
|
+
pathParams?: Record<string, unknown>
|
|
42
|
+
query?: Record<string, unknown>
|
|
43
|
+
body?: unknown
|
|
44
|
+
headers?: Record<string, string>
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Interpolate path params
|
|
48
|
+
const interpolatedPath = structured.pathParams
|
|
49
|
+
? interpolatePath(path, structured.pathParams, name)
|
|
50
|
+
: path
|
|
51
|
+
|
|
52
|
+
// Build query string
|
|
53
|
+
let url = `${basePath}${interpolatedPath}`
|
|
54
|
+
if (structured.query && Object.keys(structured.query).length > 0) {
|
|
55
|
+
const searchParams = new URLSearchParams(
|
|
56
|
+
Object.entries(structured.query).map(([k, v]) => [k, String(v)])
|
|
57
|
+
)
|
|
58
|
+
url = `${url}?${searchParams.toString()}`
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Build headers
|
|
62
|
+
const headers =
|
|
63
|
+
structured.headers && Object.keys(structured.headers).length > 0
|
|
64
|
+
? structured.headers
|
|
65
|
+
: undefined
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
url,
|
|
69
|
+
method,
|
|
70
|
+
headers,
|
|
71
|
+
body: structured.body,
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { buildAdapterRequest } from './request-builder.js'
|
|
2
|
+
import { runBeforeRequest, runAfterResponse, runOnError } from './hooks.js'
|
|
3
|
+
import { ClientRequestError } from './errors.js'
|
|
4
|
+
import type {
|
|
5
|
+
ClientAdapter,
|
|
6
|
+
ClientHooks,
|
|
7
|
+
StreamDescriptor,
|
|
8
|
+
TypedStream,
|
|
9
|
+
AdapterResponse,
|
|
10
|
+
} from './types.js'
|
|
11
|
+
|
|
12
|
+
// ── SSE item shape ────────────────────────────────────────
|
|
13
|
+
|
|
14
|
+
interface SSEItem {
|
|
15
|
+
data: unknown
|
|
16
|
+
event?: string
|
|
17
|
+
id?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// ── createTypedStream ─────────────────────────────────────
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Wraps an AsyncIterable into a TypedStream.
|
|
24
|
+
*
|
|
25
|
+
* SSE mode: each item is `{ data, event?, id? }`.
|
|
26
|
+
* - If `event === 'return'`, the data resolves `.result` and is NOT yielded.
|
|
27
|
+
* - Otherwise, `data` is yielded.
|
|
28
|
+
*
|
|
29
|
+
* Text mode: each item is yielded as-is.
|
|
30
|
+
* - `.result` resolves to `void` on completion.
|
|
31
|
+
*
|
|
32
|
+
* On error: `.result` rejects and the error is re-thrown from the async iterator.
|
|
33
|
+
*/
|
|
34
|
+
export function createTypedStream<TYield, TReturn = void>(
|
|
35
|
+
source: AsyncIterable<unknown>,
|
|
36
|
+
streamMode: 'sse' | 'text'
|
|
37
|
+
): TypedStream<TYield, TReturn> {
|
|
38
|
+
let resolveResult: (value: TReturn) => void
|
|
39
|
+
let rejectResult: (reason: unknown) => void
|
|
40
|
+
|
|
41
|
+
const resultPromise = new Promise<TReturn>((resolve, reject) => {
|
|
42
|
+
resolveResult = resolve
|
|
43
|
+
rejectResult = reject
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
async function* generate(): AsyncGenerator<TYield> {
|
|
47
|
+
try {
|
|
48
|
+
if (streamMode === 'sse') {
|
|
49
|
+
let returnValue: TReturn | undefined
|
|
50
|
+
let hasReturn = false
|
|
51
|
+
|
|
52
|
+
for await (const item of source) {
|
|
53
|
+
const sseItem = item as SSEItem
|
|
54
|
+
if (sseItem.event === 'return') {
|
|
55
|
+
returnValue = sseItem.data as TReturn
|
|
56
|
+
hasReturn = true
|
|
57
|
+
} else {
|
|
58
|
+
yield sseItem.data as TYield
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Resolve result after iteration completes
|
|
63
|
+
if (hasReturn) {
|
|
64
|
+
resolveResult(returnValue as TReturn)
|
|
65
|
+
} else {
|
|
66
|
+
resolveResult(undefined as TReturn)
|
|
67
|
+
}
|
|
68
|
+
} else {
|
|
69
|
+
// text mode: yield each item as-is
|
|
70
|
+
for await (const item of source) {
|
|
71
|
+
yield item as TYield
|
|
72
|
+
}
|
|
73
|
+
resolveResult(undefined as TReturn)
|
|
74
|
+
}
|
|
75
|
+
} catch (err) {
|
|
76
|
+
rejectResult(err)
|
|
77
|
+
throw err
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const iterator = generate()
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
[Symbol.asyncIterator]() {
|
|
85
|
+
return iterator
|
|
86
|
+
},
|
|
87
|
+
result: resultPromise,
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ── executeStream ─────────────────────────────────────────
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Executes a streaming procedure call through the adapter.
|
|
95
|
+
*
|
|
96
|
+
* Flow:
|
|
97
|
+
* 1. Build AdapterRequest from descriptor
|
|
98
|
+
* 2. Run onBeforeRequest hooks
|
|
99
|
+
* 3. Call adapter.stream()
|
|
100
|
+
* 4. On adapter error: run onError hooks, re-throw
|
|
101
|
+
* 5. Run onAfterResponse immediately (before iteration), body is null
|
|
102
|
+
* 6. If non-2xx: throw ClientRequestError
|
|
103
|
+
* 7. Return createTypedStream(streamResponse.body, descriptor.streamMode)
|
|
104
|
+
*/
|
|
105
|
+
export async function executeStream<TYield, TReturn = void>(
|
|
106
|
+
descriptor: StreamDescriptor,
|
|
107
|
+
basePath: string,
|
|
108
|
+
adapter: ClientAdapter,
|
|
109
|
+
globalHooks: ClientHooks,
|
|
110
|
+
localHooks: ClientHooks | undefined
|
|
111
|
+
): Promise<TypedStream<TYield, TReturn>> {
|
|
112
|
+
// 1. Build the initial request
|
|
113
|
+
let request = buildAdapterRequest(descriptor, basePath)
|
|
114
|
+
|
|
115
|
+
// 2. Run before-request hooks
|
|
116
|
+
const beforeCtx = await runBeforeRequest(
|
|
117
|
+
{ procedureName: descriptor.name, scope: descriptor.scope, request },
|
|
118
|
+
globalHooks,
|
|
119
|
+
localHooks
|
|
120
|
+
)
|
|
121
|
+
request = beforeCtx.request
|
|
122
|
+
|
|
123
|
+
// 3. Call the adapter
|
|
124
|
+
let streamResponse
|
|
125
|
+
try {
|
|
126
|
+
streamResponse = await adapter.stream(request)
|
|
127
|
+
} catch (err) {
|
|
128
|
+
// 4. On adapter error: run error hooks, re-throw
|
|
129
|
+
await runOnError(
|
|
130
|
+
{ procedureName: descriptor.name, scope: descriptor.scope, request, error: err },
|
|
131
|
+
globalHooks,
|
|
132
|
+
localHooks
|
|
133
|
+
)
|
|
134
|
+
throw err
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Build an AdapterResponse shape for the hooks (body is null for streams at this point)
|
|
138
|
+
const responseForHooks: AdapterResponse = {
|
|
139
|
+
status: streamResponse.status,
|
|
140
|
+
headers: streamResponse.headers,
|
|
141
|
+
body: null,
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// 5. Run after-response hooks immediately (before iteration)
|
|
145
|
+
await runAfterResponse(
|
|
146
|
+
{ procedureName: descriptor.name, scope: descriptor.scope, request, response: responseForHooks },
|
|
147
|
+
globalHooks,
|
|
148
|
+
localHooks
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
// 6. Check status after hooks (hooks may mutate responseForHooks.status)
|
|
152
|
+
if (responseForHooks.status < 200 || responseForHooks.status >= 300) {
|
|
153
|
+
throw new ClientRequestError({
|
|
154
|
+
status: responseForHooks.status,
|
|
155
|
+
headers: responseForHooks.headers,
|
|
156
|
+
body: responseForHooks.body,
|
|
157
|
+
procedureName: descriptor.name,
|
|
158
|
+
scope: descriptor.scope,
|
|
159
|
+
})
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// 7. Return the typed stream
|
|
163
|
+
return createTypedStream<TYield, TReturn>(streamResponse.body, descriptor.streamMode)
|
|
164
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// ── Adapter ──────────────────────────────────────────────
|
|
2
|
+
|
|
3
|
+
export interface ClientAdapter {
|
|
4
|
+
request(config: AdapterRequest): Promise<AdapterResponse>
|
|
5
|
+
stream(config: AdapterRequest): Promise<AdapterStreamResponse>
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface AdapterRequest {
|
|
9
|
+
url: string
|
|
10
|
+
method: string
|
|
11
|
+
headers?: Record<string, string>
|
|
12
|
+
body?: unknown
|
|
13
|
+
signal?: AbortSignal
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface AdapterResponse {
|
|
17
|
+
status: number
|
|
18
|
+
headers: Record<string, string>
|
|
19
|
+
body: unknown
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface AdapterStreamResponse {
|
|
23
|
+
status: number
|
|
24
|
+
headers: Record<string, string>
|
|
25
|
+
body: AsyncIterable<unknown>
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// ── Hooks ────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
export interface ClientHooks {
|
|
31
|
+
onBeforeRequest?(context: BeforeRequestContext): BeforeRequestContext | Promise<BeforeRequestContext>
|
|
32
|
+
onAfterResponse?(context: AfterResponseContext): void | Promise<void>
|
|
33
|
+
onError?(context: ErrorContext): void | Promise<void>
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface BeforeRequestContext {
|
|
37
|
+
procedureName: string
|
|
38
|
+
scope: string
|
|
39
|
+
request: AdapterRequest
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export interface AfterResponseContext {
|
|
43
|
+
procedureName: string
|
|
44
|
+
scope: string
|
|
45
|
+
request: AdapterRequest
|
|
46
|
+
response: AdapterResponse
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface ErrorContext {
|
|
50
|
+
procedureName: string
|
|
51
|
+
scope: string
|
|
52
|
+
request: AdapterRequest
|
|
53
|
+
error: unknown
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ── Descriptors ──────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
export interface CallDescriptor {
|
|
59
|
+
name: string
|
|
60
|
+
scope: string
|
|
61
|
+
path: string
|
|
62
|
+
method: string
|
|
63
|
+
kind: 'rpc' | 'api' | 'stream'
|
|
64
|
+
params: unknown
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface StreamDescriptor extends CallDescriptor {
|
|
68
|
+
kind: 'stream'
|
|
69
|
+
streamMode: 'sse' | 'text'
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ── TypedStream ──────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
export interface TypedStream<TYield, TReturn = void> extends AsyncIterable<TYield> {
|
|
75
|
+
/**
|
|
76
|
+
* Resolves when the stream completes with the final return value.
|
|
77
|
+
* Rejects if the stream errors before completing.
|
|
78
|
+
* Note: iteration must begin (via for-await) for this promise to settle,
|
|
79
|
+
* since resolution depends on the async generator running to completion.
|
|
80
|
+
*/
|
|
81
|
+
result: Promise<TReturn>
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ── Client Instance ──────────────────────────────────────
|
|
85
|
+
|
|
86
|
+
export type ProcedureCallOptions = ClientHooks
|
|
87
|
+
|
|
88
|
+
export interface ClientInstance {
|
|
89
|
+
basePath: string
|
|
90
|
+
adapter: ClientAdapter
|
|
91
|
+
hooks: ClientHooks
|
|
92
|
+
call<TResponse>(descriptor: CallDescriptor, options?: ProcedureCallOptions): Promise<TResponse>
|
|
93
|
+
stream<TYield, TReturn>(descriptor: StreamDescriptor, options?: ProcedureCallOptions): TypedStream<TYield, TReturn>
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ── createClient Config ──────────────────────────────────
|
|
97
|
+
|
|
98
|
+
export interface CreateClientConfig<TScopes> {
|
|
99
|
+
adapter: ClientAdapter
|
|
100
|
+
basePath: string
|
|
101
|
+
scopes: (client: ClientInstance) => TScopes
|
|
102
|
+
hooks?: ClientHooks
|
|
103
|
+
}
|