@orpc/standard-server-peer 0.0.0-next.f50512c → 0.0.0-next.f5149a8

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 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
- - **⚙️ Framework Integrations**: Seamlessly integrate with TanStack Query (React, Vue, Solid, Svelte, Angular), Pinia Colada, and more.
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
 
@@ -50,9 +53,11 @@ You can find the full documentation [here](https://orpc.unnoq.com).
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
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.
53
57
  - [@orpc/nest](https://www.npmjs.com/package/@orpc/nest): Deeply integrate oRPC with [NestJS](https://nestjs.com/).
54
58
  - [@orpc/react](https://www.npmjs.com/package/@orpc/react): Utilities for integrating oRPC with React and React Server Actions.
55
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.
56
61
  - [@orpc/vue-colada](https://www.npmjs.com/package/@orpc/vue-colada): Integration with [Pinia Colada](https://pinia-colada.esm.dev/).
57
62
  - [@orpc/hey-api](https://www.npmjs.com/package/@orpc/hey-api): [Hey API](https://heyapi.dev/) integration.
58
63
  - [@orpc/zod](https://www.npmjs.com/package/@orpc/zod): More schemas that [Zod](https://zod.dev/) doesn't support yet.
package/dist/index.d.mts CHANGED
@@ -1,33 +1,11 @@
1
- import { Promisable, AsyncIdQueueCloseOptions as AsyncIdQueueCloseOptions$1, AsyncIteratorClassCleanupFn, AsyncIteratorClass } from '@orpc/shared';
2
- import { StandardRequest, StandardResponse, EventMeta } from '@orpc/standard-server';
1
+ import { Promisable, AsyncIdQueueCloseOptions as AsyncIdQueueCloseOptions$1, SetSpanErrorOptions, AsyncIteratorClassCleanupFn, AsyncIteratorClass } from '@orpc/shared';
2
+ import { StandardRequest, EventMeta, StandardResponse, StandardBody, StandardHeaders } from '@orpc/standard-server';
3
3
 
4
4
  type EncodedMessage = string | ArrayBufferLike | Uint8Array;
5
5
  interface EncodedMessageSendFn {
6
6
  (message: EncodedMessage): Promisable<void>;
7
7
  }
8
8
 
9
- interface ClientPeerCloseOptions extends AsyncIdQueueCloseOptions$1 {
10
- /**
11
- * Should abort or not?
12
- *
13
- * @default true
14
- */
15
- abort?: boolean;
16
- }
17
- declare class ClientPeer {
18
- private readonly idGenerator;
19
- private readonly responseQueue;
20
- private readonly serverEventIteratorQueue;
21
- private readonly serverControllers;
22
- private readonly send;
23
- constructor(send: EncodedMessageSendFn);
24
- get length(): number;
25
- open(id: string): AbortController;
26
- request(request: StandardRequest): Promise<StandardResponse>;
27
- message(raw: EncodedMessage): Promise<void>;
28
- close(options?: AsyncIdQueueCloseOptions$1): void;
29
- }
30
-
31
9
  declare enum MessageType {
32
10
  REQUEST = 1,
33
11
  RESPONSE = 2,
@@ -50,25 +28,128 @@ interface ResponseMessageMap {
50
28
  [MessageType.EVENT_ITERATOR]: EventIteratorPayload;
51
29
  [MessageType.ABORT_SIGNAL]: void;
52
30
  }
31
+ interface BaseMessageFormat<P = unknown> {
32
+ /**
33
+ * Client-guaranteed unique identifier
34
+ */
35
+ i: string;
36
+ /**
37
+ * @default REQUEST | RESPONSE
38
+ */
39
+ t?: MessageType;
40
+ p: P;
41
+ }
42
+ interface SerializedEventIteratorPayload {
43
+ e: EventIteratorEvent;
44
+ d: unknown;
45
+ m?: EventMeta;
46
+ }
47
+ interface SerializedRequestPayload {
48
+ /**
49
+ * The url of the request
50
+ *
51
+ * might be relative path if origin is `orpc://localhost`
52
+ */
53
+ u: string;
54
+ b: StandardBody;
55
+ /**
56
+ * @default {}
57
+ */
58
+ h?: StandardHeaders;
59
+ /**
60
+ * @default POST
61
+ */
62
+ m?: string;
63
+ }
64
+ interface SerializedResponsePayload {
65
+ /**
66
+ * @default 200
67
+ */
68
+ s?: number;
69
+ /**
70
+ * @default {}
71
+ */
72
+ h?: StandardHeaders;
73
+ b: StandardBody;
74
+ }
53
75
  type DecodedMessageUnion<TMap extends RequestMessageMap | ResponseMessageMap> = {
54
76
  [K in keyof TMap]: [id: string, type: K, payload: TMap[K]];
55
77
  }[keyof TMap];
56
78
  type DecodedRequestMessage = DecodedMessageUnion<RequestMessageMap>;
57
79
  type DecodedResponseMessage = DecodedMessageUnion<ResponseMessageMap>;
80
+ /**
81
+ * New serialization functions without Blob handling
82
+ */
83
+ declare function serializeRequestMessage<T extends keyof RequestMessageMap>(id: string, type: T, payload: RequestMessageMap[T]): BaseMessageFormat;
84
+ declare function deserializeRequestMessage(message: BaseMessageFormat): DecodedRequestMessage;
85
+ declare function serializeResponseMessage<T extends keyof ResponseMessageMap>(id: string, type: T, payload: ResponseMessageMap[T]): BaseMessageFormat;
86
+ declare function deserializeResponseMessage(message: BaseMessageFormat): DecodedResponseMessage;
87
+ /**
88
+ * Original encode/decode functions now using the new serialize/deserialize functions
89
+ */
58
90
  declare function encodeRequestMessage<T extends keyof RequestMessageMap>(id: string, type: T, payload: RequestMessageMap[T]): Promise<EncodedMessage>;
59
91
  declare function decodeRequestMessage(raw: EncodedMessage): Promise<DecodedRequestMessage>;
60
92
  declare function encodeResponseMessage<T extends keyof ResponseMessageMap>(id: string, type: T, payload: ResponseMessageMap[T]): Promise<EncodedMessage>;
61
93
  declare function decodeResponseMessage(raw: EncodedMessage): Promise<DecodedResponseMessage>;
62
94
 
95
+ interface experimental_RequestMessageSendFn {
96
+ (message: DecodedRequestMessage): Promisable<void>;
97
+ }
98
+ interface ClientPeerCloseOptions extends AsyncIdQueueCloseOptions$1 {
99
+ /**
100
+ * Should abort or not?
101
+ *
102
+ * @default true
103
+ */
104
+ abort?: boolean;
105
+ }
106
+ declare class ClientPeer {
107
+ private readonly peer;
108
+ constructor(send: EncodedMessageSendFn);
109
+ get length(): number;
110
+ open(id: string): AbortController;
111
+ request(request: StandardRequest): Promise<StandardResponse>;
112
+ message(raw: EncodedMessage): Promise<void>;
113
+ close(options?: AsyncIdQueueCloseOptions$1): void;
114
+ }
115
+ declare class experimental_ClientPeerWithoutCodec {
116
+ private readonly idGenerator;
117
+ /**
118
+ * Queue of responses sent from server, awaiting consumption
119
+ */
120
+ private readonly responseQueue;
121
+ /**
122
+ * Queue of event iterator messages sent from server, awaiting consumption
123
+ */
124
+ private readonly serverEventIteratorQueue;
125
+ /**
126
+ * Controllers used to signal that the client should stop sending event iterator messages
127
+ */
128
+ private readonly serverControllers;
129
+ /**
130
+ * Cleanup functions invoked when the request/response is closed
131
+ */
132
+ private readonly cleanupFns;
133
+ private readonly send;
134
+ constructor(send: experimental_RequestMessageSendFn);
135
+ get length(): number;
136
+ open(id: string): AbortController;
137
+ request(request: StandardRequest): Promise<StandardResponse>;
138
+ message([id, type, payload]: DecodedResponseMessage): Promise<void>;
139
+ close(options?: AsyncIdQueueCloseOptions$1): void;
140
+ }
141
+
63
142
  interface AsyncIdQueueCloseOptions {
64
143
  id?: string;
65
144
  reason?: unknown;
66
145
  }
67
146
  declare class AsyncIdQueue<T> {
68
147
  private readonly openIds;
69
- private readonly items;
70
- private readonly pendingPulls;
148
+ private readonly queues;
149
+ private readonly waiters;
71
150
  get length(): number;
151
+ get waiterIds(): string[];
152
+ hasBufferedItems(id: string): boolean;
72
153
  open(id: string): void;
73
154
  isOpen(id: string): boolean;
74
155
  push(id: string, item: T): void;
@@ -77,9 +158,17 @@ declare class AsyncIdQueue<T> {
77
158
  assertOpen(id: string): void;
78
159
  }
79
160
 
80
- declare function toEventIterator(queue: AsyncIdQueue<EventIteratorPayload>, id: string, cleanup: AsyncIteratorClassCleanupFn): AsyncIteratorClass<unknown>;
161
+ interface ToEventIteratorOptions extends SetSpanErrorOptions {
162
+ }
163
+ declare function toEventIterator(queue: AsyncIdQueue<EventIteratorPayload>, id: string, cleanup: AsyncIteratorClassCleanupFn, options?: ToEventIteratorOptions): AsyncIteratorClass<unknown>;
81
164
  declare function resolveEventIterator(iterator: AsyncIterator<any>, callback: (payload: EventIteratorPayload) => Promise<'next' | 'abort'>): Promise<void>;
82
165
 
166
+ interface experimental_ResponseMessageSendFn {
167
+ (message: DecodedResponseMessage): Promisable<void>;
168
+ }
169
+ interface ServerPeerHandleRequestFn {
170
+ (request: StandardRequest): Promise<StandardResponse>;
171
+ }
83
172
  interface ServerPeerCloseOptions extends AsyncIdQueueCloseOptions$1 {
84
173
  /**
85
174
  * Should abort or not?
@@ -89,16 +178,43 @@ interface ServerPeerCloseOptions extends AsyncIdQueueCloseOptions$1 {
89
178
  abort?: boolean;
90
179
  }
91
180
  declare class ServerPeer {
181
+ private readonly peer;
182
+ constructor(send: EncodedMessageSendFn);
183
+ get length(): number;
184
+ open(id: string): AbortController;
185
+ /**
186
+ * @todo This method will return Promise<void> in the next major version.
187
+ */
188
+ message(raw: EncodedMessage, handleRequest?: ServerPeerHandleRequestFn): Promise<[id: string, StandardRequest | undefined]>;
189
+ /**
190
+ * @deprecated Please pass the `handleRequest` (second arg) function to the `message` method instead.
191
+ */
192
+ response(id: string, response: StandardResponse): Promise<void>;
193
+ close({ abort, ...options }?: ServerPeerCloseOptions): void;
194
+ }
195
+ declare class experimental_ServerPeerWithoutCodec {
196
+ /**
197
+ * Queue of event iterator messages sent from client, awaiting consumption
198
+ */
92
199
  private readonly clientEventIteratorQueue;
200
+ /**
201
+ * Map of active client request controllers, should be synced to request signal
202
+ */
93
203
  private readonly clientControllers;
94
204
  private readonly send;
95
- constructor(send: EncodedMessageSendFn);
205
+ constructor(send: experimental_ResponseMessageSendFn);
96
206
  get length(): number;
97
207
  open(id: string): AbortController;
98
- message(raw: EncodedMessage): Promise<[id: string, StandardRequest | undefined]>;
208
+ /**
209
+ * @todo This method will return Promise<void> in the next major version.
210
+ */
211
+ message([id, type, payload]: DecodedRequestMessage, handleRequest?: ServerPeerHandleRequestFn): Promise<[id: string, StandardRequest | undefined]>;
212
+ /**
213
+ * @deprecated Please pass the `handleRequest` (second arg) function to the `message` method instead.
214
+ */
99
215
  response(id: string, response: StandardResponse): Promise<void>;
100
216
  close({ abort, ...options }?: ServerPeerCloseOptions): void;
101
217
  }
102
218
 
103
- export { ClientPeer, MessageType, ServerPeer, decodeRequestMessage, decodeResponseMessage, encodeRequestMessage, encodeResponseMessage, resolveEventIterator, toEventIterator };
104
- export type { ClientPeerCloseOptions, DecodedRequestMessage, DecodedResponseMessage, EncodedMessage, EncodedMessageSendFn, EventIteratorEvent, EventIteratorPayload, RequestMessageMap, ResponseMessageMap, ServerPeerCloseOptions };
219
+ export { ClientPeer, MessageType, ServerPeer, decodeRequestMessage, decodeResponseMessage, deserializeRequestMessage, deserializeResponseMessage, encodeRequestMessage, encodeResponseMessage, experimental_ClientPeerWithoutCodec, experimental_ServerPeerWithoutCodec, resolveEventIterator, serializeRequestMessage, serializeResponseMessage, toEventIterator };
220
+ export type { BaseMessageFormat, ClientPeerCloseOptions, DecodedMessageUnion, DecodedRequestMessage, DecodedResponseMessage, EncodedMessage, EncodedMessageSendFn, EventIteratorEvent, EventIteratorPayload, RequestMessageMap, ResponseMessageMap, SerializedEventIteratorPayload, SerializedRequestPayload, SerializedResponsePayload, ServerPeerCloseOptions, ServerPeerHandleRequestFn, ToEventIteratorOptions, experimental_RequestMessageSendFn, experimental_ResponseMessageSendFn };
package/dist/index.d.ts CHANGED
@@ -1,33 +1,11 @@
1
- import { Promisable, AsyncIdQueueCloseOptions as AsyncIdQueueCloseOptions$1, AsyncIteratorClassCleanupFn, AsyncIteratorClass } from '@orpc/shared';
2
- import { StandardRequest, StandardResponse, EventMeta } from '@orpc/standard-server';
1
+ import { Promisable, AsyncIdQueueCloseOptions as AsyncIdQueueCloseOptions$1, SetSpanErrorOptions, AsyncIteratorClassCleanupFn, AsyncIteratorClass } from '@orpc/shared';
2
+ import { StandardRequest, EventMeta, StandardResponse, StandardBody, StandardHeaders } from '@orpc/standard-server';
3
3
 
4
4
  type EncodedMessage = string | ArrayBufferLike | Uint8Array;
5
5
  interface EncodedMessageSendFn {
6
6
  (message: EncodedMessage): Promisable<void>;
7
7
  }
8
8
 
9
- interface ClientPeerCloseOptions extends AsyncIdQueueCloseOptions$1 {
10
- /**
11
- * Should abort or not?
12
- *
13
- * @default true
14
- */
15
- abort?: boolean;
16
- }
17
- declare class ClientPeer {
18
- private readonly idGenerator;
19
- private readonly responseQueue;
20
- private readonly serverEventIteratorQueue;
21
- private readonly serverControllers;
22
- private readonly send;
23
- constructor(send: EncodedMessageSendFn);
24
- get length(): number;
25
- open(id: string): AbortController;
26
- request(request: StandardRequest): Promise<StandardResponse>;
27
- message(raw: EncodedMessage): Promise<void>;
28
- close(options?: AsyncIdQueueCloseOptions$1): void;
29
- }
30
-
31
9
  declare enum MessageType {
32
10
  REQUEST = 1,
33
11
  RESPONSE = 2,
@@ -50,25 +28,128 @@ interface ResponseMessageMap {
50
28
  [MessageType.EVENT_ITERATOR]: EventIteratorPayload;
51
29
  [MessageType.ABORT_SIGNAL]: void;
52
30
  }
31
+ interface BaseMessageFormat<P = unknown> {
32
+ /**
33
+ * Client-guaranteed unique identifier
34
+ */
35
+ i: string;
36
+ /**
37
+ * @default REQUEST | RESPONSE
38
+ */
39
+ t?: MessageType;
40
+ p: P;
41
+ }
42
+ interface SerializedEventIteratorPayload {
43
+ e: EventIteratorEvent;
44
+ d: unknown;
45
+ m?: EventMeta;
46
+ }
47
+ interface SerializedRequestPayload {
48
+ /**
49
+ * The url of the request
50
+ *
51
+ * might be relative path if origin is `orpc://localhost`
52
+ */
53
+ u: string;
54
+ b: StandardBody;
55
+ /**
56
+ * @default {}
57
+ */
58
+ h?: StandardHeaders;
59
+ /**
60
+ * @default POST
61
+ */
62
+ m?: string;
63
+ }
64
+ interface SerializedResponsePayload {
65
+ /**
66
+ * @default 200
67
+ */
68
+ s?: number;
69
+ /**
70
+ * @default {}
71
+ */
72
+ h?: StandardHeaders;
73
+ b: StandardBody;
74
+ }
53
75
  type DecodedMessageUnion<TMap extends RequestMessageMap | ResponseMessageMap> = {
54
76
  [K in keyof TMap]: [id: string, type: K, payload: TMap[K]];
55
77
  }[keyof TMap];
56
78
  type DecodedRequestMessage = DecodedMessageUnion<RequestMessageMap>;
57
79
  type DecodedResponseMessage = DecodedMessageUnion<ResponseMessageMap>;
80
+ /**
81
+ * New serialization functions without Blob handling
82
+ */
83
+ declare function serializeRequestMessage<T extends keyof RequestMessageMap>(id: string, type: T, payload: RequestMessageMap[T]): BaseMessageFormat;
84
+ declare function deserializeRequestMessage(message: BaseMessageFormat): DecodedRequestMessage;
85
+ declare function serializeResponseMessage<T extends keyof ResponseMessageMap>(id: string, type: T, payload: ResponseMessageMap[T]): BaseMessageFormat;
86
+ declare function deserializeResponseMessage(message: BaseMessageFormat): DecodedResponseMessage;
87
+ /**
88
+ * Original encode/decode functions now using the new serialize/deserialize functions
89
+ */
58
90
  declare function encodeRequestMessage<T extends keyof RequestMessageMap>(id: string, type: T, payload: RequestMessageMap[T]): Promise<EncodedMessage>;
59
91
  declare function decodeRequestMessage(raw: EncodedMessage): Promise<DecodedRequestMessage>;
60
92
  declare function encodeResponseMessage<T extends keyof ResponseMessageMap>(id: string, type: T, payload: ResponseMessageMap[T]): Promise<EncodedMessage>;
61
93
  declare function decodeResponseMessage(raw: EncodedMessage): Promise<DecodedResponseMessage>;
62
94
 
95
+ interface experimental_RequestMessageSendFn {
96
+ (message: DecodedRequestMessage): Promisable<void>;
97
+ }
98
+ interface ClientPeerCloseOptions extends AsyncIdQueueCloseOptions$1 {
99
+ /**
100
+ * Should abort or not?
101
+ *
102
+ * @default true
103
+ */
104
+ abort?: boolean;
105
+ }
106
+ declare class ClientPeer {
107
+ private readonly peer;
108
+ constructor(send: EncodedMessageSendFn);
109
+ get length(): number;
110
+ open(id: string): AbortController;
111
+ request(request: StandardRequest): Promise<StandardResponse>;
112
+ message(raw: EncodedMessage): Promise<void>;
113
+ close(options?: AsyncIdQueueCloseOptions$1): void;
114
+ }
115
+ declare class experimental_ClientPeerWithoutCodec {
116
+ private readonly idGenerator;
117
+ /**
118
+ * Queue of responses sent from server, awaiting consumption
119
+ */
120
+ private readonly responseQueue;
121
+ /**
122
+ * Queue of event iterator messages sent from server, awaiting consumption
123
+ */
124
+ private readonly serverEventIteratorQueue;
125
+ /**
126
+ * Controllers used to signal that the client should stop sending event iterator messages
127
+ */
128
+ private readonly serverControllers;
129
+ /**
130
+ * Cleanup functions invoked when the request/response is closed
131
+ */
132
+ private readonly cleanupFns;
133
+ private readonly send;
134
+ constructor(send: experimental_RequestMessageSendFn);
135
+ get length(): number;
136
+ open(id: string): AbortController;
137
+ request(request: StandardRequest): Promise<StandardResponse>;
138
+ message([id, type, payload]: DecodedResponseMessage): Promise<void>;
139
+ close(options?: AsyncIdQueueCloseOptions$1): void;
140
+ }
141
+
63
142
  interface AsyncIdQueueCloseOptions {
64
143
  id?: string;
65
144
  reason?: unknown;
66
145
  }
67
146
  declare class AsyncIdQueue<T> {
68
147
  private readonly openIds;
69
- private readonly items;
70
- private readonly pendingPulls;
148
+ private readonly queues;
149
+ private readonly waiters;
71
150
  get length(): number;
151
+ get waiterIds(): string[];
152
+ hasBufferedItems(id: string): boolean;
72
153
  open(id: string): void;
73
154
  isOpen(id: string): boolean;
74
155
  push(id: string, item: T): void;
@@ -77,9 +158,17 @@ declare class AsyncIdQueue<T> {
77
158
  assertOpen(id: string): void;
78
159
  }
79
160
 
80
- declare function toEventIterator(queue: AsyncIdQueue<EventIteratorPayload>, id: string, cleanup: AsyncIteratorClassCleanupFn): AsyncIteratorClass<unknown>;
161
+ interface ToEventIteratorOptions extends SetSpanErrorOptions {
162
+ }
163
+ declare function toEventIterator(queue: AsyncIdQueue<EventIteratorPayload>, id: string, cleanup: AsyncIteratorClassCleanupFn, options?: ToEventIteratorOptions): AsyncIteratorClass<unknown>;
81
164
  declare function resolveEventIterator(iterator: AsyncIterator<any>, callback: (payload: EventIteratorPayload) => Promise<'next' | 'abort'>): Promise<void>;
82
165
 
166
+ interface experimental_ResponseMessageSendFn {
167
+ (message: DecodedResponseMessage): Promisable<void>;
168
+ }
169
+ interface ServerPeerHandleRequestFn {
170
+ (request: StandardRequest): Promise<StandardResponse>;
171
+ }
83
172
  interface ServerPeerCloseOptions extends AsyncIdQueueCloseOptions$1 {
84
173
  /**
85
174
  * Should abort or not?
@@ -89,16 +178,43 @@ interface ServerPeerCloseOptions extends AsyncIdQueueCloseOptions$1 {
89
178
  abort?: boolean;
90
179
  }
91
180
  declare class ServerPeer {
181
+ private readonly peer;
182
+ constructor(send: EncodedMessageSendFn);
183
+ get length(): number;
184
+ open(id: string): AbortController;
185
+ /**
186
+ * @todo This method will return Promise<void> in the next major version.
187
+ */
188
+ message(raw: EncodedMessage, handleRequest?: ServerPeerHandleRequestFn): Promise<[id: string, StandardRequest | undefined]>;
189
+ /**
190
+ * @deprecated Please pass the `handleRequest` (second arg) function to the `message` method instead.
191
+ */
192
+ response(id: string, response: StandardResponse): Promise<void>;
193
+ close({ abort, ...options }?: ServerPeerCloseOptions): void;
194
+ }
195
+ declare class experimental_ServerPeerWithoutCodec {
196
+ /**
197
+ * Queue of event iterator messages sent from client, awaiting consumption
198
+ */
92
199
  private readonly clientEventIteratorQueue;
200
+ /**
201
+ * Map of active client request controllers, should be synced to request signal
202
+ */
93
203
  private readonly clientControllers;
94
204
  private readonly send;
95
- constructor(send: EncodedMessageSendFn);
205
+ constructor(send: experimental_ResponseMessageSendFn);
96
206
  get length(): number;
97
207
  open(id: string): AbortController;
98
- message(raw: EncodedMessage): Promise<[id: string, StandardRequest | undefined]>;
208
+ /**
209
+ * @todo This method will return Promise<void> in the next major version.
210
+ */
211
+ message([id, type, payload]: DecodedRequestMessage, handleRequest?: ServerPeerHandleRequestFn): Promise<[id: string, StandardRequest | undefined]>;
212
+ /**
213
+ * @deprecated Please pass the `handleRequest` (second arg) function to the `message` method instead.
214
+ */
99
215
  response(id: string, response: StandardResponse): Promise<void>;
100
216
  close({ abort, ...options }?: ServerPeerCloseOptions): void;
101
217
  }
102
218
 
103
- export { ClientPeer, MessageType, ServerPeer, decodeRequestMessage, decodeResponseMessage, encodeRequestMessage, encodeResponseMessage, resolveEventIterator, toEventIterator };
104
- export type { ClientPeerCloseOptions, DecodedRequestMessage, DecodedResponseMessage, EncodedMessage, EncodedMessageSendFn, EventIteratorEvent, EventIteratorPayload, RequestMessageMap, ResponseMessageMap, ServerPeerCloseOptions };
219
+ export { ClientPeer, MessageType, ServerPeer, decodeRequestMessage, decodeResponseMessage, deserializeRequestMessage, deserializeResponseMessage, encodeRequestMessage, encodeResponseMessage, experimental_ClientPeerWithoutCodec, experimental_ServerPeerWithoutCodec, resolveEventIterator, serializeRequestMessage, serializeResponseMessage, toEventIterator };
220
+ export type { BaseMessageFormat, ClientPeerCloseOptions, DecodedMessageUnion, DecodedRequestMessage, DecodedResponseMessage, EncodedMessage, EncodedMessageSendFn, EventIteratorEvent, EventIteratorPayload, RequestMessageMap, ResponseMessageMap, SerializedEventIteratorPayload, SerializedRequestPayload, SerializedResponsePayload, ServerPeerCloseOptions, ServerPeerHandleRequestFn, ToEventIteratorOptions, experimental_RequestMessageSendFn, experimental_ResponseMessageSendFn };
package/dist/index.mjs CHANGED
@@ -1,6 +1,8 @@
1
- import { isAsyncIteratorObject, stringifyJSON, readAsBuffer, AsyncIteratorClass, isTypescriptObject, SequentialIdGenerator, AsyncIdQueue } from '@orpc/shared';
2
- import { generateContentDisposition, flattenHeader, getFilenameFromContentDisposition, withEventMeta, ErrorEvent, getEventMeta, isEventIteratorHeaders, experimental_HibernationEventIterator } from '@orpc/standard-server';
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";
@@ -8,7 +10,7 @@ var MessageType = /* @__PURE__ */ ((MessageType2) => {
8
10
  MessageType2[MessageType2["ABORT_SIGNAL"] = 4] = "ABORT_SIGNAL";
9
11
  return MessageType2;
10
12
  })(MessageType || {});
11
- async function encodeRequestMessage(id, type, payload) {
13
+ function serializeRequestMessage(id, type, payload) {
12
14
  if (type === 3 /* EVENT_ITERATOR */) {
13
15
  const eventPayload = payload;
14
16
  const serializedPayload2 = {
@@ -16,35 +18,26 @@ async function encodeRequestMessage(id, type, payload) {
16
18
  d: eventPayload.data,
17
19
  m: eventPayload.meta
18
20
  };
19
- return encodeRawMessage({ i: id, t: type, p: serializedPayload2 });
21
+ return { i: id, t: type, p: serializedPayload2 };
20
22
  }
21
23
  if (type === 4 /* ABORT_SIGNAL */) {
22
- return encodeRawMessage({ i: id, t: type, p: payload });
24
+ return { i: id, t: type, p: payload };
23
25
  }
24
26
  const request = payload;
25
- const { body: processedBody, headers: processedHeaders } = await serializeBodyAndHeaders(
26
- request.body,
27
- request.headers
28
- );
29
27
  const serializedPayload = {
30
- u: request.url.toString().replace(/^orpc:\//, "/"),
31
- b: processedBody instanceof Blob ? void 0 : processedBody,
32
- h: Object.keys(processedHeaders).length > 0 ? processedHeaders : void 0,
28
+ u: request.url.toString().replace(SHORTABLE_ORIGIN_MATCHER, "/"),
29
+ b: request.body,
30
+ h: Object.keys(request.headers).length > 0 ? request.headers : void 0,
33
31
  m: request.method === "POST" ? void 0 : request.method
34
32
  };
35
- const baseMessage = {
33
+ return {
36
34
  i: id,
37
35
  p: serializedPayload
38
36
  };
39
- if (processedBody instanceof Blob) {
40
- return encodeRawMessage(baseMessage, processedBody);
41
- }
42
- return encodeRawMessage(baseMessage);
43
37
  }
44
- async function decodeRequestMessage(raw) {
45
- const { json: message, buffer } = await decodeRawMessage(raw);
38
+ function deserializeRequestMessage(message) {
46
39
  const id = message.i;
47
- const type = message.t;
40
+ const type = message.t ?? 1 /* REQUEST */;
48
41
  if (type === 3 /* EVENT_ITERATOR */) {
49
42
  const payload2 = message.p;
50
43
  return [id, type, { event: payload2.e, data: payload2.d, meta: payload2.m }];
@@ -53,11 +46,14 @@ async function decodeRequestMessage(raw) {
53
46
  return [id, type, message.p];
54
47
  }
55
48
  const payload = message.p;
56
- const headers = payload.h ?? {};
57
- const body = await deserializeBody(headers, payload.b, buffer);
58
- return [id, 1 /* REQUEST */, { url: new URL(payload.u, "orpc:/"), headers, method: payload.m ?? "POST", body }];
49
+ return [id, 1 /* REQUEST */, {
50
+ url: payload.u.startsWith("/") ? new URL(`${SHORTABLE_ORIGIN}${payload.u}`) : new URL(payload.u),
51
+ headers: payload.h ?? {},
52
+ method: payload.m ?? "POST",
53
+ body: payload.b
54
+ }];
59
55
  }
60
- async function encodeResponseMessage(id, type, payload) {
56
+ function serializeResponseMessage(id, type, payload) {
61
57
  if (type === 3 /* EVENT_ITERATOR */) {
62
58
  const eventPayload = payload;
63
59
  const serializedPayload2 = {
@@ -65,32 +61,23 @@ async function encodeResponseMessage(id, type, payload) {
65
61
  d: eventPayload.data,
66
62
  m: eventPayload.meta
67
63
  };
68
- return encodeRawMessage({ i: id, t: type, p: serializedPayload2 });
64
+ return { i: id, t: type, p: serializedPayload2 };
69
65
  }
70
66
  if (type === 4 /* ABORT_SIGNAL */) {
71
- return encodeRawMessage({ i: id, t: type, p: void 0 });
67
+ return { i: id, t: type, p: void 0 };
72
68
  }
73
69
  const response = payload;
74
- const { body: processedBody, headers: processedHeaders } = await serializeBodyAndHeaders(
75
- response.body,
76
- response.headers
77
- );
78
70
  const serializedPayload = {
79
71
  s: response.status === 200 ? void 0 : response.status,
80
- h: Object.keys(processedHeaders).length > 0 ? processedHeaders : void 0,
81
- b: processedBody instanceof Blob ? void 0 : processedBody
72
+ h: Object.keys(response.headers).length > 0 ? response.headers : void 0,
73
+ b: response.body
82
74
  };
83
- const baseMessage = {
75
+ return {
84
76
  i: id,
85
77
  p: serializedPayload
86
78
  };
87
- if (processedBody instanceof Blob) {
88
- return encodeRawMessage(baseMessage, processedBody);
89
- }
90
- return encodeRawMessage(baseMessage);
91
79
  }
92
- async function decodeResponseMessage(raw) {
93
- const { json: message, buffer } = await decodeRawMessage(raw);
80
+ function deserializeResponseMessage(message) {
94
81
  const id = message.i;
95
82
  const type = message.t;
96
83
  if (type === 3 /* EVENT_ITERATOR */) {
@@ -101,9 +88,71 @@ async function decodeResponseMessage(raw) {
101
88
  return [id, type, message.p];
102
89
  }
103
90
  const payload = message.p;
104
- const headers = payload.h ?? {};
105
- const body = await deserializeBody(headers, payload.b, buffer);
106
- return [id, 2 /* RESPONSE */, { status: payload.s ?? 200, headers, body }];
91
+ return [id, 2 /* RESPONSE */, {
92
+ status: payload.s ?? 200,
93
+ headers: payload.h ?? {},
94
+ body: payload.b
95
+ }];
96
+ }
97
+ async function encodeRequestMessage(id, type, payload) {
98
+ if (type === 3 /* EVENT_ITERATOR */ || type === 4 /* ABORT_SIGNAL */) {
99
+ return encodeRawMessage(serializeRequestMessage(id, type, payload));
100
+ }
101
+ const request = payload;
102
+ const { body: processedBody, headers: processedHeaders } = await serializeBodyAndHeaders(
103
+ request.body,
104
+ request.headers
105
+ );
106
+ const modifiedRequest = {
107
+ ...request,
108
+ body: processedBody instanceof Blob ? void 0 : processedBody,
109
+ headers: processedHeaders
110
+ };
111
+ const baseMessage = serializeRequestMessage(id, 1 /* REQUEST */, modifiedRequest);
112
+ if (processedBody instanceof Blob) {
113
+ return encodeRawMessage(baseMessage, processedBody);
114
+ }
115
+ return encodeRawMessage(baseMessage);
116
+ }
117
+ async function decodeRequestMessage(raw) {
118
+ const { json: message, buffer } = await decodeRawMessage(raw);
119
+ const [id, type, payload] = deserializeRequestMessage(message);
120
+ if (type === 3 /* EVENT_ITERATOR */ || type === 4 /* ABORT_SIGNAL */) {
121
+ return [id, type, payload];
122
+ }
123
+ const request = payload;
124
+ const body = await deserializeBody(request.headers, request.body, buffer);
125
+ return [id, type, { ...request, body }];
126
+ }
127
+ async function encodeResponseMessage(id, type, payload) {
128
+ if (type === 3 /* EVENT_ITERATOR */ || type === 4 /* ABORT_SIGNAL */) {
129
+ return encodeRawMessage(serializeResponseMessage(id, type, payload));
130
+ }
131
+ const response = payload;
132
+ const { body: processedBody, headers: processedHeaders } = await serializeBodyAndHeaders(
133
+ response.body,
134
+ response.headers
135
+ );
136
+ const modifiedResponse = {
137
+ ...response,
138
+ body: processedBody instanceof Blob ? void 0 : processedBody,
139
+ headers: processedHeaders
140
+ };
141
+ const baseMessage = serializeResponseMessage(id, 2 /* RESPONSE */, modifiedResponse);
142
+ if (processedBody instanceof Blob) {
143
+ return encodeRawMessage(baseMessage, processedBody);
144
+ }
145
+ return encodeRawMessage(baseMessage);
146
+ }
147
+ async function decodeResponseMessage(raw) {
148
+ const { json: message, buffer } = await decodeRawMessage(raw);
149
+ const [id, type, payload] = deserializeResponseMessage(message);
150
+ if (type === 3 /* EVENT_ITERATOR */ || type === 4 /* ABORT_SIGNAL */) {
151
+ return [id, type, payload];
152
+ }
153
+ const response = payload;
154
+ const body = await deserializeBody(response.headers, response.body, buffer);
155
+ return [id, type, { ...response, body }];
107
156
  }
108
157
  async function serializeBodyAndHeaders(body, originalHeaders) {
109
158
  const headers = { ...originalHeaders };
@@ -179,134 +228,252 @@ async function decodeRawMessage(raw) {
179
228
  };
180
229
  }
181
230
 
182
- function toEventIterator(queue, id, cleanup) {
231
+ function toEventIterator(queue, id, cleanup, options = {}) {
232
+ let span;
183
233
  return new AsyncIteratorClass(async () => {
184
- const item = await queue.pull(id);
185
- switch (item.event) {
186
- case "message": {
187
- let data = item.data;
188
- if (item.meta && isTypescriptObject(data)) {
189
- data = withEventMeta(data, item.meta);
234
+ span ??= startSpan("consume_event_iterator_stream");
235
+ try {
236
+ const item = await runInSpanContext(span, () => queue.pull(id));
237
+ switch (item.event) {
238
+ case "message": {
239
+ let data = item.data;
240
+ if (item.meta && isTypescriptObject(data)) {
241
+ data = withEventMeta(data, item.meta);
242
+ }
243
+ span?.addEvent("message");
244
+ return { value: data, done: false };
190
245
  }
191
- return { value: data, done: false };
192
- }
193
- case "error": {
194
- let error = new ErrorEvent({
195
- data: item.data
196
- });
197
- if (item.meta) {
198
- error = withEventMeta(error, item.meta);
246
+ case "error": {
247
+ let error = new ErrorEvent({
248
+ data: item.data
249
+ });
250
+ if (item.meta) {
251
+ error = withEventMeta(error, item.meta);
252
+ }
253
+ span?.addEvent("error");
254
+ throw error;
199
255
  }
200
- throw error;
201
- }
202
- case "done": {
203
- let data = item.data;
204
- if (item.meta && isTypescriptObject(data)) {
205
- data = withEventMeta(data, item.meta);
256
+ case "done": {
257
+ let data = item.data;
258
+ if (item.meta && isTypescriptObject(data)) {
259
+ data = withEventMeta(data, item.meta);
260
+ }
261
+ span?.addEvent("done");
262
+ return { value: data, done: true };
206
263
  }
207
- return { value: data, done: true };
208
264
  }
209
- }
210
- }, cleanup);
211
- }
212
- async function resolveEventIterator(iterator, callback) {
213
- while (true) {
214
- const payload = await (async () => {
215
- try {
216
- const { value, done } = await iterator.next();
217
- if (done) {
218
- return { event: "done", data: value, meta: getEventMeta(value) };
219
- }
220
- return { event: "message", data: value, meta: getEventMeta(value) };
221
- } catch (err) {
222
- return {
223
- meta: getEventMeta(err),
224
- event: "error",
225
- data: err instanceof ErrorEvent ? err.data : void 0
226
- };
265
+ } catch (e) {
266
+ if (!(e instanceof ErrorEvent)) {
267
+ setSpanError(span, e, options);
227
268
  }
228
- })();
269
+ throw e;
270
+ }
271
+ }, async (reason) => {
229
272
  try {
230
- const direction = await callback(payload);
231
- if (payload.event === "done" || payload.event === "error") {
232
- return;
273
+ if (reason !== "next") {
274
+ span?.addEvent("cancelled");
233
275
  }
234
- if (direction === "abort") {
276
+ await runInSpanContext(span, () => cleanup(reason));
277
+ } catch (e) {
278
+ setSpanError(span, e, options);
279
+ throw e;
280
+ } finally {
281
+ span?.end();
282
+ }
283
+ });
284
+ }
285
+ function resolveEventIterator(iterator, callback) {
286
+ return runWithSpan(
287
+ { name: "stream_event_iterator" },
288
+ async (span) => {
289
+ while (true) {
290
+ const payload = await (async () => {
291
+ try {
292
+ const { value, done } = await iterator.next();
293
+ if (done) {
294
+ span?.addEvent("done");
295
+ return { event: "done", data: value, meta: getEventMeta(value) };
296
+ }
297
+ span?.addEvent("message");
298
+ return { event: "message", data: value, meta: getEventMeta(value) };
299
+ } catch (err) {
300
+ if (err instanceof ErrorEvent) {
301
+ span?.addEvent("error");
302
+ return {
303
+ event: "error",
304
+ data: err.data,
305
+ meta: getEventMeta(err)
306
+ };
307
+ } else {
308
+ try {
309
+ await callback({ event: "error", data: void 0 });
310
+ } catch (err2) {
311
+ setSpanError(span, err);
312
+ throw err2;
313
+ }
314
+ throw err;
315
+ }
316
+ }
317
+ })();
318
+ let isInvokeCleanupFn = false;
235
319
  try {
236
- await iterator.return?.();
237
- } catch {
320
+ const direction = await callback(payload);
321
+ if (payload.event === "done" || payload.event === "error") {
322
+ return;
323
+ }
324
+ if (direction === "abort") {
325
+ isInvokeCleanupFn = true;
326
+ await iterator.return?.();
327
+ return;
328
+ }
329
+ } catch (err) {
330
+ if (!isInvokeCleanupFn) {
331
+ try {
332
+ await iterator.return?.();
333
+ } catch (err2) {
334
+ setSpanError(span, err);
335
+ throw err2;
336
+ }
337
+ }
338
+ throw err;
238
339
  }
239
- return;
240
- }
241
- } catch (err) {
242
- try {
243
- await iterator.return?.();
244
- } catch {
245
340
  }
246
- throw err;
247
341
  }
248
- }
342
+ );
249
343
  }
250
344
 
251
345
  class ClientPeer {
346
+ peer;
347
+ constructor(send) {
348
+ this.peer = new experimental_ClientPeerWithoutCodec(async ([id, type, payload]) => {
349
+ await send(await encodeRequestMessage(id, type, payload));
350
+ });
351
+ }
352
+ get length() {
353
+ return this.peer.length;
354
+ }
355
+ open(id) {
356
+ return this.peer.open(id);
357
+ }
358
+ async request(request) {
359
+ return this.peer.request(request);
360
+ }
361
+ async message(raw) {
362
+ return this.peer.message(await decodeResponseMessage(raw));
363
+ }
364
+ close(options = {}) {
365
+ return this.peer.close(options);
366
+ }
367
+ }
368
+ class experimental_ClientPeerWithoutCodec {
252
369
  idGenerator = new SequentialIdGenerator();
370
+ /**
371
+ * Queue of responses sent from server, awaiting consumption
372
+ */
253
373
  responseQueue = new AsyncIdQueue();
374
+ /**
375
+ * Queue of event iterator messages sent from server, awaiting consumption
376
+ */
254
377
  serverEventIteratorQueue = new AsyncIdQueue();
378
+ /**
379
+ * Controllers used to signal that the client should stop sending event iterator messages
380
+ */
255
381
  serverControllers = /* @__PURE__ */ new Map();
382
+ /**
383
+ * Cleanup functions invoked when the request/response is closed
384
+ */
385
+ cleanupFns = /* @__PURE__ */ new Map();
256
386
  send;
257
387
  constructor(send) {
258
- this.send = async (id, ...rest) => encodeRequestMessage(id, ...rest).then(async (raw) => {
388
+ this.send = async (message) => {
389
+ const id = message[0];
259
390
  if (this.serverControllers.has(id)) {
260
- await send(raw);
391
+ await send(message);
261
392
  }
262
- });
393
+ };
263
394
  }
264
395
  get length() {
265
- return (+this.responseQueue.length + this.serverEventIteratorQueue.length + this.serverControllers.size) / 3;
396
+ return (+this.responseQueue.length + this.serverEventIteratorQueue.length + this.serverControllers.size + this.cleanupFns.size) / 4;
266
397
  }
267
398
  open(id) {
268
399
  this.serverEventIteratorQueue.open(id);
269
400
  this.responseQueue.open(id);
270
401
  const controller = new AbortController();
271
402
  this.serverControllers.set(id, controller);
403
+ this.cleanupFns.set(id, []);
272
404
  return controller;
273
405
  }
274
406
  async request(request) {
275
407
  const signal = request.signal;
276
- if (signal?.aborted) {
277
- throw signal.reason;
278
- }
279
- const id = this.idGenerator.generate();
280
- const serverController = this.open(id);
281
- return new Promise((resolve, reject) => {
282
- this.send(id, MessageType.REQUEST, request).then(async () => {
408
+ return runWithSpan(
409
+ { name: "send_peer_request", signal },
410
+ async () => {
283
411
  if (signal?.aborted) {
284
- await this.send(id, MessageType.ABORT_SIGNAL, void 0);
285
- this.close({ id, reason: signal.reason });
286
- return;
412
+ throw signal.reason;
287
413
  }
288
- signal?.addEventListener("abort", async () => {
289
- await this.send(id, MessageType.ABORT_SIGNAL, void 0);
290
- this.close({ id, reason: signal.reason });
291
- }, { once: true });
292
- if (isAsyncIteratorObject(request.body)) {
293
- await resolveEventIterator(request.body, async (payload) => {
294
- if (serverController.signal.aborted) {
295
- return "abort";
296
- }
297
- await this.send(id, MessageType.EVENT_ITERATOR, payload);
298
- return "next";
414
+ const id = this.idGenerator.generate();
415
+ const serverController = this.open(id);
416
+ try {
417
+ const otelConfig = getGlobalOtelConfig();
418
+ if (otelConfig) {
419
+ const headers = clone(request.headers);
420
+ otelConfig.propagation.inject(otelConfig.context.active(), headers);
421
+ request = { ...request, headers };
422
+ }
423
+ await this.send([id, MessageType.REQUEST, request]);
424
+ if (signal?.aborted) {
425
+ await this.send([id, MessageType.ABORT_SIGNAL, void 0]);
426
+ throw signal.reason;
427
+ }
428
+ let abortListener;
429
+ signal?.addEventListener("abort", abortListener = async () => {
430
+ await this.send([id, MessageType.ABORT_SIGNAL, void 0]);
431
+ this.close({ id, reason: signal.reason });
432
+ }, { once: true });
433
+ this.cleanupFns.get(id)?.push(() => {
434
+ signal?.removeEventListener("abort", abortListener);
299
435
  });
436
+ if (isAsyncIteratorObject(request.body)) {
437
+ const iterator = request.body;
438
+ void resolveEventIterator(iterator, async (payload) => {
439
+ if (serverController.signal.aborted) {
440
+ return "abort";
441
+ }
442
+ await this.send([id, MessageType.EVENT_ITERATOR, payload]);
443
+ return "next";
444
+ });
445
+ }
446
+ const response = await this.responseQueue.pull(id);
447
+ if (isEventIteratorHeaders(response.headers)) {
448
+ const iterator = toEventIterator(
449
+ this.serverEventIteratorQueue,
450
+ id,
451
+ async (reason) => {
452
+ try {
453
+ if (reason !== "next") {
454
+ await this.send([id, MessageType.ABORT_SIGNAL, void 0]);
455
+ }
456
+ } finally {
457
+ this.close({ id });
458
+ }
459
+ },
460
+ { signal }
461
+ );
462
+ return {
463
+ ...response,
464
+ body: iterator
465
+ };
466
+ }
467
+ this.close({ id });
468
+ return response;
469
+ } catch (err) {
470
+ this.close({ id, reason: err });
471
+ throw err;
300
472
  }
301
- }).catch((err) => {
302
- this.close({ id, reason: err });
303
- reject(err);
304
- });
305
- this.responseQueue.pull(id).then(resolve).catch(reject);
306
- });
473
+ }
474
+ );
307
475
  }
308
- async message(raw) {
309
- const [id, type, payload] = await decodeResponseMessage(raw);
476
+ async message([id, type, payload]) {
310
477
  if (type === MessageType.ABORT_SIGNAL) {
311
478
  this.serverControllers.get(id)?.abort();
312
479
  return;
@@ -320,31 +487,19 @@ class ClientPeer {
320
487
  if (!this.responseQueue.isOpen(id)) {
321
488
  return;
322
489
  }
323
- if (isEventIteratorHeaders(payload.headers)) {
324
- this.responseQueue.push(id, {
325
- ...payload,
326
- body: toEventIterator(this.serverEventIteratorQueue, id, async (reason) => {
327
- try {
328
- if (reason !== "next") {
329
- await this.send(id, MessageType.ABORT_SIGNAL, void 0);
330
- }
331
- } finally {
332
- this.close({ id });
333
- }
334
- })
335
- });
336
- } else {
337
- this.responseQueue.push(id, payload);
338
- this.close({ id });
339
- }
490
+ this.responseQueue.push(id, payload);
340
491
  }
341
492
  close(options = {}) {
342
493
  if (options.id !== void 0) {
343
494
  this.serverControllers.get(options.id)?.abort(options.reason);
344
495
  this.serverControllers.delete(options.id);
496
+ this.cleanupFns.get(options.id)?.forEach((fn) => fn());
497
+ this.cleanupFns.delete(options.id);
345
498
  } else {
346
499
  this.serverControllers.forEach((c) => c.abort(options.reason));
347
500
  this.serverControllers.clear();
501
+ this.cleanupFns.forEach((fns) => fns.forEach((fn) => fn()));
502
+ this.cleanupFns.clear();
348
503
  }
349
504
  this.responseQueue.close(options);
350
505
  this.serverEventIteratorQueue.close(options);
@@ -352,15 +507,51 @@ class ClientPeer {
352
507
  }
353
508
 
354
509
  class ServerPeer {
510
+ peer;
511
+ constructor(send) {
512
+ this.peer = new experimental_ServerPeerWithoutCodec(async ([id, type, payload]) => {
513
+ await send(await encodeResponseMessage(id, type, payload));
514
+ });
515
+ }
516
+ get length() {
517
+ return this.peer.length;
518
+ }
519
+ open(id) {
520
+ return this.peer.open(id);
521
+ }
522
+ /**
523
+ * @todo This method will return Promise<void> in the next major version.
524
+ */
525
+ async message(raw, handleRequest) {
526
+ return this.peer.message(await decodeRequestMessage(raw), handleRequest);
527
+ }
528
+ /**
529
+ * @deprecated Please pass the `handleRequest` (second arg) function to the `message` method instead.
530
+ */
531
+ async response(id, response) {
532
+ return this.peer.response(id, response);
533
+ }
534
+ close({ abort = true, ...options } = {}) {
535
+ return this.peer.close({ ...options, abort });
536
+ }
537
+ }
538
+ class experimental_ServerPeerWithoutCodec {
539
+ /**
540
+ * Queue of event iterator messages sent from client, awaiting consumption
541
+ */
355
542
  clientEventIteratorQueue = new AsyncIdQueue();
543
+ /**
544
+ * Map of active client request controllers, should be synced to request signal
545
+ */
356
546
  clientControllers = /* @__PURE__ */ new Map();
357
547
  send;
358
548
  constructor(send) {
359
- this.send = (id, ...rest) => encodeResponseMessage(id, ...rest).then(async (raw) => {
549
+ this.send = async (message) => {
550
+ const id = message[0];
360
551
  if (this.clientControllers.has(id)) {
361
- await send(raw);
552
+ await send(message);
362
553
  }
363
- });
554
+ };
364
555
  }
365
556
  get length() {
366
557
  return (this.clientEventIteratorQueue.length + this.clientControllers.size) / 2;
@@ -371,10 +562,12 @@ class ServerPeer {
371
562
  this.clientControllers.set(id, controller);
372
563
  return controller;
373
564
  }
374
- async message(raw) {
375
- const [id, type, payload] = await decodeRequestMessage(raw);
565
+ /**
566
+ * @todo This method will return Promise<void> in the next major version.
567
+ */
568
+ async message([id, type, payload], handleRequest) {
376
569
  if (type === MessageType.ABORT_SIGNAL) {
377
- this.close({ id });
570
+ this.close({ id, reason: new AbortError("Client aborted the request") });
378
571
  return [id, void 0];
379
572
  }
380
573
  if (type === MessageType.EVENT_ITERATOR) {
@@ -384,51 +577,89 @@ class ServerPeer {
384
577
  return [id, void 0];
385
578
  }
386
579
  const clientController = this.open(id);
580
+ const signal = clientController.signal;
387
581
  const request = {
388
582
  ...payload,
389
- signal: clientController.signal,
390
- body: isEventIteratorHeaders(payload.headers) ? toEventIterator(this.clientEventIteratorQueue, id, async (reason) => {
391
- if (reason !== "next") {
392
- await this.send(id, MessageType.ABORT_SIGNAL, void 0);
393
- }
394
- }) : payload.body
583
+ signal,
584
+ body: isEventIteratorHeaders(payload.headers) ? toEventIterator(
585
+ this.clientEventIteratorQueue,
586
+ id,
587
+ async (reason) => {
588
+ if (reason !== "next") {
589
+ await this.send([id, MessageType.ABORT_SIGNAL, void 0]);
590
+ }
591
+ },
592
+ { signal }
593
+ ) : payload.body
395
594
  };
595
+ if (handleRequest) {
596
+ let context;
597
+ const otelConfig = getGlobalOtelConfig();
598
+ if (otelConfig) {
599
+ context = otelConfig.propagation.extract(otelConfig.context.active(), request.headers);
600
+ }
601
+ await runWithSpan(
602
+ { name: "receive_peer_request", context },
603
+ async () => {
604
+ const response = await runWithSpan(
605
+ { name: "handle_request" },
606
+ async () => {
607
+ try {
608
+ return await handleRequest(request);
609
+ } catch (reason) {
610
+ this.close({ id, reason, abort: false });
611
+ throw reason;
612
+ }
613
+ }
614
+ );
615
+ await runWithSpan(
616
+ { name: "send_peer_response" },
617
+ () => this.response(id, response)
618
+ );
619
+ }
620
+ );
621
+ }
396
622
  return [id, request];
397
623
  }
624
+ /**
625
+ * @deprecated Please pass the `handleRequest` (second arg) function to the `message` method instead.
626
+ */
398
627
  async response(id, response) {
399
628
  const signal = this.clientControllers.get(id)?.signal;
400
629
  if (!signal || signal.aborted) {
401
630
  return;
402
631
  }
403
- await this.send(id, MessageType.RESPONSE, response).then(async () => {
632
+ try {
633
+ await this.send([id, MessageType.RESPONSE, response]);
404
634
  if (!signal.aborted && isAsyncIteratorObject(response.body)) {
405
- if (response.body instanceof experimental_HibernationEventIterator) {
635
+ if (response.body instanceof HibernationEventIterator) {
406
636
  response.body.hibernationCallback?.(id);
407
637
  } else {
408
- await resolveEventIterator(response.body, async (payload) => {
638
+ const iterator = response.body;
639
+ await resolveEventIterator(iterator, async (payload) => {
409
640
  if (signal.aborted) {
410
641
  return "abort";
411
642
  }
412
- await this.send(id, MessageType.EVENT_ITERATOR, payload);
643
+ await this.send([id, MessageType.EVENT_ITERATOR, payload]);
413
644
  return "next";
414
645
  });
415
646
  }
416
647
  }
417
648
  this.close({ id, abort: false });
418
- }).catch((reason) => {
649
+ } catch (reason) {
419
650
  this.close({ id, reason, abort: false });
420
651
  throw reason;
421
- });
652
+ }
422
653
  }
423
654
  close({ abort = true, ...options } = {}) {
424
655
  if (options.id === void 0) {
425
656
  if (abort) {
426
- this.clientControllers.forEach((c) => c.abort());
657
+ this.clientControllers.forEach((c) => c.abort(options.reason));
427
658
  }
428
659
  this.clientControllers.clear();
429
660
  } else {
430
661
  if (abort) {
431
- this.clientControllers.get(options.id)?.abort();
662
+ this.clientControllers.get(options.id)?.abort(options.reason);
432
663
  }
433
664
  this.clientControllers.delete(options.id);
434
665
  }
@@ -436,4 +667,4 @@ class ServerPeer {
436
667
  }
437
668
  }
438
669
 
439
- export { ClientPeer, MessageType, ServerPeer, decodeRequestMessage, decodeResponseMessage, encodeRequestMessage, encodeResponseMessage, resolveEventIterator, toEventIterator };
670
+ export { ClientPeer, MessageType, ServerPeer, decodeRequestMessage, decodeResponseMessage, deserializeRequestMessage, deserializeResponseMessage, encodeRequestMessage, encodeResponseMessage, experimental_ClientPeerWithoutCodec, experimental_ServerPeerWithoutCodec, resolveEventIterator, serializeRequestMessage, serializeResponseMessage, 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.f50512c",
4
+ "version": "0.0.0-next.f5149a8",
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.f50512c",
27
- "@orpc/standard-server": "0.0.0-next.f50512c"
26
+ "@orpc/standard-server": "0.0.0-next.f5149a8",
27
+ "@orpc/shared": "0.0.0-next.f5149a8"
28
28
  },
29
29
  "scripts": {
30
30
  "build": "unbuild",