@fishjam-cloud/composition 0.29.0-rc.1 → 0.29.0
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/chunk-XAZDKX25.mjs +301 -0
- package/dist/core.d.mts +84 -0
- package/dist/core.d.ts +84 -0
- package/dist/core.js +327 -0
- package/dist/core.mjs +6 -0
- package/dist/index.d.mts +30 -1
- package/dist/index.d.ts +30 -1
- package/dist/index.js +336 -2
- package/dist/index.mjs +34 -2
- package/dist/types-LgEdO1AS.d.mts +45 -0
- package/dist/types-LgEdO1AS.d.ts +45 -0
- package/package.json +23 -6
package/dist/index.js
CHANGED
|
@@ -20,11 +20,345 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
-
eventBus: () => eventBus
|
|
23
|
+
eventBus: () => eventBus,
|
|
24
|
+
usePeer: () => usePeer,
|
|
25
|
+
usePeers: () => usePeers,
|
|
26
|
+
useRoom: () => useRoom,
|
|
27
|
+
useSpeakingState: () => useSpeakingState
|
|
24
28
|
});
|
|
25
29
|
module.exports = __toCommonJS(index_exports);
|
|
30
|
+
|
|
31
|
+
// src/hooks.ts
|
|
32
|
+
var import_react = require("react");
|
|
33
|
+
|
|
34
|
+
// src/store.ts
|
|
35
|
+
var EMPTY_SNAPSHOT = { peers: [], vad: {} };
|
|
36
|
+
var assertNever = (event) => {
|
|
37
|
+
throw new Error(`Unhandled composition event: ${JSON.stringify(event)}`);
|
|
38
|
+
};
|
|
39
|
+
var normalizeMetadata = (raw) => {
|
|
40
|
+
if (raw == null) return {};
|
|
41
|
+
if (typeof raw === "string") {
|
|
42
|
+
try {
|
|
43
|
+
const parsed = JSON.parse(raw);
|
|
44
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
45
|
+
} catch {
|
|
46
|
+
return {};
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return raw;
|
|
50
|
+
};
|
|
51
|
+
var splitMetadata = (raw) => {
|
|
52
|
+
const obj = normalizeMetadata(raw);
|
|
53
|
+
return { peer: obj.peer, server: obj.server };
|
|
54
|
+
};
|
|
55
|
+
var roleOf = (stream) => {
|
|
56
|
+
const type = stream.video?.metadata.type ?? stream.audio?.metadata.type;
|
|
57
|
+
if (type === "camera" || type === "microphone") return "camera";
|
|
58
|
+
if (type === "screenShareVideo" || type === "screenShareAudio") return "screenShare";
|
|
59
|
+
return "custom";
|
|
60
|
+
};
|
|
61
|
+
var CompositionStore = class {
|
|
62
|
+
peers = /* @__PURE__ */ new Map();
|
|
63
|
+
roomId;
|
|
64
|
+
vad = /* @__PURE__ */ new Map();
|
|
65
|
+
listeners = /* @__PURE__ */ new Set();
|
|
66
|
+
cachedSnapshot = EMPTY_SNAPSHOT;
|
|
67
|
+
cachedPeers = EMPTY_SNAPSHOT.peers;
|
|
68
|
+
subscribe = (cb) => {
|
|
69
|
+
this.listeners.add(cb);
|
|
70
|
+
return () => this.listeners.delete(cb);
|
|
71
|
+
};
|
|
72
|
+
getSnapshot = () => {
|
|
73
|
+
if (this.cachedSnapshot === null) {
|
|
74
|
+
this.cachedSnapshot = { peers: this.cachedPeers, roomId: this.roomId, vad: this.vadRecord() };
|
|
75
|
+
}
|
|
76
|
+
return this.cachedSnapshot;
|
|
77
|
+
};
|
|
78
|
+
getPeers = () => this.cachedPeers;
|
|
79
|
+
getPeer = (peerId) => this.getPeers().find((peer) => peer.id === peerId);
|
|
80
|
+
getRoomId = () => this.roomId;
|
|
81
|
+
getVadStatus = (peerId) => {
|
|
82
|
+
const peer = this.peers.get(peerId);
|
|
83
|
+
if (!peer) return "silence";
|
|
84
|
+
for (const track of peer.tracks.values())
|
|
85
|
+
if (track.type === "audio" && track.inputId && this.vad.get(track.inputId) === "speech") return "speech";
|
|
86
|
+
return "silence";
|
|
87
|
+
};
|
|
88
|
+
reset() {
|
|
89
|
+
this.peers.clear();
|
|
90
|
+
this.vad.clear();
|
|
91
|
+
this.roomId = void 0;
|
|
92
|
+
this.cachedSnapshot = null;
|
|
93
|
+
this.cachedPeers = [];
|
|
94
|
+
this.commit();
|
|
95
|
+
}
|
|
96
|
+
seedFromRoom(room) {
|
|
97
|
+
this.peers.clear();
|
|
98
|
+
this.vad.clear();
|
|
99
|
+
this.roomId = room.id;
|
|
100
|
+
for (const peer of room.peers) {
|
|
101
|
+
const internal = {
|
|
102
|
+
id: peer.id,
|
|
103
|
+
metadata: splitMetadata(peer.metadata),
|
|
104
|
+
tracks: /* @__PURE__ */ new Map()
|
|
105
|
+
};
|
|
106
|
+
for (const track of peer.tracks ?? []) {
|
|
107
|
+
if (!track.id) continue;
|
|
108
|
+
internal.tracks.set(track.id, { id: track.id, metadata: normalizeMetadata(track.metadata), type: track.type });
|
|
109
|
+
}
|
|
110
|
+
this.peers.set(peer.id, internal);
|
|
111
|
+
}
|
|
112
|
+
this.rebuildPeers();
|
|
113
|
+
this.commit();
|
|
114
|
+
}
|
|
115
|
+
applyNotification(event) {
|
|
116
|
+
if (event.data.roomId !== this.roomId) return;
|
|
117
|
+
let changed;
|
|
118
|
+
switch (event.type) {
|
|
119
|
+
case "peerConnected":
|
|
120
|
+
changed = this.onPeerConnected(event.data);
|
|
121
|
+
break;
|
|
122
|
+
case "peerDisconnected":
|
|
123
|
+
changed = this.onPeerDisconnected(event.data);
|
|
124
|
+
break;
|
|
125
|
+
case "peerMetadataUpdated":
|
|
126
|
+
changed = this.onPeerMetadataUpdated(event.data);
|
|
127
|
+
break;
|
|
128
|
+
case "trackAdded":
|
|
129
|
+
changed = this.onTrackAdded(event.data);
|
|
130
|
+
break;
|
|
131
|
+
case "trackRemoved":
|
|
132
|
+
changed = this.onTrackRemoved(event.data);
|
|
133
|
+
break;
|
|
134
|
+
case "trackMetadataUpdated":
|
|
135
|
+
changed = this.onTrackMetadataUpdated(event.data);
|
|
136
|
+
break;
|
|
137
|
+
case "trackForwarding":
|
|
138
|
+
changed = this.onTrackForwarding(event.data);
|
|
139
|
+
break;
|
|
140
|
+
case "trackForwardingRemoved":
|
|
141
|
+
changed = this.onTrackForwardingRemoved(event.data);
|
|
142
|
+
break;
|
|
143
|
+
case "vadNotification":
|
|
144
|
+
changed = this.onVadNotification(event.data);
|
|
145
|
+
break;
|
|
146
|
+
default:
|
|
147
|
+
return assertNever(event);
|
|
148
|
+
}
|
|
149
|
+
if (changed) this.commit();
|
|
150
|
+
}
|
|
151
|
+
ensurePeer(peerId) {
|
|
152
|
+
let peer = this.peers.get(peerId);
|
|
153
|
+
if (!peer) {
|
|
154
|
+
peer = { id: peerId, metadata: { peer: void 0, server: void 0 }, tracks: /* @__PURE__ */ new Map() };
|
|
155
|
+
this.peers.set(peerId, peer);
|
|
156
|
+
}
|
|
157
|
+
return peer;
|
|
158
|
+
}
|
|
159
|
+
onPeerConnected(data) {
|
|
160
|
+
if (this.peers.has(data.peerId)) return false;
|
|
161
|
+
this.ensurePeer(data.peerId);
|
|
162
|
+
this.replacePeer(data.peerId);
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
onPeerDisconnected(data) {
|
|
166
|
+
const peer = this.peers.get(data.peerId);
|
|
167
|
+
if (!peer) return false;
|
|
168
|
+
for (const { inputId } of peer.tracks.values()) if (inputId) this.vad.delete(inputId);
|
|
169
|
+
this.peers.delete(data.peerId);
|
|
170
|
+
this.replacePeer(data.peerId);
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
onPeerMetadataUpdated(data) {
|
|
174
|
+
this.ensurePeer(data.peerId).metadata = splitMetadata(data.metadata);
|
|
175
|
+
this.replacePeer(data.peerId);
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
onTrackAdded(data) {
|
|
179
|
+
if (!data.peerId || !data.track) return false;
|
|
180
|
+
const peer = this.ensurePeer(data.peerId);
|
|
181
|
+
peer.tracks.set(data.track.id, {
|
|
182
|
+
id: data.track.id,
|
|
183
|
+
metadata: normalizeMetadata(data.track.metadata),
|
|
184
|
+
type: data.track.type
|
|
185
|
+
});
|
|
186
|
+
this.replacePeer(data.peerId);
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
onTrackMetadataUpdated(data) {
|
|
190
|
+
if (!data.peerId || !data.track) return false;
|
|
191
|
+
const peer = this.peers.get(data.peerId);
|
|
192
|
+
if (!peer) return false;
|
|
193
|
+
const metadata = normalizeMetadata(data.track.metadata);
|
|
194
|
+
const inputId = peer.tracks.get(data.track.id)?.inputId;
|
|
195
|
+
peer.tracks.set(data.track.id, { id: data.track.id, metadata, type: data.track.type, inputId });
|
|
196
|
+
this.replacePeer(data.peerId);
|
|
197
|
+
return true;
|
|
198
|
+
}
|
|
199
|
+
onTrackRemoved(data) {
|
|
200
|
+
if (!data.peerId || !data.track) return false;
|
|
201
|
+
const peer = this.peers.get(data.peerId);
|
|
202
|
+
if (!peer) return false;
|
|
203
|
+
const track = peer.tracks.get(data.track.id);
|
|
204
|
+
if (!track) return false;
|
|
205
|
+
peer.tracks.delete(data.track.id);
|
|
206
|
+
if (track.type === "audio" && track.inputId) this.vad.delete(track.inputId);
|
|
207
|
+
this.replacePeer(data.peerId);
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
onTrackForwarding(data) {
|
|
211
|
+
const peer = this.ensurePeer(data.peerId);
|
|
212
|
+
if (data.videoTrack) {
|
|
213
|
+
peer.tracks.set(data.videoTrack.id, {
|
|
214
|
+
id: data.videoTrack.id,
|
|
215
|
+
metadata: normalizeMetadata(data.videoTrack.metadata),
|
|
216
|
+
inputId: data.inputId,
|
|
217
|
+
type: data.videoTrack.type
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
if (data.audioTrack) {
|
|
221
|
+
peer.tracks.set(data.audioTrack.id, {
|
|
222
|
+
id: data.audioTrack.id,
|
|
223
|
+
metadata: normalizeMetadata(data.audioTrack.metadata),
|
|
224
|
+
inputId: data.inputId,
|
|
225
|
+
type: data.audioTrack.type
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
this.replacePeer(data.peerId);
|
|
229
|
+
return true;
|
|
230
|
+
}
|
|
231
|
+
onTrackForwardingRemoved(data) {
|
|
232
|
+
let changed = this.vad.delete(data.inputId);
|
|
233
|
+
const peer = this.peers.get(data.peerId);
|
|
234
|
+
if (!peer) {
|
|
235
|
+
if (changed) this.cachedSnapshot = null;
|
|
236
|
+
return changed;
|
|
237
|
+
}
|
|
238
|
+
for (const track of peer.tracks.values()) {
|
|
239
|
+
if (track.inputId === data.inputId) {
|
|
240
|
+
track.inputId = void 0;
|
|
241
|
+
changed = true;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (changed) this.replacePeer(data.peerId);
|
|
245
|
+
return changed;
|
|
246
|
+
}
|
|
247
|
+
onVadNotification(data) {
|
|
248
|
+
const peer = this.peers.get(data.peerId);
|
|
249
|
+
if (!peer) return false;
|
|
250
|
+
const track = peer.tracks.get(data.trackId);
|
|
251
|
+
if (!track || !track.inputId) return false;
|
|
252
|
+
if (this.vad.get(track.inputId) === data.status) return false;
|
|
253
|
+
this.vad.set(track.inputId, data.status);
|
|
254
|
+
this.cachedSnapshot = null;
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
commit() {
|
|
258
|
+
for (const cb of this.listeners) cb();
|
|
259
|
+
}
|
|
260
|
+
replacePeer(peerId) {
|
|
261
|
+
const internal = this.peers.get(peerId);
|
|
262
|
+
const next = this.cachedPeers.slice();
|
|
263
|
+
const idx = next.findIndex((peer) => peer.id === peerId);
|
|
264
|
+
if (!internal) {
|
|
265
|
+
if (idx >= 0) next.splice(idx, 1);
|
|
266
|
+
} else if (idx >= 0) {
|
|
267
|
+
next[idx] = this.derivePeer(internal);
|
|
268
|
+
} else {
|
|
269
|
+
next.push(this.derivePeer(internal));
|
|
270
|
+
}
|
|
271
|
+
this.cachedPeers = next;
|
|
272
|
+
this.cachedSnapshot = null;
|
|
273
|
+
}
|
|
274
|
+
rebuildPeers() {
|
|
275
|
+
this.cachedPeers = Array.from(this.peers.values(), (peer) => this.derivePeer(peer));
|
|
276
|
+
this.cachedSnapshot = null;
|
|
277
|
+
}
|
|
278
|
+
vadRecord() {
|
|
279
|
+
const vad = {};
|
|
280
|
+
for (const [inputId, status] of this.vad) vad[inputId] = status;
|
|
281
|
+
return vad;
|
|
282
|
+
}
|
|
283
|
+
derivePeer(peer) {
|
|
284
|
+
const streams = /* @__PURE__ */ new Map();
|
|
285
|
+
let cameraStream;
|
|
286
|
+
let screenShareStream;
|
|
287
|
+
const customStreams = [];
|
|
288
|
+
for (const track of peer.tracks.values()) {
|
|
289
|
+
if (!track.inputId) continue;
|
|
290
|
+
const stream = streams.get(track.inputId) || { inputId: track.inputId };
|
|
291
|
+
if (track.type === "audio")
|
|
292
|
+
stream.audio = {
|
|
293
|
+
id: track.id,
|
|
294
|
+
paused: Boolean(track.metadata.paused),
|
|
295
|
+
metadata: track.metadata,
|
|
296
|
+
type: "audio"
|
|
297
|
+
};
|
|
298
|
+
else
|
|
299
|
+
stream.video = {
|
|
300
|
+
id: track.id,
|
|
301
|
+
paused: Boolean(track.metadata.paused),
|
|
302
|
+
metadata: track.metadata,
|
|
303
|
+
type: "video"
|
|
304
|
+
};
|
|
305
|
+
streams.set(track.inputId, stream);
|
|
306
|
+
}
|
|
307
|
+
for (const stream of streams.values()) {
|
|
308
|
+
const role = roleOf(stream);
|
|
309
|
+
switch (role) {
|
|
310
|
+
case "camera":
|
|
311
|
+
cameraStream = stream;
|
|
312
|
+
break;
|
|
313
|
+
case "screenShare":
|
|
314
|
+
screenShareStream = stream;
|
|
315
|
+
break;
|
|
316
|
+
default:
|
|
317
|
+
customStreams.push(stream);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return {
|
|
321
|
+
id: peer.id,
|
|
322
|
+
metadata: peer.metadata,
|
|
323
|
+
streams: Array.from(streams.values()),
|
|
324
|
+
cameraStream,
|
|
325
|
+
screenShareStream,
|
|
326
|
+
customStreams
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
var compositionStore = new CompositionStore();
|
|
331
|
+
|
|
332
|
+
// src/hooks.ts
|
|
333
|
+
function usePeers() {
|
|
334
|
+
const peers = (0, import_react.useSyncExternalStore)(compositionStore.subscribe, compositionStore.getPeers, compositionStore.getPeers);
|
|
335
|
+
return peers;
|
|
336
|
+
}
|
|
337
|
+
function usePeer(peerId) {
|
|
338
|
+
const getSnapshot = (0, import_react.useCallback)(() => compositionStore.getPeer(peerId), [peerId]);
|
|
339
|
+
const peer = (0, import_react.useSyncExternalStore)(compositionStore.subscribe, getSnapshot, getSnapshot);
|
|
340
|
+
return peer;
|
|
341
|
+
}
|
|
342
|
+
function useRoom() {
|
|
343
|
+
const roomId = (0, import_react.useSyncExternalStore)(
|
|
344
|
+
compositionStore.subscribe,
|
|
345
|
+
compositionStore.getRoomId,
|
|
346
|
+
compositionStore.getRoomId
|
|
347
|
+
);
|
|
348
|
+
return roomId ? { id: roomId } : void 0;
|
|
349
|
+
}
|
|
350
|
+
function useSpeakingState(peerId) {
|
|
351
|
+
const getSnapshot = (0, import_react.useCallback)(() => compositionStore.getVadStatus(peerId), [peerId]);
|
|
352
|
+
return (0, import_react.useSyncExternalStore)(compositionStore.subscribe, getSnapshot, getSnapshot);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// src/eventBus.ts
|
|
26
356
|
var eventBus = globalThis.eventBus;
|
|
27
357
|
// Annotate the CommonJS export names for ESM import in node:
|
|
28
358
|
0 && (module.exports = {
|
|
29
|
-
eventBus
|
|
359
|
+
eventBus,
|
|
360
|
+
usePeer,
|
|
361
|
+
usePeers,
|
|
362
|
+
useRoom,
|
|
363
|
+
useSpeakingState
|
|
30
364
|
});
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
1
|
-
|
|
1
|
+
import {
|
|
2
|
+
compositionStore
|
|
3
|
+
} from "./chunk-XAZDKX25.mjs";
|
|
4
|
+
|
|
5
|
+
// src/hooks.ts
|
|
6
|
+
import { useCallback, useSyncExternalStore } from "react";
|
|
7
|
+
function usePeers() {
|
|
8
|
+
const peers = useSyncExternalStore(compositionStore.subscribe, compositionStore.getPeers, compositionStore.getPeers);
|
|
9
|
+
return peers;
|
|
10
|
+
}
|
|
11
|
+
function usePeer(peerId) {
|
|
12
|
+
const getSnapshot = useCallback(() => compositionStore.getPeer(peerId), [peerId]);
|
|
13
|
+
const peer = useSyncExternalStore(compositionStore.subscribe, getSnapshot, getSnapshot);
|
|
14
|
+
return peer;
|
|
15
|
+
}
|
|
16
|
+
function useRoom() {
|
|
17
|
+
const roomId = useSyncExternalStore(
|
|
18
|
+
compositionStore.subscribe,
|
|
19
|
+
compositionStore.getRoomId,
|
|
20
|
+
compositionStore.getRoomId
|
|
21
|
+
);
|
|
22
|
+
return roomId ? { id: roomId } : void 0;
|
|
23
|
+
}
|
|
24
|
+
function useSpeakingState(peerId) {
|
|
25
|
+
const getSnapshot = useCallback(() => compositionStore.getVadStatus(peerId), [peerId]);
|
|
26
|
+
return useSyncExternalStore(compositionStore.subscribe, getSnapshot, getSnapshot);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// src/eventBus.ts
|
|
2
30
|
var eventBus = globalThis.eventBus;
|
|
3
31
|
export {
|
|
4
|
-
eventBus
|
|
32
|
+
eventBus,
|
|
33
|
+
usePeer,
|
|
34
|
+
usePeers,
|
|
35
|
+
useRoom,
|
|
36
|
+
useSpeakingState
|
|
5
37
|
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State of a single fishjam track.
|
|
3
|
+
* The `paused` flag mirrors the track's mute state.
|
|
4
|
+
*/
|
|
5
|
+
type TrackState = {
|
|
6
|
+
/** fishjam track id */
|
|
7
|
+
id: string;
|
|
8
|
+
/** mute state, read from the built-in `paused` metadata key */
|
|
9
|
+
paused: boolean;
|
|
10
|
+
metadata: Record<string, unknown>;
|
|
11
|
+
};
|
|
12
|
+
type VideoTrackState = TrackState & {
|
|
13
|
+
type: 'video';
|
|
14
|
+
};
|
|
15
|
+
type AudioTrackState = TrackState & {
|
|
16
|
+
type: 'audio';
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* A forwarded input.
|
|
20
|
+
* `inputId` is the handle a template passes to `<InputStream inputId={...} />`.
|
|
21
|
+
* A stream carries at most one video and one audio track.
|
|
22
|
+
*/
|
|
23
|
+
type Stream = {
|
|
24
|
+
inputId: string;
|
|
25
|
+
video?: VideoTrackState;
|
|
26
|
+
audio?: AudioTrackState;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* A room peer projected for composition templates.
|
|
30
|
+
* Streams are split by role: camera, screen share, or custom.
|
|
31
|
+
* The camera stream's audio is the peer's mic.
|
|
32
|
+
*/
|
|
33
|
+
type PeerWithStreams<PeerMetadata = unknown, ServerMetadata = unknown> = {
|
|
34
|
+
id: string;
|
|
35
|
+
metadata: {
|
|
36
|
+
peer: PeerMetadata;
|
|
37
|
+
server: ServerMetadata;
|
|
38
|
+
};
|
|
39
|
+
streams: Stream[];
|
|
40
|
+
cameraStream?: Stream;
|
|
41
|
+
screenShareStream?: Stream;
|
|
42
|
+
customStreams: Stream[];
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export type { AudioTrackState as A, PeerWithStreams as P, Stream as S, TrackState as T, VideoTrackState as V };
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State of a single fishjam track.
|
|
3
|
+
* The `paused` flag mirrors the track's mute state.
|
|
4
|
+
*/
|
|
5
|
+
type TrackState = {
|
|
6
|
+
/** fishjam track id */
|
|
7
|
+
id: string;
|
|
8
|
+
/** mute state, read from the built-in `paused` metadata key */
|
|
9
|
+
paused: boolean;
|
|
10
|
+
metadata: Record<string, unknown>;
|
|
11
|
+
};
|
|
12
|
+
type VideoTrackState = TrackState & {
|
|
13
|
+
type: 'video';
|
|
14
|
+
};
|
|
15
|
+
type AudioTrackState = TrackState & {
|
|
16
|
+
type: 'audio';
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* A forwarded input.
|
|
20
|
+
* `inputId` is the handle a template passes to `<InputStream inputId={...} />`.
|
|
21
|
+
* A stream carries at most one video and one audio track.
|
|
22
|
+
*/
|
|
23
|
+
type Stream = {
|
|
24
|
+
inputId: string;
|
|
25
|
+
video?: VideoTrackState;
|
|
26
|
+
audio?: AudioTrackState;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* A room peer projected for composition templates.
|
|
30
|
+
* Streams are split by role: camera, screen share, or custom.
|
|
31
|
+
* The camera stream's audio is the peer's mic.
|
|
32
|
+
*/
|
|
33
|
+
type PeerWithStreams<PeerMetadata = unknown, ServerMetadata = unknown> = {
|
|
34
|
+
id: string;
|
|
35
|
+
metadata: {
|
|
36
|
+
peer: PeerMetadata;
|
|
37
|
+
server: ServerMetadata;
|
|
38
|
+
};
|
|
39
|
+
streams: Stream[];
|
|
40
|
+
cameraStream?: Stream;
|
|
41
|
+
screenShareStream?: Stream;
|
|
42
|
+
customStreams: Stream[];
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export type { AudioTrackState as A, PeerWithStreams as P, Stream as S, TrackState as T, VideoTrackState as V };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fishjam-cloud/composition",
|
|
3
|
-
"version": "0.29.0
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.29.0",
|
|
4
|
+
"description": "React hooks and event bus for building Fishjam composition templates",
|
|
5
5
|
"homepage": "https://github.com/fishjam-cloud/js-server-sdk",
|
|
6
6
|
"author": "Fishjam Team",
|
|
7
7
|
"repository": {
|
|
@@ -32,10 +32,15 @@
|
|
|
32
32
|
"types": "./dist/index.d.ts",
|
|
33
33
|
"import": "./dist/index.mjs",
|
|
34
34
|
"require": "./dist/index.js"
|
|
35
|
+
},
|
|
36
|
+
"./core": {
|
|
37
|
+
"types": "./dist/core.d.ts",
|
|
38
|
+
"import": "./dist/core.mjs",
|
|
39
|
+
"require": "./dist/core.js"
|
|
35
40
|
}
|
|
36
41
|
},
|
|
37
42
|
"scripts": {
|
|
38
|
-
"build": "tsup
|
|
43
|
+
"build": "tsup",
|
|
39
44
|
"format": "prettier --write .",
|
|
40
45
|
"format:check": "prettier --check .",
|
|
41
46
|
"typecheck": "tsc --noEmit",
|
|
@@ -45,18 +50,30 @@
|
|
|
45
50
|
},
|
|
46
51
|
"tsup": {
|
|
47
52
|
"entry": [
|
|
48
|
-
"src/index.ts"
|
|
53
|
+
"src/index.ts",
|
|
54
|
+
"src/core.ts"
|
|
49
55
|
],
|
|
50
56
|
"minify": false,
|
|
51
57
|
"format": [
|
|
52
58
|
"cjs",
|
|
53
59
|
"esm"
|
|
54
60
|
],
|
|
55
|
-
"outDir": "dist"
|
|
61
|
+
"outDir": "dist",
|
|
62
|
+
"dts": true,
|
|
63
|
+
"clean": true
|
|
64
|
+
},
|
|
65
|
+
"peerDependencies": {
|
|
66
|
+
"@fishjam-cloud/js-server-sdk": "^0.29.0",
|
|
67
|
+
"react": ">=18"
|
|
56
68
|
},
|
|
57
69
|
"devDependencies": {
|
|
70
|
+
"@fishjam-cloud/js-server-sdk": "0.29.0",
|
|
71
|
+
"@types/react": "^19.0.0",
|
|
72
|
+
"@types/react-dom": "^19.1.9",
|
|
58
73
|
"eslint": "^9.33.0",
|
|
59
74
|
"prettier": "^3.6.2",
|
|
75
|
+
"react": "^19.0.0",
|
|
76
|
+
"react-dom": "^19.0.0",
|
|
60
77
|
"tsup": "^8.4.0",
|
|
61
78
|
"vitest": "^3.0.0"
|
|
62
79
|
},
|
|
@@ -65,4 +82,4 @@
|
|
|
65
82
|
"eslint --fix --config eslint.config.mjs"
|
|
66
83
|
]
|
|
67
84
|
}
|
|
68
|
-
}
|
|
85
|
+
}
|