@pmate/chat-sdk 0.9.9 → 0.9.11

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.
@@ -5,6 +5,8 @@ export type MeetingRoom = {
5
5
  startedAt: number;
6
6
  initiatorId: string;
7
7
  profileIds: string[];
8
+ invitationId?: string;
9
+ inviteProfileIds?: string[];
8
10
  };
9
11
  export type CallPhase = "idle" | "requesting" | "ringing" | "incoming" | "connecting" | "connected" | "ended";
10
12
  export type CallSnapshot = {
@@ -35,10 +35,11 @@ export declare class GroupMeetingClient {
35
35
  private lastSeen;
36
36
  private iceServers;
37
37
  private queue;
38
+ private inviteProfileIds;
38
39
  constructor(options: CallClientOptions);
39
40
  snapshot(): MeetingSnapshot;
40
41
  subscribe(fn: (s: MeetingSnapshot) => void): () => void;
41
- join(groupId: string): Promise<void>;
42
+ join(groupId: string, inviteProfileIds?: string[]): Promise<void>;
42
43
  leave(): void;
43
44
  dismiss(): void;
44
45
  close(): void;
@@ -14,15 +14,19 @@ export class GroupMeetingClient {
14
14
  lastSeen = 0;
15
15
  iceServers = [];
16
16
  queue = Promise.resolve();
17
+ inviteProfileIds = [];
17
18
  constructor(options) {
18
19
  this.options = options;
19
20
  }
20
21
  snapshot() { return this.state; }
21
22
  subscribe(fn) { this.listeners.add(fn); fn(this.state); return () => { this.listeners.delete(fn); }; }
22
- async join(groupId) {
23
+ async join(groupId, inviteProfileIds = []) {
24
+ if (!Array.isArray(inviteProfileIds) || inviteProfileIds.length > 256 || inviteProfileIds.some(id => typeof id !== "string" || !id || id.length > 128))
25
+ throw new Error("INVALID_INVITATION");
23
26
  if (!["idle", "ended"].includes(this.state.phase))
24
27
  return;
25
28
  this.release();
29
+ this.inviteProfileIds = [...new Set(inviteProfileIds)].filter(id => id !== this.options.profileId);
26
30
  const gen = this.generation;
27
31
  this.update({ phase: "joining", groupId, error: undefined });
28
32
  this.timer = setTimeout(() => this.fail("CONNECTION_TIMEOUT"), 30000);
@@ -39,7 +43,7 @@ export class GroupMeetingClient {
39
43
  const ws = (this.options.createWebSocket ?? (url => new WebSocket(url)))(this.options.url ?? "wss://calls.pmate.chat/call");
40
44
  this.ws = ws;
41
45
  ws.onopen = () => { if (gen === this.generation)
42
- this.send({ type: "auth", mode: "meeting", profileId: this.options.profileId, token: this.options.token }); };
46
+ this.send({ type: "auth", mode: "meeting", profileId: this.options.profileId, token: this.options.token, clientInstanceId: this.options.clientInstanceId }); };
43
47
  ws.onmessage = e => { this.lastSeen = Date.now(); this.queue = this.queue.then(async () => { if (gen === this.generation)
44
48
  await this.receive(JSON.parse(String(e.data)), gen); }).catch(() => { if (gen === this.generation)
45
49
  this.fail("CONNECTION_FAILED"); }); };
@@ -172,7 +176,7 @@ export class GroupMeetingClient {
172
176
  }
173
177
  async receive(m, gen) {
174
178
  if (m.type === "ready") {
175
- this.send({ type: "room.join", groupId: this.state.groupId, requestId: String(gen) });
179
+ this.send({ type: "room.join", groupId: this.state.groupId, requestId: String(gen), inviteProfileIds: this.inviteProfileIds });
176
180
  this.lastSeen = Date.now();
177
181
  this.heartbeat = setInterval(() => { if (Date.now() - this.lastSeen > 60000)
178
182
  this.fail("DISCONNECTED");
@@ -47,7 +47,19 @@ export class ReadSyncClient {
47
47
  async run() {
48
48
  const { appId, profileId, persistence } = this.options;
49
49
  const current = await this.loadLocal();
50
+ // Ordinary reads are monotonic: only the furthest position per thread needs
51
+ // sending. Keep every original operation in storage until the projection
52
+ // confirms it, and never rewrite an operation ID's payload during retries.
53
+ const latest = new Map();
50
54
  for (const operation of current.pending) {
55
+ if (operation.explicitReadMessageIds.length || operation.readThroughSeq <= 0)
56
+ continue;
57
+ const previous = latest.get(operation.threadId);
58
+ if (!previous || operation.readThroughSeq > previous.readThroughSeq)
59
+ latest.set(operation.threadId, operation);
60
+ }
61
+ const outgoing = current.pending.filter(operation => operation.explicitReadMessageIds.length || operation.readThroughSeq <= 0 || latest.get(operation.threadId) === operation);
62
+ for (const operation of outgoing) {
51
63
  const receipt = await this.request("/api/social/reading", {
52
64
  method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ ...operation, appId, profileId }),
53
65
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pmate/chat-sdk",
3
- "version": "0.9.9",
3
+ "version": "0.9.11",
4
4
  "description": "Cross-platform PMate chat input components, voice input primitives, and ASR helpers",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",