@kibinrpc/client 0.0.3 → 0.0.4
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 +171 -0
- package/dist/index.d.ts +1 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +85 -51
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# @kibinrpc/client
|
|
2
|
+
|
|
3
|
+
Type-safe fetch client for [kibinrpc](../../README.md) — fully inferred from your server router, with automatic batching, retry, and interceptors.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm install @kibinrpc/client
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { createKibinClient } from '@kibinrpc/client'
|
|
15
|
+
import type { AppRouter } from './server/router'
|
|
16
|
+
|
|
17
|
+
const client = createKibinClient<AppRouter>({
|
|
18
|
+
baseUrl: '/api/rpc',
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
// Return types are inferred from the server — no manual annotations
|
|
22
|
+
const user = await client.user.getUser('1')
|
|
23
|
+
const posts = await client.post.listPosts()
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Automatic batching
|
|
27
|
+
|
|
28
|
+
Concurrent calls are automatically coalesced into a single HTTP request:
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
// Both calls happen in the same tick → sent as one batched request
|
|
32
|
+
const [users, posts] = await Promise.all([
|
|
33
|
+
client.user.listUsers(),
|
|
34
|
+
client.post.listPosts(),
|
|
35
|
+
])
|
|
36
|
+
|
|
37
|
+
// Sequential calls → two separate requests
|
|
38
|
+
const user = await client.user.getUser('1')
|
|
39
|
+
const posts = await client.post.listPosts()
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
No configuration required. The server receives either a single object or an array — it handles both automatically.
|
|
43
|
+
|
|
44
|
+
## Configuration
|
|
45
|
+
|
|
46
|
+
```ts
|
|
47
|
+
const client = createKibinClient<AppRouter>({
|
|
48
|
+
baseUrl: '/api/rpc',
|
|
49
|
+
|
|
50
|
+
// Static headers sent with every request
|
|
51
|
+
headers: {
|
|
52
|
+
'X-App-Version': '1.0.0',
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
// Retry on network errors and 5xx responses
|
|
56
|
+
retry: {
|
|
57
|
+
attempts: 3, // total attempts (default: 3)
|
|
58
|
+
delay: 300, // base delay in ms, doubles each retry (default: 300)
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
interceptors: {
|
|
62
|
+
request: (ctx) => ctx,
|
|
63
|
+
response: (ctx) => ctx.data,
|
|
64
|
+
error: (ctx) => { throw ctx.error },
|
|
65
|
+
},
|
|
66
|
+
})
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Interceptors
|
|
70
|
+
|
|
71
|
+
### `request`
|
|
72
|
+
|
|
73
|
+
Runs before every call. Use it to attach auth tokens or modify arguments:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
interceptors: {
|
|
77
|
+
request(ctx) {
|
|
78
|
+
return { ...ctx, args: [{ ...ctx.args[0], token: getToken() }] }
|
|
79
|
+
},
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### `response`
|
|
84
|
+
|
|
85
|
+
Runs after every successful call. Use it to transform or log responses:
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
interceptors: {
|
|
89
|
+
response({ namespace, method, data }) {
|
|
90
|
+
console.log(`← ${namespace}.${method}`, data)
|
|
91
|
+
return data
|
|
92
|
+
},
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### `error`
|
|
97
|
+
|
|
98
|
+
Runs on every failed call (after retries are exhausted). Return a fallback value or rethrow:
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
interceptors: {
|
|
102
|
+
error({ error }) {
|
|
103
|
+
if (error.code === 'UNAUTHORIZED') {
|
|
104
|
+
window.location.href = '/login'
|
|
105
|
+
}
|
|
106
|
+
throw error
|
|
107
|
+
},
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Error handling
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
import { isKibinError } from '@kibinrpc/client'
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
await client.user.getUser('999')
|
|
118
|
+
} catch (err) {
|
|
119
|
+
if (isKibinError(err)) {
|
|
120
|
+
console.log(err.code) // e.g. 'NOT_FOUND'
|
|
121
|
+
console.log(err.message) // e.g. 'User not found'
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Retry behaviour
|
|
127
|
+
|
|
128
|
+
| Scenario | Single call | Batched call |
|
|
129
|
+
|---|---|---|
|
|
130
|
+
| Network error | retry (all attempts) | retry whole batch |
|
|
131
|
+
| HTTP 5xx | retry (all attempts) | retry only failed items |
|
|
132
|
+
| HTTP 4xx | no retry, throw immediately | no retry, reject that item |
|
|
133
|
+
|
|
134
|
+
## API
|
|
135
|
+
|
|
136
|
+
### `createKibinClient<Router>(config)`
|
|
137
|
+
|
|
138
|
+
Returns a typed proxy. Every namespace from your router becomes a property, every registered action becomes an async method.
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
const client = createKibinClient<AppRouter>({ baseUrl: '/api/rpc' })
|
|
142
|
+
|
|
143
|
+
client.user.getUser('1') // Promise<User>
|
|
144
|
+
client.post.listPosts() // Promise<Post[]>
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### `isKibinError(err)`
|
|
148
|
+
|
|
149
|
+
Type guard for `KibinError`:
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
import { isKibinError, KibinError } from '@kibinrpc/client'
|
|
153
|
+
|
|
154
|
+
isKibinError(err) // err is KibinError
|
|
155
|
+
err.code // string
|
|
156
|
+
err.message // string
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Exported types
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
import type {
|
|
163
|
+
KibinClient,
|
|
164
|
+
KibinClientConfig,
|
|
165
|
+
ClientInterceptors,
|
|
166
|
+
RequestCtx,
|
|
167
|
+
ResponseCtx,
|
|
168
|
+
ErrorCtx,
|
|
169
|
+
RetryConfig,
|
|
170
|
+
} from '@kibinrpc/client'
|
|
171
|
+
```
|
package/dist/index.d.ts
CHANGED
|
@@ -11,10 +11,7 @@ type ServiceClient<T> = { [K in keyof T as T[K] extends ((...args: never[]) => u
|
|
|
11
11
|
type ExtractServices<T> = T extends {
|
|
12
12
|
services: infer S;
|
|
13
13
|
} ? S : never;
|
|
14
|
-
type
|
|
15
|
-
type KibinClient<Router> = { [K in keyof ExtractServices<Router>]: ServiceClient<ExtractServices<Router>[K]> } & {
|
|
16
|
-
$batch: BatchFn;
|
|
17
|
-
};
|
|
14
|
+
type KibinClient<Router> = { [K in keyof ExtractServices<Router>]: ServiceClient<ExtractServices<Router>[K]> };
|
|
18
15
|
interface RequestCtx {
|
|
19
16
|
namespace: string;
|
|
20
17
|
method: string;
|
|
@@ -39,8 +36,6 @@ interface RetryConfig {
|
|
|
39
36
|
}
|
|
40
37
|
interface KibinClientConfig {
|
|
41
38
|
baseUrl: string;
|
|
42
|
-
/** Batch endpoint URL. Default: `${baseUrl}/batch` */
|
|
43
|
-
batchUrl?: string;
|
|
44
39
|
headers?: Record<string, string>;
|
|
45
40
|
retry?: RetryConfig;
|
|
46
41
|
interceptors?: ClientInterceptors;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/errors.ts","../src/types.ts","../src/client.ts"],"mappings":";cAAa,UAAA,SAAmB,KAAK;EAAA,SAC3B,IAAA;cAEG,IAAA,UAAc,OAAA;AAAA;AAAA,iBAOX,YAAA,CAAa,KAAA,YAAiB,KAAA,IAAS,UAAU;;;KCR5D,cAAA,MAAoB,CAAA,cAAc,IAAA,qCAChC,IAAA,EAAM,IAAA,KAAS,OAAA,CAAQ,OAAA,CAAQ,MAAA;AAAA,KAGjC,aAAA,oBACQ,CAAA,IAAK,CAAA,CAAE,CAAA,eAAe,IAAA,yBAA4B,CAAA,WAAY,cAAA,CAAe,CAAA,CAAE,CAAA;AAAA,KAGvF,eAAA,MAAqB,CAAC;EAAW,QAAA;AAAA,IAAsB,CAAA;AAAA,KAEhD,
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/errors.ts","../src/types.ts","../src/client.ts"],"mappings":";cAAa,UAAA,SAAmB,KAAK;EAAA,SAC3B,IAAA;cAEG,IAAA,UAAc,OAAA;AAAA;AAAA,iBAOX,YAAA,CAAa,KAAA,YAAiB,KAAA,IAAS,UAAU;;;KCR5D,cAAA,MAAoB,CAAA,cAAc,IAAA,qCAChC,IAAA,EAAM,IAAA,KAAS,OAAA,CAAQ,OAAA,CAAQ,MAAA;AAAA,KAGjC,aAAA,oBACQ,CAAA,IAAK,CAAA,CAAE,CAAA,eAAe,IAAA,yBAA4B,CAAA,WAAY,cAAA,CAAe,CAAA,CAAE,CAAA;AAAA,KAGvF,eAAA,MAAqB,CAAC;EAAW,QAAA;AAAA,IAAsB,CAAA;AAAA,KAEhD,WAAA,yBACC,eAAA,CAAgB,MAAA,IAAU,aAAA,CAAc,eAAA,CAAgB,MAAA,EAAQ,CAAA;AAAA,UAG5D,UAAA;EAChB,SAAA;EACA,MAAA;EACA,IAAA;AAAA;AAAA,UAGgB,WAAA,SAAoB,UAAU;EAC9C,IAAI;AAAA;AAAA,UAGY,QAAA,SAAiB,UAAU;EAC3C,KAAA,EAAO,UAAA;AAAA;AAAA,UAGS,kBAAA;EAChB,OAAA,IAAW,GAAA,EAAK,UAAA,KAAe,UAAA,GAAa,OAAA,CAAQ,UAAA;EACpD,QAAA,IAAY,GAAA,EAAK,WAAA,eAA0B,OAAA;EAC3C,KAAA,IAAS,GAAA,EAAK,QAAA,eAAuB,OAAA;AAAA;AAAA,UAGrB,WAAA;EAjCJ;EAmCZ,QAAA;EAnC6B;EAqC7B,KAAK;AAAA;AAAA,UAGW,iBAAA;EAChB,OAAA;EACA,OAAA,GAAU,MAAA;EACV,KAAA,GAAQ,WAAA;EACR,YAAA,GAAe,kBAAA;AAAA;;;iBCtCA,iBAAA,QAAA,CAA0B,MAAA,EAAQ,iBAAA,GAAoB,WAAA,CAAY,MAAA"}
|
package/dist/index.js
CHANGED
|
@@ -19,9 +19,9 @@ const RETRY_DEFAULTS = {
|
|
|
19
19
|
function createKibinClient(config) {
|
|
20
20
|
const maxAttempts = config.retry?.attempts ?? RETRY_DEFAULTS.attempts;
|
|
21
21
|
const baseDelay = config.retry?.delay ?? RETRY_DEFAULTS.delay;
|
|
22
|
-
const batchUrl = config.batchUrl ?? `${config.baseUrl}/batch`;
|
|
23
22
|
const { interceptors } = config;
|
|
24
|
-
let
|
|
23
|
+
let pendingBatch = [];
|
|
24
|
+
let flushScheduled = false;
|
|
25
25
|
async function rpcCall(namespace, method, args) {
|
|
26
26
|
let ctx = {
|
|
27
27
|
namespace,
|
|
@@ -29,16 +29,28 @@ function createKibinClient(config) {
|
|
|
29
29
|
args
|
|
30
30
|
};
|
|
31
31
|
if (interceptors?.request) ctx = await interceptors.request(ctx);
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
resolve,
|
|
38
|
-
reject
|
|
39
|
-
});
|
|
32
|
+
return new Promise((resolve, reject) => {
|
|
33
|
+
pendingBatch.push({
|
|
34
|
+
ctx,
|
|
35
|
+
resolve,
|
|
36
|
+
reject
|
|
40
37
|
});
|
|
41
|
-
|
|
38
|
+
if (!flushScheduled) {
|
|
39
|
+
flushScheduled = true;
|
|
40
|
+
queueMicrotask(flush);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
function flush() {
|
|
45
|
+
const batch = pendingBatch;
|
|
46
|
+
pendingBatch = [];
|
|
47
|
+
flushScheduled = false;
|
|
48
|
+
if (batch.length === 0) return;
|
|
49
|
+
if (batch.length === 1) flushSingle(batch[0]);
|
|
50
|
+
else flushBatch(batch);
|
|
51
|
+
}
|
|
52
|
+
async function flushSingle(item) {
|
|
53
|
+
const { ctx } = item;
|
|
42
54
|
let lastError;
|
|
43
55
|
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
44
56
|
if (attempt > 0) await new Promise((resolve) => setTimeout(resolve, baseDelay * 2 ** (attempt - 1)));
|
|
@@ -58,63 +70,85 @@ function createKibinClient(config) {
|
|
|
58
70
|
lastError = err;
|
|
59
71
|
continue;
|
|
60
72
|
}
|
|
61
|
-
if (interceptors?.error)
|
|
73
|
+
if (interceptors?.error) item.resolve(await interceptors.error({
|
|
62
74
|
...ctx,
|
|
63
75
|
error: err
|
|
64
|
-
});
|
|
65
|
-
|
|
76
|
+
}));
|
|
77
|
+
else item.reject(err);
|
|
78
|
+
return;
|
|
66
79
|
}
|
|
67
|
-
if (interceptors?.response)
|
|
80
|
+
if (interceptors?.response) item.resolve(await interceptors.response({
|
|
68
81
|
...ctx,
|
|
69
82
|
data: result.data
|
|
70
|
-
});
|
|
71
|
-
|
|
83
|
+
}));
|
|
84
|
+
else item.resolve(result.data);
|
|
85
|
+
return;
|
|
72
86
|
} catch (err) {
|
|
73
|
-
if (err instanceof KibinError)
|
|
87
|
+
if (err instanceof KibinError) {
|
|
88
|
+
item.reject(err);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
74
91
|
lastError = err;
|
|
75
92
|
}
|
|
76
93
|
}
|
|
77
|
-
if (lastError instanceof KibinError && interceptors?.error)
|
|
94
|
+
if (lastError instanceof KibinError && interceptors?.error) item.resolve(await interceptors.error({
|
|
78
95
|
...ctx,
|
|
79
96
|
error: lastError
|
|
80
|
-
});
|
|
81
|
-
|
|
97
|
+
}));
|
|
98
|
+
else item.reject(lastError);
|
|
82
99
|
}
|
|
83
|
-
async function
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
100
|
+
async function flushBatch(batch) {
|
|
101
|
+
let pending = [...batch];
|
|
102
|
+
const lastErrors = /* @__PURE__ */ new Map();
|
|
103
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
104
|
+
if (attempt > 0) await new Promise((r) => setTimeout(r, baseDelay * 2 ** (attempt - 1)));
|
|
105
|
+
let results;
|
|
106
|
+
try {
|
|
107
|
+
results = await (await fetch(config.baseUrl, {
|
|
108
|
+
method: "POST",
|
|
109
|
+
headers: {
|
|
110
|
+
"Content-Type": "application/json",
|
|
111
|
+
...config.headers
|
|
112
|
+
},
|
|
113
|
+
body: JSON.stringify(pending.map((item) => item.ctx))
|
|
114
|
+
})).json();
|
|
115
|
+
} catch (err) {
|
|
116
|
+
for (const item of pending) lastErrors.set(item, err);
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
const retryItems = [];
|
|
120
|
+
for (let i = 0; i < pending.length; i++) {
|
|
121
|
+
const result = results[i];
|
|
122
|
+
const item = pending[i];
|
|
123
|
+
if (result?.error) {
|
|
124
|
+
const err = new KibinError(result.error.code ?? "RPC_ERROR", result.error.message ?? "RPC Error");
|
|
125
|
+
if (result.status >= 500) {
|
|
126
|
+
retryItems.push(item);
|
|
127
|
+
lastErrors.set(item, err);
|
|
128
|
+
} else if (interceptors?.error) item.resolve(await interceptors.error({
|
|
129
|
+
...item.ctx,
|
|
130
|
+
error: err
|
|
131
|
+
}));
|
|
132
|
+
else item.reject(err);
|
|
133
|
+
} else if (interceptors?.response) item.resolve(await interceptors.response({
|
|
134
|
+
...item.ctx,
|
|
135
|
+
data: result?.data
|
|
110
136
|
}));
|
|
111
|
-
else
|
|
137
|
+
else item.resolve(result?.data);
|
|
112
138
|
}
|
|
139
|
+
pending = retryItems;
|
|
140
|
+
if (pending.length === 0) break;
|
|
141
|
+
}
|
|
142
|
+
for (const item of pending) {
|
|
143
|
+
const err = lastErrors.get(item);
|
|
144
|
+
if (err instanceof KibinError && interceptors?.error) item.resolve(await interceptors.error({
|
|
145
|
+
...item.ctx,
|
|
146
|
+
error: err
|
|
147
|
+
}));
|
|
148
|
+
else item.reject(err);
|
|
113
149
|
}
|
|
114
|
-
return Promise.all(promises);
|
|
115
150
|
}
|
|
116
151
|
return new Proxy({}, { get(_, key) {
|
|
117
|
-
if (key === "$batch") return $batch;
|
|
118
152
|
return new Proxy({}, { get(_, method) {
|
|
119
153
|
return (...args) => rpcCall(key, method, args);
|
|
120
154
|
} });
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/errors.ts","../src/client.ts"],"sourcesContent":["export class KibinError extends Error {\n\treadonly code: string;\n\n\tconstructor(code: string, message: string) {\n\t\tsuper(message);\n\t\tthis.name = 'KibinError';\n\t\tthis.code = code;\n\t}\n}\n\nexport function isKibinError(error: unknown): error is KibinError {\n\treturn error instanceof KibinError;\n}\n","import { KibinError } from './errors.js';\nimport type { KibinClient, KibinClientConfig, RequestCtx } from './types.js';\n\nconst RETRY_DEFAULTS = { attempts: 3, delay: 300 };\n\ntype RpcResult = { data?: unknown; error?: { code?: string; message?: string } };\ntype QueueItem = { ctx: RequestCtx; resolve: (v: unknown) => void; reject: (e: unknown) => void };\n\nexport function createKibinClient<Router>(config: KibinClientConfig): KibinClient<Router> {\n\tconst maxAttempts = config.retry?.attempts ?? RETRY_DEFAULTS.attempts;\n\tconst baseDelay = config.retry?.delay ?? RETRY_DEFAULTS.delay;\n\tconst
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/errors.ts","../src/client.ts"],"sourcesContent":["export class KibinError extends Error {\n\treadonly code: string;\n\n\tconstructor(code: string, message: string) {\n\t\tsuper(message);\n\t\tthis.name = 'KibinError';\n\t\tthis.code = code;\n\t}\n}\n\nexport function isKibinError(error: unknown): error is KibinError {\n\treturn error instanceof KibinError;\n}\n","import { KibinError } from './errors.js';\nimport type { KibinClient, KibinClientConfig, RequestCtx } from './types.js';\n\nconst RETRY_DEFAULTS = { attempts: 3, delay: 300 };\n\ntype RpcResult = { data?: unknown; error?: { code?: string; message?: string } };\ntype BatchRpcResult = RpcResult & { status: number };\ntype QueueItem = { ctx: RequestCtx; resolve: (v: unknown) => void; reject: (e: unknown) => void };\n\nexport function createKibinClient<Router>(config: KibinClientConfig): KibinClient<Router> {\n\tconst maxAttempts = config.retry?.attempts ?? RETRY_DEFAULTS.attempts;\n\tconst baseDelay = config.retry?.delay ?? RETRY_DEFAULTS.delay;\n\tconst { interceptors } = config;\n\n\tlet pendingBatch: QueueItem[] = [];\n\tlet flushScheduled = false;\n\n\tasync function rpcCall(namespace: string, method: string, args: unknown[]): Promise<unknown> {\n\t\tlet ctx: RequestCtx = { namespace, method, args };\n\n\t\tif (interceptors?.request) {\n\t\t\tctx = await interceptors.request(ctx);\n\t\t}\n\n\t\treturn new Promise<unknown>((resolve, reject) => {\n\t\t\tpendingBatch.push({ ctx, resolve, reject });\n\t\t\tif (!flushScheduled) {\n\t\t\t\tflushScheduled = true;\n\t\t\t\tqueueMicrotask(flush);\n\t\t\t}\n\t\t});\n\t}\n\n\tfunction flush() {\n\t\tconst batch = pendingBatch;\n\t\tpendingBatch = [];\n\t\tflushScheduled = false;\n\n\t\tif (batch.length === 0) return;\n\t\tif (batch.length === 1) {\n\t\t\tflushSingle(batch[0]);\n\t\t} else {\n\t\t\tflushBatch(batch);\n\t\t}\n\t}\n\n\tasync function flushSingle(item: QueueItem) {\n\t\tconst { ctx } = item;\n\t\tlet lastError: unknown;\n\n\t\tfor (let attempt = 0; attempt < maxAttempts; attempt++) {\n\t\t\tif (attempt > 0) {\n\t\t\t\tawait new Promise<void>((resolve) => setTimeout(resolve, baseDelay * 2 ** (attempt - 1)));\n\t\t\t}\n\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(config.baseUrl, {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { 'Content-Type': 'application/json', ...config.headers },\n\t\t\t\t\tbody: JSON.stringify(ctx),\n\t\t\t\t});\n\n\t\t\t\tconst result = (await response.json()) as RpcResult;\n\n\t\t\t\tif (result.error) {\n\t\t\t\t\tconst err = new KibinError(\n\t\t\t\t\t\tresult.error.code ?? 'RPC_ERROR',\n\t\t\t\t\t\tresult.error.message ?? 'RPC Error',\n\t\t\t\t\t);\n\t\t\t\t\tif (response.status >= 500) {\n\t\t\t\t\t\tlastError = err;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\tif (interceptors?.error) {\n\t\t\t\t\t\titem.resolve(await interceptors.error({ ...ctx, error: err }));\n\t\t\t\t\t} else {\n\t\t\t\t\t\titem.reject(err);\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (interceptors?.response) {\n\t\t\t\t\titem.resolve(await interceptors.response({ ...ctx, data: result.data }));\n\t\t\t\t} else {\n\t\t\t\t\titem.resolve(result.data);\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t} catch (err) {\n\t\t\t\tif (err instanceof KibinError) {\n\t\t\t\t\titem.reject(err);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tlastError = err;\n\t\t\t}\n\t\t}\n\n\t\tif (lastError instanceof KibinError && interceptors?.error) {\n\t\t\titem.resolve(await interceptors.error({ ...ctx, error: lastError }));\n\t\t} else {\n\t\t\titem.reject(lastError);\n\t\t}\n\t}\n\n\tasync function flushBatch(batch: QueueItem[]) {\n\t\tlet pending = [...batch];\n\t\tconst lastErrors = new Map<QueueItem, unknown>();\n\n\t\tfor (let attempt = 0; attempt < maxAttempts; attempt++) {\n\t\t\tif (attempt > 0) {\n\t\t\t\tawait new Promise<void>((r) => setTimeout(r, baseDelay * 2 ** (attempt - 1)));\n\t\t\t}\n\n\t\t\tlet results: BatchRpcResult[];\n\t\t\ttry {\n\t\t\t\tconst response = await fetch(config.baseUrl, {\n\t\t\t\t\tmethod: 'POST',\n\t\t\t\t\theaders: { 'Content-Type': 'application/json', ...config.headers },\n\t\t\t\t\tbody: JSON.stringify(pending.map((item) => item.ctx)),\n\t\t\t\t});\n\t\t\t\tresults = (await response.json()) as BatchRpcResult[];\n\t\t\t} catch (err) {\n\t\t\t\tfor (const item of pending) lastErrors.set(item, err);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst retryItems: QueueItem[] = [];\n\n\t\t\tfor (let i = 0; i < pending.length; i++) {\n\t\t\t\tconst result = results[i];\n\t\t\t\tconst item = pending[i];\n\n\t\t\t\tif (result?.error) {\n\t\t\t\t\tconst err = new KibinError(\n\t\t\t\t\t\tresult.error.code ?? 'RPC_ERROR',\n\t\t\t\t\t\tresult.error.message ?? 'RPC Error',\n\t\t\t\t\t);\n\t\t\t\t\tif (result.status >= 500) {\n\t\t\t\t\t\tretryItems.push(item);\n\t\t\t\t\t\tlastErrors.set(item, err);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (interceptors?.error) {\n\t\t\t\t\t\t\titem.resolve(await interceptors.error({ ...item.ctx, error: err }));\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\titem.reject(err);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (interceptors?.response) {\n\t\t\t\t\t\titem.resolve(await interceptors.response({ ...item.ctx, data: result?.data }));\n\t\t\t\t\t} else {\n\t\t\t\t\t\titem.resolve(result?.data);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpending = retryItems;\n\t\t\tif (pending.length === 0) break;\n\t\t}\n\n\t\tfor (const item of pending) {\n\t\t\tconst err = lastErrors.get(item);\n\t\t\tif (err instanceof KibinError && interceptors?.error) {\n\t\t\t\titem.resolve(await interceptors.error({ ...item.ctx, error: err }));\n\t\t\t} else {\n\t\t\t\titem.reject(err);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn new Proxy(\n\t\t{},\n\t\t{\n\t\t\tget(_, key: string) {\n\t\t\t\treturn new Proxy(\n\t\t\t\t\t{},\n\t\t\t\t\t{\n\t\t\t\t\t\tget(_, method: string) {\n\t\t\t\t\t\t\treturn (...args: unknown[]) => rpcCall(key, method, args);\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t},\n\t\t},\n\t) as unknown as KibinClient<Router>;\n}\n"],"mappings":";AAAA,IAAa,aAAb,cAAgC,MAAM;CACrC;CAEA,YAAY,MAAc,SAAiB;EAC1C,MAAM,OAAO;EACb,KAAK,OAAO;EACZ,KAAK,OAAO;CACb;AACD;AAEA,SAAgB,aAAa,OAAqC;CACjE,OAAO,iBAAiB;AACzB;;;ACTA,MAAM,iBAAiB;CAAE,UAAU;CAAG,OAAO;AAAI;AAMjD,SAAgB,kBAA0B,QAAgD;CACzF,MAAM,cAAc,OAAO,OAAO,YAAY,eAAe;CAC7D,MAAM,YAAY,OAAO,OAAO,SAAS,eAAe;CACxD,MAAM,EAAE,iBAAiB;CAEzB,IAAI,eAA4B,CAAC;CACjC,IAAI,iBAAiB;CAErB,eAAe,QAAQ,WAAmB,QAAgB,MAAmC;EAC5F,IAAI,MAAkB;GAAE;GAAW;GAAQ;EAAK;EAEhD,IAAI,cAAc,SACjB,MAAM,MAAM,aAAa,QAAQ,GAAG;EAGrC,OAAO,IAAI,SAAkB,SAAS,WAAW;GAChD,aAAa,KAAK;IAAE;IAAK;IAAS;GAAO,CAAC;GAC1C,IAAI,CAAC,gBAAgB;IACpB,iBAAiB;IACjB,eAAe,KAAK;GACrB;EACD,CAAC;CACF;CAEA,SAAS,QAAQ;EAChB,MAAM,QAAQ;EACd,eAAe,CAAC;EAChB,iBAAiB;EAEjB,IAAI,MAAM,WAAW,GAAG;EACxB,IAAI,MAAM,WAAW,GACpB,YAAY,MAAM,EAAE;OAEpB,WAAW,KAAK;CAElB;CAEA,eAAe,YAAY,MAAiB;EAC3C,MAAM,EAAE,QAAQ;EAChB,IAAI;EAEJ,KAAK,IAAI,UAAU,GAAG,UAAU,aAAa,WAAW;GACvD,IAAI,UAAU,GACb,MAAM,IAAI,SAAe,YAAY,WAAW,SAAS,YAAY,MAAM,UAAU,EAAE,CAAC;GAGzF,IAAI;IACH,MAAM,WAAW,MAAM,MAAM,OAAO,SAAS;KAC5C,QAAQ;KACR,SAAS;MAAE,gBAAgB;MAAoB,GAAG,OAAO;KAAQ;KACjE,MAAM,KAAK,UAAU,GAAG;IACzB,CAAC;IAED,MAAM,SAAU,MAAM,SAAS,KAAK;IAEpC,IAAI,OAAO,OAAO;KACjB,MAAM,MAAM,IAAI,WACf,OAAO,MAAM,QAAQ,aACrB,OAAO,MAAM,WAAW,WACzB;KACA,IAAI,SAAS,UAAU,KAAK;MAC3B,YAAY;MACZ;KACD;KACA,IAAI,cAAc,OACjB,KAAK,QAAQ,MAAM,aAAa,MAAM;MAAE,GAAG;MAAK,OAAO;KAAI,CAAC,CAAC;UAE7D,KAAK,OAAO,GAAG;KAEhB;IACD;IAEA,IAAI,cAAc,UACjB,KAAK,QAAQ,MAAM,aAAa,SAAS;KAAE,GAAG;KAAK,MAAM,OAAO;IAAK,CAAC,CAAC;SAEvE,KAAK,QAAQ,OAAO,IAAI;IAEzB;GACD,SAAS,KAAK;IACb,IAAI,eAAe,YAAY;KAC9B,KAAK,OAAO,GAAG;KACf;IACD;IACA,YAAY;GACb;EACD;EAEA,IAAI,qBAAqB,cAAc,cAAc,OACpD,KAAK,QAAQ,MAAM,aAAa,MAAM;GAAE,GAAG;GAAK,OAAO;EAAU,CAAC,CAAC;OAEnE,KAAK,OAAO,SAAS;CAEvB;CAEA,eAAe,WAAW,OAAoB;EAC7C,IAAI,UAAU,CAAC,GAAG,KAAK;EACvB,MAAM,6BAAa,IAAI,IAAwB;EAE/C,KAAK,IAAI,UAAU,GAAG,UAAU,aAAa,WAAW;GACvD,IAAI,UAAU,GACb,MAAM,IAAI,SAAe,MAAM,WAAW,GAAG,YAAY,MAAM,UAAU,EAAE,CAAC;GAG7E,IAAI;GACJ,IAAI;IAMH,UAAW,OAAM,MALM,MAAM,OAAO,SAAS;KAC5C,QAAQ;KACR,SAAS;MAAE,gBAAgB;MAAoB,GAAG,OAAO;KAAQ;KACjE,MAAM,KAAK,UAAU,QAAQ,KAAK,SAAS,KAAK,GAAG,CAAC;IACrD,CAAC,GACyB,KAAK;GAChC,SAAS,KAAK;IACb,KAAK,MAAM,QAAQ,SAAS,WAAW,IAAI,MAAM,GAAG;IACpD;GACD;GAEA,MAAM,aAA0B,CAAC;GAEjC,KAAK,IAAI,IAAI,GAAG,IAAI,QAAQ,QAAQ,KAAK;IACxC,MAAM,SAAS,QAAQ;IACvB,MAAM,OAAO,QAAQ;IAErB,IAAI,QAAQ,OAAO;KAClB,MAAM,MAAM,IAAI,WACf,OAAO,MAAM,QAAQ,aACrB,OAAO,MAAM,WAAW,WACzB;KACA,IAAI,OAAO,UAAU,KAAK;MACzB,WAAW,KAAK,IAAI;MACpB,WAAW,IAAI,MAAM,GAAG;KACzB,OACC,IAAI,cAAc,OACjB,KAAK,QAAQ,MAAM,aAAa,MAAM;MAAE,GAAG,KAAK;MAAK,OAAO;KAAI,CAAC,CAAC;UAElE,KAAK,OAAO,GAAG;IAGlB,OACC,IAAI,cAAc,UACjB,KAAK,QAAQ,MAAM,aAAa,SAAS;KAAE,GAAG,KAAK;KAAK,MAAM,QAAQ;IAAK,CAAC,CAAC;SAE7E,KAAK,QAAQ,QAAQ,IAAI;GAG5B;GAEA,UAAU;GACV,IAAI,QAAQ,WAAW,GAAG;EAC3B;EAEA,KAAK,MAAM,QAAQ,SAAS;GAC3B,MAAM,MAAM,WAAW,IAAI,IAAI;GAC/B,IAAI,eAAe,cAAc,cAAc,OAC9C,KAAK,QAAQ,MAAM,aAAa,MAAM;IAAE,GAAG,KAAK;IAAK,OAAO;GAAI,CAAC,CAAC;QAElE,KAAK,OAAO,GAAG;EAEjB;CACD;CAEA,OAAO,IAAI,MACV,CAAC,GACD,EACC,IAAI,GAAG,KAAa;EACnB,OAAO,IAAI,MACV,CAAC,GACD,EACC,IAAI,GAAG,QAAgB;GACtB,QAAQ,GAAG,SAAoB,QAAQ,KAAK,QAAQ,IAAI;EACzD,EACD,CACD;CACD,EACD,CACD;AACD"}
|