silgi 0.53.3 → 0.53.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.
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { ClientContext, ClientLink, ClientOptions } from "../../types.mjs";
|
|
2
|
+
import { AfterResponseHook, BeforeErrorHook, BeforeRequestHook, MisinaHooks, RetryOptions } from "misina";
|
|
3
|
+
|
|
4
|
+
//#region src/client/adapters/misina/index.d.ts
|
|
5
|
+
type OnCompleteHook = Exclude<MisinaHooks['onComplete'], undefined> extends infer H ? (H extends readonly (infer U)[] ? U : H) : never;
|
|
6
|
+
interface LinkOptions<TClientContext extends ClientContext = ClientContext> {
|
|
7
|
+
/** Server base URL (e.g. "http://localhost:3000") */
|
|
8
|
+
url: string;
|
|
9
|
+
/** Static headers or dynamic header factory */
|
|
10
|
+
headers?: Record<string, string> | ((options: ClientOptions<TClientContext>) => Record<string, string>);
|
|
11
|
+
/**
|
|
12
|
+
* Retry policy. Number sets the limit; pass a full `RetryOptions` to
|
|
13
|
+
* tune backoff, status codes, jitter. `false` disables retries.
|
|
14
|
+
*
|
|
15
|
+
* @default 0
|
|
16
|
+
*/
|
|
17
|
+
retry?: number | boolean | RetryOptions;
|
|
18
|
+
/** Per-attempt timeout in ms. `false` disables. (default: 30000) */
|
|
19
|
+
timeout?: number | false;
|
|
20
|
+
/** Wall-clock deadline across all attempts (ms). `false` disables. */
|
|
21
|
+
totalTimeout?: number | false;
|
|
22
|
+
/**
|
|
23
|
+
* Wire protocol for request/response encoding.
|
|
24
|
+
*
|
|
25
|
+
* - `'json'` — default, standard JSON
|
|
26
|
+
* - `'messagepack'` — 2-4x faster, ~50% smaller payloads
|
|
27
|
+
* - `'devalue'` — preserves Date, Map, Set, BigInt, circular refs
|
|
28
|
+
*
|
|
29
|
+
* @default 'json'
|
|
30
|
+
*/
|
|
31
|
+
protocol?: 'json' | 'messagepack' | 'devalue';
|
|
32
|
+
/**
|
|
33
|
+
* Auto-generate `Idempotency-Key` for retried mutations. `'auto'` uses
|
|
34
|
+
* `crypto.randomUUID()` when retries are enabled. Pass a string to
|
|
35
|
+
* pin one, or a function for custom generation.
|
|
36
|
+
*/
|
|
37
|
+
idempotencyKey?: false | 'auto' | string | ((request: Request) => string);
|
|
38
|
+
/** misina hooks — direct pass-through */
|
|
39
|
+
beforeRequest?: BeforeRequestHook;
|
|
40
|
+
afterResponse?: AfterResponseHook;
|
|
41
|
+
beforeError?: BeforeErrorHook;
|
|
42
|
+
onComplete?: OnCompleteHook;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Create a Silgi client link powered by misina.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```ts
|
|
49
|
+
* import { createClient } from "silgi/client"
|
|
50
|
+
* import { createLink } from "silgi/client/misina"
|
|
51
|
+
*
|
|
52
|
+
* const link = createLink({ url: "http://localhost:3000" })
|
|
53
|
+
* const client = createClient<AppRouter>(link)
|
|
54
|
+
* const users = await client.users.list({ limit: 10 })
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
declare function createLink<TClientContext extends ClientContext = ClientContext>(options: LinkOptions<TClientContext>): ClientLink<TClientContext>;
|
|
58
|
+
//#endregion
|
|
59
|
+
export { LinkOptions, createLink };
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { SilgiError, fromSilgiErrorJSON, isSilgiErrorJSON } from "../../../core/error.mjs";
|
|
2
|
+
import { eventStreamToIterator } from "../../../core/sse.mjs";
|
|
3
|
+
import { MSGPACK_CONTENT_TYPE, decode, encode } from "../../../codec/msgpack.mjs";
|
|
4
|
+
import { createMisina, isHTTPError, isNetworkError, isTimeoutError } from "misina";
|
|
5
|
+
//#region src/client/adapters/misina/index.ts
|
|
6
|
+
/**
|
|
7
|
+
* misina-based RPC transport — v2 client link.
|
|
8
|
+
*
|
|
9
|
+
* Uses misina for: retry (with Retry-After parsing), timeout, hook
|
|
10
|
+
* lifecycle, redirect security, NetworkError vs HTTPError taxonomy,
|
|
11
|
+
* idempotency keys for retried mutations.
|
|
12
|
+
*
|
|
13
|
+
* Side-by-side alternative to the ofetch link. Same Silgi semantics,
|
|
14
|
+
* different transport. Pick whichever fits your stack.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Create a Silgi client link powered by misina.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```ts
|
|
21
|
+
* import { createClient } from "silgi/client"
|
|
22
|
+
* import { createLink } from "silgi/client/misina"
|
|
23
|
+
*
|
|
24
|
+
* const link = createLink({ url: "http://localhost:3000" })
|
|
25
|
+
* const client = createClient<AppRouter>(link)
|
|
26
|
+
* const users = await client.users.list({ limit: 10 })
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
function createLink(options) {
|
|
30
|
+
const baseUrl = options.url.endsWith("/") ? options.url.slice(0, -1) : options.url;
|
|
31
|
+
const resolvedProtocol = options.protocol ?? "json";
|
|
32
|
+
const misina = createMisina({
|
|
33
|
+
timeout: options.timeout ?? 3e4,
|
|
34
|
+
totalTimeout: options.totalTimeout,
|
|
35
|
+
retry: options.retry ?? 0,
|
|
36
|
+
idempotencyKey: options.idempotencyKey,
|
|
37
|
+
throwHttpErrors: false,
|
|
38
|
+
hooks: {
|
|
39
|
+
beforeRequest: options.beforeRequest,
|
|
40
|
+
afterResponse: options.afterResponse,
|
|
41
|
+
beforeError: options.beforeError,
|
|
42
|
+
onComplete: options.onComplete
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
return { async call(path, input, callOptions) {
|
|
46
|
+
const url = `${baseUrl}/${path.map(encodeURIComponent).join("/")}`;
|
|
47
|
+
const headers = { ...typeof options.headers === "function" ? options.headers(callOptions) : options.headers };
|
|
48
|
+
let body;
|
|
49
|
+
if (resolvedProtocol === "messagepack") {
|
|
50
|
+
headers["content-type"] = MSGPACK_CONTENT_TYPE;
|
|
51
|
+
headers["accept"] = MSGPACK_CONTENT_TYPE;
|
|
52
|
+
body = input !== void 0 && input !== null ? encode(input) : void 0;
|
|
53
|
+
} else if (resolvedProtocol === "devalue") {
|
|
54
|
+
const { encode: devalueEncode, DEVALUE_CONTENT_TYPE } = await import("../../../codec/devalue.mjs");
|
|
55
|
+
headers["content-type"] = DEVALUE_CONTENT_TYPE;
|
|
56
|
+
headers["accept"] = DEVALUE_CONTENT_TYPE;
|
|
57
|
+
body = input !== void 0 && input !== null ? devalueEncode(input) : void 0;
|
|
58
|
+
} else body = input !== void 0 && input !== null ? input : void 0;
|
|
59
|
+
try {
|
|
60
|
+
const response = (await misina.post(url, body, {
|
|
61
|
+
headers,
|
|
62
|
+
signal: callOptions.signal,
|
|
63
|
+
responseType: "stream"
|
|
64
|
+
})).raw;
|
|
65
|
+
if ((response.headers.get("content-type") ?? "").includes("text/event-stream") && response.body) return eventStreamToIterator(response.body);
|
|
66
|
+
let decoded;
|
|
67
|
+
if (resolvedProtocol === "messagepack") {
|
|
68
|
+
const buf = new Uint8Array(await response.arrayBuffer());
|
|
69
|
+
decoded = buf.length > 0 ? decode(buf) : void 0;
|
|
70
|
+
} else if (resolvedProtocol === "devalue") {
|
|
71
|
+
const text = await response.text();
|
|
72
|
+
if (text) {
|
|
73
|
+
const { decode: devalueDecode } = await import("../../../codec/devalue.mjs");
|
|
74
|
+
decoded = devalueDecode(text);
|
|
75
|
+
} else decoded = void 0;
|
|
76
|
+
} else {
|
|
77
|
+
const text = await response.text();
|
|
78
|
+
if (!text) decoded = void 0;
|
|
79
|
+
else try {
|
|
80
|
+
decoded = JSON.parse(text);
|
|
81
|
+
} catch {
|
|
82
|
+
decoded = text;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (isSilgiErrorJSON(decoded)) throw fromSilgiErrorJSON(decoded);
|
|
86
|
+
return decoded;
|
|
87
|
+
} catch (error) {
|
|
88
|
+
if (error instanceof SilgiError) throw error;
|
|
89
|
+
if (isHTTPError(error)) {
|
|
90
|
+
const data = error.data;
|
|
91
|
+
if (isSilgiErrorJSON(data)) throw fromSilgiErrorJSON(data);
|
|
92
|
+
throw new SilgiError("INTERNAL_SERVER_ERROR", {
|
|
93
|
+
status: error.status ?? 500,
|
|
94
|
+
message: error.message,
|
|
95
|
+
data
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
if (isNetworkError(error) || isTimeoutError(error)) throw new SilgiError("INTERNAL_SERVER_ERROR", {
|
|
99
|
+
status: 0,
|
|
100
|
+
message: error.message
|
|
101
|
+
});
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
} };
|
|
105
|
+
}
|
|
106
|
+
//#endregion
|
|
107
|
+
export { createLink };
|
package/lib/misina.d.mts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from 'misina'
|
package/lib/misina.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from 'misina'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "silgi",
|
|
3
|
-
"version": "0.53.
|
|
3
|
+
"version": "0.53.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "The fastest end-to-end type-safe RPC framework for TypeScript — compiled pipelines, single package, every runtime",
|
|
6
6
|
"keywords": [
|
|
@@ -59,6 +59,10 @@
|
|
|
59
59
|
"import": "./dist/client/adapters/ofetch/index.mjs",
|
|
60
60
|
"types": "./dist/client/adapters/ofetch/index.d.mts"
|
|
61
61
|
},
|
|
62
|
+
"./client/misina": {
|
|
63
|
+
"import": "./dist/client/adapters/misina/index.mjs",
|
|
64
|
+
"types": "./dist/client/adapters/misina/index.d.mts"
|
|
65
|
+
},
|
|
62
66
|
"./client/plugins": {
|
|
63
67
|
"import": "./dist/client/plugins/index.mjs",
|
|
64
68
|
"types": "./dist/client/plugins/index.d.mts"
|
|
@@ -231,6 +235,10 @@
|
|
|
231
235
|
"import": "./lib/ofetch.mjs",
|
|
232
236
|
"types": "./lib/ofetch.d.mts"
|
|
233
237
|
},
|
|
238
|
+
"./misina": {
|
|
239
|
+
"import": "./lib/misina.mjs",
|
|
240
|
+
"types": "./lib/misina.d.mts"
|
|
241
|
+
},
|
|
234
242
|
"./srvx": {
|
|
235
243
|
"import": "./lib/srvx.mjs",
|
|
236
244
|
"types": "./lib/srvx.d.mts"
|
|
@@ -245,6 +253,7 @@
|
|
|
245
253
|
"devalue": "^5.6.4",
|
|
246
254
|
"get-port-please": "^3.2.0",
|
|
247
255
|
"hookable": "^6.1.0",
|
|
256
|
+
"misina": "^0.2.0",
|
|
248
257
|
"msgpackr": "^1.11.9",
|
|
249
258
|
"ocache": "^0.1.4",
|
|
250
259
|
"ofetch": "2.0.0-alpha.3",
|