@ai-matrx/meet 0.7.2 → 0.7.3
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 +20 -0
- package/dist/index.cjs +460 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +268 -3
- package/dist/index.d.ts +268 -3
- package/dist/index.js +460 -35
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +463 -38
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +463 -38
- package/dist/react.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -69,6 +69,7 @@ __export(src_exports, {
|
|
|
69
69
|
TRANSCRIPTION_ATTRIBUTES: () => TRANSCRIPTION_ATTRIBUTES,
|
|
70
70
|
TRANSCRIPTION_TOPIC: () => TRANSCRIPTION_TOPIC,
|
|
71
71
|
absenceSentence: () => absenceSentence,
|
|
72
|
+
announceMeeting: () => announceMeeting,
|
|
72
73
|
applyRoomEvent: () => applyRoomEvent,
|
|
73
74
|
asCallId: () => asCallId,
|
|
74
75
|
asMeetingId: () => asMeetingId,
|
|
@@ -83,6 +84,7 @@ __export(src_exports, {
|
|
|
83
84
|
canModerateParticipant: () => canModerateParticipant,
|
|
84
85
|
canTransition: () => canTransition,
|
|
85
86
|
captionSegment: () => captionSegment,
|
|
87
|
+
changesPatch: () => changesPatch,
|
|
86
88
|
computeStage: () => computeStage,
|
|
87
89
|
countLabel: () => countLabel,
|
|
88
90
|
createCallCenter: () => createCallCenter,
|
|
@@ -127,6 +129,7 @@ __export(src_exports, {
|
|
|
127
129
|
mapQuality: () => mapQuality,
|
|
128
130
|
meetingInviteAction: () => meetingInviteAction,
|
|
129
131
|
meetingLink: () => meetingLink,
|
|
132
|
+
meetingPath: () => meetingPath,
|
|
130
133
|
meetingSlug: () => meetingSlug,
|
|
131
134
|
meetingTopic: () => meetingTopic,
|
|
132
135
|
meetingWhen: () => meetingWhen,
|
|
@@ -143,9 +146,15 @@ __export(src_exports, {
|
|
|
143
146
|
parseRoomToken: () => parseRoomToken,
|
|
144
147
|
participantSummary: () => participantSummary,
|
|
145
148
|
playableRecordingFileId: () => playableRecordingFileId,
|
|
149
|
+
projectInvitee: () => projectInvitee,
|
|
146
150
|
projectNote: () => projectNote,
|
|
151
|
+
projectOccurrence: () => projectOccurrence,
|
|
152
|
+
projectOverride: () => projectOverride,
|
|
147
153
|
projectParticipant: () => projectParticipant,
|
|
154
|
+
projectRsvpInvitation: () => projectRsvpInvitation,
|
|
148
155
|
projectSegment: () => projectSegment,
|
|
156
|
+
projectUpcoming: () => projectUpcoming,
|
|
157
|
+
pushMeetingToGoogleCalendar: () => pushMeetingToGoogleCalendar,
|
|
149
158
|
readServerRefusal: () => readServerRefusal,
|
|
150
159
|
readableTranscript: () => readableTranscript,
|
|
151
160
|
recordingLabel: () => recordingLabel,
|
|
@@ -153,6 +162,7 @@ __export(src_exports, {
|
|
|
153
162
|
roleOf: () => roleOf,
|
|
154
163
|
roomTopic: () => roomTopic,
|
|
155
164
|
setGuestRecordAccess: () => setGuestRecordAccess,
|
|
165
|
+
settingsPatch: () => settingsPatch,
|
|
156
166
|
snapshotParticipant: () => snapshotParticipant,
|
|
157
167
|
unavailableFrom: () => unavailableFrom,
|
|
158
168
|
userTopic: () => userTopic,
|
|
@@ -187,7 +197,7 @@ var MeetError = class extends Error {
|
|
|
187
197
|
};
|
|
188
198
|
var FORBIDDEN_CODES = /* @__PURE__ */ new Set(["42501", "PGRST301", "PGRST302"]);
|
|
189
199
|
var NOT_FOUND_CODES = /* @__PURE__ */ new Set(["PGRST116", "PGRST205"]);
|
|
190
|
-
var CONFLICT_CODES = /* @__PURE__ */ new Set(["23505"]);
|
|
200
|
+
var CONFLICT_CODES = /* @__PURE__ */ new Set(["23505", "40001", "55000"]);
|
|
191
201
|
var SESSION_MARKERS = [
|
|
192
202
|
"auth session missing",
|
|
193
203
|
"jwt expired",
|
|
@@ -345,8 +355,19 @@ var MEET_ROUTES = {
|
|
|
345
355
|
*/
|
|
346
356
|
record: "/api/v1/meet/record",
|
|
347
357
|
/** A host chooses what guests may read of ONE meeting (MRF-1). */
|
|
348
|
-
recordAccess: "/api/v1/meet/record/access"
|
|
358
|
+
recordAccess: "/api/v1/meet/record/access",
|
|
359
|
+
/**
|
|
360
|
+
* Meet wave 1 (0.8): bring every invitee up to date after a write — sends the
|
|
361
|
+
* invitation / update / cancellation emails with their `.ics` and re-queues
|
|
362
|
+
* reminders. Idempotent. `{meeting_id}` is substituted by `meetingPath`.
|
|
363
|
+
*/
|
|
364
|
+
meetingAnnounce: "/api/v1/meet/meetings/{meeting_id}/announce",
|
|
365
|
+
/** Put a one-off meeting on the caller's own Google Calendar (gated by the server). */
|
|
366
|
+
meetingGoogleCalendar: "/api/v1/meet/meetings/{meeting_id}/google-calendar"
|
|
349
367
|
};
|
|
368
|
+
function meetingPath(template, meetingId) {
|
|
369
|
+
return template.replace("{meeting_id}", encodeURIComponent(meetingId));
|
|
370
|
+
}
|
|
350
371
|
var ROOM_TOKEN_HEADER = "x-meet-room-token";
|
|
351
372
|
var REFRESH_RATIO = 0.8;
|
|
352
373
|
function readString(source, key) {
|
|
@@ -476,6 +497,19 @@ function createTokenClient(options) {
|
|
|
476
497
|
"The link is wrong or the meeting was deleted. Show the 'meeting not found' screen; a durable link that 404s is never a retry."
|
|
477
498
|
);
|
|
478
499
|
}
|
|
500
|
+
if (response.status === 410) {
|
|
501
|
+
let reason = "This meeting was cancelled.";
|
|
502
|
+
try {
|
|
503
|
+
const body = await response.json();
|
|
504
|
+
if (typeof body?.detail?.message === "string") reason = body.detail.message;
|
|
505
|
+
} catch {
|
|
506
|
+
}
|
|
507
|
+
throw new MeetError(
|
|
508
|
+
"cancelled",
|
|
509
|
+
reason,
|
|
510
|
+
"Nobody can join a cancelled meeting. Show the cancelled screen with this sentence; ask the host to schedule a new one."
|
|
511
|
+
);
|
|
512
|
+
}
|
|
479
513
|
if (response.status === 423) {
|
|
480
514
|
throw new MeetError(
|
|
481
515
|
"room-locked",
|
|
@@ -677,12 +711,12 @@ function createMeetAi(options) {
|
|
|
677
711
|
async function dispatch(route, body, operation) {
|
|
678
712
|
const response = await send({ path: route, body, operation, requireAuth: true });
|
|
679
713
|
if (!response.ok) {
|
|
680
|
-
const
|
|
681
|
-
options.store.patch({ noteTakerRefusal:
|
|
714
|
+
const refusal2 = await refusalFor(response, operation);
|
|
715
|
+
options.store.patch({ noteTakerRefusal: refusal2 });
|
|
682
716
|
throw new MeetError(
|
|
683
717
|
response.status === 403 ? "forbidden" : "transport",
|
|
684
|
-
|
|
685
|
-
|
|
718
|
+
refusal2.reason,
|
|
719
|
+
refusal2.remedy
|
|
686
720
|
);
|
|
687
721
|
}
|
|
688
722
|
options.store.patch({ noteTakerRefusal: null });
|
|
@@ -720,11 +754,11 @@ function createMeetAi(options) {
|
|
|
720
754
|
...signal !== void 0 ? { signal } : {}
|
|
721
755
|
});
|
|
722
756
|
if (!response.ok) {
|
|
723
|
-
const
|
|
757
|
+
const refusal2 = await refusalFor(response, "meet.ai.ask");
|
|
724
758
|
throw new MeetError(
|
|
725
759
|
response.status === 403 || response.status === 401 ? "forbidden" : response.status === 404 ? "not-found" : "transport",
|
|
726
|
-
|
|
727
|
-
|
|
760
|
+
refusal2.reason,
|
|
761
|
+
refusal2.remedy
|
|
728
762
|
);
|
|
729
763
|
}
|
|
730
764
|
const body = response.body;
|
|
@@ -833,14 +867,14 @@ function noteTakerStatus(args) {
|
|
|
833
867
|
const row = typeof raw === "object" && raw !== null && !Array.isArray(raw) ? raw : {};
|
|
834
868
|
const recorded = readState(row["state"]);
|
|
835
869
|
const detail = typeof row["reason"] === "string" && row["reason"].length > 0 ? row["reason"] : null;
|
|
836
|
-
const
|
|
870
|
+
const refusal2 = args.refusal ?? null;
|
|
837
871
|
if (recorded === null) {
|
|
838
|
-
return { state: "absent", detail, present, refusal };
|
|
872
|
+
return { state: "absent", detail, present, refusal: refusal2 };
|
|
839
873
|
}
|
|
840
874
|
if (recorded === "transcribing" && !present) {
|
|
841
|
-
return { state: "handoff_pending", detail, present, refusal };
|
|
875
|
+
return { state: "handoff_pending", detail, present, refusal: refusal2 };
|
|
842
876
|
}
|
|
843
|
-
return { state: recorded, detail, present, refusal };
|
|
877
|
+
return { state: recorded, detail, present, refusal: refusal2 };
|
|
844
878
|
}
|
|
845
879
|
function noteTakerLabel(status) {
|
|
846
880
|
switch (status.state) {
|
|
@@ -1253,7 +1287,11 @@ var TABLES = {
|
|
|
1253
1287
|
invites: "meet_call_invites",
|
|
1254
1288
|
recordings: "meet_recordings",
|
|
1255
1289
|
transcripts: "meet_transcript_segments",
|
|
1256
|
-
notes: "meet_notes"
|
|
1290
|
+
notes: "meet_notes",
|
|
1291
|
+
/** Wave 1 (0.8): who a meeting was sent to, and what they answered. */
|
|
1292
|
+
invitees: "meet_invitees",
|
|
1293
|
+
/** Wave 1 (0.8): one occurrence of a series cancelled or moved. */
|
|
1294
|
+
occurrenceOverrides: "meet_occurrence_overrides"
|
|
1257
1295
|
};
|
|
1258
1296
|
var RPCS = {
|
|
1259
1297
|
/** Atomic: create the invite row and stamp its expiry in one statement. */
|
|
@@ -1269,10 +1307,41 @@ var RPCS = {
|
|
|
1269
1307
|
admit: "meet_admit_participant",
|
|
1270
1308
|
setLock: "meet_set_lock",
|
|
1271
1309
|
endMeeting: "meet_end_meeting",
|
|
1272
|
-
recordConsent: "meet_record_consent"
|
|
1310
|
+
recordConsent: "meet_record_consent",
|
|
1311
|
+
/*
|
|
1312
|
+
* ── MEET WAVE 1 — management (0.8). Every write is a SECURITY DEFINER door
|
|
1313
|
+
* authorized by the meeting's host or a real iam grant (editor to change,
|
|
1314
|
+
* admin = co-host to archive or name a co-host) — never an organization
|
|
1315
|
+
* match. After any of these writes, call `announceMeeting` so the server
|
|
1316
|
+
* sends invitations / updates / cancellations and re-queues reminders.
|
|
1317
|
+
*/
|
|
1318
|
+
/** Schedule with every setting at create; unset settings come from the knobs. */
|
|
1319
|
+
scheduleMeeting: "meet_schedule_meeting",
|
|
1320
|
+
/** Edit / reschedule — a JSON patch with a version compare-and-swap. */
|
|
1321
|
+
updateMeeting: "meet_update_meeting",
|
|
1322
|
+
/** Cancel is a state (`cancelled_at`); the link keeps resolving. */
|
|
1323
|
+
cancelMeeting: "meet_cancel_meeting",
|
|
1324
|
+
/** Archive (soft delete) — the invitations and exceptions cascade. */
|
|
1325
|
+
archiveMeeting: "meet_archive_meeting",
|
|
1326
|
+
restoreMeeting: "meet_restore_meeting",
|
|
1327
|
+
/** Cancel, move or restore ONE occurrence of a series by its original start. */
|
|
1328
|
+
setOccurrence: "meet_set_occurrence",
|
|
1329
|
+
addInvitees: "meet_add_invitees",
|
|
1330
|
+
setInviteeRole: "meet_set_invitee_role",
|
|
1331
|
+
removeInvitee: "meet_remove_invitee",
|
|
1332
|
+
/** A signed-in invitee answers for themselves. */
|
|
1333
|
+
respond: "meet_respond",
|
|
1334
|
+
/** The emailed RSVP link (no account): read and answer. */
|
|
1335
|
+
invitationByToken: "meet_invitation_by_token",
|
|
1336
|
+
respondByToken: "meet_respond_by_token",
|
|
1337
|
+
/** One meeting's occurrences between two instants (INVOKER — the reader's RLS). */
|
|
1338
|
+
meetingOccurrences: "meet_meeting_occurrences",
|
|
1339
|
+
/** Every occurrence the reader can see between two instants — the Upcoming list. */
|
|
1340
|
+
occurrencesBetween: "meet_occurrences_between"
|
|
1273
1341
|
};
|
|
1274
1342
|
var VOID_RPCS = [RPCS.admit, RPCS.recordConsent];
|
|
1275
|
-
var MEETING_COLUMNS = "id,organization_id,room_name,slug,title,kind,host_user_id,scheduled_for,scheduled_duration_minutes,started_at,ended_at,locked,lobby_enabled,recording_policy,ai_enabled,metadata";
|
|
1343
|
+
var MEETING_COLUMNS = "id,organization_id,room_name,slug,title,kind,host_user_id,scheduled_for,scheduled_duration_minutes,started_at,ended_at,locked,lobby_enabled,recording_policy,ai_enabled,metadata,time_zone,agenda,recurrence_rule,join_before_host,cancelled_at,cancellation_reason,deleted_at,calendar_sequence,version";
|
|
1344
|
+
var INVITEE_COLUMNS = "id,meeting_id,invitee_user_id,email,display_name,role,rsvp_state,responded_at,rsvp_note,last_sent_sequence,last_sent_at";
|
|
1276
1345
|
var RECORDING_COLUMNS = "id,meeting_id,state,started_at,ended_at,started_by,file_id,failure_reason";
|
|
1277
1346
|
var SEGMENT_COLUMNS = "id,meeting_id,identity,speaker,text,started_at,is_final,metadata";
|
|
1278
1347
|
var NOTE_COLUMNS = "id,meeting_id,text,kind,assignee_user_id,created_at,metadata";
|
|
@@ -1370,6 +1439,138 @@ function projectNote(row) {
|
|
|
1370
1439
|
source: "record"
|
|
1371
1440
|
};
|
|
1372
1441
|
}
|
|
1442
|
+
var RSVP_STATES = ["needs_action", "accepted", "declined", "tentative"];
|
|
1443
|
+
var OCCURRENCE_STATES = ["scheduled", "moved", "cancelled"];
|
|
1444
|
+
function rsvpState(value) {
|
|
1445
|
+
return RSVP_STATES.includes(value) ? value : "needs_action";
|
|
1446
|
+
}
|
|
1447
|
+
function num(row, key) {
|
|
1448
|
+
const value = row[key];
|
|
1449
|
+
return typeof value === "number" ? value : null;
|
|
1450
|
+
}
|
|
1451
|
+
function when(row, key) {
|
|
1452
|
+
const value = row[key];
|
|
1453
|
+
if (value instanceof Date) return value.toISOString();
|
|
1454
|
+
return text(row, key);
|
|
1455
|
+
}
|
|
1456
|
+
function projectInvitee(row) {
|
|
1457
|
+
const operation = "meet.projectInvitee";
|
|
1458
|
+
const userId = text(row, "invitee_user_id");
|
|
1459
|
+
return {
|
|
1460
|
+
id: requireText(row, "id", operation),
|
|
1461
|
+
meetingId: asMeetingId(requireText(row, "meeting_id", operation)),
|
|
1462
|
+
userId: userId === null ? null : asUserId(userId),
|
|
1463
|
+
email: text(row, "email"),
|
|
1464
|
+
displayName: text(row, "display_name"),
|
|
1465
|
+
role: text(row, "role") === "cohost" ? "cohost" : "invitee",
|
|
1466
|
+
rsvpState: rsvpState(text(row, "rsvp_state")),
|
|
1467
|
+
respondedAt: when(row, "responded_at"),
|
|
1468
|
+
rsvpNote: text(row, "rsvp_note"),
|
|
1469
|
+
lastSentSequence: num(row, "last_sent_sequence"),
|
|
1470
|
+
lastSentAt: when(row, "last_sent_at")
|
|
1471
|
+
};
|
|
1472
|
+
}
|
|
1473
|
+
function projectOccurrence(row) {
|
|
1474
|
+
const operation = "meet.projectOccurrence";
|
|
1475
|
+
const state = text(row, "state");
|
|
1476
|
+
return {
|
|
1477
|
+
meetingId: asMeetingId(requireText(row, "meeting_id", operation)),
|
|
1478
|
+
originalStart: when(row, "original_start") ?? requireText(row, "original_start", operation),
|
|
1479
|
+
occurrenceStart: when(row, "occurrence_start") ?? requireText(row, "occurrence_start", operation),
|
|
1480
|
+
durationMinutes: num(row, "duration_minutes"),
|
|
1481
|
+
// An unknown state is shown as scheduled, never dropped: the row is real.
|
|
1482
|
+
state: OCCURRENCE_STATES.includes(state) ? state : "scheduled",
|
|
1483
|
+
sequence: num(row, "sequence") ?? 0
|
|
1484
|
+
};
|
|
1485
|
+
}
|
|
1486
|
+
function projectUpcoming(row) {
|
|
1487
|
+
const operation = "meet.projectUpcoming";
|
|
1488
|
+
const kind = text(row, "kind");
|
|
1489
|
+
const myRole = text(row, "my_role");
|
|
1490
|
+
const myRsvp = text(row, "my_rsvp");
|
|
1491
|
+
return {
|
|
1492
|
+
...projectOccurrence(row),
|
|
1493
|
+
organizationId: asOrganizationId(requireText(row, "organization_id", operation)),
|
|
1494
|
+
title: text(row, "title") ?? "Meeting",
|
|
1495
|
+
slug: requireText(row, "slug", operation),
|
|
1496
|
+
kind: kind === "scheduled" || kind === "recurring" ? kind : "instant",
|
|
1497
|
+
hostUserId: asUserId(requireText(row, "host_user_id", operation)),
|
|
1498
|
+
timeZone: text(row, "time_zone") ?? "UTC",
|
|
1499
|
+
meetingCancelled: bool(row, "meeting_cancelled", false),
|
|
1500
|
+
myRsvp: myRsvp === null ? null : rsvpState(myRsvp),
|
|
1501
|
+
myRole: myRole === "host" || myRole === "cohost" || myRole === "invitee" ? myRole : null
|
|
1502
|
+
};
|
|
1503
|
+
}
|
|
1504
|
+
function projectOverride(row) {
|
|
1505
|
+
const operation = "meet.projectOverride";
|
|
1506
|
+
return {
|
|
1507
|
+
id: requireText(row, "id", operation),
|
|
1508
|
+
meetingId: asMeetingId(requireText(row, "meeting_id", operation)),
|
|
1509
|
+
originalStart: when(row, "original_start") ?? requireText(row, "original_start", operation),
|
|
1510
|
+
state: text(row, "state") === "moved" ? "moved" : "cancelled",
|
|
1511
|
+
newStart: when(row, "new_start"),
|
|
1512
|
+
newDurationMinutes: num(row, "new_duration_minutes"),
|
|
1513
|
+
reason: text(row, "reason"),
|
|
1514
|
+
calendarSequence: num(row, "calendar_sequence") ?? 0,
|
|
1515
|
+
deletedAt: when(row, "deleted_at")
|
|
1516
|
+
};
|
|
1517
|
+
}
|
|
1518
|
+
function projectRsvpInvitation(body) {
|
|
1519
|
+
const operation = "meet.projectRsvpInvitation";
|
|
1520
|
+
if (typeof body !== "object" || body === null) {
|
|
1521
|
+
throw invalidResponse(operation, "the RSVP door returned no object");
|
|
1522
|
+
}
|
|
1523
|
+
const row = body;
|
|
1524
|
+
if (row["ok"] !== true) {
|
|
1525
|
+
return {
|
|
1526
|
+
ok: false,
|
|
1527
|
+
message: text(row, "message") ?? "This invitation link is no longer valid \u2014 ask the host to send it again."
|
|
1528
|
+
};
|
|
1529
|
+
}
|
|
1530
|
+
const meeting = row["meeting"] ?? {};
|
|
1531
|
+
const invitee = row["invitee"] ?? {};
|
|
1532
|
+
return {
|
|
1533
|
+
ok: true,
|
|
1534
|
+
meeting: {
|
|
1535
|
+
title: text(meeting, "title") ?? "Meeting",
|
|
1536
|
+
slug: requireText(meeting, "slug", operation),
|
|
1537
|
+
scheduledFor: when(meeting, "scheduled_for"),
|
|
1538
|
+
durationMinutes: num(meeting, "duration_minutes"),
|
|
1539
|
+
timeZone: text(meeting, "time_zone") ?? "UTC",
|
|
1540
|
+
agenda: text(meeting, "agenda"),
|
|
1541
|
+
recurrenceRule: text(meeting, "recurrence_rule"),
|
|
1542
|
+
cancelledAt: when(meeting, "cancelled_at"),
|
|
1543
|
+
cancellationReason: text(meeting, "cancellation_reason"),
|
|
1544
|
+
endedAt: when(meeting, "ended_at")
|
|
1545
|
+
},
|
|
1546
|
+
invitee: {
|
|
1547
|
+
displayName: text(invitee, "display_name"),
|
|
1548
|
+
email: text(invitee, "email"),
|
|
1549
|
+
rsvpState: rsvpState(text(invitee, "rsvp_state")),
|
|
1550
|
+
respondedAt: when(invitee, "responded_at")
|
|
1551
|
+
}
|
|
1552
|
+
};
|
|
1553
|
+
}
|
|
1554
|
+
function settingsPatch(settings) {
|
|
1555
|
+
const patch = {};
|
|
1556
|
+
if (settings.lobbyEnabled !== void 0) patch["lobby_enabled"] = settings.lobbyEnabled;
|
|
1557
|
+
if (settings.recordingPolicy !== void 0) patch["recording_policy"] = settings.recordingPolicy;
|
|
1558
|
+
if (settings.aiEnabled !== void 0) patch["ai_enabled"] = settings.aiEnabled;
|
|
1559
|
+
if (settings.joinBeforeHost !== void 0) patch["join_before_host"] = settings.joinBeforeHost;
|
|
1560
|
+
return patch;
|
|
1561
|
+
}
|
|
1562
|
+
function changesPatch(changes) {
|
|
1563
|
+
const patch = settingsPatch(changes);
|
|
1564
|
+
if (changes.title !== void 0) patch["title"] = changes.title;
|
|
1565
|
+
if (changes.agenda !== void 0) patch["agenda"] = changes.agenda;
|
|
1566
|
+
if (changes.timeZone !== void 0) patch["time_zone"] = changes.timeZone;
|
|
1567
|
+
if (changes.scheduledFor !== void 0) patch["scheduled_for"] = changes.scheduledFor;
|
|
1568
|
+
if (changes.scheduledDurationMinutes !== void 0) {
|
|
1569
|
+
patch["scheduled_duration_minutes"] = changes.scheduledDurationMinutes;
|
|
1570
|
+
}
|
|
1571
|
+
if (changes.recurrenceRule !== void 0) patch["recurrence_rule"] = changes.recurrenceRule;
|
|
1572
|
+
return patch;
|
|
1573
|
+
}
|
|
1373
1574
|
function createMeetRepository(options) {
|
|
1374
1575
|
const db = options.client.schema(MEET_SCHEMA);
|
|
1375
1576
|
async function guardVoid(operation, run) {
|
|
@@ -1439,7 +1640,16 @@ function createMeetRepository(options) {
|
|
|
1439
1640
|
lobbyEnabled: bool(row, "lobby_enabled", true),
|
|
1440
1641
|
recordingPolicy: policy === "disabled" || policy === "always-on" ? policy : "host-controlled",
|
|
1441
1642
|
aiEnabled: bool(row, "ai_enabled", true),
|
|
1442
|
-
metadata: typeof metadata === "object" && metadata !== null && !Array.isArray(metadata) ? metadata : {}
|
|
1643
|
+
metadata: typeof metadata === "object" && metadata !== null && !Array.isArray(metadata) ? metadata : {},
|
|
1644
|
+
timeZone: text(row, "time_zone") ?? "UTC",
|
|
1645
|
+
agenda: text(row, "agenda"),
|
|
1646
|
+
recurrenceRule: text(row, "recurrence_rule"),
|
|
1647
|
+
joinBeforeHost: bool(row, "join_before_host", true),
|
|
1648
|
+
cancelledAt: text(row, "cancelled_at"),
|
|
1649
|
+
cancellationReason: text(row, "cancellation_reason"),
|
|
1650
|
+
deletedAt: text(row, "deleted_at"),
|
|
1651
|
+
calendarSequence: typeof row["calendar_sequence"] === "number" ? row["calendar_sequence"] : 0,
|
|
1652
|
+
version: typeof row["version"] === "number" ? row["version"] : 1
|
|
1443
1653
|
};
|
|
1444
1654
|
}
|
|
1445
1655
|
function projectRecording(row) {
|
|
@@ -1526,6 +1736,170 @@ function createMeetRepository(options) {
|
|
|
1526
1736
|
projectNote,
|
|
1527
1737
|
projectRecording,
|
|
1528
1738
|
projectAttendee,
|
|
1739
|
+
projectInvitee,
|
|
1740
|
+
async scheduleMeeting(input) {
|
|
1741
|
+
requireOrg(input.organizationId, "meet.scheduleMeeting");
|
|
1742
|
+
const row = await guard(
|
|
1743
|
+
"meet.scheduleMeeting",
|
|
1744
|
+
() => db.rpc(RPCS.scheduleMeeting, {
|
|
1745
|
+
p_organization_id: input.organizationId,
|
|
1746
|
+
p_host_user_id: input.hostUserId,
|
|
1747
|
+
p_title: input.title,
|
|
1748
|
+
p_scheduled_for: input.scheduledFor,
|
|
1749
|
+
p_time_zone: input.timeZone,
|
|
1750
|
+
p_duration_minutes: input.durationMinutes ?? null,
|
|
1751
|
+
p_agenda: input.agenda ?? null,
|
|
1752
|
+
p_recurrence_rule: input.recurrenceRule ?? null,
|
|
1753
|
+
p_settings: settingsPatch(input.settings ?? {})
|
|
1754
|
+
})
|
|
1755
|
+
);
|
|
1756
|
+
return projectMeeting(row);
|
|
1757
|
+
},
|
|
1758
|
+
async updateMeeting(meetingId, changes, options2 = {}) {
|
|
1759
|
+
const row = await guard(
|
|
1760
|
+
"meet.updateMeeting",
|
|
1761
|
+
() => db.rpc(RPCS.updateMeeting, {
|
|
1762
|
+
p_meeting_id: meetingId,
|
|
1763
|
+
p_changes: changesPatch(changes),
|
|
1764
|
+
p_expected_version: options2.expectedVersion ?? null,
|
|
1765
|
+
p_by_user_id: options2.byUserId ?? null
|
|
1766
|
+
})
|
|
1767
|
+
);
|
|
1768
|
+
return projectMeeting(row);
|
|
1769
|
+
},
|
|
1770
|
+
async cancelMeeting(meetingId, reason, byUserId = null) {
|
|
1771
|
+
const row = await guard(
|
|
1772
|
+
"meet.cancelMeeting",
|
|
1773
|
+
() => db.rpc(RPCS.cancelMeeting, {
|
|
1774
|
+
p_meeting_id: meetingId,
|
|
1775
|
+
p_reason: reason,
|
|
1776
|
+
p_by_user_id: byUserId
|
|
1777
|
+
})
|
|
1778
|
+
);
|
|
1779
|
+
return projectMeeting(row);
|
|
1780
|
+
},
|
|
1781
|
+
async archiveMeeting(meetingId, byUserId = null) {
|
|
1782
|
+
const row = await guard(
|
|
1783
|
+
"meet.archiveMeeting",
|
|
1784
|
+
() => db.rpc(RPCS.archiveMeeting, { p_meeting_id: meetingId, p_by_user_id: byUserId })
|
|
1785
|
+
);
|
|
1786
|
+
return projectMeeting(row);
|
|
1787
|
+
},
|
|
1788
|
+
async restoreMeeting(meetingId, byUserId = null) {
|
|
1789
|
+
const row = await guard(
|
|
1790
|
+
"meet.restoreMeeting",
|
|
1791
|
+
() => db.rpc(RPCS.restoreMeeting, { p_meeting_id: meetingId, p_by_user_id: byUserId })
|
|
1792
|
+
);
|
|
1793
|
+
return projectMeeting(row);
|
|
1794
|
+
},
|
|
1795
|
+
async setOccurrence(args) {
|
|
1796
|
+
const row = await guard(
|
|
1797
|
+
"meet.setOccurrence",
|
|
1798
|
+
() => db.rpc(RPCS.setOccurrence, {
|
|
1799
|
+
p_meeting_id: args.meetingId,
|
|
1800
|
+
p_original_start: args.originalStart,
|
|
1801
|
+
p_action: args.action,
|
|
1802
|
+
p_new_start: args.newStart ?? null,
|
|
1803
|
+
p_new_duration_minutes: args.newDurationMinutes ?? null,
|
|
1804
|
+
p_reason: args.reason ?? null,
|
|
1805
|
+
p_by_user_id: args.byUserId ?? null
|
|
1806
|
+
})
|
|
1807
|
+
);
|
|
1808
|
+
return projectOverride(row);
|
|
1809
|
+
},
|
|
1810
|
+
async meetingOccurrences(meetingId, { from, to, limit = 200 }) {
|
|
1811
|
+
const rows = await guard(
|
|
1812
|
+
"meet.meetingOccurrences",
|
|
1813
|
+
() => db.rpc(RPCS.meetingOccurrences, {
|
|
1814
|
+
p_meeting_id: meetingId,
|
|
1815
|
+
p_from: from,
|
|
1816
|
+
p_to: to,
|
|
1817
|
+
p_limit: limit
|
|
1818
|
+
})
|
|
1819
|
+
);
|
|
1820
|
+
return rows.map(projectOccurrence);
|
|
1821
|
+
},
|
|
1822
|
+
async occurrencesBetween({ from, to, organizationId = null, limit = 500 }) {
|
|
1823
|
+
const rows = await guard(
|
|
1824
|
+
"meet.occurrencesBetween",
|
|
1825
|
+
() => db.rpc(RPCS.occurrencesBetween, {
|
|
1826
|
+
p_from: from,
|
|
1827
|
+
p_to: to,
|
|
1828
|
+
p_organization_id: organizationId,
|
|
1829
|
+
p_limit: limit
|
|
1830
|
+
})
|
|
1831
|
+
);
|
|
1832
|
+
return rows.map(projectUpcoming);
|
|
1833
|
+
},
|
|
1834
|
+
async invitees(meetingId) {
|
|
1835
|
+
const response = await db.from(TABLES.invitees).select(INVITEE_COLUMNS).eq("meeting_id", meetingId).is("deleted_at", null).order("created_at", { ascending: true }).order("id", { ascending: true });
|
|
1836
|
+
if (response.error !== null) throw normalizeMeetError(response.error, "meet.invitees");
|
|
1837
|
+
return (response.data ?? []).map(projectInvitee);
|
|
1838
|
+
},
|
|
1839
|
+
async addInvitees(meetingId, people, byUserId = null) {
|
|
1840
|
+
const rows = await guard(
|
|
1841
|
+
"meet.addInvitees",
|
|
1842
|
+
() => db.rpc(RPCS.addInvitees, {
|
|
1843
|
+
p_meeting_id: meetingId,
|
|
1844
|
+
p_invitees: people.map((person) => ({
|
|
1845
|
+
...person.userId !== void 0 ? { user_id: person.userId } : {},
|
|
1846
|
+
...person.email !== void 0 ? { email: person.email } : {},
|
|
1847
|
+
...person.displayName !== void 0 ? { display_name: person.displayName } : {},
|
|
1848
|
+
...person.role !== void 0 ? { role: person.role } : {}
|
|
1849
|
+
})),
|
|
1850
|
+
p_by_user_id: byUserId
|
|
1851
|
+
})
|
|
1852
|
+
);
|
|
1853
|
+
return rows.map(projectInvitee);
|
|
1854
|
+
},
|
|
1855
|
+
async setInviteeRole(inviteeId, role, byUserId = null) {
|
|
1856
|
+
const row = await guard(
|
|
1857
|
+
"meet.setInviteeRole",
|
|
1858
|
+
() => db.rpc(RPCS.setInviteeRole, {
|
|
1859
|
+
p_invitee_id: inviteeId,
|
|
1860
|
+
p_role: role,
|
|
1861
|
+
p_by_user_id: byUserId
|
|
1862
|
+
})
|
|
1863
|
+
);
|
|
1864
|
+
return projectInvitee(row);
|
|
1865
|
+
},
|
|
1866
|
+
async removeInvitee(inviteeId, byUserId = null) {
|
|
1867
|
+
const row = await guard(
|
|
1868
|
+
"meet.removeInvitee",
|
|
1869
|
+
() => db.rpc(RPCS.removeInvitee, { p_invitee_id: inviteeId, p_by_user_id: byUserId })
|
|
1870
|
+
);
|
|
1871
|
+
return projectInvitee(row);
|
|
1872
|
+
},
|
|
1873
|
+
async respond(meetingId, answer, note = null) {
|
|
1874
|
+
const row = await guard(
|
|
1875
|
+
"meet.respond",
|
|
1876
|
+
() => db.rpc(RPCS.respond, {
|
|
1877
|
+
p_meeting_id: meetingId,
|
|
1878
|
+
p_response: answer,
|
|
1879
|
+
p_note: note,
|
|
1880
|
+
p_by_user_id: null
|
|
1881
|
+
})
|
|
1882
|
+
);
|
|
1883
|
+
return projectInvitee(row);
|
|
1884
|
+
},
|
|
1885
|
+
async invitationByToken(secret) {
|
|
1886
|
+
const body = await guard(
|
|
1887
|
+
"meet.invitationByToken",
|
|
1888
|
+
() => db.rpc(RPCS.invitationByToken, { p_secret: secret })
|
|
1889
|
+
);
|
|
1890
|
+
return projectRsvpInvitation(body);
|
|
1891
|
+
},
|
|
1892
|
+
async respondByToken(secret, answer, note = null) {
|
|
1893
|
+
const body = await guard(
|
|
1894
|
+
"meet.respondByToken",
|
|
1895
|
+
() => db.rpc(RPCS.respondByToken, {
|
|
1896
|
+
p_secret: secret,
|
|
1897
|
+
p_response: answer,
|
|
1898
|
+
p_note: note
|
|
1899
|
+
})
|
|
1900
|
+
);
|
|
1901
|
+
return projectRsvpInvitation(body);
|
|
1902
|
+
},
|
|
1529
1903
|
async attendees(meetingId) {
|
|
1530
1904
|
const response = await db.from(TABLES.participants).select(ATTENDEE_COLUMNS).eq("meeting_id", meetingId).order("joined_at", { ascending: true }).order("id", { ascending: true });
|
|
1531
1905
|
if (response.error !== null) throw normalizeMeetError(response.error, "meet.attendees");
|
|
@@ -2035,8 +2409,8 @@ function callStateLabel(state, outgoing) {
|
|
|
2035
2409
|
return "Call";
|
|
2036
2410
|
}
|
|
2037
2411
|
}
|
|
2038
|
-
function countLabel(
|
|
2039
|
-
return `${
|
|
2412
|
+
function countLabel(count2, singular, plural = `${singular}s`) {
|
|
2413
|
+
return `${count2} ${count2 === 1 ? singular : plural}`;
|
|
2040
2414
|
}
|
|
2041
2415
|
|
|
2042
2416
|
// src/core/invitations.ts
|
|
@@ -2180,11 +2554,11 @@ function computeStage(args) {
|
|
|
2180
2554
|
presenting: sharer !== null
|
|
2181
2555
|
};
|
|
2182
2556
|
}
|
|
2183
|
-
function gridColumns(
|
|
2184
|
-
if (
|
|
2185
|
-
if (
|
|
2186
|
-
if (
|
|
2187
|
-
if (
|
|
2557
|
+
function gridColumns(count2) {
|
|
2558
|
+
if (count2 <= 1) return 1;
|
|
2559
|
+
if (count2 <= 4) return 2;
|
|
2560
|
+
if (count2 <= 9) return 3;
|
|
2561
|
+
if (count2 <= 16) return 4;
|
|
2188
2562
|
return 5;
|
|
2189
2563
|
}
|
|
2190
2564
|
|
|
@@ -3157,11 +3531,11 @@ function createMeetingSession(options) {
|
|
|
3157
3531
|
} catch {
|
|
3158
3532
|
body = null;
|
|
3159
3533
|
}
|
|
3160
|
-
const
|
|
3534
|
+
const refusal2 = readServerRefusal(body);
|
|
3161
3535
|
throw new MeetError(
|
|
3162
3536
|
response.status === 403 ? "forbidden" : "transport",
|
|
3163
|
-
|
|
3164
|
-
|
|
3537
|
+
refusal2?.reason ?? `meet.endForEveryone: the server answered ${response.status}`,
|
|
3538
|
+
refusal2?.remedy !== void 0 && refusal2.remedy.length > 0 ? refusal2.remedy : "The meeting is still running. Only a host may end it, and the server re-checks that \u2014 being a host on this screen is not the decision."
|
|
3165
3539
|
);
|
|
3166
3540
|
}
|
|
3167
3541
|
let payload = null;
|
|
@@ -3289,6 +3663,57 @@ function fileDownloadPath(fileId) {
|
|
|
3289
3663
|
return `/files/${encodeURIComponent(fileId)}/download?inline=1`;
|
|
3290
3664
|
}
|
|
3291
3665
|
|
|
3666
|
+
// src/core/management.ts
|
|
3667
|
+
async function refusal(response, operation) {
|
|
3668
|
+
let body = null;
|
|
3669
|
+
try {
|
|
3670
|
+
body = await response.json();
|
|
3671
|
+
} catch {
|
|
3672
|
+
body = null;
|
|
3673
|
+
}
|
|
3674
|
+
const said = readServerRefusal(body);
|
|
3675
|
+
return new MeetError(
|
|
3676
|
+
response.status === 403 || response.status === 401 ? "forbidden" : response.status === 404 ? "not-found" : response.status === 409 ? "conflict" : "transport",
|
|
3677
|
+
`${operation}: ${said?.reason ?? `the server answered ${response.status}`}`,
|
|
3678
|
+
said?.remedy || "The meeting itself is saved \u2014 only the telling failed. Try again; the call is idempotent."
|
|
3679
|
+
);
|
|
3680
|
+
}
|
|
3681
|
+
function count(body, key) {
|
|
3682
|
+
const value = body[key];
|
|
3683
|
+
return typeof value === "number" ? value : 0;
|
|
3684
|
+
}
|
|
3685
|
+
async function announceMeeting(api, meetingId) {
|
|
3686
|
+
const operation = "meet.announceMeeting";
|
|
3687
|
+
const response = await api.request({
|
|
3688
|
+
path: meetingPath(MEET_ROUTES.meetingAnnounce, meetingId),
|
|
3689
|
+
method: "POST",
|
|
3690
|
+
requireAuth: true,
|
|
3691
|
+
operation
|
|
3692
|
+
});
|
|
3693
|
+
if (!response.ok) throw await refusal(response, operation);
|
|
3694
|
+
const body = await response.json();
|
|
3695
|
+
return {
|
|
3696
|
+
invitations: count(body, "invitations"),
|
|
3697
|
+
updates: count(body, "updates"),
|
|
3698
|
+
cancellations: count(body, "cancellations"),
|
|
3699
|
+
alreadyCurrent: count(body, "already_current"),
|
|
3700
|
+
remindersQueued: count(body, "reminders_queued"),
|
|
3701
|
+
remindersCancelled: count(body, "reminders_cancelled")
|
|
3702
|
+
};
|
|
3703
|
+
}
|
|
3704
|
+
async function pushMeetingToGoogleCalendar(api, meetingId, target) {
|
|
3705
|
+
const operation = "meet.pushMeetingToGoogleCalendar";
|
|
3706
|
+
const response = await api.request({
|
|
3707
|
+
path: meetingPath(MEET_ROUTES.meetingGoogleCalendar, meetingId),
|
|
3708
|
+
method: "POST",
|
|
3709
|
+
requireAuth: true,
|
|
3710
|
+
operation,
|
|
3711
|
+
body: { connection_id: target.connectionId, calendar_id: target.calendarId ?? "primary" }
|
|
3712
|
+
});
|
|
3713
|
+
if (!response.ok) throw await refusal(response, operation);
|
|
3714
|
+
return await response.json();
|
|
3715
|
+
}
|
|
3716
|
+
|
|
3292
3717
|
// src/core/room.ts
|
|
3293
3718
|
var PARTICIPANT_ROLE_ATTRIBUTE = "matrx.participant.role";
|
|
3294
3719
|
var PARTICIPANT_CAN_RECORD_ATTRIBUTE = "matrx.participant.can_record";
|
|
@@ -3863,7 +4288,7 @@ function meetingWhen(meeting, locale) {
|
|
|
3863
4288
|
if (start === null) return "Time not recorded";
|
|
3864
4289
|
const startedAt = new Date(start);
|
|
3865
4290
|
if (Number.isNaN(startedAt.getTime())) return "Time not recorded";
|
|
3866
|
-
const
|
|
4291
|
+
const when2 = startedAt.toLocaleString(locale, {
|
|
3867
4292
|
weekday: "long",
|
|
3868
4293
|
year: "numeric",
|
|
3869
4294
|
month: "long",
|
|
@@ -3871,11 +4296,11 @@ function meetingWhen(meeting, locale) {
|
|
|
3871
4296
|
hour: "numeric",
|
|
3872
4297
|
minute: "2-digit"
|
|
3873
4298
|
});
|
|
3874
|
-
if (meeting.endedAt === null || meeting.startedAt === null) return
|
|
4299
|
+
if (meeting.endedAt === null || meeting.startedAt === null) return when2;
|
|
3875
4300
|
const endedAt = new Date(meeting.endedAt);
|
|
3876
|
-
if (Number.isNaN(endedAt.getTime())) return
|
|
4301
|
+
if (Number.isNaN(endedAt.getTime())) return when2;
|
|
3877
4302
|
const elapsedMs = endedAt.getTime() - startedAt.getTime();
|
|
3878
|
-
return `${
|
|
4303
|
+
return `${when2} \xB7 ${(0, import_format.formatDurationMs)(elapsedMs, { style: "long" })}`;
|
|
3879
4304
|
}
|
|
3880
4305
|
var GUEST_RECORD_ACCESS_LEVELS = [
|
|
3881
4306
|
"none",
|
|
@@ -3951,8 +4376,8 @@ async function loadGuestMeetingRecord(args) {
|
|
|
3951
4376
|
);
|
|
3952
4377
|
}
|
|
3953
4378
|
if (!response.ok) {
|
|
3954
|
-
const
|
|
3955
|
-
return refusedBundle(meeting,
|
|
4379
|
+
const refusal2 = await readRefusal(response);
|
|
4380
|
+
return refusedBundle(meeting, refusal2.message, refusal2.remedy);
|
|
3956
4381
|
}
|
|
3957
4382
|
const body = await response.json();
|
|
3958
4383
|
const payload = typeof body["data"] === "object" && body["data"] !== null ? body["data"] : body;
|
|
@@ -4043,8 +4468,8 @@ async function setGuestRecordAccess(args) {
|
|
|
4043
4468
|
}
|
|
4044
4469
|
});
|
|
4045
4470
|
if (!response.ok) {
|
|
4046
|
-
const
|
|
4047
|
-
throw new MeetError("forbidden",
|
|
4471
|
+
const refusal2 = await readRefusal(response);
|
|
4472
|
+
throw new MeetError("forbidden", refusal2.message, refusal2.remedy);
|
|
4048
4473
|
}
|
|
4049
4474
|
const body = await response.json();
|
|
4050
4475
|
const payload = typeof body["data"] === "object" && body["data"] !== null ? body["data"] : body;
|