@ai-matrx/meet 0.8.0 → 0.9.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/CHANGELOG.md +26 -0
- package/dist/{collab-Bss-_Jfp.d.cts → collab-B6scmSST.d.cts} +18 -12
- package/dist/{collab-Bss-_Jfp.d.ts → collab-B6scmSST.d.ts} +18 -12
- package/dist/{components-BOUmOiPj.d.cts → components-BeCQ9h0h.d.cts} +1 -1
- package/dist/{components-BmgstFx_.d.ts → components-D0TqyeiY.d.ts} +1 -1
- package/dist/core.cjs +113 -50
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +55 -19
- package/dist/core.d.ts +55 -19
- package/dist/core.js +113 -50
- package/dist/core.js.map +1 -1
- package/dist/index.cjs +113 -50
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +113 -50
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +129 -57
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +4 -4
- package/dist/react.d.ts +4 -4
- package/dist/react.js +129 -57
- package/dist/react.js.map +1 -1
- package/dist/skins/meet.cjs +47 -15
- package/dist/skins/meet.cjs.map +1 -1
- package/dist/skins/meet.d.cts +2 -2
- package/dist/skins/meet.d.ts +2 -2
- package/dist/skins/meet.js +47 -15
- package/dist/skins/meet.js.map +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Changelog — `@ai-matrx/meet`
|
|
2
2
|
|
|
3
|
+
## 0.9.0
|
|
4
|
+
### S3: the meeting pass, the role/standing vocabulary, refusal phases
|
|
5
|
+
|
|
6
|
+
- **The meeting pass.** `RoomToken` gains `meetingPass`, `meetingPassExpiresAt`, `sessionId`,
|
|
7
|
+
`standing`. Every aidream meet route (record, claim, chat, collab, moderate, Q&A, captions)
|
|
8
|
+
now reads `x-meet-pass` (`MEETING_PASS_HEADER`, `meetingPassHeaders`); the LiveKit `token` is
|
|
9
|
+
for media only. `ROOM_TOKEN_HEADER` is removed (no shim). `rememberedMeetingPass(room)` reads
|
|
10
|
+
a guest's kept pass.
|
|
11
|
+
- **Role vocabulary** `host · cohost · presenter · participant · viewer`; being a guest is the
|
|
12
|
+
STANDING `guest` (`ParticipantStanding`, `isLinkFollower`, attribute
|
|
13
|
+
`matrx.participant.standing`). `data-meet-role` still renders `guest` for a link follower.
|
|
14
|
+
- **Refusals are phases.** `MeetError.reason` carries the join gate's reason code; a refused
|
|
15
|
+
join renders `refused:<reason>` (`ended` renders the `ended` phase), never pre-join.
|
|
16
|
+
- **Session-aware record:** `loadGuestMeetingRecord({ sessionId })`.
|
|
17
|
+
- **Removed:** `RPCS.endMeeting` / `repository.endMeeting` (the `meet_end_meeting` RPC is
|
|
18
|
+
dropped; `/v1/meet/end` is the one end door).
|
|
19
|
+
|
|
20
|
+
Consumer action: send `meetingPass` (not `token`) as `x-meet-pass`; read the guest badge from
|
|
21
|
+
`standing`, not `role === "guest"`; replace `ROOM_TOKEN_HEADER` with `MEETING_PASS_HEADER`.
|
|
22
|
+
|
|
23
|
+
## 0.8.1
|
|
24
|
+
|
|
25
|
+
Automatic changed-only republish (docs/metadata drift since the last tag — see
|
|
26
|
+
`git diff npm/meet/v0.8.0..npm/meet/v0.8.1 -- apps/shared/meet`).
|
|
27
|
+
No source changes intended and no consumer action required.
|
|
28
|
+
|
|
3
29
|
## 0.8.0 — session identity and global slots have one home
|
|
4
30
|
|
|
5
31
|
- The session reader is `@ai-matrx/data/session-identity` (meet supplies its words and branded
|
|
@@ -57,19 +57,23 @@ declare function isGuest(identity: MeetIdentity): boolean;
|
|
|
57
57
|
/**
|
|
58
58
|
* WHO SOMEBODY IS IN A ROOM — the SERVER's word, never this client's (MRI-A11).
|
|
59
59
|
*
|
|
60
|
-
*
|
|
61
|
-
* `
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* `
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
*
|
|
60
|
+
* THE ROLE VOCABULARY (CORE-DESIGN §2.1, S3): `host · cohost · presenter · participant ·
|
|
61
|
+
* viewer`. `host` is `meet_meetings.host_user_id`; `cohost` is a real `iam` grant on the
|
|
62
|
+
* meeting. Being a guest is a STANDING (`ParticipantStanding`), never a role: a guest is a
|
|
63
|
+
* `participant` whose standing is `guest`. Pinned against aidream's `config.py` by
|
|
64
|
+
* `test_meet_service.py`.
|
|
65
|
+
*/
|
|
66
|
+
type ParticipantRole = "host" | "cohost" | "presenter" | "participant" | "viewer";
|
|
67
|
+
/**
|
|
68
|
+
* Where a person stands with the meeting, beside the role: `member` of its organization,
|
|
69
|
+
* `invited` (shared with them), `outsider` (signed in, followed the link), `guest` (no
|
|
70
|
+
* session). The server mints it (attribute `matrx.participant.standing`).
|
|
69
71
|
*/
|
|
70
|
-
type
|
|
72
|
+
type ParticipantStanding = "member" | "invited" | "outsider" | "guest";
|
|
71
73
|
/** Rank, so "can this actor do that to that participant" is one comparison. */
|
|
72
74
|
declare const ROLE_RANK: Readonly<Record<ParticipantRole, number>>;
|
|
75
|
+
/** Does this standing follow the link-holder (guest) rules? `outsider` and `guest`. */
|
|
76
|
+
declare function isLinkFollower(standing: ParticipantStanding | null | undefined): boolean;
|
|
73
77
|
/**
|
|
74
78
|
* 🚨 THE ONE PREDICATE FOR "DOES THIS PERSON HOLD HOST POWERS".
|
|
75
79
|
*
|
|
@@ -102,6 +106,8 @@ interface MeetParticipant {
|
|
|
102
106
|
readonly displayName: string;
|
|
103
107
|
readonly avatarUrl: string | null;
|
|
104
108
|
readonly role: ParticipantRole;
|
|
109
|
+
/** Where they stand with the meeting (server-minted); null when the server sent none. */
|
|
110
|
+
readonly standing?: ParticipantStanding | null;
|
|
105
111
|
/**
|
|
106
112
|
* Whether the SERVER granted this participant the power to start a recording
|
|
107
113
|
* (MRI-A11). It is not `role === "host"`: a meeting whose recording policy is
|
|
@@ -800,6 +806,6 @@ interface MeetingActivities {
|
|
|
800
806
|
* The same `GET /v1/meet/collab` the room reads on join; a refusal is returned as data,
|
|
801
807
|
* so the record still renders everything else.
|
|
802
808
|
*/
|
|
803
|
-
declare function loadMeetingActivities(api: MeetApi, meetingId: string,
|
|
809
|
+
declare function loadMeetingActivities(api: MeetApi, meetingId: string, meetingPass?: string | null): Promise<MeetingActivities>;
|
|
804
810
|
|
|
805
|
-
export { type NoteTakerState as $, type AccessTokenSource as A, type MeetPollState as B, type CollabPanel as C, type MeetQuestion as D, EMPTY_COLLAB as E, FILE_SESSION_PATH as F, type MeetQuestionState as G, type MeetStroke as H, type InviteeInput as I, type JsonObject as J, type MeetingActivities as K, type LobbyEntry as L, type MeetingRecord as M, type MeetingAttendee as N, type OrganizationId as O, type ParticipantIdentity as P, type MeetingChanges as Q, type RoomName as R, type StageLayout as S, type TranscriptSource as T, type UserId as U, type MeetingInvitee as V, type WrapUpAnnouncement as W, type MeetingKind as X, type MeetingNoteKind as Y, type MeetingOccurrence as Z, type MeetingSettings as _, type MeetParticipant as a, ORGANIZATION_HEADER as a0, type OccurrenceOverride as a1, type OccurrenceState as a2,
|
|
811
|
+
export { type NoteTakerState as $, type AccessTokenSource as A, type MeetPollState as B, type CollabPanel as C, type MeetQuestion as D, EMPTY_COLLAB as E, FILE_SESSION_PATH as F, type MeetQuestionState as G, type MeetStroke as H, type InviteeInput as I, type JsonObject as J, type MeetingActivities as K, type LobbyEntry as L, type MeetingRecord as M, type MeetingAttendee as N, type OrganizationId as O, type ParticipantIdentity as P, type MeetingChanges as Q, type RoomName as R, type StageLayout as S, type TranscriptSource as T, type UserId as U, type MeetingInvitee as V, type WrapUpAnnouncement as W, type MeetingKind as X, type MeetingNoteKind as Y, type MeetingOccurrence as Z, type MeetingSettings as _, type MeetParticipant as a, ORGANIZATION_HEADER as a0, type OccurrenceOverride as a1, type OccurrenceState as a2, type ParticipantStanding as a3, ROLE_RANK as a4, type RecordingPolicy as a5, type RecordingRecord as a6, type RecordingState as a7, type RoomPhase as a8, type RsvpAnswer as a9, type RsvpInvitation as aa, type RsvpState as ab, type TranscriptSegment as ac, type UpcomingOccurrence as ad, applyCollabPacket as ae, asCallId as af, asMeetingId as ag, asOrganizationId as ah, asParticipantIdentity as ai, asRoomName as aj, asUserId as ak, assignedRoom as al, autoAssign as am, breakoutRemainingMs as an, collabFromState as ao, createMeetApi as ap, defaultCaptionLanguage as aq, fileDownloadPath as ar, isGuest as as, isHostRole as at, isLinkFollower as au, loadMeetingActivities as av, withItem as aw, type RecordingSource as ax, type MeetApi as b, type MeetIdentity as c, type CallInvite as d, type ParticipantRole as e, type CallMode as f, type MeetingNote as g, type MeetingId as h, type CollabSnapshot as i, type AdmissionState as j, CAPTION_LANGUAGES as k, type CallId as l, type CallState as m, type CallablePerson as n, type ConnectionQuality as o, type InviteeRole as p, type JsonValue as q, type MeetApiOptions as r, type MeetApiRequest as s, type MeetBreakoutBroadcast as t, type MeetBreakoutPlan as u, type MeetBreakoutRoom as v, type MeetCollabSettings as w, type MeetFileMedia as x, type MeetPoll as y, type MeetPollOption as z };
|
|
@@ -57,19 +57,23 @@ declare function isGuest(identity: MeetIdentity): boolean;
|
|
|
57
57
|
/**
|
|
58
58
|
* WHO SOMEBODY IS IN A ROOM — the SERVER's word, never this client's (MRI-A11).
|
|
59
59
|
*
|
|
60
|
-
*
|
|
61
|
-
* `
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* `
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
*
|
|
60
|
+
* THE ROLE VOCABULARY (CORE-DESIGN §2.1, S3): `host · cohost · presenter · participant ·
|
|
61
|
+
* viewer`. `host` is `meet_meetings.host_user_id`; `cohost` is a real `iam` grant on the
|
|
62
|
+
* meeting. Being a guest is a STANDING (`ParticipantStanding`), never a role: a guest is a
|
|
63
|
+
* `participant` whose standing is `guest`. Pinned against aidream's `config.py` by
|
|
64
|
+
* `test_meet_service.py`.
|
|
65
|
+
*/
|
|
66
|
+
type ParticipantRole = "host" | "cohost" | "presenter" | "participant" | "viewer";
|
|
67
|
+
/**
|
|
68
|
+
* Where a person stands with the meeting, beside the role: `member` of its organization,
|
|
69
|
+
* `invited` (shared with them), `outsider` (signed in, followed the link), `guest` (no
|
|
70
|
+
* session). The server mints it (attribute `matrx.participant.standing`).
|
|
69
71
|
*/
|
|
70
|
-
type
|
|
72
|
+
type ParticipantStanding = "member" | "invited" | "outsider" | "guest";
|
|
71
73
|
/** Rank, so "can this actor do that to that participant" is one comparison. */
|
|
72
74
|
declare const ROLE_RANK: Readonly<Record<ParticipantRole, number>>;
|
|
75
|
+
/** Does this standing follow the link-holder (guest) rules? `outsider` and `guest`. */
|
|
76
|
+
declare function isLinkFollower(standing: ParticipantStanding | null | undefined): boolean;
|
|
73
77
|
/**
|
|
74
78
|
* 🚨 THE ONE PREDICATE FOR "DOES THIS PERSON HOLD HOST POWERS".
|
|
75
79
|
*
|
|
@@ -102,6 +106,8 @@ interface MeetParticipant {
|
|
|
102
106
|
readonly displayName: string;
|
|
103
107
|
readonly avatarUrl: string | null;
|
|
104
108
|
readonly role: ParticipantRole;
|
|
109
|
+
/** Where they stand with the meeting (server-minted); null when the server sent none. */
|
|
110
|
+
readonly standing?: ParticipantStanding | null;
|
|
105
111
|
/**
|
|
106
112
|
* Whether the SERVER granted this participant the power to start a recording
|
|
107
113
|
* (MRI-A11). It is not `role === "host"`: a meeting whose recording policy is
|
|
@@ -800,6 +806,6 @@ interface MeetingActivities {
|
|
|
800
806
|
* The same `GET /v1/meet/collab` the room reads on join; a refusal is returned as data,
|
|
801
807
|
* so the record still renders everything else.
|
|
802
808
|
*/
|
|
803
|
-
declare function loadMeetingActivities(api: MeetApi, meetingId: string,
|
|
809
|
+
declare function loadMeetingActivities(api: MeetApi, meetingId: string, meetingPass?: string | null): Promise<MeetingActivities>;
|
|
804
810
|
|
|
805
|
-
export { type NoteTakerState as $, type AccessTokenSource as A, type MeetPollState as B, type CollabPanel as C, type MeetQuestion as D, EMPTY_COLLAB as E, FILE_SESSION_PATH as F, type MeetQuestionState as G, type MeetStroke as H, type InviteeInput as I, type JsonObject as J, type MeetingActivities as K, type LobbyEntry as L, type MeetingRecord as M, type MeetingAttendee as N, type OrganizationId as O, type ParticipantIdentity as P, type MeetingChanges as Q, type RoomName as R, type StageLayout as S, type TranscriptSource as T, type UserId as U, type MeetingInvitee as V, type WrapUpAnnouncement as W, type MeetingKind as X, type MeetingNoteKind as Y, type MeetingOccurrence as Z, type MeetingSettings as _, type MeetParticipant as a, ORGANIZATION_HEADER as a0, type OccurrenceOverride as a1, type OccurrenceState as a2,
|
|
811
|
+
export { type NoteTakerState as $, type AccessTokenSource as A, type MeetPollState as B, type CollabPanel as C, type MeetQuestion as D, EMPTY_COLLAB as E, FILE_SESSION_PATH as F, type MeetQuestionState as G, type MeetStroke as H, type InviteeInput as I, type JsonObject as J, type MeetingActivities as K, type LobbyEntry as L, type MeetingRecord as M, type MeetingAttendee as N, type OrganizationId as O, type ParticipantIdentity as P, type MeetingChanges as Q, type RoomName as R, type StageLayout as S, type TranscriptSource as T, type UserId as U, type MeetingInvitee as V, type WrapUpAnnouncement as W, type MeetingKind as X, type MeetingNoteKind as Y, type MeetingOccurrence as Z, type MeetingSettings as _, type MeetParticipant as a, ORGANIZATION_HEADER as a0, type OccurrenceOverride as a1, type OccurrenceState as a2, type ParticipantStanding as a3, ROLE_RANK as a4, type RecordingPolicy as a5, type RecordingRecord as a6, type RecordingState as a7, type RoomPhase as a8, type RsvpAnswer as a9, type RsvpInvitation as aa, type RsvpState as ab, type TranscriptSegment as ac, type UpcomingOccurrence as ad, applyCollabPacket as ae, asCallId as af, asMeetingId as ag, asOrganizationId as ah, asParticipantIdentity as ai, asRoomName as aj, asUserId as ak, assignedRoom as al, autoAssign as am, breakoutRemainingMs as an, collabFromState as ao, createMeetApi as ap, defaultCaptionLanguage as aq, fileDownloadPath as ar, isGuest as as, isHostRole as at, isLinkFollower as au, loadMeetingActivities as av, withItem as aw, type RecordingSource as ax, type MeetApi as b, type MeetIdentity as c, type CallInvite as d, type ParticipantRole as e, type CallMode as f, type MeetingNote as g, type MeetingId as h, type CollabSnapshot as i, type AdmissionState as j, CAPTION_LANGUAGES as k, type CallId as l, type CallState as m, type CallablePerson as n, type ConnectionQuality as o, type InviteeRole as p, type JsonValue as q, type MeetApiOptions as r, type MeetApiRequest as s, type MeetBreakoutBroadcast as t, type MeetBreakoutPlan as u, type MeetBreakoutRoom as v, type MeetCollabSettings as w, type MeetFileMedia as x, type MeetPoll as y, type MeetPollOption as z };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
-
import { M as MeetingRecord, P as ParticipantIdentity, R as RoomName, a as MeetParticipant } from './collab-
|
|
2
|
+
import { M as MeetingRecord, P as ParticipantIdentity, R as RoomName, a as MeetParticipant } from './collab-B6scmSST.cjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* THE PRODUCT UI (R6/R7). Not a headless kernel with a note telling the host to
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
|
-
import { M as MeetingRecord, P as ParticipantIdentity, R as RoomName, a as MeetParticipant } from './collab-
|
|
2
|
+
import { M as MeetingRecord, P as ParticipantIdentity, R as RoomName, a as MeetParticipant } from './collab-B6scmSST.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* THE PRODUCT UI (R6/R7). Not a headless kernel with a note telling the host to
|
package/dist/core.cjs
CHANGED
|
@@ -50,6 +50,7 @@ __export(core_exports, {
|
|
|
50
50
|
LISTEN_ONLY_NOTICE: () => LISTEN_ONLY_NOTICE,
|
|
51
51
|
MEETING_INVITE_KIND: () => MEETING_INVITE_KIND,
|
|
52
52
|
MEETING_INVITE_VERSION: () => MEETING_INVITE_VERSION,
|
|
53
|
+
MEETING_PASS_HEADER: () => MEETING_PASS_HEADER,
|
|
53
54
|
MEETING_RECORD_GRACE_MS: () => MEETING_RECORD_GRACE_MS,
|
|
54
55
|
MEET_ANSWER_CONTEXT_EVENT: () => MEET_ANSWER_CONTEXT_EVENT,
|
|
55
56
|
MEET_ANSWER_EVENTS: () => MEET_ANSWER_EVENTS,
|
|
@@ -65,12 +66,12 @@ __export(core_exports, {
|
|
|
65
66
|
ORGANIZATION_HEADER: () => ORGANIZATION_HEADER,
|
|
66
67
|
PARTICIPANT_CAN_RECORD_ATTRIBUTE: () => PARTICIPANT_CAN_RECORD_ATTRIBUTE,
|
|
67
68
|
PARTICIPANT_ROLE_ATTRIBUTE: () => PARTICIPANT_ROLE_ATTRIBUTE,
|
|
69
|
+
PARTICIPANT_STANDING_ATTRIBUTE: () => PARTICIPANT_STANDING_ATTRIBUTE,
|
|
68
70
|
REACTION_TTL_MS: () => REACTION_TTL_MS,
|
|
69
71
|
REFUSED_ABSENCE: () => REFUSED_ABSENCE,
|
|
70
72
|
ROLE_RANK: () => ROLE_RANK,
|
|
71
73
|
ROOM_EVENTS: () => ROOM_EVENTS,
|
|
72
74
|
ROOM_EVENT_PREFIX: () => ROOM_EVENT_PREFIX,
|
|
73
|
-
ROOM_TOKEN_HEADER: () => ROOM_TOKEN_HEADER,
|
|
74
75
|
RPCS: () => RPCS,
|
|
75
76
|
TABLES: () => TABLES,
|
|
76
77
|
TRANSCRIPTION_ATTRIBUTES: () => TRANSCRIPTION_ATTRIBUTES,
|
|
@@ -141,6 +142,7 @@ __export(core_exports, {
|
|
|
141
142
|
isGuest: () => isGuest,
|
|
142
143
|
isGuestIdentity: () => isGuestIdentity,
|
|
143
144
|
isHostRole: () => isHostRole,
|
|
145
|
+
isLinkFollower: () => isLinkFollower,
|
|
144
146
|
isListenOnly: () => isListenOnly,
|
|
145
147
|
isNoteTaker: () => isNoteTaker,
|
|
146
148
|
isRealName: () => isRealName,
|
|
@@ -159,6 +161,7 @@ __export(core_exports, {
|
|
|
159
161
|
meetingInviteAction: () => meetingInviteAction,
|
|
160
162
|
meetingLength: () => meetingLength,
|
|
161
163
|
meetingLink: () => meetingLink,
|
|
164
|
+
meetingPassHeaders: () => meetingPassHeaders,
|
|
162
165
|
meetingPath: () => meetingPath,
|
|
163
166
|
meetingSlug: () => meetingSlug,
|
|
164
167
|
meetingTopic: () => meetingTopic,
|
|
@@ -202,6 +205,7 @@ __export(core_exports, {
|
|
|
202
205
|
recordingLabel: () => recordingLabel,
|
|
203
206
|
recordingNotice: () => recordingNotice,
|
|
204
207
|
recordingOffsetMs: () => recordingOffsetMs,
|
|
208
|
+
rememberedMeetingPass: () => rememberedMeetingPass,
|
|
205
209
|
roleOf: () => roleOf,
|
|
206
210
|
roomTopic: () => roomTopic,
|
|
207
211
|
rootAttributes: () => rootAttributes,
|
|
@@ -236,11 +240,18 @@ var MeetError = class extends Error {
|
|
|
236
240
|
/** What to DO. Rendered to the user for the device codes. */
|
|
237
241
|
remedy;
|
|
238
242
|
cause;
|
|
239
|
-
|
|
243
|
+
/**
|
|
244
|
+
* The join gate's reason code (CORE-DESIGN §2.5) when the server named one: `not_found`,
|
|
245
|
+
* `cancelled`, `ended`, `locked`, `removed`, `lobby_rejects`, `not_invited`… The phase
|
|
246
|
+
* renders `refused:<reason>` (or `ended`) from it — never from the HTTP status.
|
|
247
|
+
*/
|
|
248
|
+
reason;
|
|
249
|
+
constructor(code, message, remedy, cause, reason) {
|
|
240
250
|
super(message);
|
|
241
251
|
this.name = "MeetError";
|
|
242
252
|
this.code = code;
|
|
243
253
|
this.remedy = remedy;
|
|
254
|
+
this.reason = reason ?? null;
|
|
244
255
|
if (cause !== void 0) this.cause = cause;
|
|
245
256
|
}
|
|
246
257
|
/** True when retrying after the host re-establishes a session is the fix. */
|
|
@@ -458,6 +469,17 @@ function createWebRoomTokenStorage(onDiagnostic) {
|
|
|
458
469
|
}
|
|
459
470
|
};
|
|
460
471
|
}
|
|
472
|
+
function rememberedMeetingPass(roomName, storage = createWebRoomTokenStorage()) {
|
|
473
|
+
const raw = storage.read(roomName);
|
|
474
|
+
if (raw === null) return null;
|
|
475
|
+
try {
|
|
476
|
+
const parsed = JSON.parse(raw);
|
|
477
|
+
if (parsed.standing !== "guest") return null;
|
|
478
|
+
return typeof parsed.meetingPass === "string" && parsed.meetingPass.length > 0 ? parsed.meetingPass : null;
|
|
479
|
+
} catch {
|
|
480
|
+
return null;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
461
483
|
var MEET_ROUTES = {
|
|
462
484
|
token: "/api/v1/meet/token",
|
|
463
485
|
recordingStart: "/api/v1/meet/recording/start",
|
|
@@ -471,7 +493,7 @@ var MEET_ROUTES = {
|
|
|
471
493
|
/**
|
|
472
494
|
* THE POST-MEETING RECORD FOR A READER WITH NO DATABASE (MRF-1). A `GET`,
|
|
473
495
|
* authorized by the room token this device already holds, carried in the
|
|
474
|
-
* `x-meet-
|
|
496
|
+
* `x-meet-pass` header — never a query parameter, because a bearer
|
|
475
497
|
* credential in a URL lands in access logs, referrers and history.
|
|
476
498
|
*/
|
|
477
499
|
record: "/api/v1/meet/record",
|
|
@@ -479,7 +501,7 @@ var MEET_ROUTES = {
|
|
|
479
501
|
recordAccess: "/api/v1/meet/record/access",
|
|
480
502
|
/**
|
|
481
503
|
* KEEP YOUR NOTES (2026-10-01): a guest who just created an account presents
|
|
482
|
-
* their kept pass in `x-meet-
|
|
504
|
+
* their kept pass in `x-meet-pass` with their new session, and the
|
|
483
505
|
* meeting they attended becomes theirs to read. Idempotent; never a grant.
|
|
484
506
|
*/
|
|
485
507
|
claim: "/api/v1/meet/claim",
|
|
@@ -501,7 +523,7 @@ var MEET_ROUTES = {
|
|
|
501
523
|
* Meet wave 2: EVERY in-meeting host power (mute, stop video, mute all, ask to
|
|
502
524
|
* unmute, spotlight, lower hands, co-host, hand over, rename, remove, report).
|
|
503
525
|
* Decided by the server from the meeting itself; a guest's pass rides
|
|
504
|
-
* `x-meet-
|
|
526
|
+
* `x-meet-pass`.
|
|
505
527
|
*/
|
|
506
528
|
moderate: "/api/v1/meet/moderate",
|
|
507
529
|
/** Meet wave 2: the SAVED chat — `POST` sends to everyone, `GET ?meeting_id=` reads history. */
|
|
@@ -517,7 +539,11 @@ var MEET_ROUTES = {
|
|
|
517
539
|
function meetingPath(template, meetingId) {
|
|
518
540
|
return template.replace("{meeting_id}", encodeURIComponent(meetingId));
|
|
519
541
|
}
|
|
520
|
-
var
|
|
542
|
+
var MEETING_PASS_HEADER = "x-meet-pass";
|
|
543
|
+
function meetingPassHeaders(token) {
|
|
544
|
+
const pass = token?.meetingPass ?? null;
|
|
545
|
+
return pass !== null && pass.length > 0 ? { [MEETING_PASS_HEADER]: pass } : {};
|
|
546
|
+
}
|
|
521
547
|
var REFRESH_RATIO = 0.8;
|
|
522
548
|
function readString(source, key) {
|
|
523
549
|
const value = source[key];
|
|
@@ -527,7 +553,12 @@ function readBoolean(source, key, fallback) {
|
|
|
527
553
|
const value = source[key];
|
|
528
554
|
return typeof value === "boolean" ? value : fallback;
|
|
529
555
|
}
|
|
530
|
-
var
|
|
556
|
+
var TOKEN_STANDINGS = ["member", "invited", "outsider", "guest"];
|
|
557
|
+
function readStanding(source, key) {
|
|
558
|
+
const value = source[key];
|
|
559
|
+
return TOKEN_STANDINGS.includes(value) ? value : null;
|
|
560
|
+
}
|
|
561
|
+
var TOKEN_ROLES = ["host", "cohost", "presenter", "participant", "viewer"];
|
|
531
562
|
function readRole(source, key) {
|
|
532
563
|
const value = source[key];
|
|
533
564
|
return TOKEN_ROLES.includes(value) ? value : null;
|
|
@@ -570,7 +601,12 @@ function parseRoomToken(body, roomName) {
|
|
|
570
601
|
// Deriving the role from what DID arrive keeps a host a host against a
|
|
571
602
|
// server that has not landed MRI-A11 — and never invents a host from
|
|
572
603
|
// silence, because both fallbacks below grant nothing.
|
|
573
|
-
|
|
604
|
+
// A word this package does not know grants nothing: `participant`, never a host.
|
|
605
|
+
role: readRole(payload, "role") ?? (isHost ? "host" : "participant"),
|
|
606
|
+
standing: readStanding(payload, "standing"),
|
|
607
|
+
meetingPass: readString(payload, "meeting_pass"),
|
|
608
|
+
meetingPassExpiresAt: readString(payload, "meeting_pass_expires_at"),
|
|
609
|
+
sessionId: readString(payload, "session_id"),
|
|
574
610
|
lobby: readBoolean(payload, "lobby", false),
|
|
575
611
|
breakoutRoomId: readString(payload, "breakout_room_id") ?? null
|
|
576
612
|
};
|
|
@@ -581,7 +617,7 @@ function createTokenClient(options) {
|
|
|
581
617
|
const storage = options.storage;
|
|
582
618
|
function remember(token) {
|
|
583
619
|
if (storage === void 0) return;
|
|
584
|
-
if (
|
|
620
|
+
if (token.standing !== "guest") return;
|
|
585
621
|
storage.write(token.roomName, JSON.stringify(token));
|
|
586
622
|
}
|
|
587
623
|
function recall(roomName) {
|
|
@@ -639,22 +675,28 @@ function createTokenClient(options) {
|
|
|
639
675
|
throw new MeetError(
|
|
640
676
|
"forbidden",
|
|
641
677
|
said?.message ?? "You can't join this meeting.",
|
|
642
|
-
said?.remedy ?? "Ask the person who shared the link to let you in, then open the link again."
|
|
678
|
+
said?.remedy ?? "Ask the person who shared the link to let you in, then open the link again.",
|
|
679
|
+
void 0,
|
|
680
|
+
said?.reason ?? null
|
|
643
681
|
);
|
|
644
682
|
}
|
|
645
683
|
if (response.status === 404) {
|
|
646
684
|
throw new MeetError(
|
|
647
685
|
"not-found",
|
|
648
686
|
"meet.token: no such meeting",
|
|
649
|
-
"The link is wrong or the meeting was deleted. Show the 'meeting not found' screen; a durable link that 404s is never a retry."
|
|
687
|
+
"The link is wrong or the meeting was deleted. Show the 'meeting not found' screen; a durable link that 404s is never a retry.",
|
|
688
|
+
void 0,
|
|
689
|
+
"not_found"
|
|
650
690
|
);
|
|
651
691
|
}
|
|
652
692
|
if (response.status === 410) {
|
|
653
|
-
const
|
|
693
|
+
const said = await serverSentence(response);
|
|
654
694
|
throw new MeetError(
|
|
655
695
|
"cancelled",
|
|
656
|
-
|
|
657
|
-
"Nobody can join a cancelled meeting. Show the cancelled screen with this sentence; ask the host to schedule a new one."
|
|
696
|
+
said?.message ?? "This meeting was cancelled.",
|
|
697
|
+
"Nobody can join a cancelled meeting. Show the cancelled screen with this sentence; ask the host to schedule a new one.",
|
|
698
|
+
void 0,
|
|
699
|
+
said?.reason ?? "cancelled"
|
|
658
700
|
);
|
|
659
701
|
}
|
|
660
702
|
if (response.status === 423) {
|
|
@@ -662,7 +704,9 @@ function createTokenClient(options) {
|
|
|
662
704
|
throw new MeetError(
|
|
663
705
|
"room-locked",
|
|
664
706
|
said?.message ?? "The meeting is locked.",
|
|
665
|
-
said?.remedy ?? "The host locked the room. Ask them to unlock it, then join again."
|
|
707
|
+
said?.remedy ?? "The host locked the room. Ask them to unlock it, then join again.",
|
|
708
|
+
void 0,
|
|
709
|
+
said?.reason ?? "locked"
|
|
666
710
|
);
|
|
667
711
|
}
|
|
668
712
|
if (response.status === 503) {
|
|
@@ -731,6 +775,10 @@ function placeholder(roomName) {
|
|
|
731
775
|
canRecord: false,
|
|
732
776
|
isHost: false,
|
|
733
777
|
role: "participant",
|
|
778
|
+
standing: null,
|
|
779
|
+
meetingPass: null,
|
|
780
|
+
meetingPassExpiresAt: null,
|
|
781
|
+
sessionId: null,
|
|
734
782
|
lobby: false,
|
|
735
783
|
breakoutRoomId: null
|
|
736
784
|
};
|
|
@@ -743,7 +791,8 @@ async function serverSentence(response) {
|
|
|
743
791
|
const message = typeof source.user_message === "string" && source.user_message.trim() !== "" ? source.user_message : typeof source.message === "string" && source.message.trim() !== "" ? source.message : null;
|
|
744
792
|
if (message === null) return null;
|
|
745
793
|
const remedy = typeof source.remedy === "string" && source.remedy.trim() !== "" ? source.remedy : void 0;
|
|
746
|
-
|
|
794
|
+
const reason = typeof source.reason === "string" && source.reason !== "" ? source.reason : null;
|
|
795
|
+
return { message, remedy, reason };
|
|
747
796
|
} catch {
|
|
748
797
|
return null;
|
|
749
798
|
}
|
|
@@ -932,7 +981,7 @@ function createMeetAi(options) {
|
|
|
932
981
|
operation: "meet.ai.ask",
|
|
933
982
|
// A GUEST MAY ASK. The server decides whether it can answer.
|
|
934
983
|
requireAuth: false,
|
|
935
|
-
...roomToken !== null ? { headers: { [
|
|
984
|
+
...roomToken !== null ? { headers: { [MEETING_PASS_HEADER]: roomToken } } : {},
|
|
936
985
|
...signal !== void 0 ? { signal } : {}
|
|
937
986
|
});
|
|
938
987
|
if (!response.ok) {
|
|
@@ -1192,11 +1241,15 @@ function isGuest(identity) {
|
|
|
1192
1241
|
return identity.userId === null;
|
|
1193
1242
|
}
|
|
1194
1243
|
var ROLE_RANK = {
|
|
1195
|
-
host:
|
|
1196
|
-
cohost:
|
|
1244
|
+
host: 4,
|
|
1245
|
+
cohost: 3,
|
|
1246
|
+
presenter: 2,
|
|
1197
1247
|
participant: 1,
|
|
1198
|
-
|
|
1248
|
+
viewer: 0
|
|
1199
1249
|
};
|
|
1250
|
+
function isLinkFollower(standing) {
|
|
1251
|
+
return standing === "guest" || standing === "outsider";
|
|
1252
|
+
}
|
|
1200
1253
|
function isHostRole(role) {
|
|
1201
1254
|
return role === "host" || role === "cohost";
|
|
1202
1255
|
}
|
|
@@ -1749,15 +1802,15 @@ var CAPTION_LANGUAGES = [
|
|
|
1749
1802
|
{ tag: "id", name: "Indonesian" },
|
|
1750
1803
|
{ tag: "sv", name: "Swedish" }
|
|
1751
1804
|
];
|
|
1752
|
-
async function loadMeetingActivities(api, meetingId,
|
|
1805
|
+
async function loadMeetingActivities(api, meetingId, meetingPass = null) {
|
|
1753
1806
|
let response;
|
|
1754
1807
|
try {
|
|
1755
1808
|
response = await api.request({
|
|
1756
1809
|
path: `${MEET_ROUTES.collab}?meeting_id=${encodeURIComponent(meetingId)}`,
|
|
1757
1810
|
method: "GET",
|
|
1758
1811
|
operation: "meet.loadMeetingActivities",
|
|
1759
|
-
requireAuth:
|
|
1760
|
-
...
|
|
1812
|
+
requireAuth: meetingPass === null,
|
|
1813
|
+
...meetingPass !== null ? { headers: { [MEETING_PASS_HEADER]: meetingPass } } : {}
|
|
1761
1814
|
});
|
|
1762
1815
|
} catch (error) {
|
|
1763
1816
|
return {
|
|
@@ -1814,7 +1867,6 @@ var RPCS = {
|
|
|
1814
1867
|
meetingBySlug: "meet_meeting_by_slug",
|
|
1815
1868
|
admit: "meet_admit_participant",
|
|
1816
1869
|
setLock: "meet_set_lock",
|
|
1817
|
-
endMeeting: "meet_end_meeting",
|
|
1818
1870
|
recordConsent: "meet_record_consent",
|
|
1819
1871
|
/*
|
|
1820
1872
|
* ── MEET WAVE 1 — management (0.8). Every write is a SECURITY DEFINER door
|
|
@@ -2228,7 +2280,7 @@ function createMeetRepository(options) {
|
|
|
2228
2280
|
const operation = "meet.projectAttendee";
|
|
2229
2281
|
const role = text(row, "role");
|
|
2230
2282
|
const admission = text(row, "admission_state");
|
|
2231
|
-
const known = ["host", "cohost", "participant", "
|
|
2283
|
+
const known = ["host", "cohost", "presenter", "participant", "viewer"];
|
|
2232
2284
|
const states = ["knocking", "admitted", "denied", "removed"];
|
|
2233
2285
|
const userId = text(row, "participant_user_id");
|
|
2234
2286
|
const identity = requireText(row, "identity", operation);
|
|
@@ -2543,16 +2595,6 @@ function createMeetRepository(options) {
|
|
|
2543
2595
|
);
|
|
2544
2596
|
return projectMeeting(row);
|
|
2545
2597
|
},
|
|
2546
|
-
async endMeeting(meetingId, byUserId) {
|
|
2547
|
-
const row = await guard(
|
|
2548
|
-
"meet.endMeeting",
|
|
2549
|
-
() => db.rpc(RPCS.endMeeting, {
|
|
2550
|
-
p_meeting_id: meetingId,
|
|
2551
|
-
p_by_user_id: byUserId
|
|
2552
|
-
})
|
|
2553
|
-
);
|
|
2554
|
-
return projectMeeting(row);
|
|
2555
|
-
},
|
|
2556
2598
|
async transcript(meetingId, limit = 1e3) {
|
|
2557
2599
|
const response = await db.from(TABLES.transcripts).select(SEGMENT_COLUMNS).eq("meeting_id", meetingId).order("started_at", { ascending: true }).order("id", { ascending: true }).limit(limit);
|
|
2558
2600
|
if (response.error !== null) throw normalizeMeetError(response.error, "meet.transcript");
|
|
@@ -2881,6 +2923,7 @@ var DEFAULT_EFFECTS = {
|
|
|
2881
2923
|
};
|
|
2882
2924
|
var NO_GRANTS = {
|
|
2883
2925
|
role: "participant",
|
|
2926
|
+
standing: null,
|
|
2884
2927
|
canRecord: false,
|
|
2885
2928
|
canPublish: false,
|
|
2886
2929
|
canSubscribe: false
|
|
@@ -2927,6 +2970,11 @@ function effectiveRole(snapshot) {
|
|
|
2927
2970
|
const fromToken = snapshot.grants.role;
|
|
2928
2971
|
return ROLE_RANK[fromRoster] >= ROLE_RANK[fromToken] ? fromRoster : fromToken;
|
|
2929
2972
|
}
|
|
2973
|
+
function effectiveStanding(snapshot) {
|
|
2974
|
+
if (snapshot.grants.standing != null) return snapshot.grants.standing;
|
|
2975
|
+
const me = snapshot.participants.find((p) => p.identity === snapshot.localIdentity);
|
|
2976
|
+
return me?.standing ?? null;
|
|
2977
|
+
}
|
|
2930
2978
|
var REACTION_TTL_MS = 5e3;
|
|
2931
2979
|
var CHAT_LIMIT = 200;
|
|
2932
2980
|
function createMeetStore(initial = {}) {
|
|
@@ -3419,7 +3467,7 @@ function applyModeration(store, payload, now) {
|
|
|
3419
3467
|
return ignored(`a moderation kind this version does not know: ${String(kind)}`);
|
|
3420
3468
|
}
|
|
3421
3469
|
}
|
|
3422
|
-
var ROLES = ["host", "cohost", "participant", "
|
|
3470
|
+
var ROLES = ["host", "cohost", "presenter", "participant", "viewer"];
|
|
3423
3471
|
|
|
3424
3472
|
// src/core/devices.ts
|
|
3425
3473
|
var KINDS = ["audioinput", "videoinput", "audiooutput"];
|
|
@@ -4614,7 +4662,6 @@ function kindOf(person) {
|
|
|
4614
4662
|
if (person.isAgent) return "assistant";
|
|
4615
4663
|
if (person.role === "host") return "host";
|
|
4616
4664
|
if (person.role === "cohost") return "cohost";
|
|
4617
|
-
if (person.role === "guest") return person.userId !== null ? "signed-in guest" : "guest";
|
|
4618
4665
|
if (person.userId === null) return "guest";
|
|
4619
4666
|
return "member";
|
|
4620
4667
|
}
|
|
@@ -4899,7 +4946,7 @@ function meetingLength(meeting) {
|
|
|
4899
4946
|
if (start === null || end === null || end < start) return null;
|
|
4900
4947
|
return (0, import_format.formatDurationMs)(end - start, { style: "long" });
|
|
4901
4948
|
}
|
|
4902
|
-
async function loadMeetingChat(api, meetingId,
|
|
4949
|
+
async function loadMeetingChat(api, meetingId, meetingPass = null) {
|
|
4903
4950
|
const operation = "meet.loadMeetingChat";
|
|
4904
4951
|
let response;
|
|
4905
4952
|
try {
|
|
@@ -4907,8 +4954,8 @@ async function loadMeetingChat(api, meetingId, roomToken = null) {
|
|
|
4907
4954
|
path: `${MEET_ROUTES.chat}?meeting_id=${encodeURIComponent(meetingId)}`,
|
|
4908
4955
|
method: "GET",
|
|
4909
4956
|
operation,
|
|
4910
|
-
requireAuth:
|
|
4911
|
-
...
|
|
4957
|
+
requireAuth: meetingPass === null,
|
|
4958
|
+
...meetingPass !== null ? { headers: { [MEETING_PASS_HEADER]: meetingPass } } : {}
|
|
4912
4959
|
});
|
|
4913
4960
|
} catch (error) {
|
|
4914
4961
|
return {
|
|
@@ -5052,7 +5099,12 @@ async function pushMeetingToGoogleCalendar(api, meetingId, target) {
|
|
|
5052
5099
|
// src/core/room.ts
|
|
5053
5100
|
var PARTICIPANT_ROLE_ATTRIBUTE = "matrx.participant.role";
|
|
5054
5101
|
var PARTICIPANT_CAN_RECORD_ATTRIBUTE = "matrx.participant.can_record";
|
|
5055
|
-
var
|
|
5102
|
+
var PARTICIPANT_STANDING_ATTRIBUTE = "matrx.participant.standing";
|
|
5103
|
+
var KNOWN_STANDINGS = ["member", "invited", "outsider", "guest"];
|
|
5104
|
+
function parseStanding(value) {
|
|
5105
|
+
return KNOWN_STANDINGS.includes(value) ? value : null;
|
|
5106
|
+
}
|
|
5107
|
+
var KNOWN_ROLES = ["host", "cohost", "presenter", "participant", "viewer"];
|
|
5056
5108
|
function parseRole(value) {
|
|
5057
5109
|
return KNOWN_ROLES.includes(value) ? value : "participant";
|
|
5058
5110
|
}
|
|
@@ -5086,6 +5138,9 @@ function projectParticipant(driver, joinedAt) {
|
|
|
5086
5138
|
// ATTRIBUTE FIRST, metadata second — see the constants above. Both are the
|
|
5087
5139
|
// server's; the attribute is simply the one that always arrives.
|
|
5088
5140
|
role: parseRole(driver.attributes[PARTICIPANT_ROLE_ATTRIBUTE] ?? metadata.role),
|
|
5141
|
+
standing: parseStanding(
|
|
5142
|
+
driver.attributes[PARTICIPANT_STANDING_ATTRIBUTE] ?? metadata.standing
|
|
5143
|
+
),
|
|
5089
5144
|
canRecord: driver.attributes[PARTICIPANT_CAN_RECORD_ATTRIBUTE] === "true" || metadata.can_record === true,
|
|
5090
5145
|
isLocal: driver.isLocal,
|
|
5091
5146
|
isAgent: metadata.is_agent === true,
|
|
@@ -5158,7 +5213,7 @@ function createRoomEngine(options) {
|
|
|
5158
5213
|
const normalized = normalizeMeetError(error, operation);
|
|
5159
5214
|
store.patch({
|
|
5160
5215
|
phase: "failed",
|
|
5161
|
-
failure: { message: normalized.message, remedy: normalized.remedy }
|
|
5216
|
+
failure: { message: normalized.message, remedy: normalized.remedy, reason: normalized.reason }
|
|
5162
5217
|
});
|
|
5163
5218
|
report(
|
|
5164
5219
|
normalized.code === "session-unavailable" ? "warn" : "error",
|
|
@@ -5362,6 +5417,7 @@ function createRoomEngine(options) {
|
|
|
5362
5417
|
breakoutRoomId: token.breakoutRoomId ?? null,
|
|
5363
5418
|
grants: {
|
|
5364
5419
|
role: token.role,
|
|
5420
|
+
standing: token.standing,
|
|
5365
5421
|
canRecord: token.canRecord,
|
|
5366
5422
|
canPublish: token.canPublish,
|
|
5367
5423
|
canSubscribe: token.canSubscribe
|
|
@@ -5707,7 +5763,7 @@ function createInRoomClient(options) {
|
|
|
5707
5763
|
}
|
|
5708
5764
|
function headers() {
|
|
5709
5765
|
const token = options.resolveRoomToken();
|
|
5710
|
-
return token === null ? void 0 : { [
|
|
5766
|
+
return token === null ? void 0 : { [MEETING_PASS_HEADER]: token };
|
|
5711
5767
|
}
|
|
5712
5768
|
function refusal2(response, operation, remedy) {
|
|
5713
5769
|
return refusalFromResponse(response, operation, remedy);
|
|
@@ -6188,7 +6244,7 @@ function summarizeMeetingRecordRestrictions(restrictions) {
|
|
|
6188
6244
|
async function loadGuestMeetingRecord(args) {
|
|
6189
6245
|
const { api, repository, meetingId, meeting, roomToken } = args;
|
|
6190
6246
|
const session = args.session === true;
|
|
6191
|
-
if (roomToken
|
|
6247
|
+
if (roomToken?.meetingPass == null && !session) {
|
|
6192
6248
|
return refusedBundle(
|
|
6193
6249
|
meeting,
|
|
6194
6250
|
"This meeting's record is only readable by the people who were in it.",
|
|
@@ -6198,12 +6254,12 @@ async function loadGuestMeetingRecord(args) {
|
|
|
6198
6254
|
let response;
|
|
6199
6255
|
try {
|
|
6200
6256
|
response = await api.request({
|
|
6201
|
-
path: `${MEET_ROUTES.record}?meeting_id=${encodeURIComponent(meetingId)}
|
|
6257
|
+
path: `${MEET_ROUTES.record}?meeting_id=${encodeURIComponent(meetingId)}` + (args.sessionId ? `&session_id=${encodeURIComponent(args.sessionId)}` : ""),
|
|
6202
6258
|
method: "GET",
|
|
6203
6259
|
// A GUEST HAS NO SESSION AND IS STILL A FIRST-CLASS READER (D6).
|
|
6204
6260
|
requireAuth: session,
|
|
6205
6261
|
operation: "meet.record",
|
|
6206
|
-
headers: roomToken
|
|
6262
|
+
headers: roomToken?.meetingPass != null ? { [MEETING_PASS_HEADER]: roomToken.meetingPass } : {}
|
|
6207
6263
|
});
|
|
6208
6264
|
} catch (error) {
|
|
6209
6265
|
return refusedBundle(
|
|
@@ -6591,6 +6647,10 @@ function takeMoveRequest(store) {
|
|
|
6591
6647
|
}
|
|
6592
6648
|
|
|
6593
6649
|
// src/core/observation.ts
|
|
6650
|
+
function observedRole(role, standing) {
|
|
6651
|
+
if (role === "host" || role === "cohost") return role;
|
|
6652
|
+
return isLinkFollower(standing) ? "guest" : "participant";
|
|
6653
|
+
}
|
|
6594
6654
|
function observedDevice(track) {
|
|
6595
6655
|
if (track.enabled) return "on";
|
|
6596
6656
|
const refused = track.unavailable;
|
|
@@ -6618,6 +6678,8 @@ function observedPhase(input) {
|
|
|
6618
6678
|
const raw = snapshot.phase;
|
|
6619
6679
|
const live = raw === "connected" || raw === "reconnecting";
|
|
6620
6680
|
if (endedRecord(snapshot, input.meeting ?? null) && !live) return "ended";
|
|
6681
|
+
const reason = raw === "failed" ? snapshot.failure?.reason ?? null : null;
|
|
6682
|
+
if (reason !== null) return reason === "ended" ? "ended" : `refused:${reason}`;
|
|
6621
6683
|
switch (raw) {
|
|
6622
6684
|
case "idle":
|
|
6623
6685
|
case "failed":
|
|
@@ -6638,12 +6700,13 @@ function observe(input) {
|
|
|
6638
6700
|
const { snapshot } = input;
|
|
6639
6701
|
const phase = observedPhase(input);
|
|
6640
6702
|
const inCall = phase === "in_call";
|
|
6641
|
-
const
|
|
6642
|
-
const canAdmit =
|
|
6703
|
+
const actual = inCall && snapshot !== null ? effectiveRole(snapshot) : "none";
|
|
6704
|
+
const canAdmit = actual !== "none" && ADMITTER_ROLES.has(actual);
|
|
6705
|
+
const role = actual === "none" || snapshot === null ? "none" : observedRole(actual, effectiveStanding(snapshot));
|
|
6643
6706
|
const deviceInPlay = phase === "joining" || inCall;
|
|
6644
6707
|
return {
|
|
6645
6708
|
phase,
|
|
6646
|
-
reason: null,
|
|
6709
|
+
reason: phase.startsWith("refused:") ? phase.slice("refused:".length) : null,
|
|
6647
6710
|
role,
|
|
6648
6711
|
connection: snapshot?.phase === "reconnecting" ? "reconnecting" : "stable",
|
|
6649
6712
|
hostPresent: inCall && snapshot !== null ? snapshot.participants.some((p) => ADMITTER_ROLES.has(p.role)) : null,
|
|
@@ -6709,7 +6772,7 @@ function participantAttributes(p) {
|
|
|
6709
6772
|
"data-meet-participant": "",
|
|
6710
6773
|
"data-meet-participant-identity": p.identity,
|
|
6711
6774
|
"data-meet-participant-name": p.displayName,
|
|
6712
|
-
"data-meet-participant-role": p.role,
|
|
6775
|
+
"data-meet-participant-role": observedRole(p.role, p.standing),
|
|
6713
6776
|
"data-meet-participant-self": String(p.isLocal)
|
|
6714
6777
|
};
|
|
6715
6778
|
const quality = observedQuality(p.quality);
|