@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/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;
@@ -2022,11 +2046,11 @@ function createMeetingSession(options) {
2022
2046
  } catch {
2023
2047
  body = null;
2024
2048
  }
2025
- const refusal = readServerRefusal(body);
2049
+ const refusal2 = readServerRefusal(body);
2026
2050
  throw new MeetError(
2027
2051
  response.status === 403 ? "forbidden" : "transport",
2028
- refusal?.reason ?? `meet.endForEveryone: the server answered ${response.status}`,
2029
- 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."
2030
2054
  );
2031
2055
  }
2032
2056
  let payload = null;
@@ -2069,7 +2093,11 @@ var TABLES = {
2069
2093
  invites: "meet_call_invites",
2070
2094
  recordings: "meet_recordings",
2071
2095
  transcripts: "meet_transcript_segments",
2072
- 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"
2073
2101
  };
2074
2102
  var RPCS = {
2075
2103
  /** Atomic: create the invite row and stamp its expiry in one statement. */
@@ -2085,10 +2113,41 @@ var RPCS = {
2085
2113
  admit: "meet_admit_participant",
2086
2114
  setLock: "meet_set_lock",
2087
2115
  endMeeting: "meet_end_meeting",
2088
- 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"
2089
2147
  };
2090
2148
  var VOID_RPCS = [RPCS.admit, RPCS.recordConsent];
2091
- 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";
2092
2151
  var RECORDING_COLUMNS = "id,meeting_id,state,started_at,ended_at,started_by,file_id,failure_reason";
2093
2152
  var SEGMENT_COLUMNS = "id,meeting_id,identity,speaker,text,started_at,is_final,metadata";
2094
2153
  var NOTE_COLUMNS = "id,meeting_id,text,kind,assignee_user_id,created_at,metadata";
@@ -2186,6 +2245,138 @@ function projectNote(row) {
2186
2245
  source: "record"
2187
2246
  };
2188
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
+ }
2189
2380
  function createMeetRepository(options) {
2190
2381
  const db = options.client.schema(MEET_SCHEMA);
2191
2382
  async function guardVoid(operation, run) {
@@ -2255,7 +2446,16 @@ function createMeetRepository(options) {
2255
2446
  lobbyEnabled: bool(row, "lobby_enabled", true),
2256
2447
  recordingPolicy: policy === "disabled" || policy === "always-on" ? policy : "host-controlled",
2257
2448
  aiEnabled: bool(row, "ai_enabled", true),
2258
- 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
2259
2459
  };
2260
2460
  }
2261
2461
  function projectRecording(row) {
@@ -2342,6 +2542,170 @@ function createMeetRepository(options) {
2342
2542
  projectNote,
2343
2543
  projectRecording,
2344
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
+ },
2345
2709
  async attendees(meetingId) {
2346
2710
  const response = await db.from(TABLES.participants).select(ATTENDEE_COLUMNS).eq("meeting_id", meetingId).order("joined_at", { ascending: true }).order("id", { ascending: true });
2347
2711
  if (response.error !== null) throw normalizeMeetError(response.error, "meet.attendees");
@@ -2769,14 +3133,14 @@ function noteTakerStatus(args) {
2769
3133
  const row = typeof raw === "object" && raw !== null && !Array.isArray(raw) ? raw : {};
2770
3134
  const recorded = readState(row["state"]);
2771
3135
  const detail = typeof row["reason"] === "string" && row["reason"].length > 0 ? row["reason"] : null;
2772
- const refusal = args.refusal ?? null;
3136
+ const refusal2 = args.refusal ?? null;
2773
3137
  if (recorded === null) {
2774
- return { state: "absent", detail, present, refusal };
3138
+ return { state: "absent", detail, present, refusal: refusal2 };
2775
3139
  }
2776
3140
  if (recorded === "transcribing" && !present) {
2777
- return { state: "handoff_pending", detail, present, refusal };
3141
+ return { state: "handoff_pending", detail, present, refusal: refusal2 };
2778
3142
  }
2779
- return { state: recorded, detail, present, refusal };
3143
+ return { state: recorded, detail, present, refusal: refusal2 };
2780
3144
  }
2781
3145
  function noteTakerLabel(status) {
2782
3146
  switch (status.state) {
@@ -3801,11 +4165,11 @@ function computeStage(args) {
3801
4165
  presenting: sharer !== null
3802
4166
  };
3803
4167
  }
3804
- function gridColumns(count) {
3805
- if (count <= 1) return 1;
3806
- if (count <= 4) return 2;
3807
- if (count <= 9) return 3;
3808
- 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;
3809
4173
  return 5;
3810
4174
  }
3811
4175
 
@@ -3979,7 +4343,7 @@ function meetingWhen(meeting, locale) {
3979
4343
  if (start === null) return "Time not recorded";
3980
4344
  const startedAt = new Date(start);
3981
4345
  if (Number.isNaN(startedAt.getTime())) return "Time not recorded";
3982
- const when = startedAt.toLocaleString(locale, {
4346
+ const when2 = startedAt.toLocaleString(locale, {
3983
4347
  weekday: "long",
3984
4348
  year: "numeric",
3985
4349
  month: "long",
@@ -3987,11 +4351,11 @@ function meetingWhen(meeting, locale) {
3987
4351
  hour: "numeric",
3988
4352
  minute: "2-digit"
3989
4353
  });
3990
- if (meeting.endedAt === null || meeting.startedAt === null) return when;
4354
+ if (meeting.endedAt === null || meeting.startedAt === null) return when2;
3991
4355
  const endedAt = new Date(meeting.endedAt);
3992
- if (Number.isNaN(endedAt.getTime())) return when;
4356
+ if (Number.isNaN(endedAt.getTime())) return when2;
3993
4357
  const elapsedMs = endedAt.getTime() - startedAt.getTime();
3994
- return `${when} \xB7 ${formatDurationMs(elapsedMs, { style: "long" })}`;
4358
+ return `${when2} \xB7 ${formatDurationMs(elapsedMs, { style: "long" })}`;
3995
4359
  }
3996
4360
  var GUEST_RECORD_ACCESS_LEVELS = [
3997
4361
  "none",
@@ -4098,8 +4462,8 @@ async function loadGuestMeetingRecord(args) {
4098
4462
  );
4099
4463
  }
4100
4464
  if (!response.ok) {
4101
- const refusal = await readRefusal(response);
4102
- return refusedBundle(meeting, refusal.message, refusal.remedy);
4465
+ const refusal2 = await readRefusal(response);
4466
+ return refusedBundle(meeting, refusal2.message, refusal2.remedy);
4103
4467
  }
4104
4468
  const body = await response.json();
4105
4469
  const payload = typeof body["data"] === "object" && body["data"] !== null ? body["data"] : body;
@@ -4190,8 +4554,8 @@ async function setGuestRecordAccess(args) {
4190
4554
  }
4191
4555
  });
4192
4556
  if (!response.ok) {
4193
- const refusal = await readRefusal(response);
4194
- throw new MeetError("forbidden", refusal.message, refusal.remedy);
4557
+ const refusal2 = await readRefusal(response);
4558
+ throw new MeetError("forbidden", refusal2.message, refusal2.remedy);
4195
4559
  }
4196
4560
  const body = await response.json();
4197
4561
  const payload = typeof body["data"] === "object" && body["data"] !== null ? body["data"] : body;
@@ -4858,8 +5222,8 @@ function callStateLabel(state, outgoing) {
4858
5222
  return "Call";
4859
5223
  }
4860
5224
  }
4861
- function countLabel(count, singular, plural = `${singular}s`) {
4862
- return `${count} ${count === 1 ? singular : plural}`;
5225
+ function countLabel(count2, singular, plural = `${singular}s`) {
5226
+ return `${count2} ${count2 === 1 ? singular : plural}`;
4863
5227
  }
4864
5228
 
4865
5229
  // src/react/icons.tsx
@@ -5917,7 +6281,7 @@ function ScheduleMeetingBody({
5917
6281
  host
5918
6282
  }) {
5919
6283
  const [title, setTitle] = useState3("");
5920
- const [when, setWhen] = useState3("");
6284
+ const [when2, setWhen] = useState3("");
5921
6285
  const [kind, setKind] = useState3("scheduled");
5922
6286
  const [busy, setBusy] = useState3(false);
5923
6287
  const [link, setLink] = useState3(null);
@@ -5937,7 +6301,7 @@ function ScheduleMeetingBody({
5937
6301
  hostUserId: host.identity.userId,
5938
6302
  title: title.trim(),
5939
6303
  kind,
5940
- scheduledFor: when.length > 0 ? new Date(when).toISOString() : null
6304
+ scheduledFor: when2.length > 0 ? new Date(when2).toISOString() : null
5941
6305
  }).then((meeting) => {
5942
6306
  setLink(meetingLink(appBaseUrl, meeting.slug));
5943
6307
  onScheduled?.(meeting.slug);
@@ -5967,7 +6331,7 @@ function ScheduleMeetingBody({
5967
6331
  {
5968
6332
  className: "mx-meet__input",
5969
6333
  type: "datetime-local",
5970
- value: when,
6334
+ value: when2,
5971
6335
  onChange: (event) => setWhen(event.target.value)
5972
6336
  }
5973
6337
  )
@@ -6547,6 +6911,57 @@ function createMeetingInviteHandler(options) {
6547
6911
  }
6548
6912
  };
6549
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
+ }
6550
6965
  export {
6551
6966
  AiPanel,
6552
6967
  AttendanceNotice,
@@ -6632,6 +7047,7 @@ export {
6632
7047
  TRANSCRIPTION_TOPIC,
6633
7048
  WifiLowIcon,
6634
7049
  absenceSentence,
7050
+ announceMeeting,
6635
7051
  applyRoomEvent,
6636
7052
  asCallId,
6637
7053
  asMeetingId,
@@ -6646,6 +7062,7 @@ export {
6646
7062
  canModerateParticipant,
6647
7063
  canTransition,
6648
7064
  captionSegment,
7065
+ changesPatch,
6649
7066
  computeStage,
6650
7067
  countLabel,
6651
7068
  createCallCenter,
@@ -6690,6 +7107,7 @@ export {
6690
7107
  mapQuality,
6691
7108
  meetingInviteAction,
6692
7109
  meetingLink,
7110
+ meetingPath,
6693
7111
  meetingSlug,
6694
7112
  meetingTopic,
6695
7113
  meetingWhen,
@@ -6706,9 +7124,15 @@ export {
6706
7124
  parseRoomToken,
6707
7125
  participantSummary,
6708
7126
  playableRecordingFileId,
7127
+ projectInvitee,
6709
7128
  projectNote,
7129
+ projectOccurrence,
7130
+ projectOverride,
6710
7131
  projectParticipant,
7132
+ projectRsvpInvitation,
6711
7133
  projectSegment,
7134
+ projectUpcoming,
7135
+ pushMeetingToGoogleCalendar,
6712
7136
  readServerRefusal,
6713
7137
  readableTranscript,
6714
7138
  recordingLabel,
@@ -6716,6 +7140,7 @@ export {
6716
7140
  roleOf,
6717
7141
  roomTopic,
6718
7142
  setGuestRecordAccess,
7143
+ settingsPatch,
6719
7144
  snapshotParticipant,
6720
7145
  unavailableFrom,
6721
7146
  useCalls,