@ai-matrx/meet 0.7.1 → 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/dist/react.js CHANGED
@@ -45,7 +45,7 @@ var MeetError = class extends Error {
45
45
  };
46
46
  var FORBIDDEN_CODES = /* @__PURE__ */ new Set(["42501", "PGRST301", "PGRST302"]);
47
47
  var NOT_FOUND_CODES = /* @__PURE__ */ new Set(["PGRST116", "PGRST205"]);
48
- var CONFLICT_CODES = /* @__PURE__ */ new Set(["23505"]);
48
+ var CONFLICT_CODES = /* @__PURE__ */ new Set(["23505", "40001", "55000"]);
49
49
  var SESSION_MARKERS = [
50
50
  "auth session missing",
51
51
  "jwt expired",
@@ -203,8 +203,19 @@ var MEET_ROUTES = {
203
203
  */
204
204
  record: "/api/v1/meet/record",
205
205
  /** A host chooses what guests may read of ONE meeting (MRF-1). */
206
- recordAccess: "/api/v1/meet/record/access"
206
+ recordAccess: "/api/v1/meet/record/access",
207
+ /**
208
+ * Meet wave 1 (0.8): bring every invitee up to date after a write — sends the
209
+ * invitation / update / cancellation emails with their `.ics` and re-queues
210
+ * reminders. Idempotent. `{meeting_id}` is substituted by `meetingPath`.
211
+ */
212
+ meetingAnnounce: "/api/v1/meet/meetings/{meeting_id}/announce",
213
+ /** Put a one-off meeting on the caller's own Google Calendar (gated by the server). */
214
+ meetingGoogleCalendar: "/api/v1/meet/meetings/{meeting_id}/google-calendar"
207
215
  };
216
+ function meetingPath(template, meetingId) {
217
+ return template.replace("{meeting_id}", encodeURIComponent(meetingId));
218
+ }
208
219
  var ROOM_TOKEN_HEADER = "x-meet-room-token";
209
220
  var REFRESH_RATIO = 0.8;
210
221
  function readString(source, key) {
@@ -334,6 +345,19 @@ function createTokenClient(options) {
334
345
  "The link is wrong or the meeting was deleted. Show the 'meeting not found' screen; a durable link that 404s is never a retry."
335
346
  );
336
347
  }
348
+ if (response.status === 410) {
349
+ let reason = "This meeting was cancelled.";
350
+ try {
351
+ const body = await response.json();
352
+ if (typeof body?.detail?.message === "string") reason = body.detail.message;
353
+ } catch {
354
+ }
355
+ throw new MeetError(
356
+ "cancelled",
357
+ reason,
358
+ "Nobody can join a cancelled meeting. Show the cancelled screen with this sentence; ask the host to schedule a new one."
359
+ );
360
+ }
337
361
  if (response.status === 423) {
338
362
  throw new MeetError(
339
363
  "room-locked",
@@ -535,12 +559,12 @@ function createMeetAi(options) {
535
559
  async function dispatch(route, body, operation) {
536
560
  const response = await send({ path: route, body, operation, requireAuth: true });
537
561
  if (!response.ok) {
538
- const refusal = await refusalFor(response, operation);
539
- options.store.patch({ noteTakerRefusal: refusal });
562
+ const refusal2 = await refusalFor(response, operation);
563
+ options.store.patch({ noteTakerRefusal: refusal2 });
540
564
  throw new MeetError(
541
565
  response.status === 403 ? "forbidden" : "transport",
542
- refusal.reason,
543
- refusal.remedy
566
+ refusal2.reason,
567
+ refusal2.remedy
544
568
  );
545
569
  }
546
570
  options.store.patch({ noteTakerRefusal: null });
@@ -578,11 +602,11 @@ function createMeetAi(options) {
578
602
  ...signal !== void 0 ? { signal } : {}
579
603
  });
580
604
  if (!response.ok) {
581
- const refusal = await refusalFor(response, "meet.ai.ask");
605
+ const refusal2 = await refusalFor(response, "meet.ai.ask");
582
606
  throw new MeetError(
583
607
  response.status === 403 || response.status === 401 ? "forbidden" : response.status === 404 ? "not-found" : "transport",
584
- refusal.reason,
585
- refusal.remedy
608
+ refusal2.reason,
609
+ refusal2.remedy
586
610
  );
587
611
  }
588
612
  const body = response.body;
@@ -1726,6 +1750,15 @@ function createMeetingSession(options) {
1726
1750
  const { manager, repository, store, identity } = options;
1727
1751
  let channel = null;
1728
1752
  let meetingChannel = null;
1753
+ async function readMeeting(meetingId) {
1754
+ try {
1755
+ return await repository.meeting(meetingId);
1756
+ } catch (error) {
1757
+ const slug = store.snapshot().meeting?.slug;
1758
+ if (slug === void 0) throw error;
1759
+ return repository.meetingBySlug(slug);
1760
+ }
1761
+ }
1729
1762
  function requireHostUser(operation) {
1730
1763
  if (identity.userId === null || !isHostRole(effectiveRole(store.snapshot()))) {
1731
1764
  throw new MeetError(
@@ -1806,7 +1839,7 @@ function createMeetingSession(options) {
1806
1839
  onBackfill: async () => {
1807
1840
  const meeting = store.snapshot().meeting;
1808
1841
  if (meeting === null) return;
1809
- const fresh = isGuest(identity) ? await repository.meetingBySlug(meeting.slug) : await repository.meeting(meeting.id);
1842
+ const fresh = isGuest(identity) ? await repository.meetingBySlug(meeting.slug) : await readMeeting(meeting.id);
1810
1843
  store.patch({ meeting: fresh, locked: fresh.locked });
1811
1844
  }
1812
1845
  });
@@ -1901,7 +1934,7 @@ function createMeetingSession(options) {
1901
1934
  // a recovered surface from looking healthy and being wrong.
1902
1935
  onBackfill: async () => {
1903
1936
  const [meeting, transcript, notes, recordings, attendees] = await Promise.all([
1904
- repository.meeting(meetingId),
1937
+ readMeeting(meetingId),
1905
1938
  repository.transcript(meetingId),
1906
1939
  repository.notes(meetingId),
1907
1940
  repository.recordings(meetingId),
@@ -2013,11 +2046,11 @@ function createMeetingSession(options) {
2013
2046
  } catch {
2014
2047
  body = null;
2015
2048
  }
2016
- const refusal = readServerRefusal(body);
2049
+ const refusal2 = readServerRefusal(body);
2017
2050
  throw new MeetError(
2018
2051
  response.status === 403 ? "forbidden" : "transport",
2019
- refusal?.reason ?? `meet.endForEveryone: the server answered ${response.status}`,
2020
- refusal?.remedy !== void 0 && refusal.remedy.length > 0 ? refusal.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."
2052
+ refusal2?.reason ?? `meet.endForEveryone: the server answered ${response.status}`,
2053
+ 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."
2021
2054
  );
2022
2055
  }
2023
2056
  let payload = null;
@@ -2060,7 +2093,11 @@ var TABLES = {
2060
2093
  invites: "meet_call_invites",
2061
2094
  recordings: "meet_recordings",
2062
2095
  transcripts: "meet_transcript_segments",
2063
- notes: "meet_notes"
2096
+ notes: "meet_notes",
2097
+ /** Wave 1 (0.8): who a meeting was sent to, and what they answered. */
2098
+ invitees: "meet_invitees",
2099
+ /** Wave 1 (0.8): one occurrence of a series cancelled or moved. */
2100
+ occurrenceOverrides: "meet_occurrence_overrides"
2064
2101
  };
2065
2102
  var RPCS = {
2066
2103
  /** Atomic: create the invite row and stamp its expiry in one statement. */
@@ -2076,10 +2113,41 @@ var RPCS = {
2076
2113
  admit: "meet_admit_participant",
2077
2114
  setLock: "meet_set_lock",
2078
2115
  endMeeting: "meet_end_meeting",
2079
- recordConsent: "meet_record_consent"
2116
+ recordConsent: "meet_record_consent",
2117
+ /*
2118
+ * ── MEET WAVE 1 — management (0.8). Every write is a SECURITY DEFINER door
2119
+ * authorized by the meeting's host or a real iam grant (editor to change,
2120
+ * admin = co-host to archive or name a co-host) — never an organization
2121
+ * match. After any of these writes, call `announceMeeting` so the server
2122
+ * sends invitations / updates / cancellations and re-queues reminders.
2123
+ */
2124
+ /** Schedule with every setting at create; unset settings come from the knobs. */
2125
+ scheduleMeeting: "meet_schedule_meeting",
2126
+ /** Edit / reschedule — a JSON patch with a version compare-and-swap. */
2127
+ updateMeeting: "meet_update_meeting",
2128
+ /** Cancel is a state (`cancelled_at`); the link keeps resolving. */
2129
+ cancelMeeting: "meet_cancel_meeting",
2130
+ /** Archive (soft delete) — the invitations and exceptions cascade. */
2131
+ archiveMeeting: "meet_archive_meeting",
2132
+ restoreMeeting: "meet_restore_meeting",
2133
+ /** Cancel, move or restore ONE occurrence of a series by its original start. */
2134
+ setOccurrence: "meet_set_occurrence",
2135
+ addInvitees: "meet_add_invitees",
2136
+ setInviteeRole: "meet_set_invitee_role",
2137
+ removeInvitee: "meet_remove_invitee",
2138
+ /** A signed-in invitee answers for themselves. */
2139
+ respond: "meet_respond",
2140
+ /** The emailed RSVP link (no account): read and answer. */
2141
+ invitationByToken: "meet_invitation_by_token",
2142
+ respondByToken: "meet_respond_by_token",
2143
+ /** One meeting's occurrences between two instants (INVOKER — the reader's RLS). */
2144
+ meetingOccurrences: "meet_meeting_occurrences",
2145
+ /** Every occurrence the reader can see between two instants — the Upcoming list. */
2146
+ occurrencesBetween: "meet_occurrences_between"
2080
2147
  };
2081
2148
  var VOID_RPCS = [RPCS.admit, RPCS.recordConsent];
2082
- 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";
2149
+ 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";
2150
+ 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";
2083
2151
  var RECORDING_COLUMNS = "id,meeting_id,state,started_at,ended_at,started_by,file_id,failure_reason";
2084
2152
  var SEGMENT_COLUMNS = "id,meeting_id,identity,speaker,text,started_at,is_final,metadata";
2085
2153
  var NOTE_COLUMNS = "id,meeting_id,text,kind,assignee_user_id,created_at,metadata";
@@ -2177,6 +2245,138 @@ function projectNote(row) {
2177
2245
  source: "record"
2178
2246
  };
2179
2247
  }
2248
+ var RSVP_STATES = ["needs_action", "accepted", "declined", "tentative"];
2249
+ var OCCURRENCE_STATES = ["scheduled", "moved", "cancelled"];
2250
+ function rsvpState(value) {
2251
+ return RSVP_STATES.includes(value) ? value : "needs_action";
2252
+ }
2253
+ function num(row, key) {
2254
+ const value = row[key];
2255
+ return typeof value === "number" ? value : null;
2256
+ }
2257
+ function when(row, key) {
2258
+ const value = row[key];
2259
+ if (value instanceof Date) return value.toISOString();
2260
+ return text(row, key);
2261
+ }
2262
+ function projectInvitee(row) {
2263
+ const operation = "meet.projectInvitee";
2264
+ const userId = text(row, "invitee_user_id");
2265
+ return {
2266
+ id: requireText(row, "id", operation),
2267
+ meetingId: asMeetingId(requireText(row, "meeting_id", operation)),
2268
+ userId: userId === null ? null : asUserId(userId),
2269
+ email: text(row, "email"),
2270
+ displayName: text(row, "display_name"),
2271
+ role: text(row, "role") === "cohost" ? "cohost" : "invitee",
2272
+ rsvpState: rsvpState(text(row, "rsvp_state")),
2273
+ respondedAt: when(row, "responded_at"),
2274
+ rsvpNote: text(row, "rsvp_note"),
2275
+ lastSentSequence: num(row, "last_sent_sequence"),
2276
+ lastSentAt: when(row, "last_sent_at")
2277
+ };
2278
+ }
2279
+ function projectOccurrence(row) {
2280
+ const operation = "meet.projectOccurrence";
2281
+ const state = text(row, "state");
2282
+ return {
2283
+ meetingId: asMeetingId(requireText(row, "meeting_id", operation)),
2284
+ originalStart: when(row, "original_start") ?? requireText(row, "original_start", operation),
2285
+ occurrenceStart: when(row, "occurrence_start") ?? requireText(row, "occurrence_start", operation),
2286
+ durationMinutes: num(row, "duration_minutes"),
2287
+ // An unknown state is shown as scheduled, never dropped: the row is real.
2288
+ state: OCCURRENCE_STATES.includes(state) ? state : "scheduled",
2289
+ sequence: num(row, "sequence") ?? 0
2290
+ };
2291
+ }
2292
+ function projectUpcoming(row) {
2293
+ const operation = "meet.projectUpcoming";
2294
+ const kind = text(row, "kind");
2295
+ const myRole = text(row, "my_role");
2296
+ const myRsvp = text(row, "my_rsvp");
2297
+ return {
2298
+ ...projectOccurrence(row),
2299
+ organizationId: asOrganizationId(requireText(row, "organization_id", operation)),
2300
+ title: text(row, "title") ?? "Meeting",
2301
+ slug: requireText(row, "slug", operation),
2302
+ kind: kind === "scheduled" || kind === "recurring" ? kind : "instant",
2303
+ hostUserId: asUserId(requireText(row, "host_user_id", operation)),
2304
+ timeZone: text(row, "time_zone") ?? "UTC",
2305
+ meetingCancelled: bool(row, "meeting_cancelled", false),
2306
+ myRsvp: myRsvp === null ? null : rsvpState(myRsvp),
2307
+ myRole: myRole === "host" || myRole === "cohost" || myRole === "invitee" ? myRole : null
2308
+ };
2309
+ }
2310
+ function projectOverride(row) {
2311
+ const operation = "meet.projectOverride";
2312
+ return {
2313
+ id: requireText(row, "id", operation),
2314
+ meetingId: asMeetingId(requireText(row, "meeting_id", operation)),
2315
+ originalStart: when(row, "original_start") ?? requireText(row, "original_start", operation),
2316
+ state: text(row, "state") === "moved" ? "moved" : "cancelled",
2317
+ newStart: when(row, "new_start"),
2318
+ newDurationMinutes: num(row, "new_duration_minutes"),
2319
+ reason: text(row, "reason"),
2320
+ calendarSequence: num(row, "calendar_sequence") ?? 0,
2321
+ deletedAt: when(row, "deleted_at")
2322
+ };
2323
+ }
2324
+ function projectRsvpInvitation(body) {
2325
+ const operation = "meet.projectRsvpInvitation";
2326
+ if (typeof body !== "object" || body === null) {
2327
+ throw invalidResponse(operation, "the RSVP door returned no object");
2328
+ }
2329
+ const row = body;
2330
+ if (row["ok"] !== true) {
2331
+ return {
2332
+ ok: false,
2333
+ message: text(row, "message") ?? "This invitation link is no longer valid \u2014 ask the host to send it again."
2334
+ };
2335
+ }
2336
+ const meeting = row["meeting"] ?? {};
2337
+ const invitee = row["invitee"] ?? {};
2338
+ return {
2339
+ ok: true,
2340
+ meeting: {
2341
+ title: text(meeting, "title") ?? "Meeting",
2342
+ slug: requireText(meeting, "slug", operation),
2343
+ scheduledFor: when(meeting, "scheduled_for"),
2344
+ durationMinutes: num(meeting, "duration_minutes"),
2345
+ timeZone: text(meeting, "time_zone") ?? "UTC",
2346
+ agenda: text(meeting, "agenda"),
2347
+ recurrenceRule: text(meeting, "recurrence_rule"),
2348
+ cancelledAt: when(meeting, "cancelled_at"),
2349
+ cancellationReason: text(meeting, "cancellation_reason"),
2350
+ endedAt: when(meeting, "ended_at")
2351
+ },
2352
+ invitee: {
2353
+ displayName: text(invitee, "display_name"),
2354
+ email: text(invitee, "email"),
2355
+ rsvpState: rsvpState(text(invitee, "rsvp_state")),
2356
+ respondedAt: when(invitee, "responded_at")
2357
+ }
2358
+ };
2359
+ }
2360
+ function settingsPatch(settings) {
2361
+ const patch = {};
2362
+ if (settings.lobbyEnabled !== void 0) patch["lobby_enabled"] = settings.lobbyEnabled;
2363
+ if (settings.recordingPolicy !== void 0) patch["recording_policy"] = settings.recordingPolicy;
2364
+ if (settings.aiEnabled !== void 0) patch["ai_enabled"] = settings.aiEnabled;
2365
+ if (settings.joinBeforeHost !== void 0) patch["join_before_host"] = settings.joinBeforeHost;
2366
+ return patch;
2367
+ }
2368
+ function changesPatch(changes) {
2369
+ const patch = settingsPatch(changes);
2370
+ if (changes.title !== void 0) patch["title"] = changes.title;
2371
+ if (changes.agenda !== void 0) patch["agenda"] = changes.agenda;
2372
+ if (changes.timeZone !== void 0) patch["time_zone"] = changes.timeZone;
2373
+ if (changes.scheduledFor !== void 0) patch["scheduled_for"] = changes.scheduledFor;
2374
+ if (changes.scheduledDurationMinutes !== void 0) {
2375
+ patch["scheduled_duration_minutes"] = changes.scheduledDurationMinutes;
2376
+ }
2377
+ if (changes.recurrenceRule !== void 0) patch["recurrence_rule"] = changes.recurrenceRule;
2378
+ return patch;
2379
+ }
2180
2380
  function createMeetRepository(options) {
2181
2381
  const db = options.client.schema(MEET_SCHEMA);
2182
2382
  async function guardVoid(operation, run) {
@@ -2246,7 +2446,16 @@ function createMeetRepository(options) {
2246
2446
  lobbyEnabled: bool(row, "lobby_enabled", true),
2247
2447
  recordingPolicy: policy === "disabled" || policy === "always-on" ? policy : "host-controlled",
2248
2448
  aiEnabled: bool(row, "ai_enabled", true),
2249
- metadata: typeof metadata === "object" && metadata !== null && !Array.isArray(metadata) ? metadata : {}
2449
+ metadata: typeof metadata === "object" && metadata !== null && !Array.isArray(metadata) ? metadata : {},
2450
+ timeZone: text(row, "time_zone") ?? "UTC",
2451
+ agenda: text(row, "agenda"),
2452
+ recurrenceRule: text(row, "recurrence_rule"),
2453
+ joinBeforeHost: bool(row, "join_before_host", true),
2454
+ cancelledAt: text(row, "cancelled_at"),
2455
+ cancellationReason: text(row, "cancellation_reason"),
2456
+ deletedAt: text(row, "deleted_at"),
2457
+ calendarSequence: typeof row["calendar_sequence"] === "number" ? row["calendar_sequence"] : 0,
2458
+ version: typeof row["version"] === "number" ? row["version"] : 1
2250
2459
  };
2251
2460
  }
2252
2461
  function projectRecording(row) {
@@ -2333,6 +2542,170 @@ function createMeetRepository(options) {
2333
2542
  projectNote,
2334
2543
  projectRecording,
2335
2544
  projectAttendee,
2545
+ projectInvitee,
2546
+ async scheduleMeeting(input) {
2547
+ requireOrg(input.organizationId, "meet.scheduleMeeting");
2548
+ const row = await guard(
2549
+ "meet.scheduleMeeting",
2550
+ () => db.rpc(RPCS.scheduleMeeting, {
2551
+ p_organization_id: input.organizationId,
2552
+ p_host_user_id: input.hostUserId,
2553
+ p_title: input.title,
2554
+ p_scheduled_for: input.scheduledFor,
2555
+ p_time_zone: input.timeZone,
2556
+ p_duration_minutes: input.durationMinutes ?? null,
2557
+ p_agenda: input.agenda ?? null,
2558
+ p_recurrence_rule: input.recurrenceRule ?? null,
2559
+ p_settings: settingsPatch(input.settings ?? {})
2560
+ })
2561
+ );
2562
+ return projectMeeting(row);
2563
+ },
2564
+ async updateMeeting(meetingId, changes, options2 = {}) {
2565
+ const row = await guard(
2566
+ "meet.updateMeeting",
2567
+ () => db.rpc(RPCS.updateMeeting, {
2568
+ p_meeting_id: meetingId,
2569
+ p_changes: changesPatch(changes),
2570
+ p_expected_version: options2.expectedVersion ?? null,
2571
+ p_by_user_id: options2.byUserId ?? null
2572
+ })
2573
+ );
2574
+ return projectMeeting(row);
2575
+ },
2576
+ async cancelMeeting(meetingId, reason, byUserId = null) {
2577
+ const row = await guard(
2578
+ "meet.cancelMeeting",
2579
+ () => db.rpc(RPCS.cancelMeeting, {
2580
+ p_meeting_id: meetingId,
2581
+ p_reason: reason,
2582
+ p_by_user_id: byUserId
2583
+ })
2584
+ );
2585
+ return projectMeeting(row);
2586
+ },
2587
+ async archiveMeeting(meetingId, byUserId = null) {
2588
+ const row = await guard(
2589
+ "meet.archiveMeeting",
2590
+ () => db.rpc(RPCS.archiveMeeting, { p_meeting_id: meetingId, p_by_user_id: byUserId })
2591
+ );
2592
+ return projectMeeting(row);
2593
+ },
2594
+ async restoreMeeting(meetingId, byUserId = null) {
2595
+ const row = await guard(
2596
+ "meet.restoreMeeting",
2597
+ () => db.rpc(RPCS.restoreMeeting, { p_meeting_id: meetingId, p_by_user_id: byUserId })
2598
+ );
2599
+ return projectMeeting(row);
2600
+ },
2601
+ async setOccurrence(args) {
2602
+ const row = await guard(
2603
+ "meet.setOccurrence",
2604
+ () => db.rpc(RPCS.setOccurrence, {
2605
+ p_meeting_id: args.meetingId,
2606
+ p_original_start: args.originalStart,
2607
+ p_action: args.action,
2608
+ p_new_start: args.newStart ?? null,
2609
+ p_new_duration_minutes: args.newDurationMinutes ?? null,
2610
+ p_reason: args.reason ?? null,
2611
+ p_by_user_id: args.byUserId ?? null
2612
+ })
2613
+ );
2614
+ return projectOverride(row);
2615
+ },
2616
+ async meetingOccurrences(meetingId, { from, to, limit = 200 }) {
2617
+ const rows = await guard(
2618
+ "meet.meetingOccurrences",
2619
+ () => db.rpc(RPCS.meetingOccurrences, {
2620
+ p_meeting_id: meetingId,
2621
+ p_from: from,
2622
+ p_to: to,
2623
+ p_limit: limit
2624
+ })
2625
+ );
2626
+ return rows.map(projectOccurrence);
2627
+ },
2628
+ async occurrencesBetween({ from, to, organizationId = null, limit = 500 }) {
2629
+ const rows = await guard(
2630
+ "meet.occurrencesBetween",
2631
+ () => db.rpc(RPCS.occurrencesBetween, {
2632
+ p_from: from,
2633
+ p_to: to,
2634
+ p_organization_id: organizationId,
2635
+ p_limit: limit
2636
+ })
2637
+ );
2638
+ return rows.map(projectUpcoming);
2639
+ },
2640
+ async invitees(meetingId) {
2641
+ 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 });
2642
+ if (response.error !== null) throw normalizeMeetError(response.error, "meet.invitees");
2643
+ return (response.data ?? []).map(projectInvitee);
2644
+ },
2645
+ async addInvitees(meetingId, people, byUserId = null) {
2646
+ const rows = await guard(
2647
+ "meet.addInvitees",
2648
+ () => db.rpc(RPCS.addInvitees, {
2649
+ p_meeting_id: meetingId,
2650
+ p_invitees: people.map((person) => ({
2651
+ ...person.userId !== void 0 ? { user_id: person.userId } : {},
2652
+ ...person.email !== void 0 ? { email: person.email } : {},
2653
+ ...person.displayName !== void 0 ? { display_name: person.displayName } : {},
2654
+ ...person.role !== void 0 ? { role: person.role } : {}
2655
+ })),
2656
+ p_by_user_id: byUserId
2657
+ })
2658
+ );
2659
+ return rows.map(projectInvitee);
2660
+ },
2661
+ async setInviteeRole(inviteeId, role, byUserId = null) {
2662
+ const row = await guard(
2663
+ "meet.setInviteeRole",
2664
+ () => db.rpc(RPCS.setInviteeRole, {
2665
+ p_invitee_id: inviteeId,
2666
+ p_role: role,
2667
+ p_by_user_id: byUserId
2668
+ })
2669
+ );
2670
+ return projectInvitee(row);
2671
+ },
2672
+ async removeInvitee(inviteeId, byUserId = null) {
2673
+ const row = await guard(
2674
+ "meet.removeInvitee",
2675
+ () => db.rpc(RPCS.removeInvitee, { p_invitee_id: inviteeId, p_by_user_id: byUserId })
2676
+ );
2677
+ return projectInvitee(row);
2678
+ },
2679
+ async respond(meetingId, answer, note = null) {
2680
+ const row = await guard(
2681
+ "meet.respond",
2682
+ () => db.rpc(RPCS.respond, {
2683
+ p_meeting_id: meetingId,
2684
+ p_response: answer,
2685
+ p_note: note,
2686
+ p_by_user_id: null
2687
+ })
2688
+ );
2689
+ return projectInvitee(row);
2690
+ },
2691
+ async invitationByToken(secret) {
2692
+ const body = await guard(
2693
+ "meet.invitationByToken",
2694
+ () => db.rpc(RPCS.invitationByToken, { p_secret: secret })
2695
+ );
2696
+ return projectRsvpInvitation(body);
2697
+ },
2698
+ async respondByToken(secret, answer, note = null) {
2699
+ const body = await guard(
2700
+ "meet.respondByToken",
2701
+ () => db.rpc(RPCS.respondByToken, {
2702
+ p_secret: secret,
2703
+ p_response: answer,
2704
+ p_note: note
2705
+ })
2706
+ );
2707
+ return projectRsvpInvitation(body);
2708
+ },
2336
2709
  async attendees(meetingId) {
2337
2710
  const response = await db.from(TABLES.participants).select(ATTENDEE_COLUMNS).eq("meeting_id", meetingId).order("joined_at", { ascending: true }).order("id", { ascending: true });
2338
2711
  if (response.error !== null) throw normalizeMeetError(response.error, "meet.attendees");
@@ -2760,14 +3133,14 @@ function noteTakerStatus(args) {
2760
3133
  const row = typeof raw === "object" && raw !== null && !Array.isArray(raw) ? raw : {};
2761
3134
  const recorded = readState(row["state"]);
2762
3135
  const detail = typeof row["reason"] === "string" && row["reason"].length > 0 ? row["reason"] : null;
2763
- const refusal = args.refusal ?? null;
3136
+ const refusal2 = args.refusal ?? null;
2764
3137
  if (recorded === null) {
2765
- return { state: "absent", detail, present, refusal };
3138
+ return { state: "absent", detail, present, refusal: refusal2 };
2766
3139
  }
2767
3140
  if (recorded === "transcribing" && !present) {
2768
- return { state: "handoff_pending", detail, present, refusal };
3141
+ return { state: "handoff_pending", detail, present, refusal: refusal2 };
2769
3142
  }
2770
- return { state: recorded, detail, present, refusal };
3143
+ return { state: recorded, detail, present, refusal: refusal2 };
2771
3144
  }
2772
3145
  function noteTakerLabel(status) {
2773
3146
  switch (status.state) {
@@ -3792,11 +4165,11 @@ function computeStage(args) {
3792
4165
  presenting: sharer !== null
3793
4166
  };
3794
4167
  }
3795
- function gridColumns(count) {
3796
- if (count <= 1) return 1;
3797
- if (count <= 4) return 2;
3798
- if (count <= 9) return 3;
3799
- if (count <= 16) return 4;
4168
+ function gridColumns(count2) {
4169
+ if (count2 <= 1) return 1;
4170
+ if (count2 <= 4) return 2;
4171
+ if (count2 <= 9) return 3;
4172
+ if (count2 <= 16) return 4;
3800
4173
  return 5;
3801
4174
  }
3802
4175
 
@@ -3970,7 +4343,7 @@ function meetingWhen(meeting, locale) {
3970
4343
  if (start === null) return "Time not recorded";
3971
4344
  const startedAt = new Date(start);
3972
4345
  if (Number.isNaN(startedAt.getTime())) return "Time not recorded";
3973
- const when = startedAt.toLocaleString(locale, {
4346
+ const when2 = startedAt.toLocaleString(locale, {
3974
4347
  weekday: "long",
3975
4348
  year: "numeric",
3976
4349
  month: "long",
@@ -3978,11 +4351,11 @@ function meetingWhen(meeting, locale) {
3978
4351
  hour: "numeric",
3979
4352
  minute: "2-digit"
3980
4353
  });
3981
- if (meeting.endedAt === null || meeting.startedAt === null) return when;
4354
+ if (meeting.endedAt === null || meeting.startedAt === null) return when2;
3982
4355
  const endedAt = new Date(meeting.endedAt);
3983
- if (Number.isNaN(endedAt.getTime())) return when;
4356
+ if (Number.isNaN(endedAt.getTime())) return when2;
3984
4357
  const elapsedMs = endedAt.getTime() - startedAt.getTime();
3985
- return `${when} \xB7 ${formatDurationMs(elapsedMs, { style: "long" })}`;
4358
+ return `${when2} \xB7 ${formatDurationMs(elapsedMs, { style: "long" })}`;
3986
4359
  }
3987
4360
  var GUEST_RECORD_ACCESS_LEVELS = [
3988
4361
  "none",
@@ -4089,8 +4462,8 @@ async function loadGuestMeetingRecord(args) {
4089
4462
  );
4090
4463
  }
4091
4464
  if (!response.ok) {
4092
- const refusal = await readRefusal(response);
4093
- return refusedBundle(meeting, refusal.message, refusal.remedy);
4465
+ const refusal2 = await readRefusal(response);
4466
+ return refusedBundle(meeting, refusal2.message, refusal2.remedy);
4094
4467
  }
4095
4468
  const body = await response.json();
4096
4469
  const payload = typeof body["data"] === "object" && body["data"] !== null ? body["data"] : body;
@@ -4181,8 +4554,8 @@ async function setGuestRecordAccess(args) {
4181
4554
  }
4182
4555
  });
4183
4556
  if (!response.ok) {
4184
- const refusal = await readRefusal(response);
4185
- throw new MeetError("forbidden", refusal.message, refusal.remedy);
4557
+ const refusal2 = await readRefusal(response);
4558
+ throw new MeetError("forbidden", refusal2.message, refusal2.remedy);
4186
4559
  }
4187
4560
  const body = await response.json();
4188
4561
  const payload = typeof body["data"] === "object" && body["data"] !== null ? body["data"] : body;
@@ -4849,8 +5222,8 @@ function callStateLabel(state, outgoing) {
4849
5222
  return "Call";
4850
5223
  }
4851
5224
  }
4852
- function countLabel(count, singular, plural = `${singular}s`) {
4853
- return `${count} ${count === 1 ? singular : plural}`;
5225
+ function countLabel(count2, singular, plural = `${singular}s`) {
5226
+ return `${count2} ${count2 === 1 ? singular : plural}`;
4854
5227
  }
4855
5228
 
4856
5229
  // src/react/icons.tsx
@@ -5908,7 +6281,7 @@ function ScheduleMeetingBody({
5908
6281
  host
5909
6282
  }) {
5910
6283
  const [title, setTitle] = useState3("");
5911
- const [when, setWhen] = useState3("");
6284
+ const [when2, setWhen] = useState3("");
5912
6285
  const [kind, setKind] = useState3("scheduled");
5913
6286
  const [busy, setBusy] = useState3(false);
5914
6287
  const [link, setLink] = useState3(null);
@@ -5928,7 +6301,7 @@ function ScheduleMeetingBody({
5928
6301
  hostUserId: host.identity.userId,
5929
6302
  title: title.trim(),
5930
6303
  kind,
5931
- scheduledFor: when.length > 0 ? new Date(when).toISOString() : null
6304
+ scheduledFor: when2.length > 0 ? new Date(when2).toISOString() : null
5932
6305
  }).then((meeting) => {
5933
6306
  setLink(meetingLink(appBaseUrl, meeting.slug));
5934
6307
  onScheduled?.(meeting.slug);
@@ -5958,7 +6331,7 @@ function ScheduleMeetingBody({
5958
6331
  {
5959
6332
  className: "mx-meet__input",
5960
6333
  type: "datetime-local",
5961
- value: when,
6334
+ value: when2,
5962
6335
  onChange: (event) => setWhen(event.target.value)
5963
6336
  }
5964
6337
  )
@@ -6538,6 +6911,57 @@ function createMeetingInviteHandler(options) {
6538
6911
  }
6539
6912
  };
6540
6913
  }
6914
+
6915
+ // src/core/management.ts
6916
+ async function refusal(response, operation) {
6917
+ let body = null;
6918
+ try {
6919
+ body = await response.json();
6920
+ } catch {
6921
+ body = null;
6922
+ }
6923
+ const said = readServerRefusal(body);
6924
+ return new MeetError(
6925
+ response.status === 403 || response.status === 401 ? "forbidden" : response.status === 404 ? "not-found" : response.status === 409 ? "conflict" : "transport",
6926
+ `${operation}: ${said?.reason ?? `the server answered ${response.status}`}`,
6927
+ said?.remedy || "The meeting itself is saved \u2014 only the telling failed. Try again; the call is idempotent."
6928
+ );
6929
+ }
6930
+ function count(body, key) {
6931
+ const value = body[key];
6932
+ return typeof value === "number" ? value : 0;
6933
+ }
6934
+ async function announceMeeting(api, meetingId) {
6935
+ const operation = "meet.announceMeeting";
6936
+ const response = await api.request({
6937
+ path: meetingPath(MEET_ROUTES.meetingAnnounce, meetingId),
6938
+ method: "POST",
6939
+ requireAuth: true,
6940
+ operation
6941
+ });
6942
+ if (!response.ok) throw await refusal(response, operation);
6943
+ const body = await response.json();
6944
+ return {
6945
+ invitations: count(body, "invitations"),
6946
+ updates: count(body, "updates"),
6947
+ cancellations: count(body, "cancellations"),
6948
+ alreadyCurrent: count(body, "already_current"),
6949
+ remindersQueued: count(body, "reminders_queued"),
6950
+ remindersCancelled: count(body, "reminders_cancelled")
6951
+ };
6952
+ }
6953
+ async function pushMeetingToGoogleCalendar(api, meetingId, target) {
6954
+ const operation = "meet.pushMeetingToGoogleCalendar";
6955
+ const response = await api.request({
6956
+ path: meetingPath(MEET_ROUTES.meetingGoogleCalendar, meetingId),
6957
+ method: "POST",
6958
+ requireAuth: true,
6959
+ operation,
6960
+ body: { connection_id: target.connectionId, calendar_id: target.calendarId ?? "primary" }
6961
+ });
6962
+ if (!response.ok) throw await refusal(response, operation);
6963
+ return await response.json();
6964
+ }
6541
6965
  export {
6542
6966
  AiPanel,
6543
6967
  AttendanceNotice,
@@ -6623,6 +7047,7 @@ export {
6623
7047
  TRANSCRIPTION_TOPIC,
6624
7048
  WifiLowIcon,
6625
7049
  absenceSentence,
7050
+ announceMeeting,
6626
7051
  applyRoomEvent,
6627
7052
  asCallId,
6628
7053
  asMeetingId,
@@ -6637,6 +7062,7 @@ export {
6637
7062
  canModerateParticipant,
6638
7063
  canTransition,
6639
7064
  captionSegment,
7065
+ changesPatch,
6640
7066
  computeStage,
6641
7067
  countLabel,
6642
7068
  createCallCenter,
@@ -6681,6 +7107,7 @@ export {
6681
7107
  mapQuality,
6682
7108
  meetingInviteAction,
6683
7109
  meetingLink,
7110
+ meetingPath,
6684
7111
  meetingSlug,
6685
7112
  meetingTopic,
6686
7113
  meetingWhen,
@@ -6697,9 +7124,15 @@ export {
6697
7124
  parseRoomToken,
6698
7125
  participantSummary,
6699
7126
  playableRecordingFileId,
7127
+ projectInvitee,
6700
7128
  projectNote,
7129
+ projectOccurrence,
7130
+ projectOverride,
6701
7131
  projectParticipant,
7132
+ projectRsvpInvitation,
6702
7133
  projectSegment,
7134
+ projectUpcoming,
7135
+ pushMeetingToGoogleCalendar,
6703
7136
  readServerRefusal,
6704
7137
  readableTranscript,
6705
7138
  recordingLabel,
@@ -6707,6 +7140,7 @@ export {
6707
7140
  roleOf,
6708
7141
  roomTopic,
6709
7142
  setGuestRecordAccess,
7143
+ settingsPatch,
6710
7144
  snapshotParticipant,
6711
7145
  unavailableFrom,
6712
7146
  useCalls,