@meshagent/meshagent 0.3.1 → 0.4.1
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/CHANGELOG.md +6 -0
- package/dist/browser/entrypoint.js +7956 -5611
- package/dist/browser/helpers.d.ts +10 -4
- package/dist/browser/helpers.js +14 -39
- package/dist/browser/index.d.ts +1 -0
- package/dist/browser/index.js +1 -0
- package/dist/browser/participant-token.d.ts +4 -4
- package/dist/browser/participant-token.js +4 -9
- package/dist/browser/protocol.d.ts +5 -1
- package/dist/browser/protocol.js +7 -2
- package/dist/browser/room-client.d.ts +4 -1
- package/dist/browser/room-client.js +2 -2
- package/dist/browser/sync-client.d.ts +5 -1
- package/dist/browser/sync-client.js +13 -2
- package/dist/esm/agent-client.d.ts +88 -0
- package/dist/esm/agent-client.js +166 -0
- package/dist/esm/agent.d.ts +103 -0
- package/dist/esm/agent.js +218 -0
- package/dist/esm/client.d.ts +90 -0
- package/dist/esm/client.js +443 -0
- package/dist/esm/completer.d.ts +9 -0
- package/dist/esm/completer.js +21 -0
- package/dist/esm/data-types.d.ts +44 -0
- package/dist/esm/data-types.js +110 -0
- package/dist/esm/database-client.d.ts +77 -0
- package/dist/esm/database-client.js +109 -0
- package/dist/esm/developer-client.d.ts +13 -0
- package/dist/esm/developer-client.js +31 -0
- package/dist/esm/document.d.ts +84 -0
- package/dist/esm/document.js +522 -0
- package/dist/esm/entrypoint.d.ts +49722 -0
- package/dist/esm/entrypoint.js +6313 -0
- package/dist/esm/event-emitter.d.ts +13 -0
- package/dist/esm/event-emitter.js +34 -0
- package/dist/esm/helpers.d.ts +32 -0
- package/dist/esm/helpers.js +46 -0
- package/dist/esm/index.d.ts +25 -0
- package/dist/esm/index.js +25 -0
- package/dist/esm/messaging-client.d.ts +76 -0
- package/dist/esm/messaging-client.js +241 -0
- package/dist/esm/participant-token.d.ts +36 -0
- package/dist/esm/participant-token.js +91 -0
- package/dist/esm/participant.d.ts +18 -0
- package/dist/esm/participant.js +36 -0
- package/dist/esm/protocol.d.ts +91 -0
- package/dist/esm/protocol.js +287 -0
- package/dist/esm/queues-client.d.ts +26 -0
- package/dist/esm/queues-client.js +42 -0
- package/dist/esm/requirement.d.ts +25 -0
- package/dist/esm/requirement.js +42 -0
- package/dist/esm/response.d.ts +60 -0
- package/dist/esm/response.js +128 -0
- package/dist/esm/room-client.d.ts +46 -0
- package/dist/esm/room-client.js +106 -0
- package/dist/esm/room-event.d.ts +60 -0
- package/dist/esm/room-event.js +72 -0
- package/dist/esm/room-server-client.d.ts +19 -0
- package/dist/esm/room-server-client.js +45 -0
- package/dist/esm/runtime.d.ts +6 -0
- package/dist/esm/runtime.js +1 -0
- package/dist/esm/schema.d.ts +83 -0
- package/dist/esm/schema.js +312 -0
- package/dist/esm/storage-client.d.ts +38 -0
- package/dist/esm/storage-client.js +79 -0
- package/dist/esm/stream-controller.d.ts +8 -0
- package/dist/esm/stream-controller.js +51 -0
- package/dist/esm/sync-client.d.ts +37 -0
- package/dist/esm/sync-client.js +125 -0
- package/dist/esm/utils.d.ts +14 -0
- package/dist/esm/utils.js +44 -0
- package/dist/node/entrypoint.js +9 -4
- package/dist/node/helpers.d.ts +10 -4
- package/dist/node/helpers.js +14 -39
- package/dist/node/index.d.ts +1 -0
- package/dist/node/index.js +1 -0
- package/dist/node/participant-token.d.ts +4 -4
- package/dist/node/participant-token.js +4 -9
- package/dist/node/protocol.d.ts +5 -1
- package/dist/node/protocol.js +7 -2
- package/dist/node/room-client.d.ts +4 -1
- package/dist/node/room-client.js +2 -2
- package/dist/node/sync-client.d.ts +5 -1
- package/dist/node/sync-client.js +13 -2
- package/package.json +4 -3
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type EventHandler<T> = (event: T) => void;
|
|
2
|
+
export type EventName = string | symbol;
|
|
3
|
+
export declare class EventEmitter<T> {
|
|
4
|
+
private eventMap;
|
|
5
|
+
constructor();
|
|
6
|
+
addListener: (eventName: EventName, callback: EventHandler<T>) => void;
|
|
7
|
+
removeListener: (eventName: EventName, callback: EventHandler<T>) => void;
|
|
8
|
+
notifyListeners: (eventName: EventName, event: T) => void;
|
|
9
|
+
on: (eventName: EventName, callback: EventHandler<T>) => void;
|
|
10
|
+
off: (eventName: EventName, callback: EventHandler<T>) => void;
|
|
11
|
+
emit: (eventName: EventName, event: T) => void;
|
|
12
|
+
dispose(): void;
|
|
13
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export class EventEmitter {
|
|
2
|
+
constructor() {
|
|
3
|
+
this.addListener = (eventName, callback) => {
|
|
4
|
+
const listeners = this.eventMap.get(eventName);
|
|
5
|
+
if (listeners) {
|
|
6
|
+
listeners.push(callback);
|
|
7
|
+
}
|
|
8
|
+
else {
|
|
9
|
+
this.eventMap.set(eventName, [callback]);
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
this.removeListener = (eventName, callback) => {
|
|
13
|
+
const listeners = this.eventMap.get(eventName) || [];
|
|
14
|
+
this.eventMap.set(eventName, listeners.filter((listener) => listener !== callback));
|
|
15
|
+
};
|
|
16
|
+
this.notifyListeners = (eventName, event) => {
|
|
17
|
+
const listeners = this.eventMap.get(eventName) || [];
|
|
18
|
+
listeners.forEach((listener) => listener(event));
|
|
19
|
+
};
|
|
20
|
+
this.on = (eventName, callback) => {
|
|
21
|
+
this.addListener(eventName, callback);
|
|
22
|
+
};
|
|
23
|
+
this.off = (eventName, callback) => {
|
|
24
|
+
this.removeListener(eventName, callback);
|
|
25
|
+
};
|
|
26
|
+
this.emit = (eventName, event) => {
|
|
27
|
+
this.notifyListeners(eventName, event);
|
|
28
|
+
};
|
|
29
|
+
this.eventMap = new Map();
|
|
30
|
+
}
|
|
31
|
+
dispose() {
|
|
32
|
+
this.eventMap.clear();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { MeshSchema } from './schema';
|
|
2
|
+
import { RoomClient } from './room-client';
|
|
3
|
+
import { ParticipantToken } from './participant-token';
|
|
4
|
+
import { WebSocketClientProtocol } from './protocol';
|
|
5
|
+
export declare function validateSchemaName(name: string): void;
|
|
6
|
+
export declare function deploySchema({ room, schema, name, overwrite }: {
|
|
7
|
+
room: RoomClient;
|
|
8
|
+
schema: MeshSchema;
|
|
9
|
+
name: string;
|
|
10
|
+
overwrite?: boolean;
|
|
11
|
+
}): Promise<void>;
|
|
12
|
+
export declare function meshagentBaseUrl(baseUrl?: string): string;
|
|
13
|
+
export declare function websocketRoomUrl({ roomName, apiUrl }: {
|
|
14
|
+
roomName: string;
|
|
15
|
+
apiUrl?: string;
|
|
16
|
+
}): string;
|
|
17
|
+
export declare function participantToken({ participantName, roomName, role, projectId, apiKeyId, }: {
|
|
18
|
+
participantName: string;
|
|
19
|
+
roomName: string;
|
|
20
|
+
role?: string;
|
|
21
|
+
projectId: string;
|
|
22
|
+
apiKeyId: string;
|
|
23
|
+
}): ParticipantToken;
|
|
24
|
+
export declare function websocketProtocol({ participantName, roomName, role, projectId, apiKeyId, secret, apiUrl }: {
|
|
25
|
+
participantName: string;
|
|
26
|
+
roomName: string;
|
|
27
|
+
role?: string;
|
|
28
|
+
projectId: string;
|
|
29
|
+
apiKeyId: string;
|
|
30
|
+
secret: string;
|
|
31
|
+
apiUrl?: string;
|
|
32
|
+
}): Promise<WebSocketClientProtocol>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { MeshSchemaValidationException } from './schema';
|
|
2
|
+
import { ParticipantToken } from './participant-token';
|
|
3
|
+
import { WebSocketClientProtocol } from './protocol';
|
|
4
|
+
export function validateSchemaName(name) {
|
|
5
|
+
if (name.includes('.')) {
|
|
6
|
+
throw new MeshSchemaValidationException("schema name cannot contain '.'");
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export async function deploySchema({ room, schema, name, overwrite = true }) {
|
|
10
|
+
validateSchemaName(name);
|
|
11
|
+
const handle = await room.storage.open(`.schemas/${name}.json`, { overwrite });
|
|
12
|
+
const data = Buffer.from(JSON.stringify(schema.toJson()), 'utf-8');
|
|
13
|
+
await room.storage.write(handle, data);
|
|
14
|
+
await room.storage.close(handle);
|
|
15
|
+
}
|
|
16
|
+
export function meshagentBaseUrl(baseUrl) {
|
|
17
|
+
if (baseUrl) {
|
|
18
|
+
return baseUrl;
|
|
19
|
+
}
|
|
20
|
+
return 'https://api.meshagent.com';
|
|
21
|
+
}
|
|
22
|
+
export function websocketRoomUrl({ roomName, apiUrl }) {
|
|
23
|
+
const baseUrl = apiUrl || 'wss://api.meshagent.com';
|
|
24
|
+
let url = baseUrl;
|
|
25
|
+
if (baseUrl.startsWith('https:')) {
|
|
26
|
+
url = 'wss:' + baseUrl.substring('https:'.length);
|
|
27
|
+
}
|
|
28
|
+
else if (baseUrl.startsWith('http:')) {
|
|
29
|
+
url = 'ws:' + baseUrl.substring('http:'.length);
|
|
30
|
+
}
|
|
31
|
+
return `${url}/rooms/${roomName}`;
|
|
32
|
+
}
|
|
33
|
+
export function participantToken({ participantName, roomName, role, projectId, apiKeyId, }) {
|
|
34
|
+
const token = new ParticipantToken({ name: participantName, projectId, apiKeyId });
|
|
35
|
+
token.addRoomGrant(roomName);
|
|
36
|
+
if (role) {
|
|
37
|
+
token.addRoleGrant(role);
|
|
38
|
+
}
|
|
39
|
+
return token;
|
|
40
|
+
}
|
|
41
|
+
export async function websocketProtocol({ participantName, roomName, role, projectId, apiKeyId, secret, apiUrl }) {
|
|
42
|
+
const url = websocketRoomUrl({ roomName, apiUrl });
|
|
43
|
+
const token = participantToken({ participantName, roomName, role, projectId, apiKeyId });
|
|
44
|
+
const jwt = await token.toJwt({ token: secret });
|
|
45
|
+
return new WebSocketClientProtocol({ url, token: jwt });
|
|
46
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export * from './agent';
|
|
2
|
+
export * from './event-emitter';
|
|
3
|
+
export * from './client';
|
|
4
|
+
export * from './completer';
|
|
5
|
+
export * from './data-types';
|
|
6
|
+
export * from './database-client';
|
|
7
|
+
export * from './developer-client';
|
|
8
|
+
export * from './document';
|
|
9
|
+
export * from './messaging-client';
|
|
10
|
+
export * from './participant-token';
|
|
11
|
+
export * from './participant';
|
|
12
|
+
export * from './protocol';
|
|
13
|
+
export * from './queues-client';
|
|
14
|
+
export * from './requirement';
|
|
15
|
+
export * from './response';
|
|
16
|
+
export * from './room-client';
|
|
17
|
+
export * from './room-event';
|
|
18
|
+
export * from './room-server-client';
|
|
19
|
+
export * from './runtime';
|
|
20
|
+
export * from './schema';
|
|
21
|
+
export * from './storage-client';
|
|
22
|
+
export * from './stream-controller';
|
|
23
|
+
export * from './sync-client';
|
|
24
|
+
export * from './helpers';
|
|
25
|
+
export * from './utils';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export * from './agent';
|
|
2
|
+
export * from './event-emitter';
|
|
3
|
+
export * from './client';
|
|
4
|
+
export * from './completer';
|
|
5
|
+
export * from './data-types';
|
|
6
|
+
export * from './database-client';
|
|
7
|
+
export * from './developer-client';
|
|
8
|
+
export * from './document';
|
|
9
|
+
export * from './messaging-client';
|
|
10
|
+
export * from './participant-token';
|
|
11
|
+
export * from './participant';
|
|
12
|
+
export * from './protocol';
|
|
13
|
+
export * from './queues-client';
|
|
14
|
+
export * from './requirement';
|
|
15
|
+
export * from './response';
|
|
16
|
+
export * from './room-client';
|
|
17
|
+
export * from './room-event';
|
|
18
|
+
export * from './room-server-client';
|
|
19
|
+
export * from './runtime';
|
|
20
|
+
export * from './schema';
|
|
21
|
+
export * from './storage-client';
|
|
22
|
+
export * from './stream-controller';
|
|
23
|
+
export * from './sync-client';
|
|
24
|
+
export * from './helpers';
|
|
25
|
+
export * from './utils';
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { EventEmitter } from "./event-emitter";
|
|
2
|
+
import { RoomClient } from "./room-client";
|
|
3
|
+
import { Participant, RemoteParticipant } from "./participant";
|
|
4
|
+
import { RoomMessageEvent } from "./room-event";
|
|
5
|
+
import { StreamController } from "./stream-controller";
|
|
6
|
+
export declare class MessageStreamChunk {
|
|
7
|
+
header: Record<string, any>;
|
|
8
|
+
data?: Uint8Array;
|
|
9
|
+
constructor({ header, data }: {
|
|
10
|
+
header: Record<string, any>;
|
|
11
|
+
data?: Uint8Array;
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
export declare class MessagingClient extends EventEmitter<RoomMessageEvent> {
|
|
15
|
+
private client;
|
|
16
|
+
private _streamWriters;
|
|
17
|
+
private _streamReaders;
|
|
18
|
+
private _onStreamAcceptCallback?;
|
|
19
|
+
private _participants;
|
|
20
|
+
constructor({ room }: {
|
|
21
|
+
room: RoomClient;
|
|
22
|
+
});
|
|
23
|
+
createStream({ to, header }: {
|
|
24
|
+
to: Participant;
|
|
25
|
+
header: Record<string, any>;
|
|
26
|
+
}): Promise<MessageStreamWriter>;
|
|
27
|
+
sendMessage({ to, type, message, attachment }: {
|
|
28
|
+
to: Participant;
|
|
29
|
+
type: string;
|
|
30
|
+
message: Record<string, any>;
|
|
31
|
+
attachment?: Uint8Array;
|
|
32
|
+
}): Promise<void>;
|
|
33
|
+
enable(onStreamAccept?: (reader: MessageStreamReader) => void): Promise<void>;
|
|
34
|
+
disable(): Promise<void>;
|
|
35
|
+
broadcastMessage({ type, message, attachment }: {
|
|
36
|
+
type: string;
|
|
37
|
+
message: Record<string, any>;
|
|
38
|
+
attachment?: Uint8Array;
|
|
39
|
+
}): Promise<void>;
|
|
40
|
+
get remoteParticipants(): Iterable<RemoteParticipant>;
|
|
41
|
+
private _handleMessageSend;
|
|
42
|
+
private _onParticipantEnabled;
|
|
43
|
+
private _onParticipantAttributes;
|
|
44
|
+
private _onParticipantDisabled;
|
|
45
|
+
private _onMessagingEnabled;
|
|
46
|
+
private _onStreamOpen;
|
|
47
|
+
private _onStreamAccept;
|
|
48
|
+
private _onStreamReject;
|
|
49
|
+
private _onStreamChunk;
|
|
50
|
+
private _onStreamClose;
|
|
51
|
+
dispose(): void;
|
|
52
|
+
}
|
|
53
|
+
export declare class MessageStreamWriter {
|
|
54
|
+
private _streamId;
|
|
55
|
+
private _to;
|
|
56
|
+
private _client;
|
|
57
|
+
constructor({ streamId, to, client }: {
|
|
58
|
+
streamId: string;
|
|
59
|
+
to: Participant;
|
|
60
|
+
client: MessagingClient;
|
|
61
|
+
});
|
|
62
|
+
write(chunk: MessageStreamChunk): Promise<void>;
|
|
63
|
+
close(): Promise<void>;
|
|
64
|
+
}
|
|
65
|
+
export declare class MessageStreamReader {
|
|
66
|
+
_streamId: string;
|
|
67
|
+
_to: Participant;
|
|
68
|
+
_client: MessagingClient;
|
|
69
|
+
_controller: StreamController<MessageStreamChunk>;
|
|
70
|
+
constructor({ streamId, to, client, controller }: {
|
|
71
|
+
streamId: string;
|
|
72
|
+
to: Participant;
|
|
73
|
+
client: MessagingClient;
|
|
74
|
+
controller: StreamController<MessageStreamChunk>;
|
|
75
|
+
});
|
|
76
|
+
}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { v4 as uuidV4 } from "uuid";
|
|
2
|
+
import { EventEmitter } from "./event-emitter";
|
|
3
|
+
import { RemoteParticipant } from "./participant";
|
|
4
|
+
import { RoomMessage } from "./room-event";
|
|
5
|
+
import { splitMessageHeader, splitMessagePayload } from "./utils";
|
|
6
|
+
import { StreamController } from "./stream-controller";
|
|
7
|
+
import { Completer } from "./completer";
|
|
8
|
+
export class MessageStreamChunk {
|
|
9
|
+
constructor({ header, data }) {
|
|
10
|
+
this.header = header;
|
|
11
|
+
this.data = data;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export class MessagingClient extends EventEmitter {
|
|
15
|
+
constructor({ room }) {
|
|
16
|
+
super();
|
|
17
|
+
this._streamWriters = {};
|
|
18
|
+
this._streamReaders = {};
|
|
19
|
+
this._participants = {};
|
|
20
|
+
this.client = room;
|
|
21
|
+
this.client.protocol.addHandler("messaging.send", this._handleMessageSend.bind(this));
|
|
22
|
+
}
|
|
23
|
+
async createStream({ to, header }) {
|
|
24
|
+
const streamId = uuidV4();
|
|
25
|
+
const completer = new Completer();
|
|
26
|
+
this._streamWriters[streamId] = completer;
|
|
27
|
+
await this.sendMessage({
|
|
28
|
+
to,
|
|
29
|
+
type: "stream.open",
|
|
30
|
+
message: { stream_id: streamId, header },
|
|
31
|
+
});
|
|
32
|
+
return completer.fut;
|
|
33
|
+
}
|
|
34
|
+
async sendMessage({ to, type, message, attachment }) {
|
|
35
|
+
await this.client.sendRequest("messaging.send", {
|
|
36
|
+
to_participant_id: to.id,
|
|
37
|
+
type,
|
|
38
|
+
message,
|
|
39
|
+
}, attachment);
|
|
40
|
+
}
|
|
41
|
+
async enable(onStreamAccept) {
|
|
42
|
+
await this.client.sendRequest("messaging.enable", {});
|
|
43
|
+
this._onStreamAcceptCallback = onStreamAccept;
|
|
44
|
+
}
|
|
45
|
+
async disable() {
|
|
46
|
+
await this.client.sendRequest("messaging.disable", {});
|
|
47
|
+
}
|
|
48
|
+
async broadcastMessage({ type, message, attachment }) {
|
|
49
|
+
await this.client.sendRequest("messaging.broadcast", { type, message }, attachment);
|
|
50
|
+
}
|
|
51
|
+
get remoteParticipants() {
|
|
52
|
+
return Object.values(this._participants);
|
|
53
|
+
}
|
|
54
|
+
async _handleMessageSend(protocol, messageId, type, bytes) {
|
|
55
|
+
const headerStr = splitMessageHeader(bytes || new Uint8Array());
|
|
56
|
+
const payload = splitMessagePayload(bytes || new Uint8Array());
|
|
57
|
+
const header = JSON.parse(headerStr);
|
|
58
|
+
const message = new RoomMessage({
|
|
59
|
+
fromParticipantId: header["from_participant_id"],
|
|
60
|
+
type: header["type"],
|
|
61
|
+
message: header["message"],
|
|
62
|
+
attachment: payload,
|
|
63
|
+
});
|
|
64
|
+
switch (message.type) {
|
|
65
|
+
case "messaging.enabled":
|
|
66
|
+
this._onMessagingEnabled(message);
|
|
67
|
+
break;
|
|
68
|
+
case "participant.attributes":
|
|
69
|
+
this._onParticipantAttributes(message);
|
|
70
|
+
break;
|
|
71
|
+
case "participant.enabled":
|
|
72
|
+
this._onParticipantEnabled(message);
|
|
73
|
+
break;
|
|
74
|
+
case "participant.disabled":
|
|
75
|
+
this._onParticipantDisabled(message);
|
|
76
|
+
break;
|
|
77
|
+
case "stream.open":
|
|
78
|
+
this._onStreamOpen(message);
|
|
79
|
+
break;
|
|
80
|
+
case "stream.accept":
|
|
81
|
+
this._onStreamAccept(message);
|
|
82
|
+
break;
|
|
83
|
+
case "stream.reject":
|
|
84
|
+
this._onStreamReject(message);
|
|
85
|
+
break;
|
|
86
|
+
case "stream.chunk":
|
|
87
|
+
this._onStreamChunk(message);
|
|
88
|
+
break;
|
|
89
|
+
case "stream.close":
|
|
90
|
+
this._onStreamClose(message);
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
const messageEvent = { message };
|
|
94
|
+
this.client.emit(messageEvent);
|
|
95
|
+
this.emit("message", messageEvent);
|
|
96
|
+
}
|
|
97
|
+
_onParticipantEnabled(message) {
|
|
98
|
+
const data = message.message;
|
|
99
|
+
const p = new RemoteParticipant(this.client, data["id"], data["role"]);
|
|
100
|
+
for (const [k, v] of Object.entries(data["attributes"] || {})) {
|
|
101
|
+
p._attributes[k] = v;
|
|
102
|
+
}
|
|
103
|
+
this._participants[data["id"]] = p;
|
|
104
|
+
this.emit("participant_added", { message });
|
|
105
|
+
}
|
|
106
|
+
_onParticipantAttributes(message) {
|
|
107
|
+
const part = this._participants[message.fromParticipantId];
|
|
108
|
+
if (!part)
|
|
109
|
+
return;
|
|
110
|
+
const attrObj = message.message["attributes"];
|
|
111
|
+
for (const [k, v] of Object.entries(attrObj)) {
|
|
112
|
+
part._attributes[k] = v;
|
|
113
|
+
}
|
|
114
|
+
this.emit("participant_attributes_updated", { message });
|
|
115
|
+
}
|
|
116
|
+
_onParticipantDisabled(message) {
|
|
117
|
+
const part = this._participants[message.message["id"]];
|
|
118
|
+
if (part) {
|
|
119
|
+
delete this._participants[message.message["id"]];
|
|
120
|
+
this.emit("participant_removed", { message });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
_onMessagingEnabled(message) {
|
|
124
|
+
const participants = message.message["participants"];
|
|
125
|
+
for (const data of participants) {
|
|
126
|
+
const rp = new RemoteParticipant(this.client, data["id"], data["role"]);
|
|
127
|
+
for (const [k, v] of Object.entries(data["attributes"] || {})) {
|
|
128
|
+
rp._attributes[k] = v;
|
|
129
|
+
}
|
|
130
|
+
this._participants[data["id"]] = rp;
|
|
131
|
+
}
|
|
132
|
+
this.emit("messaging_enabled", { message });
|
|
133
|
+
}
|
|
134
|
+
_onStreamOpen(message) {
|
|
135
|
+
const from = [...this.remoteParticipants]
|
|
136
|
+
.find((x) => x.id === message.fromParticipantId);
|
|
137
|
+
if (!from)
|
|
138
|
+
return;
|
|
139
|
+
const streamId = message.message["stream_id"];
|
|
140
|
+
const controller = new StreamController();
|
|
141
|
+
const reader = new MessageStreamReader({
|
|
142
|
+
streamId,
|
|
143
|
+
to: from,
|
|
144
|
+
client: this,
|
|
145
|
+
controller,
|
|
146
|
+
});
|
|
147
|
+
try {
|
|
148
|
+
if (!this._onStreamAcceptCallback) {
|
|
149
|
+
throw new Error("streams are not allowed by this client");
|
|
150
|
+
}
|
|
151
|
+
this._onStreamAcceptCallback(reader);
|
|
152
|
+
this.sendMessage({
|
|
153
|
+
to: from,
|
|
154
|
+
type: "stream.accept",
|
|
155
|
+
message: { stream_id: streamId },
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
catch (e) {
|
|
159
|
+
this.sendMessage({
|
|
160
|
+
to: from,
|
|
161
|
+
type: "stream.reject",
|
|
162
|
+
message: { stream_id: streamId, error: String(e) },
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
this._streamReaders[streamId] = reader;
|
|
166
|
+
this.emit("stream_opened", { message });
|
|
167
|
+
}
|
|
168
|
+
_onStreamAccept(message) {
|
|
169
|
+
const streamId = message.message["stream_id"];
|
|
170
|
+
const writerCompleter = this._streamWriters[streamId];
|
|
171
|
+
if (!writerCompleter)
|
|
172
|
+
return;
|
|
173
|
+
const from = [...this.remoteParticipants].find((x) => x.id === message.fromParticipantId);
|
|
174
|
+
if (!from)
|
|
175
|
+
return;
|
|
176
|
+
writerCompleter.complete(new MessageStreamWriter({
|
|
177
|
+
streamId,
|
|
178
|
+
to: from,
|
|
179
|
+
client: this,
|
|
180
|
+
}));
|
|
181
|
+
}
|
|
182
|
+
_onStreamReject(message) {
|
|
183
|
+
const streamId = message.message["stream_id"];
|
|
184
|
+
const writerCompleter = this._streamWriters[streamId];
|
|
185
|
+
if (!writerCompleter)
|
|
186
|
+
return;
|
|
187
|
+
writerCompleter.completeError(new Error("The stream was rejected by the remote client"));
|
|
188
|
+
}
|
|
189
|
+
_onStreamChunk(message) {
|
|
190
|
+
const streamId = message.message["stream_id"];
|
|
191
|
+
const reader = this._streamReaders[streamId];
|
|
192
|
+
if (!reader)
|
|
193
|
+
return;
|
|
194
|
+
reader._controller.add(new MessageStreamChunk({
|
|
195
|
+
header: message.message,
|
|
196
|
+
data: message.attachment,
|
|
197
|
+
}));
|
|
198
|
+
}
|
|
199
|
+
_onStreamClose(message) {
|
|
200
|
+
const streamId = message.message["stream_id"];
|
|
201
|
+
const reader = this._streamReaders[streamId];
|
|
202
|
+
if (!reader)
|
|
203
|
+
return;
|
|
204
|
+
reader._controller.close();
|
|
205
|
+
delete this._streamReaders[streamId];
|
|
206
|
+
}
|
|
207
|
+
dispose() {
|
|
208
|
+
super.dispose();
|
|
209
|
+
this.client.protocol.removeHandler("messaging.send");
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
export class MessageStreamWriter {
|
|
213
|
+
constructor({ streamId, to, client }) {
|
|
214
|
+
this._streamId = streamId;
|
|
215
|
+
this._to = to;
|
|
216
|
+
this._client = client;
|
|
217
|
+
}
|
|
218
|
+
async write(chunk) {
|
|
219
|
+
await this._client.sendMessage({
|
|
220
|
+
to: this._to,
|
|
221
|
+
type: "stream.chunk",
|
|
222
|
+
message: { stream_id: this._streamId, header: chunk.header },
|
|
223
|
+
attachment: chunk.data,
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
async close() {
|
|
227
|
+
await this._client.sendMessage({
|
|
228
|
+
to: this._to,
|
|
229
|
+
type: "stream.close",
|
|
230
|
+
message: { stream_id: this._streamId },
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
export class MessageStreamReader {
|
|
235
|
+
constructor({ streamId, to, client, controller }) {
|
|
236
|
+
this._streamId = streamId;
|
|
237
|
+
this._to = to;
|
|
238
|
+
this._client = client;
|
|
239
|
+
this._controller = controller;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export declare class ParticipantGrant {
|
|
2
|
+
name: string;
|
|
3
|
+
scope?: string;
|
|
4
|
+
constructor({ name, scope }: {
|
|
5
|
+
name: string;
|
|
6
|
+
scope?: string;
|
|
7
|
+
});
|
|
8
|
+
toJson(): Record<string, any>;
|
|
9
|
+
static fromJson(json: Record<string, any>): ParticipantGrant;
|
|
10
|
+
}
|
|
11
|
+
export declare class ParticipantToken {
|
|
12
|
+
name: string;
|
|
13
|
+
projectId?: string;
|
|
14
|
+
apiKeyId?: string;
|
|
15
|
+
grants: ParticipantGrant[];
|
|
16
|
+
extra?: Record<string, any>;
|
|
17
|
+
constructor({ name, projectId, apiKeyId, extra, grants, }: {
|
|
18
|
+
name: string;
|
|
19
|
+
projectId?: string;
|
|
20
|
+
apiKeyId?: string;
|
|
21
|
+
extra?: Record<string, any>;
|
|
22
|
+
grants?: ParticipantGrant[];
|
|
23
|
+
});
|
|
24
|
+
get isAgent(): boolean;
|
|
25
|
+
addRoleGrant(role: string): void;
|
|
26
|
+
addRoomGrant(roomName: string): void;
|
|
27
|
+
toJson(): Record<string, any>;
|
|
28
|
+
toJwt({ token }: {
|
|
29
|
+
token: string;
|
|
30
|
+
}): Promise<string>;
|
|
31
|
+
static fromJson(json: Record<string, any>): ParticipantToken;
|
|
32
|
+
static fromJwt(jwtStr: string, options: {
|
|
33
|
+
token: string;
|
|
34
|
+
verify?: boolean;
|
|
35
|
+
}): Promise<ParticipantToken>;
|
|
36
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { decodeJwt, jwtVerify, SignJWT } from "jose";
|
|
2
|
+
export class ParticipantGrant {
|
|
3
|
+
constructor({ name, scope }) {
|
|
4
|
+
this.name = name;
|
|
5
|
+
this.scope = scope;
|
|
6
|
+
}
|
|
7
|
+
toJson() {
|
|
8
|
+
return {
|
|
9
|
+
name: this.name,
|
|
10
|
+
scope: this.scope,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
static fromJson(json) {
|
|
14
|
+
return new ParticipantGrant({
|
|
15
|
+
name: json["name"],
|
|
16
|
+
scope: json["scope"],
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export class ParticipantToken {
|
|
21
|
+
constructor({ name, projectId, apiKeyId, extra, grants, }) {
|
|
22
|
+
this.name = name;
|
|
23
|
+
this.projectId = projectId;
|
|
24
|
+
this.apiKeyId = apiKeyId;
|
|
25
|
+
this.extra = extra ?? {};
|
|
26
|
+
this.grants = grants ?? [];
|
|
27
|
+
}
|
|
28
|
+
get isAgent() {
|
|
29
|
+
for (const grant of this.grants) {
|
|
30
|
+
if (grant.name === "role" && grant.scope === "agent") {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
addRoleGrant(role) {
|
|
37
|
+
this.grants.push(new ParticipantGrant({ name: "role", scope: role }));
|
|
38
|
+
}
|
|
39
|
+
addRoomGrant(roomName) {
|
|
40
|
+
this.grants.push(new ParticipantGrant({ name: "room", scope: roomName }));
|
|
41
|
+
}
|
|
42
|
+
toJson() {
|
|
43
|
+
return {
|
|
44
|
+
name: this.name,
|
|
45
|
+
...(this.projectId ? { sub: this.projectId } : {}),
|
|
46
|
+
...(this.apiKeyId ? { kid: this.apiKeyId } : {}),
|
|
47
|
+
grants: this.grants.map((g) => g.toJson()),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
async toJwt({ token }) {
|
|
51
|
+
const secretKey = new TextEncoder().encode(token);
|
|
52
|
+
const payload = {
|
|
53
|
+
...this.toJson(),
|
|
54
|
+
...this.extra,
|
|
55
|
+
};
|
|
56
|
+
const jwt = await new SignJWT(payload)
|
|
57
|
+
.setProtectedHeader({ alg: "HS256", typ: "JWT" })
|
|
58
|
+
.sign(secretKey);
|
|
59
|
+
return jwt;
|
|
60
|
+
}
|
|
61
|
+
static fromJson(json) {
|
|
62
|
+
const { name, sub, grants, kid, ...rest } = json;
|
|
63
|
+
const extra = { ...rest };
|
|
64
|
+
return new ParticipantToken({
|
|
65
|
+
name: name,
|
|
66
|
+
projectId: sub,
|
|
67
|
+
apiKeyId: kid,
|
|
68
|
+
grants: grants?.map((g) => ParticipantGrant.fromJson(g)),
|
|
69
|
+
extra,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
static async fromJwt(jwtStr, options) {
|
|
73
|
+
const { token, verify = true } = options;
|
|
74
|
+
if (verify) {
|
|
75
|
+
const secretKey = new TextEncoder().encode(token);
|
|
76
|
+
const { payload } = await jwtVerify(jwtStr, secretKey, {
|
|
77
|
+
algorithms: ["HS256"],
|
|
78
|
+
});
|
|
79
|
+
return ParticipantToken.fromJson(payload);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
try {
|
|
83
|
+
const payload = decodeJwt(jwtStr);
|
|
84
|
+
return ParticipantToken.fromJson(payload);
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
throw new Error("Failed to decode JWT");
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { RoomClient } from "./room-client";
|
|
2
|
+
export declare abstract class Participant {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
protected readonly client: RoomClient;
|
|
5
|
+
protected _attributes: Record<string, any>;
|
|
6
|
+
protected _connections: string[];
|
|
7
|
+
constructor(client: RoomClient, id: string);
|
|
8
|
+
get connections(): ReadonlyArray<string>;
|
|
9
|
+
getAttribute(name: string): any;
|
|
10
|
+
}
|
|
11
|
+
export declare class RemoteParticipant extends Participant {
|
|
12
|
+
readonly role: string;
|
|
13
|
+
constructor(client: RoomClient, id: string, role: string);
|
|
14
|
+
}
|
|
15
|
+
export declare class LocalParticipant extends Participant {
|
|
16
|
+
constructor(client: RoomClient, id: string);
|
|
17
|
+
setAttribute(name: string, value: any): Promise<void>;
|
|
18
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { packMessage } from "./utils";
|
|
2
|
+
export class Participant {
|
|
3
|
+
constructor(client, id) {
|
|
4
|
+
this._attributes = {};
|
|
5
|
+
this._connections = [];
|
|
6
|
+
this.client = client;
|
|
7
|
+
this.id = id;
|
|
8
|
+
}
|
|
9
|
+
get connections() {
|
|
10
|
+
return this._connections;
|
|
11
|
+
}
|
|
12
|
+
getAttribute(name) {
|
|
13
|
+
return this._attributes[name];
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export class RemoteParticipant extends Participant {
|
|
17
|
+
constructor(client, id, role) {
|
|
18
|
+
super(client, id);
|
|
19
|
+
this.role = role;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export class LocalParticipant extends Participant {
|
|
23
|
+
constructor(client, id) {
|
|
24
|
+
super(client, id);
|
|
25
|
+
}
|
|
26
|
+
async setAttribute(name, value) {
|
|
27
|
+
this._attributes[name] = value;
|
|
28
|
+
try {
|
|
29
|
+
const payload = packMessage({ [name]: value });
|
|
30
|
+
await this.client.protocol.send("set_attributes", payload);
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
console.warn("Unable to send attribute changes", err);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|