@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/README.md +9 -4
- package/dist/index.d.mts +148 -33
- package/dist/index.d.ts +148 -33
- package/dist/index.mjs +423 -205
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { isAsyncIteratorObject, stringifyJSON, AsyncIteratorClass, isTypescriptObject, SequentialIdGenerator, AsyncIdQueue } from '@orpc/shared';
|
|
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 = "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
|
-
|
|
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
|
|
21
|
+
return { i: id, t: type, p: serializedPayload2 };
|
|
20
22
|
}
|
|
21
23
|
if (type === 4 /* ABORT_SIGNAL */) {
|
|
22
|
-
return
|
|
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(
|
|
31
|
-
b:
|
|
32
|
-
h: Object.keys(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
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
|
|
64
|
+
return { i: id, t: type, p: serializedPayload2 };
|
|
82
65
|
}
|
|
83
66
|
if (type === 4 /* ABORT_SIGNAL */) {
|
|
84
|
-
return
|
|
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(
|
|
94
|
-
b:
|
|
72
|
+
h: Object.keys(response.headers).length > 0 ? response.headers : void 0,
|
|
73
|
+
b: response.body
|
|
95
74
|
};
|
|
96
|
-
|
|
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
|
-
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
163
|
-
|
|
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,
|
|
202
|
+
async function encodeRawMessage(data, blob) {
|
|
167
203
|
const json = stringifyJSON(data);
|
|
168
|
-
if (
|
|
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
|
-
|
|
175
|
-
])
|
|
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 =
|
|
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.
|
|
188
|
-
const
|
|
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
|
-
|
|
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
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
data =
|
|
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
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
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
|
-
|
|
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
|
-
|
|
244
|
-
|
|
245
|
-
return;
|
|
273
|
+
if (reason !== "next") {
|
|
274
|
+
span?.addEvent("cancelled");
|
|
246
275
|
}
|
|
247
|
-
|
|
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
|
|
250
|
-
|
|
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 (
|
|
388
|
+
this.send = async (message) => {
|
|
389
|
+
const id = message[0];
|
|
272
390
|
if (this.serverControllers.has(id)) {
|
|
273
|
-
await send(
|
|
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) /
|
|
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
|
-
|
|
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 () => {
|
|
408
|
+
return runWithSpan(
|
|
409
|
+
{ name: "send_peer_request", signal },
|
|
410
|
+
async () => {
|
|
296
411
|
if (signal?.aborted) {
|
|
297
|
-
|
|
298
|
-
this.close({ id, reason: signal.reason });
|
|
299
|
-
return;
|
|
412
|
+
throw signal.reason;
|
|
300
413
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
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
|
-
}
|
|
315
|
-
|
|
316
|
-
reject(err);
|
|
317
|
-
});
|
|
318
|
-
this.responseQueue.pull(id).then(resolve).catch(reject);
|
|
319
|
-
});
|
|
473
|
+
}
|
|
474
|
+
);
|
|
320
475
|
}
|
|
321
|
-
async message(
|
|
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
|
-
|
|
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 =
|
|
549
|
+
this.send = async (message) => {
|
|
550
|
+
const id = message[0];
|
|
373
551
|
if (this.clientControllers.has(id)) {
|
|
374
|
-
await send(
|
|
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
|
-
|
|
388
|
-
|
|
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
|
|
403
|
-
body: isEventIteratorHeaders(payload.headers) ? toEventIterator(
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
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
|
-
|
|
632
|
+
try {
|
|
633
|
+
await this.send([id, MessageType.RESPONSE, response]);
|
|
417
634
|
if (!signal.aborted && isAsyncIteratorObject(response.body)) {
|
|
418
|
-
if (response.body instanceof
|
|
635
|
+
if (response.body instanceof HibernationEventIterator) {
|
|
419
636
|
response.body.hibernationCallback?.(id);
|
|
420
637
|
} else {
|
|
421
|
-
|
|
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
|
-
}
|
|
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,
|
|
670
|
+
export { ClientPeer, MessageType, ServerPeer, decodeRequestMessage, decodeResponseMessage, deserializeRequestMessage, deserializeResponseMessage, encodeRequestMessage, encodeResponseMessage, experimental_ClientPeerWithoutCodec, experimental_ServerPeerWithoutCodec, resolveEventIterator, serializeRequestMessage, serializeResponseMessage, toEventIterator };
|