@livekit/rtc-node 0.13.33 → 0.13.35
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 +0 -5
- package/dist/{audio_stream-CnODFAmo.d.cts → audio_stream-BNVaONuH.d.cts} +36 -11
- package/dist/audio_stream.d.cts +2 -2
- package/dist/data_streams/index.d.cts +1 -1
- package/dist/data_streams/stream_reader.cjs +97 -58
- package/dist/data_streams/stream_reader.cjs.map +1 -1
- package/dist/data_streams/stream_reader.d.cts +1 -1
- package/dist/data_streams/stream_reader.d.ts +28 -11
- package/dist/data_streams/stream_reader.d.ts.map +1 -1
- package/dist/data_streams/stream_writer.d.cts +1 -1
- package/dist/data_streams/types.cjs.map +1 -1
- package/dist/data_streams/types.d.cts +1 -1
- package/dist/data_streams/types.d.ts +6 -0
- package/dist/data_streams/types.d.ts.map +1 -1
- package/dist/e2ee.cjs +5 -1
- package/dist/e2ee.cjs.map +1 -1
- package/dist/e2ee.d.cts +3 -1
- package/dist/e2ee.d.ts +3 -1
- package/dist/e2ee.d.ts.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/participant.cjs +178 -196
- package/dist/participant.cjs.map +1 -1
- package/dist/participant.d.cts +2 -2
- package/dist/participant.d.ts +22 -5
- package/dist/participant.d.ts.map +1 -1
- package/dist/room.cjs +166 -126
- package/dist/room.cjs.map +1 -1
- package/dist/room.d.cts +3 -3
- package/dist/room.d.ts +16 -8
- package/dist/room.d.ts.map +1 -1
- package/dist/tests/e2e_common.cjs +157 -0
- package/dist/tests/e2e_common.cjs.map +1 -0
- package/dist/tests/e2e_common.d.cts +56 -0
- package/dist/tests/e2e_common.d.ts +37 -0
- package/dist/tests/e2e_common.d.ts.map +1 -0
- package/dist/track.d.cts +2 -2
- package/dist/track_publication.d.cts +2 -2
- package/dist/{types-_PdPVish.d.cts → types-f7wTlFjs.d.cts} +34 -11
- package/dist/utils.cjs +27 -2
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.cts +8 -1
- package/dist/utils.d.ts +6 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/version.cjs +1 -1
- package/dist/version.cjs.map +1 -1
- package/dist/version.d.cts +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/video_stream.d.cts +2 -2
- package/package.json +2 -2
- package/src/audio_stream_room_lifecycle.test.ts +4 -1
- package/src/data_streams/stream_reader.ts +132 -82
- package/src/data_streams/types.ts +6 -0
- package/src/e2ee.ts +7 -0
- package/src/participant.ts +251 -228
- package/src/room.ts +233 -144
- package/src/tests/e2e.test.ts +50 -139
- package/src/tests/e2e_common.ts +163 -0
- package/src/tests/e2e_data_streams.test.ts +639 -0
- package/src/utils.ts +30 -0
- package/src/version.ts +1 -1
package/dist/e2ee.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import { EncryptionType } from '@livekit/rtc-ffi-bindings';
|
|
1
|
+
import { EncryptionType, KeyDerivationFunction } from '@livekit/rtc-ffi-bindings';
|
|
2
2
|
export interface KeyProviderOptions {
|
|
3
3
|
sharedKey?: Uint8Array;
|
|
4
4
|
ratchetSalt?: Uint8Array;
|
|
5
5
|
ratchetWindowSize?: number;
|
|
6
6
|
failureTolerance?: number;
|
|
7
|
+
keyRingSize?: number;
|
|
8
|
+
keyDerivationFunction?: KeyDerivationFunction;
|
|
7
9
|
}
|
|
8
10
|
export declare const defaultKeyProviderOptions: KeyProviderOptions;
|
|
9
11
|
export interface E2EEOptions {
|
package/dist/e2ee.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"e2ee.d.ts","sourceRoot":"","sources":["../src/e2ee.ts"],"names":[],"mappings":"AAWA,OAAO,EAGL,cAAc,
|
|
1
|
+
{"version":3,"file":"e2ee.d.ts","sourceRoot":"","sources":["../src/e2ee.ts"],"names":[],"mappings":"AAWA,OAAO,EAGL,cAAc,EAKd,qBAAqB,EAKtB,MAAM,2BAA2B,CAAC;AASnC,MAAM,WAAW,kBAAkB;IACjC,SAAS,CAAC,EAAE,UAAU,CAAC;IACvB,WAAW,CAAC,EAAE,UAAU,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qBAAqB,CAAC,EAAE,qBAAqB,CAAC;CAC/C;AAED,eAAO,MAAM,yBAAyB,EAAE,kBAMvC,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,kBAAkB,EAAE,kBAAkB,CAAC;IACvC,cAAc,CAAC,EAAE,cAAc,CAAC;CACjC;AAED,eAAO,MAAM,kBAAkB,EAAE,WAGhC,CAAC;AAEF,qBAAa,WAAW;IACtB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,EAAE,kBAAkB,CAAC;IAE5B,eAAe;gBACH,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB;IAKxD,YAAY,CAAC,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM;IAoBpD,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU;IAqB7C,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU;IAqB9C,MAAM,CAAC,mBAAmB,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;IAoBpD,SAAS,CAAC,mBAAmB,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU;IAsBpE,UAAU,CAAC,mBAAmB,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,UAAU;CAqBtE;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,UAAU,CAAa;IAC/B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;gBAEL,UAAU,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO;IAO/F,UAAU,CAAC,OAAO,EAAE,OAAO;IAqB3B,WAAW,CAAC,QAAQ,EAAE,MAAM;CAoB7B;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,WAAW,CAAc;IAEjC,OAAO,EAAE,OAAO,CAAC;gBAEL,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW;IAWlD,UAAU,CAAC,OAAO,EAAE,OAAO;IAoB3B,aAAa,IAAI,YAAY,EAAE;CA8BhC"}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
export { AudioFrame, combineAudioFrames } from './audio_frame.cjs';
|
|
2
2
|
export { AudioResampler, AudioResamplerQuality } from './audio_resampler.cjs';
|
|
3
3
|
export { AudioSource } from './audio_source.cjs';
|
|
4
|
-
export { A as AudioStream, a as AudioTrack, C as ConnectError, L as LocalAudioTrack, b as LocalParticipant, c as LocalTrack, d as LocalTrackPublication, e as LocalVideoTrack, N as NoiseCancellationOptions, P as Participant, R as RemoteAudioTrack, f as RemoteParticipant, g as RemoteTrack, h as RemoteTrackPublication, i as RemoteVideoTrack, j as Room, k as
|
|
4
|
+
export { A as AudioStream, a as AudioTrack, C as ConnectError, L as LocalAudioTrack, b as LocalParticipant, c as LocalTrack, d as LocalTrackPublication, e as LocalVideoTrack, N as NoiseCancellationOptions, P as Participant, R as RemoteAudioTrack, f as RemoteParticipant, g as RemoteTrack, h as RemoteTrackPublication, i as RemoteVideoTrack, j as Room, k as RoomDataStreamOptions, l as RoomEvent, m as RoomOptions, n as RtcConfiguration, T as Track, o as TrackPublication, V as VideoTrack } from './audio_stream-BNVaONuH.cjs';
|
|
5
5
|
export { AudioFilter } from './audio_filter.cjs';
|
|
6
6
|
export { AudioMixer, AudioMixerOptions } from './audio_mixer.cjs';
|
|
7
|
-
export { B as BaseStreamInfo, a as ByteStreamHandler, b as ByteStreamInfo, c as ByteStreamOptions, d as ByteStreamReader, D as DataStreamOptions, S as StreamController, T as TextStreamHandler, e as TextStreamInfo, f as TextStreamOptions, g as TextStreamReader } from './types-
|
|
7
|
+
export { B as BaseStreamInfo, a as ByteStreamHandler, b as ByteStreamInfo, c as ByteStreamOptions, d as ByteStreamReader, D as DataStreamOptions, S as StreamController, T as TextStreamHandler, e as TextStreamInfo, f as TextStreamOptions, g as TextStreamReader } from './types-f7wTlFjs.cjs';
|
|
8
8
|
export { ByteStreamWriter, TextStreamWriter } from './data_streams/stream_writer.cjs';
|
|
9
9
|
export { E2EEManager, E2EEOptions, FrameCryptor, KeyProvider, KeyProviderOptions } from './e2ee.cjs';
|
|
10
10
|
import './ffi_client.cjs';
|
|
11
|
-
export { ConnectionQuality, ConnectionState, ContinualGatheringPolicy, DataPacketKind, DisconnectReason, EncryptionState, EncryptionType, IceServer, IceTransportType, ParticipantKind, ParticipantKindDetail,
|
|
11
|
+
export { ConnectionQuality, ConnectionState, ContinualGatheringPolicy, DataPacketKind, DisconnectReason, EncryptionState, EncryptionType, IceServer, IceTransportType, ParticipantKind, ParticipantKindDetail, SimulateScenarioKind, StreamState, TrackKind, TrackPublishOptions, TrackSource, VideoBufferType, VideoCodec, VideoRotation, livekitDispose as dispose } from '@livekit/rtc-ffi-bindings';
|
|
12
12
|
export { PerformRpcParams, RpcError, RpcInvocationData } from './rpc.cjs';
|
|
13
13
|
export { Transcription, TranscriptionSegment } from './transcription.cjs';
|
|
14
14
|
export { ChatMessage } from './types.cjs';
|
package/dist/participant.cjs
CHANGED
|
@@ -27,14 +27,14 @@ var import_mutex = require("@livekit/mutex");
|
|
|
27
27
|
var import_rtc_ffi_bindings = require("@livekit/rtc-ffi-bindings");
|
|
28
28
|
var import_rtc_ffi_bindings2 = require("@livekit/rtc-ffi-bindings");
|
|
29
29
|
var import_rtc_ffi_bindings3 = require("@livekit/rtc-ffi-bindings");
|
|
30
|
-
var
|
|
30
|
+
var import_node_url = require("node:url");
|
|
31
31
|
var import_data_streams = require("./data_streams/index.cjs");
|
|
32
32
|
var import_ffi_client = require("./ffi_client.cjs");
|
|
33
33
|
var import_log = require("./log.cjs");
|
|
34
34
|
var import_rpc = require("./rpc.cjs");
|
|
35
35
|
var import_track_publication = require("./track_publication.cjs");
|
|
36
36
|
var import_utils = require("./utils.cjs");
|
|
37
|
-
|
|
37
|
+
var import_utils2 = require("./utils.cjs");
|
|
38
38
|
class Participant {
|
|
39
39
|
constructor(owned_info) {
|
|
40
40
|
this.trackPublications = /* @__PURE__ */ new Map();
|
|
@@ -73,31 +73,60 @@ class LocalParticipant extends Participant {
|
|
|
73
73
|
constructor(info, ffiEventLock, disconnectSignal) {
|
|
74
74
|
super(info);
|
|
75
75
|
this.rpcHandlers = /* @__PURE__ */ new Map();
|
|
76
|
+
// Handle ids of data stream writers that have been opened but not yet
|
|
77
|
+
// closed. The FFI close request consumes the handle (take_handle), so an
|
|
78
|
+
// entry is removed as soon as a close is sent for it; whatever is left when
|
|
79
|
+
// the room disconnects is dropped by disposeOpenStreamWriters(). Only the
|
|
80
|
+
// ids are kept — wrapping them in FfiHandle would make GC drop a handle the
|
|
81
|
+
// close request already consumed.
|
|
82
|
+
this.openStreamWriters = /* @__PURE__ */ new Set();
|
|
76
83
|
this.trackPublications = /* @__PURE__ */ new Map();
|
|
77
|
-
this.
|
|
78
|
-
const type = "sendStreamChunk";
|
|
84
|
+
this.textStreamWriterWrite = async (req) => {
|
|
79
85
|
const res = import_ffi_client.FfiClient.instance.request({
|
|
80
|
-
message: { case:
|
|
86
|
+
message: { case: "textStreamWrite", value: req }
|
|
81
87
|
});
|
|
82
88
|
const cb = await import_ffi_client.FfiClient.instance.waitFor(
|
|
83
|
-
(ev) => ev.message.case ==
|
|
89
|
+
(ev) => ev.message.case == "textStreamWriterWrite" && ev.message.value.asyncId == res.asyncId,
|
|
84
90
|
{ signal: this.disconnectSignal }
|
|
85
91
|
);
|
|
86
92
|
if (cb.error) {
|
|
87
|
-
throw new Error(cb.error);
|
|
93
|
+
throw new Error(cb.error.description);
|
|
88
94
|
}
|
|
89
95
|
};
|
|
90
|
-
this.
|
|
91
|
-
const type = "sendStreamTrailer";
|
|
96
|
+
this.textStreamWriterClose = async (req) => {
|
|
92
97
|
const res = import_ffi_client.FfiClient.instance.request({
|
|
93
|
-
message: { case:
|
|
98
|
+
message: { case: "textStreamClose", value: req }
|
|
94
99
|
});
|
|
95
100
|
const cb = await import_ffi_client.FfiClient.instance.waitFor(
|
|
96
|
-
(ev) => ev.message.case ==
|
|
101
|
+
(ev) => ev.message.case == "textStreamWriterClose" && ev.message.value.asyncId == res.asyncId,
|
|
97
102
|
{ signal: this.disconnectSignal }
|
|
98
103
|
);
|
|
99
104
|
if (cb.error) {
|
|
100
|
-
throw new Error(cb.error);
|
|
105
|
+
throw new Error(cb.error.description);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
this.byteStreamWriterWrite = async (req) => {
|
|
109
|
+
const res = import_ffi_client.FfiClient.instance.request({
|
|
110
|
+
message: { case: "byteStreamWrite", value: req }
|
|
111
|
+
});
|
|
112
|
+
const cb = await import_ffi_client.FfiClient.instance.waitFor(
|
|
113
|
+
(ev) => ev.message.case == "byteStreamWriterWrite" && ev.message.value.asyncId == res.asyncId,
|
|
114
|
+
{ signal: this.disconnectSignal }
|
|
115
|
+
);
|
|
116
|
+
if (cb.error) {
|
|
117
|
+
throw new Error(cb.error.description);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
this.byteStreamWriterClose = async (req) => {
|
|
121
|
+
const res = import_ffi_client.FfiClient.instance.request({
|
|
122
|
+
message: { case: "byteStreamClose", value: req }
|
|
123
|
+
});
|
|
124
|
+
const cb = await import_ffi_client.FfiClient.instance.waitFor(
|
|
125
|
+
(ev) => ev.message.case == "byteStreamWriterClose" && ev.message.value.asyncId == res.asyncId,
|
|
126
|
+
{ signal: this.disconnectSignal }
|
|
127
|
+
);
|
|
128
|
+
if (cb.error) {
|
|
129
|
+
throw new Error(cb.error.description);
|
|
101
130
|
}
|
|
102
131
|
};
|
|
103
132
|
this.ffiEventLock = ffiEventLock;
|
|
@@ -185,88 +214,55 @@ class LocalParticipant extends Participant {
|
|
|
185
214
|
* Well suited for TTS and/or streaming LLM output. If you want to simply send a text then use sendText() instead
|
|
186
215
|
*/
|
|
187
216
|
async streamText(options) {
|
|
188
|
-
const
|
|
189
|
-
const streamId = (options == null ? void 0 : options.streamId) ?? crypto.randomUUID();
|
|
190
|
-
const destinationIdentities = options == null ? void 0 : options.destinationIdentities;
|
|
191
|
-
const info = {
|
|
192
|
-
streamId,
|
|
193
|
-
mimeType: "text/plain",
|
|
194
|
-
topic: (options == null ? void 0 : options.topic) ?? "",
|
|
195
|
-
timestamp: Date.now(),
|
|
196
|
-
totalSize: options == null ? void 0 : options.totalSize
|
|
197
|
-
};
|
|
198
|
-
const headerReq = new import_rtc_ffi_bindings2.SendStreamHeaderRequest({
|
|
199
|
-
senderIdentity,
|
|
200
|
-
destinationIdentities,
|
|
217
|
+
const req = new import_rtc_ffi_bindings2.TextStreamOpenRequest({
|
|
201
218
|
localParticipantHandle: this.ffi_handle.handle,
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
mimeType: info.mimeType,
|
|
205
|
-
topic: info.topic,
|
|
206
|
-
timestamp: (0, import_utils.numberToBigInt)(info.timestamp),
|
|
207
|
-
totalLength: (0, import_utils.numberToBigInt)(info.totalSize),
|
|
219
|
+
options: new import_rtc_ffi_bindings2.StreamTextOptions({
|
|
220
|
+
topic: (options == null ? void 0 : options.topic) ?? "",
|
|
208
221
|
attributes: options == null ? void 0 : options.attributes,
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
operationType: import_rtc_ffi_bindings2.DataStream_OperationType.CREATE,
|
|
213
|
-
version: 0,
|
|
214
|
-
replyToStreamId: "",
|
|
215
|
-
generated: false
|
|
216
|
-
})
|
|
217
|
-
}
|
|
222
|
+
destinationIdentities: options == null ? void 0 : options.destinationIdentities,
|
|
223
|
+
id: options == null ? void 0 : options.streamId,
|
|
224
|
+
senderIdentity: (options == null ? void 0 : options.senderIdentity) ?? this.identity
|
|
218
225
|
})
|
|
219
226
|
});
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
const
|
|
224
|
-
|
|
227
|
+
const res = import_ffi_client.FfiClient.instance.request({
|
|
228
|
+
message: { case: "textStreamOpen", value: req }
|
|
229
|
+
});
|
|
230
|
+
const cb = await import_ffi_client.FfiClient.instance.waitFor(
|
|
231
|
+
(ev) => ev.message.case == "textStreamOpen" && ev.message.value.asyncId == res.asyncId,
|
|
232
|
+
{ signal: this.disconnectSignal }
|
|
233
|
+
);
|
|
234
|
+
if (cb.result.case !== "writer") {
|
|
235
|
+
throw new Error(cb.result.case === "error" ? cb.result.value.description : "unknown error");
|
|
236
|
+
}
|
|
237
|
+
const writerHandle = cb.result.value.handle.id;
|
|
238
|
+
const info = (0, import_utils.textStreamInfoFromProto)(cb.result.value.info);
|
|
239
|
+
this.openStreamWriters.add(writerHandle);
|
|
240
|
+
const closeWriter = async (reason) => {
|
|
241
|
+
if (!this.openStreamWriters.delete(writerHandle)) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
await this.textStreamWriterClose(new import_rtc_ffi_bindings2.TextStreamWriterCloseRequest({ writerHandle, reason }));
|
|
245
|
+
};
|
|
225
246
|
const writableStream = new WritableStream({
|
|
226
247
|
// Implement the sink
|
|
227
|
-
async
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
content: textByteChunk,
|
|
235
|
-
streamId,
|
|
236
|
-
chunkIndex: (0, import_utils.numberToBigInt)(nextChunkId)
|
|
237
|
-
})
|
|
248
|
+
write: async (text) => {
|
|
249
|
+
try {
|
|
250
|
+
await this.textStreamWriterWrite(
|
|
251
|
+
new import_rtc_ffi_bindings2.TextStreamWriterWriteRequest({ writerHandle, text })
|
|
252
|
+
);
|
|
253
|
+
} catch (err) {
|
|
254
|
+
await closeWriter(err instanceof Error ? err.message : String(err ?? "")).catch(() => {
|
|
238
255
|
});
|
|
239
|
-
|
|
240
|
-
nextChunkId += 1;
|
|
256
|
+
throw err;
|
|
241
257
|
}
|
|
242
258
|
},
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
senderIdentity,
|
|
246
|
-
localParticipantHandle: localHandle,
|
|
247
|
-
destinationIdentities,
|
|
248
|
-
trailer: new import_rtc_ffi_bindings2.DataStream_Trailer({
|
|
249
|
-
streamId,
|
|
250
|
-
reason: ""
|
|
251
|
-
})
|
|
252
|
-
});
|
|
253
|
-
await sendTrailer(trailerReq);
|
|
254
|
-
},
|
|
255
|
-
// Send a trailer with the error reason so the remote side's stream
|
|
259
|
+
close: () => closeWriter(),
|
|
260
|
+
// Close the stream with the error reason so the remote side's stream
|
|
256
261
|
// controller is closed instead of waiting for data that won't arrive.
|
|
257
|
-
async
|
|
262
|
+
abort: async (err) => {
|
|
258
263
|
import_log.log.error(err, "Sink Error");
|
|
259
264
|
try {
|
|
260
|
-
|
|
261
|
-
senderIdentity,
|
|
262
|
-
localParticipantHandle: localHandle,
|
|
263
|
-
destinationIdentities,
|
|
264
|
-
trailer: new import_rtc_ffi_bindings2.DataStream_Trailer({
|
|
265
|
-
streamId,
|
|
266
|
-
reason: err instanceof Error ? err.message : String(err ?? "")
|
|
267
|
-
})
|
|
268
|
-
});
|
|
269
|
-
await sendTrailer(trailerReq);
|
|
265
|
+
await closeWriter(err instanceof Error ? err.message : String(err ?? ""));
|
|
270
266
|
} catch {
|
|
271
267
|
}
|
|
272
268
|
}
|
|
@@ -275,104 +271,82 @@ class LocalParticipant extends Participant {
|
|
|
275
271
|
return writer;
|
|
276
272
|
}
|
|
277
273
|
async sendText(text, options) {
|
|
278
|
-
const
|
|
279
|
-
|
|
280
|
-
|
|
274
|
+
const req = new import_rtc_ffi_bindings2.StreamSendTextRequest({
|
|
275
|
+
localParticipantHandle: this.ffi_handle.handle,
|
|
276
|
+
options: new import_rtc_ffi_bindings2.StreamTextOptions({
|
|
277
|
+
topic: (options == null ? void 0 : options.topic) ?? "",
|
|
278
|
+
attributes: options == null ? void 0 : options.attributes,
|
|
279
|
+
destinationIdentities: options == null ? void 0 : options.destinationIdentities,
|
|
280
|
+
id: options == null ? void 0 : options.streamId,
|
|
281
|
+
senderIdentity: this.identity,
|
|
282
|
+
compress: options == null ? void 0 : options.compress
|
|
283
|
+
}),
|
|
284
|
+
text
|
|
285
|
+
});
|
|
286
|
+
const res = import_ffi_client.FfiClient.instance.request({
|
|
287
|
+
message: { case: "sendText", value: req }
|
|
281
288
|
});
|
|
282
|
-
await
|
|
283
|
-
|
|
284
|
-
|
|
289
|
+
const cb = await import_ffi_client.FfiClient.instance.waitFor(
|
|
290
|
+
(ev) => ev.message.case == "sendText" && ev.message.value.asyncId == res.asyncId,
|
|
291
|
+
{ signal: this.disconnectSignal }
|
|
292
|
+
);
|
|
293
|
+
if (cb.result.case !== "info") {
|
|
294
|
+
throw new Error(cb.result.case === "error" ? cb.result.value.description : "unknown error");
|
|
295
|
+
}
|
|
296
|
+
return (0, import_utils.textStreamInfoFromProto)(cb.result.value);
|
|
285
297
|
}
|
|
286
298
|
async streamBytes(options) {
|
|
287
|
-
const
|
|
288
|
-
const streamId = (options == null ? void 0 : options.streamId) ?? crypto.randomUUID();
|
|
289
|
-
const destinationIdentities = options == null ? void 0 : options.destinationIdentities;
|
|
290
|
-
const info = {
|
|
291
|
-
streamId,
|
|
292
|
-
mimeType: (options == null ? void 0 : options.mimeType) ?? "application/octet-stream",
|
|
293
|
-
topic: (options == null ? void 0 : options.topic) ?? "",
|
|
294
|
-
timestamp: Date.now(),
|
|
295
|
-
attributes: options == null ? void 0 : options.attributes,
|
|
296
|
-
totalSize: options == null ? void 0 : options.totalSize,
|
|
297
|
-
name: (options == null ? void 0 : options.name) ?? "unknown"
|
|
298
|
-
};
|
|
299
|
-
const headerReq = new import_rtc_ffi_bindings2.SendStreamHeaderRequest({
|
|
300
|
-
senderIdentity,
|
|
301
|
-
destinationIdentities,
|
|
299
|
+
const req = new import_rtc_ffi_bindings2.ByteStreamOpenRequest({
|
|
302
300
|
localParticipantHandle: this.ffi_handle.handle,
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
value: new import_rtc_ffi_bindings2.DataStream_ByteHeader({
|
|
313
|
-
name: info.name
|
|
314
|
-
})
|
|
315
|
-
}
|
|
301
|
+
options: new import_rtc_ffi_bindings2.StreamByteOptions({
|
|
302
|
+
topic: (options == null ? void 0 : options.topic) ?? "",
|
|
303
|
+
attributes: options == null ? void 0 : options.attributes,
|
|
304
|
+
destinationIdentities: options == null ? void 0 : options.destinationIdentities,
|
|
305
|
+
id: options == null ? void 0 : options.streamId,
|
|
306
|
+
name: (options == null ? void 0 : options.name) ?? "unknown",
|
|
307
|
+
mimeType: (options == null ? void 0 : options.mimeType) ?? "application/octet-stream",
|
|
308
|
+
totalLength: (0, import_utils2.numberToBigInt)(options == null ? void 0 : options.totalSize),
|
|
309
|
+
senderIdentity: this.identity
|
|
316
310
|
})
|
|
317
311
|
});
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
const
|
|
322
|
-
|
|
323
|
-
|
|
312
|
+
const res = import_ffi_client.FfiClient.instance.request({
|
|
313
|
+
message: { case: "byteStreamOpen", value: req }
|
|
314
|
+
});
|
|
315
|
+
const cb = await import_ffi_client.FfiClient.instance.waitFor(
|
|
316
|
+
(ev) => ev.message.case == "byteStreamOpen" && ev.message.value.asyncId == res.asyncId,
|
|
317
|
+
{ signal: this.disconnectSignal }
|
|
318
|
+
);
|
|
319
|
+
if (cb.result.case !== "writer") {
|
|
320
|
+
throw new Error(cb.result.case === "error" ? cb.result.value.description : "unknown error");
|
|
321
|
+
}
|
|
322
|
+
const writerHandle = cb.result.value.handle.id;
|
|
323
|
+
const info = (0, import_utils.byteStreamInfoFromProto)(cb.result.value.info);
|
|
324
|
+
this.openStreamWriters.add(writerHandle);
|
|
325
|
+
const closeWriter = async (reason) => {
|
|
326
|
+
if (!this.openStreamWriters.delete(writerHandle)) {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
await this.byteStreamWriterClose(new import_rtc_ffi_bindings2.ByteStreamWriterCloseRequest({ writerHandle, reason }));
|
|
330
|
+
};
|
|
324
331
|
const writableStream = new WritableStream({
|
|
325
|
-
async
|
|
326
|
-
const unlock = await writeMutex.lock();
|
|
327
|
-
let byteOffset = 0;
|
|
332
|
+
write: async (chunk) => {
|
|
328
333
|
try {
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
content: subChunk,
|
|
337
|
-
streamId,
|
|
338
|
-
chunkIndex: (0, import_utils.numberToBigInt)(chunkId)
|
|
339
|
-
})
|
|
340
|
-
});
|
|
341
|
-
await sendChunk(chunkRequest);
|
|
342
|
-
chunkId += 1;
|
|
343
|
-
byteOffset += subChunk.byteLength;
|
|
344
|
-
}
|
|
345
|
-
} finally {
|
|
346
|
-
unlock();
|
|
334
|
+
await this.byteStreamWriterWrite(
|
|
335
|
+
new import_rtc_ffi_bindings2.ByteStreamWriterWriteRequest({ writerHandle, bytes: chunk })
|
|
336
|
+
);
|
|
337
|
+
} catch (err) {
|
|
338
|
+
await closeWriter(err instanceof Error ? err.message : String(err ?? "")).catch(() => {
|
|
339
|
+
});
|
|
340
|
+
throw err;
|
|
347
341
|
}
|
|
348
342
|
},
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
senderIdentity,
|
|
352
|
-
localParticipantHandle: localHandle,
|
|
353
|
-
destinationIdentities,
|
|
354
|
-
trailer: new import_rtc_ffi_bindings2.DataStream_Trailer({
|
|
355
|
-
streamId,
|
|
356
|
-
reason: ""
|
|
357
|
-
})
|
|
358
|
-
});
|
|
359
|
-
await sendTrailer(trailerReq);
|
|
360
|
-
},
|
|
361
|
-
// Send a trailer with the error reason so the remote side's stream
|
|
343
|
+
close: () => closeWriter(),
|
|
344
|
+
// Close the stream with the error reason so the remote side's stream
|
|
362
345
|
// controller is closed instead of waiting for data that won't arrive.
|
|
363
|
-
async
|
|
346
|
+
abort: async (err) => {
|
|
364
347
|
import_log.log.error(err, "Sink error");
|
|
365
348
|
try {
|
|
366
|
-
|
|
367
|
-
senderIdentity,
|
|
368
|
-
localParticipantHandle: localHandle,
|
|
369
|
-
destinationIdentities,
|
|
370
|
-
trailer: new import_rtc_ffi_bindings2.DataStream_Trailer({
|
|
371
|
-
streamId,
|
|
372
|
-
reason: err instanceof Error ? err.message : String(err ?? "")
|
|
373
|
-
})
|
|
374
|
-
});
|
|
375
|
-
await sendTrailer(trailerReq);
|
|
349
|
+
await closeWriter(err instanceof Error ? err.message : String(err ?? ""));
|
|
376
350
|
} catch {
|
|
377
351
|
}
|
|
378
352
|
}
|
|
@@ -382,41 +356,49 @@ class LocalParticipant extends Participant {
|
|
|
382
356
|
}
|
|
383
357
|
/** Sends a file provided as PathLike to specified recipients */
|
|
384
358
|
async sendFile(path, options) {
|
|
385
|
-
const
|
|
386
|
-
const
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
});
|
|
400
|
-
for await (const chunk of stream) {
|
|
401
|
-
await writer.write(chunk);
|
|
402
|
-
}
|
|
403
|
-
await writer.close();
|
|
404
|
-
} finally {
|
|
405
|
-
await file.close();
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
async sendStreamHeader(req) {
|
|
409
|
-
const type = "sendStreamHeader";
|
|
359
|
+
const filePath = path instanceof URL ? (0, import_node_url.fileURLToPath)(path) : path.toString();
|
|
360
|
+
const req = new import_rtc_ffi_bindings2.StreamSendFileRequest({
|
|
361
|
+
localParticipantHandle: this.ffi_handle.handle,
|
|
362
|
+
options: new import_rtc_ffi_bindings2.StreamByteOptions({
|
|
363
|
+
topic: (options == null ? void 0 : options.topic) ?? "",
|
|
364
|
+
attributes: options == null ? void 0 : options.attributes,
|
|
365
|
+
destinationIdentities: options == null ? void 0 : options.destinationIdentities,
|
|
366
|
+
name: (options == null ? void 0 : options.name) ?? "unknown",
|
|
367
|
+
mimeType: (options == null ? void 0 : options.mimeType) ?? "application/octet-stream",
|
|
368
|
+
senderIdentity: this.identity,
|
|
369
|
+
compress: options == null ? void 0 : options.compress
|
|
370
|
+
}),
|
|
371
|
+
filePath
|
|
372
|
+
});
|
|
410
373
|
const res = import_ffi_client.FfiClient.instance.request({
|
|
411
|
-
message: { case:
|
|
374
|
+
message: { case: "sendFile", value: req }
|
|
412
375
|
});
|
|
413
376
|
const cb = await import_ffi_client.FfiClient.instance.waitFor(
|
|
414
|
-
(ev) => ev.message.case ==
|
|
377
|
+
(ev) => ev.message.case == "sendFile" && ev.message.value.asyncId == res.asyncId,
|
|
415
378
|
{ signal: this.disconnectSignal }
|
|
416
379
|
);
|
|
417
|
-
if (cb.error) {
|
|
418
|
-
throw new Error(cb.
|
|
380
|
+
if (cb.result.case === "error") {
|
|
381
|
+
throw new Error(cb.result.value.description);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Drops the native writers of any data streams that were opened but never
|
|
386
|
+
* closed — a writer the caller abandoned, or one whose write failed. Their
|
|
387
|
+
* handles would otherwise be held by the FFI server for the lifetime of the
|
|
388
|
+
* process. The room is already gone at this point, so the handles are dropped
|
|
389
|
+
* directly rather than closed: there is no end-of-stream left to deliver.
|
|
390
|
+
*
|
|
391
|
+
* @internal
|
|
392
|
+
*/
|
|
393
|
+
disposeOpenStreamWriters() {
|
|
394
|
+
for (const writerHandle of this.openStreamWriters) {
|
|
395
|
+
try {
|
|
396
|
+
new import_ffi_client.FfiHandle(writerHandle).dispose();
|
|
397
|
+
} catch (err) {
|
|
398
|
+
import_log.log.warn("failed to dispose data stream writer handle: %s", err);
|
|
399
|
+
}
|
|
419
400
|
}
|
|
401
|
+
this.openStreamWriters.clear();
|
|
420
402
|
}
|
|
421
403
|
/**
|
|
422
404
|
* Sends a chat message to participants in the room
|