@orpc/standard-server-peer 0.0.0-next.fa8d145 → 0.0.0-next.fc1437c
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 +11 -8
- package/dist/index.d.mts +54 -23
- package/dist/index.d.ts +54 -23
- package/dist/index.mjs +298 -170
- package/package.json +3 -3
package/README.md
CHANGED
@@ -17,6 +17,9 @@
|
|
17
17
|
<a href="https://discord.gg/TXEbwRBvQn">
|
18
18
|
<img alt="Discord" src="https://img.shields.io/discord/1308966753044398161?color=7389D8&label&logo=discord&logoColor=ffffff" />
|
19
19
|
</a>
|
20
|
+
<a href="https://deepwiki.com/unnoq/orpc">
|
21
|
+
<img src="https://deepwiki.com/badge.svg" alt="Ask DeepWiki">
|
22
|
+
</a>
|
20
23
|
</div>
|
21
24
|
|
22
25
|
<h3 align="center">Typesafe APIs Made Simple 🪄</h3>
|
@@ -30,7 +33,8 @@
|
|
30
33
|
- **🔗 End-to-End Type Safety**: Ensure type-safe inputs, outputs, and errors from client to server.
|
31
34
|
- **📘 First-Class OpenAPI**: Built-in support that fully adheres to the OpenAPI standard.
|
32
35
|
- **📝 Contract-First Development**: Optionally define your API contract before implementation.
|
33
|
-
-
|
36
|
+
- **🔍 First-Class OpenTelemetry**: Seamlessly integrate with OpenTelemetry for observability.
|
37
|
+
- **⚙️ Framework Integrations**: Seamlessly integrate with TanStack Query (React, Vue, Solid, Svelte, Angular), SWR, Pinia Colada, and more.
|
34
38
|
- **🚀 Server Actions**: Fully compatible with React Server Actions on Next.js, TanStack Start, and other platforms.
|
35
39
|
- **🔠 Standard Schema Support**: Works out of the box with Zod, Valibot, ArkType, and other schema validators.
|
36
40
|
- **🗃️ Native Types**: Supports native types like Date, File, Blob, BigInt, URL, and more.
|
@@ -38,7 +42,6 @@
|
|
38
42
|
- **📡 SSE & Streaming**: Enjoy full type-safe support for SSE and streaming.
|
39
43
|
- **🌍 Multi-Runtime Support**: Fast and lightweight on Cloudflare, Deno, Bun, Node.js, and beyond.
|
40
44
|
- **🔌 Extendability**: Easily extend functionality with plugins, middleware, and interceptors.
|
41
|
-
- **🛡️ Reliability**: Well-tested, TypeScript-based, production-ready, and MIT licensed.
|
42
45
|
|
43
46
|
## Documentation
|
44
47
|
|
@@ -49,14 +52,14 @@ You can find the full documentation [here](https://orpc.unnoq.com).
|
|
49
52
|
- [@orpc/contract](https://www.npmjs.com/package/@orpc/contract): Build your API contract.
|
50
53
|
- [@orpc/server](https://www.npmjs.com/package/@orpc/server): Build your API or implement API contract.
|
51
54
|
- [@orpc/client](https://www.npmjs.com/package/@orpc/client): Consume your API on the client with type-safety.
|
52
|
-
- [@orpc/
|
55
|
+
- [@orpc/openapi](https://www.npmjs.com/package/@orpc/openapi): Generate OpenAPI specs and handle OpenAPI requests.
|
56
|
+
- [@orpc/otel](https://www.npmjs.com/package/@orpc/otel): [OpenTelemetry](https://opentelemetry.io/) integration for observability.
|
57
|
+
- [@orpc/nest](https://www.npmjs.com/package/@orpc/nest): Deeply integrate oRPC with [NestJS](https://nestjs.com/).
|
53
58
|
- [@orpc/react](https://www.npmjs.com/package/@orpc/react): Utilities for integrating oRPC with React and React Server Actions.
|
54
|
-
- [@orpc/
|
55
|
-
- [@orpc/
|
56
|
-
- [@orpc/solid-query](https://www.npmjs.com/package/@orpc/solid-query): Integration with [Solid Query](https://tanstack.com/query/latest/docs/framework/solid/overview).
|
57
|
-
- [@orpc/svelte-query](https://www.npmjs.com/package/@orpc/svelte-query): Integration with [Svelte Query](https://tanstack.com/query/latest/docs/framework/svelte/overview).
|
59
|
+
- [@orpc/tanstack-query](https://www.npmjs.com/package/@orpc/tanstack-query): [TanStack Query](https://tanstack.com/query/latest) integration.
|
60
|
+
- [@orpc/experimental-react-swr](https://www.npmjs.com/package/@orpc/experimental-react-swr): [SWR](https://swr.vercel.app/) integration.
|
58
61
|
- [@orpc/vue-colada](https://www.npmjs.com/package/@orpc/vue-colada): Integration with [Pinia Colada](https://pinia-colada.esm.dev/).
|
59
|
-
- [@orpc/
|
62
|
+
- [@orpc/hey-api](https://www.npmjs.com/package/@orpc/hey-api): [Hey API](https://heyapi.dev/) integration.
|
60
63
|
- [@orpc/zod](https://www.npmjs.com/package/@orpc/zod): More schemas that [Zod](https://zod.dev/) doesn't support yet.
|
61
64
|
- [@orpc/valibot](https://www.npmjs.com/package/@orpc/valibot): OpenAPI spec generation from [Valibot](https://valibot.dev/).
|
62
65
|
- [@orpc/arktype](https://www.npmjs.com/package/@orpc/arktype): OpenAPI spec generation from [ArkType](https://arktype.io/).
|
package/dist/index.d.mts
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
import { Promisable, AsyncIdQueueCloseOptions as AsyncIdQueueCloseOptions$1,
|
2
|
-
import { StandardRequest, StandardResponse, EventMeta
|
1
|
+
import { Promisable, AsyncIdQueueCloseOptions as AsyncIdQueueCloseOptions$1, SetSpanErrorOptions, AsyncIteratorClassCleanupFn, AsyncIteratorClass } from '@orpc/shared';
|
2
|
+
import { StandardRequest, StandardResponse, EventMeta } from '@orpc/standard-server';
|
3
3
|
|
4
|
-
type EncodedMessage = string | ArrayBufferLike |
|
4
|
+
type EncodedMessage = string | ArrayBufferLike | Uint8Array;
|
5
5
|
interface EncodedMessageSendFn {
|
6
6
|
(message: EncodedMessage): Promisable<void>;
|
7
7
|
}
|
@@ -16,13 +16,26 @@ interface ClientPeerCloseOptions extends AsyncIdQueueCloseOptions$1 {
|
|
16
16
|
}
|
17
17
|
declare class ClientPeer {
|
18
18
|
private readonly idGenerator;
|
19
|
+
/**
|
20
|
+
* Queue of responses sent from server, awaiting consumption
|
21
|
+
*/
|
19
22
|
private readonly responseQueue;
|
23
|
+
/**
|
24
|
+
* Queue of event iterator messages sent from server, awaiting consumption
|
25
|
+
*/
|
20
26
|
private readonly serverEventIteratorQueue;
|
27
|
+
/**
|
28
|
+
* Controllers used to signal that the client should stop sending event iterator messages
|
29
|
+
*/
|
21
30
|
private readonly serverControllers;
|
31
|
+
/**
|
32
|
+
* Cleanup functions invoked when the request/response is closed
|
33
|
+
*/
|
34
|
+
private readonly cleanupFns;
|
22
35
|
private readonly send;
|
23
36
|
constructor(send: EncodedMessageSendFn);
|
24
37
|
get length(): number;
|
25
|
-
open(id:
|
38
|
+
open(id: string): AbortController;
|
26
39
|
request(request: StandardRequest): Promise<StandardResponse>;
|
27
40
|
message(raw: EncodedMessage): Promise<void>;
|
28
41
|
close(options?: AsyncIdQueueCloseOptions$1): void;
|
@@ -51,36 +64,42 @@ interface ResponseMessageMap {
|
|
51
64
|
[MessageType.ABORT_SIGNAL]: void;
|
52
65
|
}
|
53
66
|
type DecodedMessageUnion<TMap extends RequestMessageMap | ResponseMessageMap> = {
|
54
|
-
[K in keyof TMap]: [id:
|
67
|
+
[K in keyof TMap]: [id: string, type: K, payload: TMap[K]];
|
55
68
|
}[keyof TMap];
|
56
69
|
type DecodedRequestMessage = DecodedMessageUnion<RequestMessageMap>;
|
57
70
|
type DecodedResponseMessage = DecodedMessageUnion<ResponseMessageMap>;
|
58
|
-
declare function encodeRequestMessage<T extends keyof RequestMessageMap>(id:
|
71
|
+
declare function encodeRequestMessage<T extends keyof RequestMessageMap>(id: string, type: T, payload: RequestMessageMap[T]): Promise<EncodedMessage>;
|
59
72
|
declare function decodeRequestMessage(raw: EncodedMessage): Promise<DecodedRequestMessage>;
|
60
|
-
declare function encodeResponseMessage<T extends keyof ResponseMessageMap>(id:
|
73
|
+
declare function encodeResponseMessage<T extends keyof ResponseMessageMap>(id: string, type: T, payload: ResponseMessageMap[T]): Promise<EncodedMessage>;
|
61
74
|
declare function decodeResponseMessage(raw: EncodedMessage): Promise<DecodedResponseMessage>;
|
62
|
-
declare function isEventIteratorHeaders(headers: StandardHeaders): boolean;
|
63
75
|
|
64
76
|
interface AsyncIdQueueCloseOptions {
|
65
|
-
id?:
|
66
|
-
reason?:
|
77
|
+
id?: string;
|
78
|
+
reason?: unknown;
|
67
79
|
}
|
68
80
|
declare class AsyncIdQueue<T> {
|
69
81
|
private readonly openIds;
|
70
|
-
private readonly
|
71
|
-
private readonly
|
82
|
+
private readonly queues;
|
83
|
+
private readonly waiters;
|
72
84
|
get length(): number;
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
85
|
+
get waiterIds(): string[];
|
86
|
+
hasBufferedItems(id: string): boolean;
|
87
|
+
open(id: string): void;
|
88
|
+
isOpen(id: string): boolean;
|
89
|
+
push(id: string, item: T): void;
|
90
|
+
pull(id: string): Promise<T>;
|
77
91
|
close({ id, reason }?: AsyncIdQueueCloseOptions): void;
|
78
|
-
assertOpen(id:
|
92
|
+
assertOpen(id: string): void;
|
79
93
|
}
|
80
94
|
|
81
|
-
|
95
|
+
interface ToEventIteratorOptions extends SetSpanErrorOptions {
|
96
|
+
}
|
97
|
+
declare function toEventIterator(queue: AsyncIdQueue<EventIteratorPayload>, id: string, cleanup: AsyncIteratorClassCleanupFn, options?: ToEventIteratorOptions): AsyncIteratorClass<unknown>;
|
82
98
|
declare function resolveEventIterator(iterator: AsyncIterator<any>, callback: (payload: EventIteratorPayload) => Promise<'next' | 'abort'>): Promise<void>;
|
83
99
|
|
100
|
+
interface ServerPeerHandleRequestFn {
|
101
|
+
(request: StandardRequest): Promise<StandardResponse>;
|
102
|
+
}
|
84
103
|
interface ServerPeerCloseOptions extends AsyncIdQueueCloseOptions$1 {
|
85
104
|
/**
|
86
105
|
* Should abort or not?
|
@@ -90,16 +109,28 @@ interface ServerPeerCloseOptions extends AsyncIdQueueCloseOptions$1 {
|
|
90
109
|
abort?: boolean;
|
91
110
|
}
|
92
111
|
declare class ServerPeer {
|
112
|
+
/**
|
113
|
+
* Queue of event iterator messages sent from client, awaiting consumption
|
114
|
+
*/
|
93
115
|
private readonly clientEventIteratorQueue;
|
116
|
+
/**
|
117
|
+
* Map of active client request controllers, should be synced to request signal
|
118
|
+
*/
|
94
119
|
private readonly clientControllers;
|
95
120
|
private readonly send;
|
96
121
|
constructor(send: EncodedMessageSendFn);
|
97
122
|
get length(): number;
|
98
|
-
open(id:
|
99
|
-
|
100
|
-
|
123
|
+
open(id: string): AbortController;
|
124
|
+
/**
|
125
|
+
* @todo This method will return Promise<void> in the next major version.
|
126
|
+
*/
|
127
|
+
message(raw: EncodedMessage, handleRequest?: ServerPeerHandleRequestFn): Promise<[id: string, StandardRequest | undefined]>;
|
128
|
+
/**
|
129
|
+
* @deprecated Please pass the `handleRequest` (second arg) function to the `message` method instead.
|
130
|
+
*/
|
131
|
+
response(id: string, response: StandardResponse): Promise<void>;
|
101
132
|
close({ abort, ...options }?: ServerPeerCloseOptions): void;
|
102
133
|
}
|
103
134
|
|
104
|
-
export { ClientPeer, MessageType, ServerPeer, decodeRequestMessage, decodeResponseMessage, encodeRequestMessage, encodeResponseMessage,
|
105
|
-
export type { ClientPeerCloseOptions, DecodedRequestMessage, DecodedResponseMessage, EncodedMessage, EncodedMessageSendFn, EventIteratorEvent, EventIteratorPayload, RequestMessageMap, ResponseMessageMap, ServerPeerCloseOptions };
|
135
|
+
export { ClientPeer, MessageType, ServerPeer, decodeRequestMessage, decodeResponseMessage, encodeRequestMessage, encodeResponseMessage, resolveEventIterator, toEventIterator };
|
136
|
+
export type { ClientPeerCloseOptions, DecodedRequestMessage, DecodedResponseMessage, EncodedMessage, EncodedMessageSendFn, EventIteratorEvent, EventIteratorPayload, RequestMessageMap, ResponseMessageMap, ServerPeerCloseOptions, ServerPeerHandleRequestFn, ToEventIteratorOptions };
|
package/dist/index.d.ts
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
import { Promisable, AsyncIdQueueCloseOptions as AsyncIdQueueCloseOptions$1,
|
2
|
-
import { StandardRequest, StandardResponse, EventMeta
|
1
|
+
import { Promisable, AsyncIdQueueCloseOptions as AsyncIdQueueCloseOptions$1, SetSpanErrorOptions, AsyncIteratorClassCleanupFn, AsyncIteratorClass } from '@orpc/shared';
|
2
|
+
import { StandardRequest, StandardResponse, EventMeta } from '@orpc/standard-server';
|
3
3
|
|
4
|
-
type EncodedMessage = string | ArrayBufferLike |
|
4
|
+
type EncodedMessage = string | ArrayBufferLike | Uint8Array;
|
5
5
|
interface EncodedMessageSendFn {
|
6
6
|
(message: EncodedMessage): Promisable<void>;
|
7
7
|
}
|
@@ -16,13 +16,26 @@ interface ClientPeerCloseOptions extends AsyncIdQueueCloseOptions$1 {
|
|
16
16
|
}
|
17
17
|
declare class ClientPeer {
|
18
18
|
private readonly idGenerator;
|
19
|
+
/**
|
20
|
+
* Queue of responses sent from server, awaiting consumption
|
21
|
+
*/
|
19
22
|
private readonly responseQueue;
|
23
|
+
/**
|
24
|
+
* Queue of event iterator messages sent from server, awaiting consumption
|
25
|
+
*/
|
20
26
|
private readonly serverEventIteratorQueue;
|
27
|
+
/**
|
28
|
+
* Controllers used to signal that the client should stop sending event iterator messages
|
29
|
+
*/
|
21
30
|
private readonly serverControllers;
|
31
|
+
/**
|
32
|
+
* Cleanup functions invoked when the request/response is closed
|
33
|
+
*/
|
34
|
+
private readonly cleanupFns;
|
22
35
|
private readonly send;
|
23
36
|
constructor(send: EncodedMessageSendFn);
|
24
37
|
get length(): number;
|
25
|
-
open(id:
|
38
|
+
open(id: string): AbortController;
|
26
39
|
request(request: StandardRequest): Promise<StandardResponse>;
|
27
40
|
message(raw: EncodedMessage): Promise<void>;
|
28
41
|
close(options?: AsyncIdQueueCloseOptions$1): void;
|
@@ -51,36 +64,42 @@ interface ResponseMessageMap {
|
|
51
64
|
[MessageType.ABORT_SIGNAL]: void;
|
52
65
|
}
|
53
66
|
type DecodedMessageUnion<TMap extends RequestMessageMap | ResponseMessageMap> = {
|
54
|
-
[K in keyof TMap]: [id:
|
67
|
+
[K in keyof TMap]: [id: string, type: K, payload: TMap[K]];
|
55
68
|
}[keyof TMap];
|
56
69
|
type DecodedRequestMessage = DecodedMessageUnion<RequestMessageMap>;
|
57
70
|
type DecodedResponseMessage = DecodedMessageUnion<ResponseMessageMap>;
|
58
|
-
declare function encodeRequestMessage<T extends keyof RequestMessageMap>(id:
|
71
|
+
declare function encodeRequestMessage<T extends keyof RequestMessageMap>(id: string, type: T, payload: RequestMessageMap[T]): Promise<EncodedMessage>;
|
59
72
|
declare function decodeRequestMessage(raw: EncodedMessage): Promise<DecodedRequestMessage>;
|
60
|
-
declare function encodeResponseMessage<T extends keyof ResponseMessageMap>(id:
|
73
|
+
declare function encodeResponseMessage<T extends keyof ResponseMessageMap>(id: string, type: T, payload: ResponseMessageMap[T]): Promise<EncodedMessage>;
|
61
74
|
declare function decodeResponseMessage(raw: EncodedMessage): Promise<DecodedResponseMessage>;
|
62
|
-
declare function isEventIteratorHeaders(headers: StandardHeaders): boolean;
|
63
75
|
|
64
76
|
interface AsyncIdQueueCloseOptions {
|
65
|
-
id?:
|
66
|
-
reason?:
|
77
|
+
id?: string;
|
78
|
+
reason?: unknown;
|
67
79
|
}
|
68
80
|
declare class AsyncIdQueue<T> {
|
69
81
|
private readonly openIds;
|
70
|
-
private readonly
|
71
|
-
private readonly
|
82
|
+
private readonly queues;
|
83
|
+
private readonly waiters;
|
72
84
|
get length(): number;
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
85
|
+
get waiterIds(): string[];
|
86
|
+
hasBufferedItems(id: string): boolean;
|
87
|
+
open(id: string): void;
|
88
|
+
isOpen(id: string): boolean;
|
89
|
+
push(id: string, item: T): void;
|
90
|
+
pull(id: string): Promise<T>;
|
77
91
|
close({ id, reason }?: AsyncIdQueueCloseOptions): void;
|
78
|
-
assertOpen(id:
|
92
|
+
assertOpen(id: string): void;
|
79
93
|
}
|
80
94
|
|
81
|
-
|
95
|
+
interface ToEventIteratorOptions extends SetSpanErrorOptions {
|
96
|
+
}
|
97
|
+
declare function toEventIterator(queue: AsyncIdQueue<EventIteratorPayload>, id: string, cleanup: AsyncIteratorClassCleanupFn, options?: ToEventIteratorOptions): AsyncIteratorClass<unknown>;
|
82
98
|
declare function resolveEventIterator(iterator: AsyncIterator<any>, callback: (payload: EventIteratorPayload) => Promise<'next' | 'abort'>): Promise<void>;
|
83
99
|
|
100
|
+
interface ServerPeerHandleRequestFn {
|
101
|
+
(request: StandardRequest): Promise<StandardResponse>;
|
102
|
+
}
|
84
103
|
interface ServerPeerCloseOptions extends AsyncIdQueueCloseOptions$1 {
|
85
104
|
/**
|
86
105
|
* Should abort or not?
|
@@ -90,16 +109,28 @@ interface ServerPeerCloseOptions extends AsyncIdQueueCloseOptions$1 {
|
|
90
109
|
abort?: boolean;
|
91
110
|
}
|
92
111
|
declare class ServerPeer {
|
112
|
+
/**
|
113
|
+
* Queue of event iterator messages sent from client, awaiting consumption
|
114
|
+
*/
|
93
115
|
private readonly clientEventIteratorQueue;
|
116
|
+
/**
|
117
|
+
* Map of active client request controllers, should be synced to request signal
|
118
|
+
*/
|
94
119
|
private readonly clientControllers;
|
95
120
|
private readonly send;
|
96
121
|
constructor(send: EncodedMessageSendFn);
|
97
122
|
get length(): number;
|
98
|
-
open(id:
|
99
|
-
|
100
|
-
|
123
|
+
open(id: string): AbortController;
|
124
|
+
/**
|
125
|
+
* @todo This method will return Promise<void> in the next major version.
|
126
|
+
*/
|
127
|
+
message(raw: EncodedMessage, handleRequest?: ServerPeerHandleRequestFn): Promise<[id: string, StandardRequest | undefined]>;
|
128
|
+
/**
|
129
|
+
* @deprecated Please pass the `handleRequest` (second arg) function to the `message` method instead.
|
130
|
+
*/
|
131
|
+
response(id: string, response: StandardResponse): Promise<void>;
|
101
132
|
close({ abort, ...options }?: ServerPeerCloseOptions): void;
|
102
133
|
}
|
103
134
|
|
104
|
-
export { ClientPeer, MessageType, ServerPeer, decodeRequestMessage, decodeResponseMessage, encodeRequestMessage, encodeResponseMessage,
|
105
|
-
export type { ClientPeerCloseOptions, DecodedRequestMessage, DecodedResponseMessage, EncodedMessage, EncodedMessageSendFn, EventIteratorEvent, EventIteratorPayload, RequestMessageMap, ResponseMessageMap, ServerPeerCloseOptions };
|
135
|
+
export { ClientPeer, MessageType, ServerPeer, decodeRequestMessage, decodeResponseMessage, encodeRequestMessage, encodeResponseMessage, resolveEventIterator, toEventIterator };
|
136
|
+
export type { ClientPeerCloseOptions, DecodedRequestMessage, DecodedResponseMessage, EncodedMessage, EncodedMessageSendFn, EventIteratorEvent, EventIteratorPayload, RequestMessageMap, ResponseMessageMap, ServerPeerCloseOptions, ServerPeerHandleRequestFn, ToEventIteratorOptions };
|
package/dist/index.mjs
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
-
import { isAsyncIteratorObject, stringifyJSON,
|
2
|
-
import { flattenHeader, getFilenameFromContentDisposition,
|
1
|
+
import { isAsyncIteratorObject, stringifyJSON, readAsBuffer, AsyncIteratorClass, startSpan, runInSpanContext, isTypescriptObject, setSpanError, runWithSpan, SequentialIdGenerator, AsyncIdQueue, getGlobalOtelConfig, clone, AbortError } from '@orpc/shared';
|
2
|
+
import { generateContentDisposition, flattenHeader, getFilenameFromContentDisposition, withEventMeta, ErrorEvent, getEventMeta, isEventIteratorHeaders, HibernationEventIterator } from '@orpc/standard-server';
|
3
3
|
|
4
|
+
const SHORTABLE_ORIGIN = "orpc://localhost";
|
5
|
+
const SHORTABLE_ORIGIN_MATCHER = /^orpc:\/\/localhost\//;
|
4
6
|
var MessageType = /* @__PURE__ */ ((MessageType2) => {
|
5
7
|
MessageType2[MessageType2["REQUEST"] = 1] = "REQUEST";
|
6
8
|
MessageType2[MessageType2["RESPONSE"] = 2] = "RESPONSE";
|
@@ -22,12 +24,12 @@ async function encodeRequestMessage(id, type, payload) {
|
|
22
24
|
return encodeRawMessage({ i: id, t: type, p: payload });
|
23
25
|
}
|
24
26
|
const request = payload;
|
25
|
-
const { body: processedBody, headers: processedHeaders } = await
|
27
|
+
const { body: processedBody, headers: processedHeaders } = await serializeBodyAndHeaders(
|
26
28
|
request.body,
|
27
29
|
request.headers
|
28
30
|
);
|
29
31
|
const serializedPayload = {
|
30
|
-
u: request.url.toString().replace(
|
32
|
+
u: request.url.toString().replace(SHORTABLE_ORIGIN_MATCHER, "/"),
|
31
33
|
b: processedBody instanceof Blob ? void 0 : processedBody,
|
32
34
|
h: Object.keys(processedHeaders).length > 0 ? processedHeaders : void 0,
|
33
35
|
m: request.method === "POST" ? void 0 : request.method
|
@@ -42,7 +44,7 @@ async function encodeRequestMessage(id, type, payload) {
|
|
42
44
|
return encodeRawMessage(baseMessage);
|
43
45
|
}
|
44
46
|
async function decodeRequestMessage(raw) {
|
45
|
-
const { json: message,
|
47
|
+
const { json: message, buffer } = await decodeRawMessage(raw);
|
46
48
|
const id = message.i;
|
47
49
|
const type = message.t;
|
48
50
|
if (type === 3 /* EVENT_ITERATOR */) {
|
@@ -54,21 +56,13 @@ async function decodeRequestMessage(raw) {
|
|
54
56
|
}
|
55
57
|
const payload = message.p;
|
56
58
|
const headers = payload.h ?? {};
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
} else {
|
65
|
-
const filename = getFilenameFromContentDisposition(contentDisposition) ?? "blob";
|
66
|
-
body = new File([blobData], filename, { type: contentType });
|
67
|
-
}
|
68
|
-
} else if (contentType?.startsWith("application/x-www-form-urlencoded") && typeof body === "string") {
|
69
|
-
body = new URLSearchParams(body);
|
70
|
-
}
|
71
|
-
return [id, 1 /* REQUEST */, { url: new URL(payload.u, "orpc:/"), headers, method: payload.m ?? "POST", body }];
|
59
|
+
const body = await deserializeBody(headers, payload.b, buffer);
|
60
|
+
return [id, 1 /* REQUEST */, {
|
61
|
+
url: payload.u.startsWith("/") ? new URL(`${SHORTABLE_ORIGIN}${payload.u}`) : new URL(payload.u),
|
62
|
+
headers,
|
63
|
+
method: payload.m ?? "POST",
|
64
|
+
body
|
65
|
+
}];
|
72
66
|
}
|
73
67
|
async function encodeResponseMessage(id, type, payload) {
|
74
68
|
if (type === 3 /* EVENT_ITERATOR */) {
|
@@ -84,7 +78,7 @@ async function encodeResponseMessage(id, type, payload) {
|
|
84
78
|
return encodeRawMessage({ i: id, t: type, p: void 0 });
|
85
79
|
}
|
86
80
|
const response = payload;
|
87
|
-
const { body: processedBody, headers: processedHeaders } = await
|
81
|
+
const { body: processedBody, headers: processedHeaders } = await serializeBodyAndHeaders(
|
88
82
|
response.body,
|
89
83
|
response.headers
|
90
84
|
);
|
@@ -103,7 +97,7 @@ async function encodeResponseMessage(id, type, payload) {
|
|
103
97
|
return encodeRawMessage(baseMessage);
|
104
98
|
}
|
105
99
|
async function decodeResponseMessage(raw) {
|
106
|
-
const { json: message,
|
100
|
+
const { json: message, buffer } = await decodeRawMessage(raw);
|
107
101
|
const id = message.i;
|
108
102
|
const type = message.t;
|
109
103
|
if (type === 3 /* EVENT_ITERATOR */) {
|
@@ -115,23 +109,10 @@ async function decodeResponseMessage(raw) {
|
|
115
109
|
}
|
116
110
|
const payload = message.p;
|
117
111
|
const headers = payload.h ?? {};
|
118
|
-
|
119
|
-
const contentType = flattenHeader(headers["content-type"]);
|
120
|
-
if (blobData) {
|
121
|
-
const contentDisposition = flattenHeader(headers["content-disposition"]);
|
122
|
-
if (contentDisposition === void 0 && contentType?.startsWith("multipart/form-data")) {
|
123
|
-
const tempRes = new Response(blobData, { headers: { "content-type": contentType } });
|
124
|
-
body = await tempRes.formData();
|
125
|
-
} else {
|
126
|
-
const filename = getFilenameFromContentDisposition(contentDisposition) ?? "blob";
|
127
|
-
body = new File([blobData], filename, { type: contentType });
|
128
|
-
}
|
129
|
-
} else if (contentType?.startsWith("application/x-www-form-urlencoded") && typeof body === "string") {
|
130
|
-
body = new URLSearchParams(body);
|
131
|
-
}
|
112
|
+
const body = await deserializeBody(headers, payload.b, buffer);
|
132
113
|
return [id, 2 /* RESPONSE */, { status: payload.s ?? 200, headers, body }];
|
133
114
|
}
|
134
|
-
async function
|
115
|
+
async function serializeBodyAndHeaders(body, originalHeaders) {
|
135
116
|
const headers = { ...originalHeaders };
|
136
117
|
const originalContentDisposition = headers["content-disposition"];
|
137
118
|
delete headers["content-type"];
|
@@ -159,113 +140,184 @@ async function prepareBodyAndHeadersForSerialization(body, originalHeaders) {
|
|
159
140
|
}
|
160
141
|
return { body, headers };
|
161
142
|
}
|
162
|
-
function
|
163
|
-
|
143
|
+
async function deserializeBody(headers, body, buffer) {
|
144
|
+
const contentType = flattenHeader(headers["content-type"]);
|
145
|
+
const contentDisposition = flattenHeader(headers["content-disposition"]);
|
146
|
+
if (typeof contentDisposition === "string") {
|
147
|
+
const filename = getFilenameFromContentDisposition(contentDisposition) ?? "blob";
|
148
|
+
return new File(buffer === void 0 ? [] : [buffer], filename, { type: contentType });
|
149
|
+
}
|
150
|
+
if (contentType?.startsWith("multipart/form-data")) {
|
151
|
+
const tempRes = new Response(buffer, { headers: { "content-type": contentType } });
|
152
|
+
return tempRes.formData();
|
153
|
+
}
|
154
|
+
if (contentType?.startsWith("application/x-www-form-urlencoded") && typeof body === "string") {
|
155
|
+
return new URLSearchParams(body);
|
156
|
+
}
|
157
|
+
return body;
|
164
158
|
}
|
165
159
|
const JSON_AND_BINARY_DELIMITER = 255;
|
166
|
-
async function encodeRawMessage(data,
|
160
|
+
async function encodeRawMessage(data, blob) {
|
167
161
|
const json = stringifyJSON(data);
|
168
|
-
if (
|
162
|
+
if (blob === void 0 || blob.size === 0) {
|
169
163
|
return json;
|
170
164
|
}
|
171
|
-
return new Blob([
|
165
|
+
return readAsBuffer(new Blob([
|
172
166
|
new TextEncoder().encode(json),
|
173
167
|
new Uint8Array([JSON_AND_BINARY_DELIMITER]),
|
174
|
-
|
175
|
-
]);
|
168
|
+
blob
|
169
|
+
]));
|
176
170
|
}
|
177
171
|
async function decodeRawMessage(raw) {
|
178
172
|
if (typeof raw === "string") {
|
179
173
|
return { json: JSON.parse(raw) };
|
180
174
|
}
|
181
|
-
const buffer =
|
175
|
+
const buffer = raw instanceof Uint8Array ? raw : new Uint8Array(raw);
|
182
176
|
const delimiterIndex = buffer.indexOf(JSON_AND_BINARY_DELIMITER);
|
183
177
|
if (delimiterIndex === -1) {
|
184
178
|
const jsonPart2 = new TextDecoder().decode(buffer);
|
185
179
|
return { json: JSON.parse(jsonPart2) };
|
186
180
|
}
|
187
|
-
const jsonPart = new TextDecoder().decode(buffer.
|
188
|
-
const
|
181
|
+
const jsonPart = new TextDecoder().decode(buffer.subarray(0, delimiterIndex));
|
182
|
+
const bufferPart = buffer.subarray(delimiterIndex + 1);
|
189
183
|
return {
|
190
184
|
json: JSON.parse(jsonPart),
|
191
|
-
|
185
|
+
buffer: bufferPart
|
192
186
|
};
|
193
187
|
}
|
194
188
|
|
195
|
-
function toEventIterator(queue, id, cleanup) {
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
189
|
+
function toEventIterator(queue, id, cleanup, options = {}) {
|
190
|
+
let span;
|
191
|
+
return new AsyncIteratorClass(async () => {
|
192
|
+
span ??= startSpan("consume_event_iterator_stream");
|
193
|
+
try {
|
194
|
+
const item = await runInSpanContext(span, () => queue.pull(id));
|
195
|
+
switch (item.event) {
|
196
|
+
case "message": {
|
197
|
+
let data = item.data;
|
198
|
+
if (item.meta && isTypescriptObject(data)) {
|
199
|
+
data = withEventMeta(data, item.meta);
|
200
|
+
}
|
201
|
+
span?.addEvent("message");
|
202
|
+
return { value: data, done: false };
|
203
203
|
}
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
204
|
+
case "error": {
|
205
|
+
let error = new ErrorEvent({
|
206
|
+
data: item.data
|
207
|
+
});
|
208
|
+
if (item.meta) {
|
209
|
+
error = withEventMeta(error, item.meta);
|
210
|
+
}
|
211
|
+
span?.addEvent("error");
|
212
|
+
throw error;
|
212
213
|
}
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
214
|
+
case "done": {
|
215
|
+
let data = item.data;
|
216
|
+
if (item.meta && isTypescriptObject(data)) {
|
217
|
+
data = withEventMeta(data, item.meta);
|
218
|
+
}
|
219
|
+
span?.addEvent("done");
|
220
|
+
return { value: data, done: true };
|
219
221
|
}
|
220
|
-
return { value: data, done: true };
|
221
222
|
}
|
222
|
-
}
|
223
|
-
|
224
|
-
|
225
|
-
async function resolveEventIterator(iterator, callback) {
|
226
|
-
while (true) {
|
227
|
-
const payload = await (async () => {
|
228
|
-
try {
|
229
|
-
const { value, done } = await iterator.next();
|
230
|
-
if (done) {
|
231
|
-
return { event: "done", data: value, meta: getEventMeta(value) };
|
232
|
-
}
|
233
|
-
return { event: "message", data: value, meta: getEventMeta(value) };
|
234
|
-
} catch (err) {
|
235
|
-
return {
|
236
|
-
meta: getEventMeta(err),
|
237
|
-
event: "error",
|
238
|
-
data: err instanceof ErrorEvent ? err.data : void 0
|
239
|
-
};
|
223
|
+
} catch (e) {
|
224
|
+
if (!(e instanceof ErrorEvent)) {
|
225
|
+
setSpanError(span, e, options);
|
240
226
|
}
|
241
|
-
|
227
|
+
throw e;
|
228
|
+
}
|
229
|
+
}, async (reason) => {
|
242
230
|
try {
|
243
|
-
|
244
|
-
|
245
|
-
return;
|
231
|
+
if (reason !== "next") {
|
232
|
+
span?.addEvent("cancelled");
|
246
233
|
}
|
247
|
-
|
234
|
+
await runInSpanContext(span, () => cleanup(reason));
|
235
|
+
} catch (e) {
|
236
|
+
setSpanError(span, e, options);
|
237
|
+
throw e;
|
238
|
+
} finally {
|
239
|
+
span?.end();
|
240
|
+
}
|
241
|
+
});
|
242
|
+
}
|
243
|
+
function resolveEventIterator(iterator, callback) {
|
244
|
+
return runWithSpan(
|
245
|
+
{ name: "stream_event_iterator" },
|
246
|
+
async (span) => {
|
247
|
+
while (true) {
|
248
|
+
const payload = await (async () => {
|
249
|
+
try {
|
250
|
+
const { value, done } = await iterator.next();
|
251
|
+
if (done) {
|
252
|
+
span?.addEvent("done");
|
253
|
+
return { event: "done", data: value, meta: getEventMeta(value) };
|
254
|
+
}
|
255
|
+
span?.addEvent("message");
|
256
|
+
return { event: "message", data: value, meta: getEventMeta(value) };
|
257
|
+
} catch (err) {
|
258
|
+
if (err instanceof ErrorEvent) {
|
259
|
+
span?.addEvent("error");
|
260
|
+
return {
|
261
|
+
event: "error",
|
262
|
+
data: err.data,
|
263
|
+
meta: getEventMeta(err)
|
264
|
+
};
|
265
|
+
} else {
|
266
|
+
try {
|
267
|
+
await callback({ event: "error", data: void 0 });
|
268
|
+
} catch (err2) {
|
269
|
+
setSpanError(span, err);
|
270
|
+
throw err2;
|
271
|
+
}
|
272
|
+
throw err;
|
273
|
+
}
|
274
|
+
}
|
275
|
+
})();
|
276
|
+
let isInvokeCleanupFn = false;
|
248
277
|
try {
|
249
|
-
await
|
250
|
-
|
278
|
+
const direction = await callback(payload);
|
279
|
+
if (payload.event === "done" || payload.event === "error") {
|
280
|
+
return;
|
281
|
+
}
|
282
|
+
if (direction === "abort") {
|
283
|
+
isInvokeCleanupFn = true;
|
284
|
+
await iterator.return?.();
|
285
|
+
return;
|
286
|
+
}
|
287
|
+
} catch (err) {
|
288
|
+
if (!isInvokeCleanupFn) {
|
289
|
+
try {
|
290
|
+
await iterator.return?.();
|
291
|
+
} catch (err2) {
|
292
|
+
setSpanError(span, err);
|
293
|
+
throw err2;
|
294
|
+
}
|
295
|
+
}
|
296
|
+
throw err;
|
251
297
|
}
|
252
|
-
return;
|
253
|
-
}
|
254
|
-
} catch (err) {
|
255
|
-
try {
|
256
|
-
await iterator.return?.();
|
257
|
-
} catch {
|
258
298
|
}
|
259
|
-
throw err;
|
260
299
|
}
|
261
|
-
|
300
|
+
);
|
262
301
|
}
|
263
302
|
|
264
303
|
class ClientPeer {
|
265
304
|
idGenerator = new SequentialIdGenerator();
|
305
|
+
/**
|
306
|
+
* Queue of responses sent from server, awaiting consumption
|
307
|
+
*/
|
266
308
|
responseQueue = new AsyncIdQueue();
|
309
|
+
/**
|
310
|
+
* Queue of event iterator messages sent from server, awaiting consumption
|
311
|
+
*/
|
267
312
|
serverEventIteratorQueue = new AsyncIdQueue();
|
313
|
+
/**
|
314
|
+
* Controllers used to signal that the client should stop sending event iterator messages
|
315
|
+
*/
|
268
316
|
serverControllers = /* @__PURE__ */ new Map();
|
317
|
+
/**
|
318
|
+
* Cleanup functions invoked when the request/response is closed
|
319
|
+
*/
|
320
|
+
cleanupFns = /* @__PURE__ */ new Map();
|
269
321
|
send;
|
270
322
|
constructor(send) {
|
271
323
|
this.send = async (id, ...rest) => encodeRequestMessage(id, ...rest).then(async (raw) => {
|
@@ -275,48 +327,85 @@ class ClientPeer {
|
|
275
327
|
});
|
276
328
|
}
|
277
329
|
get length() {
|
278
|
-
return (+this.responseQueue.length + this.serverEventIteratorQueue.length + this.serverControllers.size) /
|
330
|
+
return (+this.responseQueue.length + this.serverEventIteratorQueue.length + this.serverControllers.size + this.cleanupFns.size) / 4;
|
279
331
|
}
|
280
332
|
open(id) {
|
281
333
|
this.serverEventIteratorQueue.open(id);
|
282
334
|
this.responseQueue.open(id);
|
283
335
|
const controller = new AbortController();
|
284
336
|
this.serverControllers.set(id, controller);
|
337
|
+
this.cleanupFns.set(id, []);
|
285
338
|
return controller;
|
286
339
|
}
|
287
340
|
async request(request) {
|
288
341
|
const signal = request.signal;
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
const id = this.idGenerator.generate();
|
293
|
-
const serverController = this.open(id);
|
294
|
-
return new Promise((resolve, reject) => {
|
295
|
-
this.send(id, MessageType.REQUEST, request).then(async () => {
|
342
|
+
return runWithSpan(
|
343
|
+
{ name: "send_peer_request", signal },
|
344
|
+
async () => {
|
296
345
|
if (signal?.aborted) {
|
297
|
-
|
298
|
-
this.close({ id, reason: signal.reason });
|
299
|
-
return;
|
346
|
+
throw signal.reason;
|
300
347
|
}
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
348
|
+
const id = this.idGenerator.generate();
|
349
|
+
const serverController = this.open(id);
|
350
|
+
try {
|
351
|
+
const otelConfig = getGlobalOtelConfig();
|
352
|
+
if (otelConfig) {
|
353
|
+
const headers = clone(request.headers);
|
354
|
+
otelConfig.propagation.inject(otelConfig.context.active(), headers);
|
355
|
+
request = { ...request, headers };
|
356
|
+
}
|
357
|
+
await this.send(id, MessageType.REQUEST, request);
|
358
|
+
if (signal?.aborted) {
|
359
|
+
await this.send(id, MessageType.ABORT_SIGNAL, void 0);
|
360
|
+
throw signal.reason;
|
361
|
+
}
|
362
|
+
let abortListener;
|
363
|
+
signal?.addEventListener("abort", abortListener = async () => {
|
364
|
+
await this.send(id, MessageType.ABORT_SIGNAL, void 0);
|
365
|
+
this.close({ id, reason: signal.reason });
|
366
|
+
}, { once: true });
|
367
|
+
this.cleanupFns.get(id)?.push(() => {
|
368
|
+
signal?.removeEventListener("abort", abortListener);
|
312
369
|
});
|
370
|
+
if (isAsyncIteratorObject(request.body)) {
|
371
|
+
const iterator = request.body;
|
372
|
+
void resolveEventIterator(iterator, async (payload) => {
|
373
|
+
if (serverController.signal.aborted) {
|
374
|
+
return "abort";
|
375
|
+
}
|
376
|
+
await this.send(id, MessageType.EVENT_ITERATOR, payload);
|
377
|
+
return "next";
|
378
|
+
});
|
379
|
+
}
|
380
|
+
const response = await this.responseQueue.pull(id);
|
381
|
+
if (isEventIteratorHeaders(response.headers)) {
|
382
|
+
const iterator = toEventIterator(
|
383
|
+
this.serverEventIteratorQueue,
|
384
|
+
id,
|
385
|
+
async (reason) => {
|
386
|
+
try {
|
387
|
+
if (reason !== "next") {
|
388
|
+
await this.send(id, MessageType.ABORT_SIGNAL, void 0);
|
389
|
+
}
|
390
|
+
} finally {
|
391
|
+
this.close({ id });
|
392
|
+
}
|
393
|
+
},
|
394
|
+
{ signal }
|
395
|
+
);
|
396
|
+
return {
|
397
|
+
...response,
|
398
|
+
body: iterator
|
399
|
+
};
|
400
|
+
}
|
401
|
+
this.close({ id });
|
402
|
+
return response;
|
403
|
+
} catch (err) {
|
404
|
+
this.close({ id, reason: err });
|
405
|
+
throw err;
|
313
406
|
}
|
314
|
-
}
|
315
|
-
|
316
|
-
reject(err);
|
317
|
-
});
|
318
|
-
this.responseQueue.pull(id).then(resolve).catch(reject);
|
319
|
-
});
|
407
|
+
}
|
408
|
+
);
|
320
409
|
}
|
321
410
|
async message(raw) {
|
322
411
|
const [id, type, payload] = await decodeResponseMessage(raw);
|
@@ -333,31 +422,19 @@ class ClientPeer {
|
|
333
422
|
if (!this.responseQueue.isOpen(id)) {
|
334
423
|
return;
|
335
424
|
}
|
336
|
-
|
337
|
-
this.responseQueue.push(id, {
|
338
|
-
...payload,
|
339
|
-
body: toEventIterator(this.serverEventIteratorQueue, id, async (reason) => {
|
340
|
-
try {
|
341
|
-
if (reason !== "next") {
|
342
|
-
await this.send(id, MessageType.ABORT_SIGNAL, void 0);
|
343
|
-
}
|
344
|
-
} finally {
|
345
|
-
this.close({ id });
|
346
|
-
}
|
347
|
-
})
|
348
|
-
});
|
349
|
-
} else {
|
350
|
-
this.responseQueue.push(id, payload);
|
351
|
-
this.close({ id });
|
352
|
-
}
|
425
|
+
this.responseQueue.push(id, payload);
|
353
426
|
}
|
354
427
|
close(options = {}) {
|
355
428
|
if (options.id !== void 0) {
|
356
429
|
this.serverControllers.get(options.id)?.abort(options.reason);
|
357
430
|
this.serverControllers.delete(options.id);
|
431
|
+
this.cleanupFns.get(options.id)?.forEach((fn) => fn());
|
432
|
+
this.cleanupFns.delete(options.id);
|
358
433
|
} else {
|
359
434
|
this.serverControllers.forEach((c) => c.abort(options.reason));
|
360
435
|
this.serverControllers.clear();
|
436
|
+
this.cleanupFns.forEach((fns) => fns.forEach((fn) => fn()));
|
437
|
+
this.cleanupFns.clear();
|
361
438
|
}
|
362
439
|
this.responseQueue.close(options);
|
363
440
|
this.serverEventIteratorQueue.close(options);
|
@@ -365,7 +442,13 @@ class ClientPeer {
|
|
365
442
|
}
|
366
443
|
|
367
444
|
class ServerPeer {
|
445
|
+
/**
|
446
|
+
* Queue of event iterator messages sent from client, awaiting consumption
|
447
|
+
*/
|
368
448
|
clientEventIteratorQueue = new AsyncIdQueue();
|
449
|
+
/**
|
450
|
+
* Map of active client request controllers, should be synced to request signal
|
451
|
+
*/
|
369
452
|
clientControllers = /* @__PURE__ */ new Map();
|
370
453
|
send;
|
371
454
|
constructor(send) {
|
@@ -384,10 +467,13 @@ class ServerPeer {
|
|
384
467
|
this.clientControllers.set(id, controller);
|
385
468
|
return controller;
|
386
469
|
}
|
387
|
-
|
470
|
+
/**
|
471
|
+
* @todo This method will return Promise<void> in the next major version.
|
472
|
+
*/
|
473
|
+
async message(raw, handleRequest) {
|
388
474
|
const [id, type, payload] = await decodeRequestMessage(raw);
|
389
475
|
if (type === MessageType.ABORT_SIGNAL) {
|
390
|
-
this.close({ id });
|
476
|
+
this.close({ id, reason: new AbortError("Client aborted the request") });
|
391
477
|
return [id, void 0];
|
392
478
|
}
|
393
479
|
if (type === MessageType.EVENT_ITERATOR) {
|
@@ -397,47 +483,89 @@ class ServerPeer {
|
|
397
483
|
return [id, void 0];
|
398
484
|
}
|
399
485
|
const clientController = this.open(id);
|
486
|
+
const signal = clientController.signal;
|
400
487
|
const request = {
|
401
488
|
...payload,
|
402
|
-
signal
|
403
|
-
body: isEventIteratorHeaders(payload.headers) ? toEventIterator(
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
489
|
+
signal,
|
490
|
+
body: isEventIteratorHeaders(payload.headers) ? toEventIterator(
|
491
|
+
this.clientEventIteratorQueue,
|
492
|
+
id,
|
493
|
+
async (reason) => {
|
494
|
+
if (reason !== "next") {
|
495
|
+
await this.send(id, MessageType.ABORT_SIGNAL, void 0);
|
496
|
+
}
|
497
|
+
},
|
498
|
+
{ signal }
|
499
|
+
) : payload.body
|
408
500
|
};
|
501
|
+
if (handleRequest) {
|
502
|
+
let context;
|
503
|
+
const otelConfig = getGlobalOtelConfig();
|
504
|
+
if (otelConfig) {
|
505
|
+
context = otelConfig.propagation.extract(otelConfig.context.active(), request.headers);
|
506
|
+
}
|
507
|
+
await runWithSpan(
|
508
|
+
{ name: "receive_peer_request", context },
|
509
|
+
async () => {
|
510
|
+
const response = await runWithSpan(
|
511
|
+
{ name: "handle_request" },
|
512
|
+
async () => {
|
513
|
+
try {
|
514
|
+
return await handleRequest(request);
|
515
|
+
} catch (reason) {
|
516
|
+
this.close({ id, reason, abort: false });
|
517
|
+
throw reason;
|
518
|
+
}
|
519
|
+
}
|
520
|
+
);
|
521
|
+
await runWithSpan(
|
522
|
+
{ name: "send_peer_response" },
|
523
|
+
() => this.response(id, response)
|
524
|
+
);
|
525
|
+
}
|
526
|
+
);
|
527
|
+
}
|
409
528
|
return [id, request];
|
410
529
|
}
|
530
|
+
/**
|
531
|
+
* @deprecated Please pass the `handleRequest` (second arg) function to the `message` method instead.
|
532
|
+
*/
|
411
533
|
async response(id, response) {
|
412
534
|
const signal = this.clientControllers.get(id)?.signal;
|
413
535
|
if (!signal || signal.aborted) {
|
414
536
|
return;
|
415
537
|
}
|
416
|
-
|
538
|
+
try {
|
539
|
+
await this.send(id, MessageType.RESPONSE, response);
|
417
540
|
if (!signal.aborted && isAsyncIteratorObject(response.body)) {
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
await
|
423
|
-
|
424
|
-
|
541
|
+
if (response.body instanceof HibernationEventIterator) {
|
542
|
+
response.body.hibernationCallback?.(id);
|
543
|
+
} else {
|
544
|
+
const iterator = response.body;
|
545
|
+
await resolveEventIterator(iterator, async (payload) => {
|
546
|
+
if (signal.aborted) {
|
547
|
+
return "abort";
|
548
|
+
}
|
549
|
+
await this.send(id, MessageType.EVENT_ITERATOR, payload);
|
550
|
+
return "next";
|
551
|
+
});
|
552
|
+
}
|
425
553
|
}
|
426
554
|
this.close({ id, abort: false });
|
427
|
-
}
|
555
|
+
} catch (reason) {
|
428
556
|
this.close({ id, reason, abort: false });
|
429
557
|
throw reason;
|
430
|
-
}
|
558
|
+
}
|
431
559
|
}
|
432
560
|
close({ abort = true, ...options } = {}) {
|
433
561
|
if (options.id === void 0) {
|
434
562
|
if (abort) {
|
435
|
-
this.clientControllers.forEach((c) => c.abort());
|
563
|
+
this.clientControllers.forEach((c) => c.abort(options.reason));
|
436
564
|
}
|
437
565
|
this.clientControllers.clear();
|
438
566
|
} else {
|
439
567
|
if (abort) {
|
440
|
-
this.clientControllers.get(options.id)?.abort();
|
568
|
+
this.clientControllers.get(options.id)?.abort(options.reason);
|
441
569
|
}
|
442
570
|
this.clientControllers.delete(options.id);
|
443
571
|
}
|
@@ -445,4 +573,4 @@ class ServerPeer {
|
|
445
573
|
}
|
446
574
|
}
|
447
575
|
|
448
|
-
export { ClientPeer, MessageType, ServerPeer, decodeRequestMessage, decodeResponseMessage, encodeRequestMessage, encodeResponseMessage,
|
576
|
+
export { ClientPeer, MessageType, ServerPeer, decodeRequestMessage, decodeResponseMessage, encodeRequestMessage, encodeResponseMessage, resolveEventIterator, toEventIterator };
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@orpc/standard-server-peer",
|
3
3
|
"type": "module",
|
4
|
-
"version": "0.0.0-next.
|
4
|
+
"version": "0.0.0-next.fc1437c",
|
5
5
|
"license": "MIT",
|
6
6
|
"homepage": "https://unnoq.com",
|
7
7
|
"repository": {
|
@@ -23,8 +23,8 @@
|
|
23
23
|
"dist"
|
24
24
|
],
|
25
25
|
"dependencies": {
|
26
|
-
"@orpc/shared": "0.0.0-next.
|
27
|
-
"@orpc/standard-server": "0.0.0-next.
|
26
|
+
"@orpc/shared": "0.0.0-next.fc1437c",
|
27
|
+
"@orpc/standard-server": "0.0.0-next.fc1437c"
|
28
28
|
},
|
29
29
|
"scripts": {
|
30
30
|
"build": "unbuild",
|