@kibinrpc/client 0.1.1 → 0.2.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/dist/index.d.ts +10 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +50 -6
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -6,7 +6,10 @@ type ServiceClient<T> = { [K in keyof T as T[K] extends ((...args: never[]) => u
|
|
|
6
6
|
type ExtractServices<T> = T extends {
|
|
7
7
|
services: infer S;
|
|
8
8
|
} ? S : never;
|
|
9
|
-
type
|
|
9
|
+
type RouterServices<Router> = { [K in keyof ExtractServices<Router>]: ServiceClient<ExtractServices<Router>[K]> };
|
|
10
|
+
type KibinClient<Router> = RouterServices<Router> & {
|
|
11
|
+
$unbatched: RouterServices<Router>;
|
|
12
|
+
};
|
|
10
13
|
interface RequestCtx {
|
|
11
14
|
namespace: string;
|
|
12
15
|
method: string;
|
|
@@ -31,7 +34,12 @@ interface RetryConfig {
|
|
|
31
34
|
}
|
|
32
35
|
interface KibinClientConfig {
|
|
33
36
|
baseUrl: string;
|
|
34
|
-
headers
|
|
37
|
+
/** Static headers or a function returning headers (sync or async). Called once per fetch attempt. */
|
|
38
|
+
headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);
|
|
39
|
+
/** Per-attempt timeout in ms. Uses `AbortSignal.timeout()` internally. */
|
|
40
|
+
timeout?: number;
|
|
41
|
+
/** AbortSignal to cancel all requests from this client. */
|
|
42
|
+
signal?: AbortSignal;
|
|
35
43
|
retry?: RetryConfig;
|
|
36
44
|
interceptors?: ClientInterceptors;
|
|
37
45
|
}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/client.ts"],"mappings":";;;KAEK,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,
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/client.ts"],"mappings":";;;KAEK,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,KAEvD,cAAA,yBACQ,eAAA,CAAgB,MAAA,IAAU,aAAA,CAAc,eAAA,CAAgB,MAAA,EAAQ,CAAA;AAAA,KAGjE,WAAA,WAAsB,cAAA,CAAe,MAAA;EAChD,UAAA,EAAY,cAAA,CAAe,MAAA;AAAA;AAAA,UAGX,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;EAjC8C;EAmC9D,QAAA;EAnC2F;EAqC3F,KAAK;AAAA;AAAA,UAGW,iBAAA;EAChB,OAAA;EAzCC;EA2CD,OAAA,GACG,MAAA,0BACO,MAAA,mBAAyB,OAAA,CAAQ,MAAA;EA7C1B;EA+CjB,OAAA;EA/CkC;EAiDlC,MAAA,GAAS,WAAA;EACT,KAAA,GAAQ,WAAA;EACR,YAAA,GAAe,kBAAA;AAAA;;;iBCzCA,iBAAA,QAAA,CAA0B,MAAA,EAAQ,iBAAA,GAAoB,WAAA,CAAY,MAAA"}
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,16 @@ function createKibinClient(config) {
|
|
|
14
14
|
const maxAttempts = config.retry?.attempts ?? RETRY_DEFAULTS.attempts;
|
|
15
15
|
const baseDelay = config.retry?.delay ?? RETRY_DEFAULTS.delay;
|
|
16
16
|
const { interceptors } = config;
|
|
17
|
+
async function getHeaders() {
|
|
18
|
+
if (!config.headers) return {};
|
|
19
|
+
if (typeof config.headers === "function") return await config.headers();
|
|
20
|
+
return config.headers;
|
|
21
|
+
}
|
|
22
|
+
function makeSignal() {
|
|
23
|
+
if (config.signal && config.timeout !== void 0) return AbortSignal.any([config.signal, AbortSignal.timeout(config.timeout)]);
|
|
24
|
+
if (config.signal) return config.signal;
|
|
25
|
+
if (config.timeout !== void 0) return AbortSignal.timeout(config.timeout);
|
|
26
|
+
}
|
|
17
27
|
let pendingBatch = [];
|
|
18
28
|
let flushScheduled = false;
|
|
19
29
|
async function settleError(item, err) {
|
|
@@ -38,7 +48,7 @@ function createKibinClient(config) {
|
|
|
38
48
|
}
|
|
39
49
|
else item.resolve(data);
|
|
40
50
|
}
|
|
41
|
-
async function rpcCall(namespace, method, args) {
|
|
51
|
+
async function rpcCall(namespace, method, args, skipBatch = false) {
|
|
42
52
|
let ctx = {
|
|
43
53
|
namespace,
|
|
44
54
|
method,
|
|
@@ -46,6 +56,14 @@ function createKibinClient(config) {
|
|
|
46
56
|
};
|
|
47
57
|
if (interceptors?.request) ctx = await interceptors.request(ctx);
|
|
48
58
|
return new Promise((resolve, reject) => {
|
|
59
|
+
if (skipBatch) {
|
|
60
|
+
flushSingle({
|
|
61
|
+
ctx,
|
|
62
|
+
resolve,
|
|
63
|
+
reject
|
|
64
|
+
});
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
49
67
|
pendingBatch.push({
|
|
50
68
|
ctx,
|
|
51
69
|
resolve,
|
|
@@ -70,12 +88,14 @@ function createKibinClient(config) {
|
|
|
70
88
|
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
71
89
|
if (attempt > 0) await sleep(attempt, baseDelay);
|
|
72
90
|
try {
|
|
91
|
+
const resolvedHeaders = await getHeaders();
|
|
73
92
|
const response = await fetch(config.baseUrl, {
|
|
74
93
|
method: "POST",
|
|
75
94
|
headers: {
|
|
76
95
|
"Content-Type": "application/json",
|
|
77
|
-
...
|
|
96
|
+
...resolvedHeaders
|
|
78
97
|
},
|
|
98
|
+
signal: makeSignal(),
|
|
79
99
|
body: JSON.stringify(item.ctx)
|
|
80
100
|
});
|
|
81
101
|
const result = await response.json();
|
|
@@ -91,6 +111,14 @@ function createKibinClient(config) {
|
|
|
91
111
|
await settleSuccess(item, result.data);
|
|
92
112
|
return;
|
|
93
113
|
} catch (err) {
|
|
114
|
+
if (err instanceof Error && err.name === "TimeoutError") {
|
|
115
|
+
await settleError(item, new KibinError("TIMEOUT", "Request timed out", { cause: err }));
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (err instanceof Error && err.name === "AbortError") {
|
|
119
|
+
await settleError(item, new KibinError("ABORTED", "Request was aborted", { cause: err }));
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
94
122
|
lastError = err;
|
|
95
123
|
}
|
|
96
124
|
}
|
|
@@ -104,15 +132,23 @@ function createKibinClient(config) {
|
|
|
104
132
|
if (attempt > 0) await sleep(attempt, baseDelay);
|
|
105
133
|
let results;
|
|
106
134
|
try {
|
|
135
|
+
const resolvedHeaders = await getHeaders();
|
|
107
136
|
results = await (await fetch(config.baseUrl, {
|
|
108
137
|
method: "POST",
|
|
109
138
|
headers: {
|
|
110
139
|
"Content-Type": "application/json",
|
|
111
|
-
...
|
|
140
|
+
...resolvedHeaders
|
|
112
141
|
},
|
|
142
|
+
signal: makeSignal(),
|
|
113
143
|
body: JSON.stringify(pending.map((item) => item.ctx))
|
|
114
144
|
})).json();
|
|
115
145
|
} catch (err) {
|
|
146
|
+
if (err instanceof Error && (err.name === "TimeoutError" || err.name === "AbortError")) {
|
|
147
|
+
const code = err.name === "TimeoutError" ? "TIMEOUT" : "ABORTED";
|
|
148
|
+
const msg = err.name === "TimeoutError" ? "Request timed out" : "Request was aborted";
|
|
149
|
+
await Promise.all(pending.map((item) => settleError(item, new KibinError(code, msg, { cause: err }))));
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
116
152
|
for (const item of pending) lastErrors.set(item, err);
|
|
117
153
|
continue;
|
|
118
154
|
}
|
|
@@ -144,12 +180,20 @@ function createKibinClient(config) {
|
|
|
144
180
|
return Promise.resolve();
|
|
145
181
|
}));
|
|
146
182
|
}
|
|
147
|
-
|
|
148
|
-
if (typeof key !== "string") return void 0;
|
|
183
|
+
function makeNamespaceProxy(namespace, skipBatch) {
|
|
149
184
|
return new Proxy({}, { get(_, method) {
|
|
150
185
|
if (typeof method !== "string") return void 0;
|
|
151
|
-
return (...args) => rpcCall(
|
|
186
|
+
return (...args) => rpcCall(namespace, method, args, skipBatch);
|
|
152
187
|
} });
|
|
188
|
+
}
|
|
189
|
+
const unbatchedProxy = new Proxy({}, { get(_, key) {
|
|
190
|
+
if (typeof key !== "string") return void 0;
|
|
191
|
+
return makeNamespaceProxy(key, true);
|
|
192
|
+
} });
|
|
193
|
+
return new Proxy({}, { get(_, key) {
|
|
194
|
+
if (key === "$unbatched") return unbatchedProxy;
|
|
195
|
+
if (typeof key !== "string") return void 0;
|
|
196
|
+
return makeNamespaceProxy(key, false);
|
|
153
197
|
} });
|
|
154
198
|
}
|
|
155
199
|
//#endregion
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/client.ts"],"sourcesContent":["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\nfunction rpcError(error: { code?: string; message?: string }): KibinError {\n\treturn new KibinError(error.code ?? 'RPC_ERROR', error.message ?? 'RPC Error');\n}\n\nfunction sleep(attempt: number, baseDelay: number): Promise<void> {\n\treturn new Promise<void>((r) => setTimeout(r, baseDelay * 2 ** (attempt - 1)));\n}\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 settleError(item: QueueItem, err: KibinError): Promise<void> {\n\t\tif (interceptors?.error) {\n\t\t\ttry {\n\t\t\t\titem.resolve(await interceptors.error({ ...item.ctx, error: err }));\n\t\t\t} catch (interceptorError) {\n\t\t\t\titem.reject(interceptorError);\n\t\t\t}\n\t\t} else {\n\t\t\titem.reject(err);\n\t\t}\n\t}\n\n\tasync function settleSuccess(item: QueueItem, data: unknown): Promise<void> {\n\t\tif (interceptors?.response) {\n\t\t\ttry {\n\t\t\t\titem.resolve(await interceptors.response({ ...item.ctx, data }));\n\t\t\t} catch (interceptorError) {\n\t\t\t\titem.reject(interceptorError);\n\t\t\t}\n\t\t} else {\n\t\t\titem.resolve(data);\n\t\t}\n\t}\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\tlet lastError: unknown;\n\n\t\tfor (let attempt = 0; attempt < maxAttempts; attempt++) {\n\t\t\tif (attempt > 0) await sleep(attempt, baseDelay);\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(item.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 = rpcError(result.error);\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\tawait settleError(item, err);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tawait settleSuccess(item, result.data);\n\t\t\t\treturn;\n\t\t\t} catch (err) {\n\t\t\t\tlastError = err;\n\t\t\t}\n\t\t}\n\n\t\tif (lastError instanceof KibinError) {\n\t\t\tawait settleError(item, 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) await sleep(attempt, baseDelay);\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\t\t\tconst settlements: Promise<void>[] = [];\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) {\n\t\t\t\t\titem.reject(\n\t\t\t\t\t\tnew KibinError('BATCH_MISMATCH', 'Server returned fewer results than expected'),\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (result.error) {\n\t\t\t\t\tconst err = rpcError(result.error);\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\tsettlements.push(settleError(item, err));\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tsettlements.push(settleSuccess(item, result.data));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tawait Promise.all(settlements);\n\t\t\tpending = retryItems;\n\t\t\tif (pending.length === 0) break;\n\t\t}\n\n\t\tawait Promise.all(\n\t\t\tpending.map((item) => {\n\t\t\t\tconst err = lastErrors.get(item);\n\t\t\t\tif (err instanceof KibinError) return settleError(item, err);\n\t\t\t\titem.reject(err);\n\t\t\t\treturn Promise.resolve();\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) {\n\t\t\t\tif (typeof key !== 'string') return undefined;\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) {\n\t\t\t\t\t\t\tif (typeof method !== 'string') return undefined;\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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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/client.ts"],"sourcesContent":["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\nfunction rpcError(error: { code?: string; message?: string }): KibinError {\n\treturn new KibinError(error.code ?? 'RPC_ERROR', error.message ?? 'RPC Error');\n}\n\nfunction sleep(attempt: number, baseDelay: number): Promise<void> {\n\treturn new Promise<void>((r) => setTimeout(r, baseDelay * 2 ** (attempt - 1)));\n}\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\tasync function getHeaders(): Promise<Record<string, string>> {\n\t\tif (!config.headers) return {};\n\t\tif (typeof config.headers === 'function') return await config.headers();\n\t\treturn config.headers;\n\t}\n\n\tfunction makeSignal(): AbortSignal | undefined {\n\t\tif (config.signal && config.timeout !== undefined) {\n\t\t\treturn AbortSignal.any([config.signal, AbortSignal.timeout(config.timeout)]);\n\t\t}\n\t\tif (config.signal) return config.signal;\n\t\tif (config.timeout !== undefined) return AbortSignal.timeout(config.timeout);\n\t\treturn undefined;\n\t}\n\n\tlet pendingBatch: QueueItem[] = [];\n\tlet flushScheduled = false;\n\n\tasync function settleError(item: QueueItem, err: KibinError): Promise<void> {\n\t\tif (interceptors?.error) {\n\t\t\ttry {\n\t\t\t\titem.resolve(await interceptors.error({ ...item.ctx, error: err }));\n\t\t\t} catch (interceptorError) {\n\t\t\t\titem.reject(interceptorError);\n\t\t\t}\n\t\t} else {\n\t\t\titem.reject(err);\n\t\t}\n\t}\n\n\tasync function settleSuccess(item: QueueItem, data: unknown): Promise<void> {\n\t\tif (interceptors?.response) {\n\t\t\ttry {\n\t\t\t\titem.resolve(await interceptors.response({ ...item.ctx, data }));\n\t\t\t} catch (interceptorError) {\n\t\t\t\titem.reject(interceptorError);\n\t\t\t}\n\t\t} else {\n\t\t\titem.resolve(data);\n\t\t}\n\t}\n\n\tasync function rpcCall(\n\t\tnamespace: string,\n\t\tmethod: string,\n\t\targs: unknown[],\n\t\tskipBatch = false,\n\t): 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\tif (skipBatch) {\n\t\t\t\tflushSingle({ ctx, resolve, reject });\n\t\t\t\treturn;\n\t\t\t}\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\tlet lastError: unknown;\n\n\t\tfor (let attempt = 0; attempt < maxAttempts; attempt++) {\n\t\t\tif (attempt > 0) await sleep(attempt, baseDelay);\n\n\t\t\ttry {\n\t\t\t\tconst resolvedHeaders = await getHeaders();\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', ...resolvedHeaders },\n\t\t\t\t\tsignal: makeSignal(),\n\t\t\t\t\tbody: JSON.stringify(item.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 = rpcError(result.error);\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\tawait settleError(item, err);\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tawait settleSuccess(item, result.data);\n\t\t\t\treturn;\n\t\t\t} catch (err) {\n\t\t\t\tif (err instanceof Error && err.name === 'TimeoutError') {\n\t\t\t\t\tawait settleError(item, new KibinError('TIMEOUT', 'Request timed out', { cause: err }));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (err instanceof Error && err.name === 'AbortError') {\n\t\t\t\t\tawait settleError(item, new KibinError('ABORTED', 'Request was aborted', { cause: 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) {\n\t\t\tawait settleError(item, 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) await sleep(attempt, baseDelay);\n\n\t\t\tlet results: BatchRpcResult[];\n\t\t\ttry {\n\t\t\t\tconst resolvedHeaders = await getHeaders();\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', ...resolvedHeaders },\n\t\t\t\t\tsignal: makeSignal(),\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\tif (err instanceof Error && (err.name === 'TimeoutError' || err.name === 'AbortError')) {\n\t\t\t\t\tconst code = err.name === 'TimeoutError' ? 'TIMEOUT' : 'ABORTED';\n\t\t\t\t\tconst msg = err.name === 'TimeoutError' ? 'Request timed out' : 'Request was aborted';\n\t\t\t\t\tawait Promise.all(\n\t\t\t\t\t\tpending.map((item) => settleError(item, new KibinError(code, msg, { cause: err }))),\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\t}\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\t\t\tconst settlements: Promise<void>[] = [];\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) {\n\t\t\t\t\titem.reject(\n\t\t\t\t\t\tnew KibinError('BATCH_MISMATCH', 'Server returned fewer results than expected'),\n\t\t\t\t\t);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (result.error) {\n\t\t\t\t\tconst err = rpcError(result.error);\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\tsettlements.push(settleError(item, err));\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tsettlements.push(settleSuccess(item, result.data));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tawait Promise.all(settlements);\n\t\t\tpending = retryItems;\n\t\t\tif (pending.length === 0) break;\n\t\t}\n\n\t\tawait Promise.all(\n\t\t\tpending.map((item) => {\n\t\t\t\tconst err = lastErrors.get(item);\n\t\t\t\tif (err instanceof KibinError) return settleError(item, err);\n\t\t\t\titem.reject(err);\n\t\t\t\treturn Promise.resolve();\n\t\t\t}),\n\t\t);\n\t}\n\n\tfunction makeNamespaceProxy(namespace: string, skipBatch: boolean) {\n\t\treturn new Proxy(\n\t\t\t{},\n\t\t\t{\n\t\t\t\tget(_, method) {\n\t\t\t\t\tif (typeof method !== 'string') return undefined;\n\t\t\t\t\treturn (...args: unknown[]) => rpcCall(namespace, method, args, skipBatch);\n\t\t\t\t},\n\t\t\t},\n\t\t);\n\t}\n\n\tconst unbatchedProxy = new Proxy(\n\t\t{},\n\t\t{\n\t\t\tget(_, key) {\n\t\t\t\tif (typeof key !== 'string') return undefined;\n\t\t\t\treturn makeNamespaceProxy(key, true);\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) {\n\t\t\t\tif (key === '$unbatched') return unbatchedProxy;\n\t\t\t\tif (typeof key !== 'string') return undefined;\n\t\t\t\treturn makeNamespaceProxy(key, false);\n\t\t\t},\n\t\t},\n\t) as unknown as KibinClient<Router>;\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kibinrpc/client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Type-safe and developer-friendly RPC client with end-to-end type inference",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "ixexel661",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"dist"
|
|
30
30
|
],
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@kibinrpc/shared": "0.
|
|
32
|
+
"@kibinrpc/shared": "0.2.0"
|
|
33
33
|
},
|
|
34
34
|
"publishConfig": {
|
|
35
35
|
"access": "public"
|