@kibinrpc/client 0.0.2 → 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 +32 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +132 -12
- 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
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
//#region src/errors.d.ts
|
|
2
|
+
declare class KibinError extends Error {
|
|
3
|
+
readonly code: string;
|
|
4
|
+
constructor(code: string, message: string);
|
|
5
|
+
}
|
|
6
|
+
declare function isKibinError(error: unknown): error is KibinError;
|
|
7
|
+
//#endregion
|
|
1
8
|
//#region src/types.d.ts
|
|
2
9
|
type AsyncifyMethod<T> = T extends ((...args: infer Args) => infer Return) ? (...args: Args) => Promise<Awaited<Return>> : never;
|
|
3
10
|
type ServiceClient<T> = { [K in keyof T as T[K] extends ((...args: never[]) => unknown) ? K : never]: AsyncifyMethod<T[K]> };
|
|
@@ -5,20 +12,37 @@ type ExtractServices<T> = T extends {
|
|
|
5
12
|
services: infer S;
|
|
6
13
|
} ? S : never;
|
|
7
14
|
type KibinClient<Router> = { [K in keyof ExtractServices<Router>]: ServiceClient<ExtractServices<Router>[K]> };
|
|
15
|
+
interface RequestCtx {
|
|
16
|
+
namespace: string;
|
|
17
|
+
method: string;
|
|
18
|
+
args: unknown[];
|
|
19
|
+
}
|
|
20
|
+
interface ResponseCtx extends RequestCtx {
|
|
21
|
+
data: unknown;
|
|
22
|
+
}
|
|
23
|
+
interface ErrorCtx extends RequestCtx {
|
|
24
|
+
error: KibinError;
|
|
25
|
+
}
|
|
26
|
+
interface ClientInterceptors {
|
|
27
|
+
request?: (ctx: RequestCtx) => RequestCtx | Promise<RequestCtx>;
|
|
28
|
+
response?: (ctx: ResponseCtx) => unknown | Promise<unknown>;
|
|
29
|
+
error?: (ctx: ErrorCtx) => unknown | Promise<unknown>;
|
|
30
|
+
}
|
|
31
|
+
interface RetryConfig {
|
|
32
|
+
/** Total number of attempts. Default: 3 */
|
|
33
|
+
attempts?: number;
|
|
34
|
+
/** Base delay in ms — doubles each retry (exponential backoff). Default: 300 */
|
|
35
|
+
delay?: number;
|
|
36
|
+
}
|
|
8
37
|
interface KibinClientConfig {
|
|
9
38
|
baseUrl: string;
|
|
10
39
|
headers?: Record<string, string>;
|
|
40
|
+
retry?: RetryConfig;
|
|
41
|
+
interceptors?: ClientInterceptors;
|
|
11
42
|
}
|
|
12
43
|
//#endregion
|
|
13
44
|
//#region src/client.d.ts
|
|
14
45
|
declare function createKibinClient<Router>(config: KibinClientConfig): KibinClient<Router>;
|
|
15
46
|
//#endregion
|
|
16
|
-
|
|
17
|
-
declare class KibinError extends Error {
|
|
18
|
-
readonly code: string;
|
|
19
|
-
constructor(code: string, message: string);
|
|
20
|
-
}
|
|
21
|
-
declare function isKibinError(error: unknown): error is KibinError;
|
|
22
|
-
//#endregion
|
|
23
|
-
export { type KibinClient, type KibinClientConfig, KibinError, createKibinClient, isKibinError };
|
|
47
|
+
export { type ClientInterceptors, type ErrorCtx, type KibinClient, type KibinClientConfig, KibinError, type RequestCtx, type ResponseCtx, type RetryConfig, createKibinClient, isKibinError };
|
|
24
48
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/
|
|
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
|
@@ -12,25 +12,145 @@ function isKibinError(error) {
|
|
|
12
12
|
}
|
|
13
13
|
//#endregion
|
|
14
14
|
//#region src/client.ts
|
|
15
|
+
const RETRY_DEFAULTS = {
|
|
16
|
+
attempts: 3,
|
|
17
|
+
delay: 300
|
|
18
|
+
};
|
|
15
19
|
function createKibinClient(config) {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
const maxAttempts = config.retry?.attempts ?? RETRY_DEFAULTS.attempts;
|
|
21
|
+
const baseDelay = config.retry?.delay ?? RETRY_DEFAULTS.delay;
|
|
22
|
+
const { interceptors } = config;
|
|
23
|
+
let pendingBatch = [];
|
|
24
|
+
let flushScheduled = false;
|
|
25
|
+
async function rpcCall(namespace, method, args) {
|
|
26
|
+
let ctx = {
|
|
27
|
+
namespace,
|
|
28
|
+
method,
|
|
29
|
+
args
|
|
30
|
+
};
|
|
31
|
+
if (interceptors?.request) ctx = await interceptors.request(ctx);
|
|
32
|
+
return new Promise((resolve, reject) => {
|
|
33
|
+
pendingBatch.push({
|
|
34
|
+
ctx,
|
|
35
|
+
resolve,
|
|
36
|
+
reject
|
|
37
|
+
});
|
|
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;
|
|
54
|
+
let lastError;
|
|
55
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
56
|
+
if (attempt > 0) await new Promise((resolve) => setTimeout(resolve, baseDelay * 2 ** (attempt - 1)));
|
|
57
|
+
try {
|
|
58
|
+
const response = await fetch(config.baseUrl, {
|
|
59
|
+
method: "POST",
|
|
60
|
+
headers: {
|
|
61
|
+
"Content-Type": "application/json",
|
|
62
|
+
...config.headers
|
|
63
|
+
},
|
|
64
|
+
body: JSON.stringify(ctx)
|
|
65
|
+
});
|
|
66
|
+
const result = await response.json();
|
|
67
|
+
if (result.error) {
|
|
68
|
+
const err = new KibinError(result.error.code ?? "RPC_ERROR", result.error.message ?? "RPC Error");
|
|
69
|
+
if (response.status >= 500) {
|
|
70
|
+
lastError = err;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (interceptors?.error) item.resolve(await interceptors.error({
|
|
74
|
+
...ctx,
|
|
75
|
+
error: err
|
|
76
|
+
}));
|
|
77
|
+
else item.reject(err);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (interceptors?.response) item.resolve(await interceptors.response({
|
|
81
|
+
...ctx,
|
|
82
|
+
data: result.data
|
|
83
|
+
}));
|
|
84
|
+
else item.resolve(result.data);
|
|
85
|
+
return;
|
|
86
|
+
} catch (err) {
|
|
87
|
+
if (err instanceof KibinError) {
|
|
88
|
+
item.reject(err);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
lastError = err;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (lastError instanceof KibinError && interceptors?.error) item.resolve(await interceptors.error({
|
|
95
|
+
...ctx,
|
|
96
|
+
error: lastError
|
|
97
|
+
}));
|
|
98
|
+
else item.reject(lastError);
|
|
99
|
+
}
|
|
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, {
|
|
20
108
|
method: "POST",
|
|
21
109
|
headers: {
|
|
22
110
|
"Content-Type": "application/json",
|
|
23
111
|
...config.headers
|
|
24
112
|
},
|
|
25
|
-
body: JSON.stringify(
|
|
26
|
-
namespace,
|
|
27
|
-
method,
|
|
28
|
-
args
|
|
29
|
-
})
|
|
113
|
+
body: JSON.stringify(pending.map((item) => item.ctx))
|
|
30
114
|
})).json();
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
|
136
|
+
}));
|
|
137
|
+
else item.resolve(result?.data);
|
|
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);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return new Proxy({}, { get(_, key) {
|
|
152
|
+
return new Proxy({}, { get(_, method) {
|
|
153
|
+
return (...args) => rpcCall(key, method, args);
|
|
34
154
|
} });
|
|
35
155
|
} });
|
|
36
156
|
}
|
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 } from './types.js';\n\nexport function createKibinClient<Router>(config: KibinClientConfig): KibinClient<Router> {\n\
|
|
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":"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"}
|