@orpc/standard-server-peer 0.0.0-next.fc23c8d → 0.0.0-next.fc2dc8f

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,6 +1,8 @@
1
- import { isAsyncIteratorObject, stringifyJSON, AsyncIteratorClass, isTypescriptObject, SequentialIdGenerator, AsyncIdQueue } from '@orpc/shared';
2
- import { flattenHeader, getFilenameFromContentDisposition, generateContentDisposition, withEventMeta, ErrorEvent, getEventMeta, 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 = "http://orpc";
5
+ const SHORTABLE_ORIGIN_MATCHER = /^http:\/\/orpc\//;
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 prepareBodyAndHeadersForSerialization(
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, blobData } = 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,24 +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
- let body = payload.b;
58
- const contentType = flattenHeader(headers["content-type"]);
59
- if (blobData) {
60
- const contentDisposition = flattenHeader(headers["content-disposition"]);
61
- if (contentDisposition === void 0 && contentType?.startsWith("multipart/form-data")) {
62
- const tempRes = new Response(blobData, { headers: { "content-type": contentType } });
63
- body = await tempRes.formData();
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 }];
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
+ }];
72
55
  }
73
- async function encodeResponseMessage(id, type, payload) {
56
+ function serializeResponseMessage(id, type, payload) {
74
57
  if (type === 3 /* EVENT_ITERATOR */) {
75
58
  const eventPayload = payload;
76
59
  const serializedPayload2 = {
@@ -78,32 +61,23 @@ async function encodeResponseMessage(id, type, payload) {
78
61
  d: eventPayload.data,
79
62
  m: eventPayload.meta
80
63
  };
81
- return encodeRawMessage({ i: id, t: type, p: serializedPayload2 });
64
+ return { i: id, t: type, p: serializedPayload2 };
82
65
  }
83
66
  if (type === 4 /* ABORT_SIGNAL */) {
84
- return encodeRawMessage({ i: id, t: type, p: void 0 });
67
+ return { i: id, t: type, p: void 0 };
85
68
  }
86
69
  const response = payload;
87
- const { body: processedBody, headers: processedHeaders } = await prepareBodyAndHeadersForSerialization(
88
- response.body,
89
- response.headers
90
- );
91
70
  const serializedPayload = {
92
71
  s: response.status === 200 ? void 0 : response.status,
93
- h: Object.keys(processedHeaders).length > 0 ? processedHeaders : void 0,
94
- b: processedBody instanceof Blob ? void 0 : processedBody
72
+ h: Object.keys(response.headers).length > 0 ? response.headers : void 0,
73
+ b: response.body
95
74
  };
96
- const baseMessage = {
75
+ return {
97
76
  i: id,
98
77
  p: serializedPayload
99
78
  };
100
- if (processedBody instanceof Blob) {
101
- return encodeRawMessage(baseMessage, processedBody);
102
- }
103
- return encodeRawMessage(baseMessage);
104
79
  }
105
- async function decodeResponseMessage(raw) {
106
- const { json: message, blobData } = await decodeRawMessage(raw);
80
+ function deserializeResponseMessage(message) {
107
81
  const id = message.i;
108
82
  const type = message.t;
109
83
  if (type === 3 /* EVENT_ITERATOR */) {
@@ -114,24 +88,73 @@ async function decodeResponseMessage(raw) {
114
88
  return [id, type, message.p];
115
89
  }
116
90
  const payload = message.p;
117
- const headers = payload.h ?? {};
118
- let body = payload.b;
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);
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));
131
100
  }
132
- return [id, 2 /* RESPONSE */, { status: payload.s ?? 200, headers, body }];
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);
133
146
  }
134
- async function prepareBodyAndHeadersForSerialization(body, originalHeaders) {
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 }];
156
+ }
157
+ async function serializeBodyAndHeaders(body, originalHeaders) {
135
158
  const headers = { ...originalHeaders };
136
159
  const originalContentDisposition = headers["content-disposition"];
137
160
  delete headers["content-type"];
@@ -159,167 +182,298 @@ async function prepareBodyAndHeadersForSerialization(body, originalHeaders) {
159
182
  }
160
183
  return { body, headers };
161
184
  }
162
- function isEventIteratorHeaders(headers) {
163
- return Boolean(flattenHeader(headers["content-type"])?.startsWith("text/event-stream") && headers["content-disposition"] === void 0);
185
+ async function deserializeBody(headers, body, buffer) {
186
+ const contentType = flattenHeader(headers["content-type"]);
187
+ const contentDisposition = flattenHeader(headers["content-disposition"]);
188
+ if (typeof contentDisposition === "string") {
189
+ const filename = getFilenameFromContentDisposition(contentDisposition) ?? "blob";
190
+ return new File(buffer === void 0 ? [] : [buffer], filename, { type: contentType });
191
+ }
192
+ if (contentType?.startsWith("multipart/form-data")) {
193
+ const tempRes = new Response(buffer, { headers: { "content-type": contentType } });
194
+ return tempRes.formData();
195
+ }
196
+ if (contentType?.startsWith("application/x-www-form-urlencoded") && typeof body === "string") {
197
+ return new URLSearchParams(body);
198
+ }
199
+ return body;
164
200
  }
165
201
  const JSON_AND_BINARY_DELIMITER = 255;
166
- async function encodeRawMessage(data, blobData) {
202
+ async function encodeRawMessage(data, blob) {
167
203
  const json = stringifyJSON(data);
168
- if (blobData === void 0) {
204
+ if (blob === void 0 || blob.size === 0) {
169
205
  return json;
170
206
  }
171
- return new Blob([
207
+ return readAsBuffer(new Blob([
172
208
  new TextEncoder().encode(json),
173
209
  new Uint8Array([JSON_AND_BINARY_DELIMITER]),
174
- blobData
175
- ]).arrayBuffer();
210
+ blob
211
+ ]));
176
212
  }
177
213
  async function decodeRawMessage(raw) {
178
214
  if (typeof raw === "string") {
179
215
  return { json: JSON.parse(raw) };
180
216
  }
181
- const buffer = new Uint8Array(raw instanceof Blob ? await raw.arrayBuffer() : raw);
217
+ const buffer = raw instanceof Uint8Array ? raw : new Uint8Array(raw);
182
218
  const delimiterIndex = buffer.indexOf(JSON_AND_BINARY_DELIMITER);
183
219
  if (delimiterIndex === -1) {
184
220
  const jsonPart2 = new TextDecoder().decode(buffer);
185
221
  return { json: JSON.parse(jsonPart2) };
186
222
  }
187
- const jsonPart = new TextDecoder().decode(buffer.slice(0, delimiterIndex));
188
- const blobData = buffer.slice(delimiterIndex + 1);
223
+ const jsonPart = new TextDecoder().decode(buffer.subarray(0, delimiterIndex));
224
+ const bufferPart = buffer.subarray(delimiterIndex + 1);
189
225
  return {
190
226
  json: JSON.parse(jsonPart),
191
- blobData: blobData.buffer
227
+ buffer: bufferPart
192
228
  };
193
229
  }
194
230
 
195
- function toEventIterator(queue, id, cleanup) {
231
+ function toEventIterator(queue, id, cleanup, options = {}) {
232
+ let span;
196
233
  return new AsyncIteratorClass(async () => {
197
- const item = await queue.pull(id);
198
- switch (item.event) {
199
- case "message": {
200
- let data = item.data;
201
- if (item.meta && isTypescriptObject(data)) {
202
- 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 };
203
245
  }
204
- return { value: data, done: false };
205
- }
206
- case "error": {
207
- let error = new ErrorEvent({
208
- data: item.data
209
- });
210
- if (item.meta) {
211
- 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;
212
255
  }
213
- throw error;
214
- }
215
- case "done": {
216
- let data = item.data;
217
- if (item.meta && isTypescriptObject(data)) {
218
- 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 };
219
263
  }
220
- return { value: data, done: true };
221
264
  }
222
- }
223
- }, cleanup);
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
- };
265
+ } catch (e) {
266
+ if (!(e instanceof ErrorEvent)) {
267
+ setSpanError(span, e, options);
240
268
  }
241
- })();
269
+ throw e;
270
+ }
271
+ }, async (reason) => {
242
272
  try {
243
- const direction = await callback(payload);
244
- if (payload.event === "done" || payload.event === "error") {
245
- return;
273
+ if (reason !== "next") {
274
+ span?.addEvent("cancelled");
246
275
  }
247
- 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;
248
319
  try {
249
- await iterator.return?.();
250
- } 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;
251
339
  }
252
- return;
253
- }
254
- } catch (err) {
255
- try {
256
- await iterator.return?.();
257
- } catch {
258
340
  }
259
- throw err;
260
341
  }
261
- }
342
+ );
262
343
  }
263
344
 
264
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 {
265
369
  idGenerator = new SequentialIdGenerator();
370
+ /**
371
+ * Queue of responses sent from server, awaiting consumption
372
+ */
266
373
  responseQueue = new AsyncIdQueue();
374
+ /**
375
+ * Queue of event iterator messages sent from server, awaiting consumption
376
+ */
267
377
  serverEventIteratorQueue = new AsyncIdQueue();
378
+ /**
379
+ * Controllers used to signal that the client should stop sending event iterator messages
380
+ */
268
381
  serverControllers = /* @__PURE__ */ new Map();
382
+ /**
383
+ * Cleanup functions invoked when the request/response is closed
384
+ */
385
+ cleanupFns = /* @__PURE__ */ new Map();
269
386
  send;
270
387
  constructor(send) {
271
- this.send = async (id, ...rest) => encodeRequestMessage(id, ...rest).then(async (raw) => {
388
+ this.send = async (message) => {
389
+ const id = message[0];
272
390
  if (this.serverControllers.has(id)) {
273
- await send(raw);
391
+ await send(message);
274
392
  }
275
- });
393
+ };
276
394
  }
277
395
  get length() {
278
- 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;
279
397
  }
280
398
  open(id) {
281
399
  this.serverEventIteratorQueue.open(id);
282
400
  this.responseQueue.open(id);
283
401
  const controller = new AbortController();
284
402
  this.serverControllers.set(id, controller);
403
+ this.cleanupFns.set(id, []);
285
404
  return controller;
286
405
  }
287
406
  async request(request) {
288
407
  const signal = request.signal;
289
- if (signal?.aborted) {
290
- throw signal.reason;
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 () => {
408
+ return runWithSpan(
409
+ { name: "send_peer_request", signal },
410
+ async () => {
296
411
  if (signal?.aborted) {
297
- await this.send(id, MessageType.ABORT_SIGNAL, void 0);
298
- this.close({ id, reason: signal.reason });
299
- return;
412
+ throw signal.reason;
300
413
  }
301
- signal?.addEventListener("abort", async () => {
302
- await this.send(id, MessageType.ABORT_SIGNAL, void 0);
303
- this.close({ id, reason: signal.reason });
304
- }, { once: true });
305
- if (isAsyncIteratorObject(request.body)) {
306
- await resolveEventIterator(request.body, async (payload) => {
307
- if (serverController.signal.aborted) {
308
- return "abort";
309
- }
310
- await this.send(id, MessageType.EVENT_ITERATOR, payload);
311
- 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);
312
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;
313
472
  }
314
- }).catch((err) => {
315
- this.close({ id, reason: err });
316
- reject(err);
317
- });
318
- this.responseQueue.pull(id).then(resolve).catch(reject);
319
- });
473
+ }
474
+ );
320
475
  }
321
- async message(raw) {
322
- const [id, type, payload] = await decodeResponseMessage(raw);
476
+ async message([id, type, payload]) {
323
477
  if (type === MessageType.ABORT_SIGNAL) {
324
478
  this.serverControllers.get(id)?.abort();
325
479
  return;
@@ -333,31 +487,19 @@ class ClientPeer {
333
487
  if (!this.responseQueue.isOpen(id)) {
334
488
  return;
335
489
  }
336
- if (isEventIteratorHeaders(payload.headers)) {
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
- }
490
+ this.responseQueue.push(id, payload);
353
491
  }
354
492
  close(options = {}) {
355
493
  if (options.id !== void 0) {
356
494
  this.serverControllers.get(options.id)?.abort(options.reason);
357
495
  this.serverControllers.delete(options.id);
496
+ this.cleanupFns.get(options.id)?.forEach((fn) => fn());
497
+ this.cleanupFns.delete(options.id);
358
498
  } else {
359
499
  this.serverControllers.forEach((c) => c.abort(options.reason));
360
500
  this.serverControllers.clear();
501
+ this.cleanupFns.forEach((fns) => fns.forEach((fn) => fn()));
502
+ this.cleanupFns.clear();
361
503
  }
362
504
  this.responseQueue.close(options);
363
505
  this.serverEventIteratorQueue.close(options);
@@ -365,15 +507,51 @@ class ClientPeer {
365
507
  }
366
508
 
367
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
+ */
368
542
  clientEventIteratorQueue = new AsyncIdQueue();
543
+ /**
544
+ * Map of active client request controllers, should be synced to request signal
545
+ */
369
546
  clientControllers = /* @__PURE__ */ new Map();
370
547
  send;
371
548
  constructor(send) {
372
- this.send = (id, ...rest) => encodeResponseMessage(id, ...rest).then(async (raw) => {
549
+ this.send = async (message) => {
550
+ const id = message[0];
373
551
  if (this.clientControllers.has(id)) {
374
- await send(raw);
552
+ await send(message);
375
553
  }
376
- });
554
+ };
377
555
  }
378
556
  get length() {
379
557
  return (this.clientEventIteratorQueue.length + this.clientControllers.size) / 2;
@@ -384,10 +562,12 @@ class ServerPeer {
384
562
  this.clientControllers.set(id, controller);
385
563
  return controller;
386
564
  }
387
- async message(raw) {
388
- 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) {
389
569
  if (type === MessageType.ABORT_SIGNAL) {
390
- this.close({ id });
570
+ this.close({ id, reason: new AbortError("Client aborted the request") });
391
571
  return [id, void 0];
392
572
  }
393
573
  if (type === MessageType.EVENT_ITERATOR) {
@@ -397,51 +577,89 @@ class ServerPeer {
397
577
  return [id, void 0];
398
578
  }
399
579
  const clientController = this.open(id);
580
+ const signal = clientController.signal;
400
581
  const request = {
401
582
  ...payload,
402
- signal: clientController.signal,
403
- body: isEventIteratorHeaders(payload.headers) ? toEventIterator(this.clientEventIteratorQueue, id, async (reason) => {
404
- if (reason !== "next") {
405
- await this.send(id, MessageType.ABORT_SIGNAL, void 0);
406
- }
407
- }) : 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
408
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
+ }
409
622
  return [id, request];
410
623
  }
624
+ /**
625
+ * @deprecated Please pass the `handleRequest` (second arg) function to the `message` method instead.
626
+ */
411
627
  async response(id, response) {
412
628
  const signal = this.clientControllers.get(id)?.signal;
413
629
  if (!signal || signal.aborted) {
414
630
  return;
415
631
  }
416
- await this.send(id, MessageType.RESPONSE, response).then(async () => {
632
+ try {
633
+ await this.send([id, MessageType.RESPONSE, response]);
417
634
  if (!signal.aborted && isAsyncIteratorObject(response.body)) {
418
- if (response.body instanceof experimental_HibernationEventIterator) {
635
+ if (response.body instanceof HibernationEventIterator) {
419
636
  response.body.hibernationCallback?.(id);
420
637
  } else {
421
- await resolveEventIterator(response.body, async (payload) => {
638
+ const iterator = response.body;
639
+ await resolveEventIterator(iterator, async (payload) => {
422
640
  if (signal.aborted) {
423
641
  return "abort";
424
642
  }
425
- await this.send(id, MessageType.EVENT_ITERATOR, payload);
643
+ await this.send([id, MessageType.EVENT_ITERATOR, payload]);
426
644
  return "next";
427
645
  });
428
646
  }
429
647
  }
430
648
  this.close({ id, abort: false });
431
- }).catch((reason) => {
649
+ } catch (reason) {
432
650
  this.close({ id, reason, abort: false });
433
651
  throw reason;
434
- });
652
+ }
435
653
  }
436
654
  close({ abort = true, ...options } = {}) {
437
655
  if (options.id === void 0) {
438
656
  if (abort) {
439
- this.clientControllers.forEach((c) => c.abort());
657
+ this.clientControllers.forEach((c) => c.abort(options.reason));
440
658
  }
441
659
  this.clientControllers.clear();
442
660
  } else {
443
661
  if (abort) {
444
- this.clientControllers.get(options.id)?.abort();
662
+ this.clientControllers.get(options.id)?.abort(options.reason);
445
663
  }
446
664
  this.clientControllers.delete(options.id);
447
665
  }
@@ -449,4 +667,4 @@ class ServerPeer {
449
667
  }
450
668
  }
451
669
 
452
- export { ClientPeer, MessageType, ServerPeer, decodeRequestMessage, decodeResponseMessage, encodeRequestMessage, encodeResponseMessage, isEventIteratorHeaders, resolveEventIterator, toEventIterator };
670
+ export { ClientPeer, MessageType, ServerPeer, decodeRequestMessage, decodeResponseMessage, deserializeRequestMessage, deserializeResponseMessage, encodeRequestMessage, encodeResponseMessage, experimental_ClientPeerWithoutCodec, experimental_ServerPeerWithoutCodec, resolveEventIterator, serializeRequestMessage, serializeResponseMessage, toEventIterator };