@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.cjs CHANGED
@@ -116,6 +116,7 @@ __export(react_exports, {
116
116
  TRANSCRIPTION_TOPIC: () => TRANSCRIPTION_TOPIC,
117
117
  WifiLowIcon: () => WifiLowIcon,
118
118
  absenceSentence: () => absenceSentence,
119
+ announceMeeting: () => announceMeeting,
119
120
  applyRoomEvent: () => applyRoomEvent,
120
121
  asCallId: () => asCallId,
121
122
  asMeetingId: () => asMeetingId,
@@ -130,6 +131,7 @@ __export(react_exports, {
130
131
  canModerateParticipant: () => canModerateParticipant,
131
132
  canTransition: () => canTransition,
132
133
  captionSegment: () => captionSegment,
134
+ changesPatch: () => changesPatch,
133
135
  computeStage: () => computeStage,
134
136
  countLabel: () => countLabel,
135
137
  createCallCenter: () => createCallCenter,
@@ -174,6 +176,7 @@ __export(react_exports, {
174
176
  mapQuality: () => mapQuality,
175
177
  meetingInviteAction: () => meetingInviteAction,
176
178
  meetingLink: () => meetingLink,
179
+ meetingPath: () => meetingPath,
177
180
  meetingSlug: () => meetingSlug,
178
181
  meetingTopic: () => meetingTopic,
179
182
  meetingWhen: () => meetingWhen,
@@ -190,9 +193,15 @@ __export(react_exports, {
190
193
  parseRoomToken: () => parseRoomToken,
191
194
  participantSummary: () => participantSummary,
192
195
  playableRecordingFileId: () => playableRecordingFileId,
196
+ projectInvitee: () => projectInvitee,
193
197
  projectNote: () => projectNote,
198
+ projectOccurrence: () => projectOccurrence,
199
+ projectOverride: () => projectOverride,
194
200
  projectParticipant: () => projectParticipant,
201
+ projectRsvpInvitation: () => projectRsvpInvitation,
195
202
  projectSegment: () => projectSegment,
203
+ projectUpcoming: () => projectUpcoming,
204
+ pushMeetingToGoogleCalendar: () => pushMeetingToGoogleCalendar,
196
205
  readServerRefusal: () => readServerRefusal,
197
206
  readableTranscript: () => readableTranscript,
198
207
  recordingLabel: () => recordingLabel,
@@ -200,6 +209,7 @@ __export(react_exports, {
200
209
  roleOf: () => roleOf,
201
210
  roomTopic: () => roomTopic,
202
211
  setGuestRecordAccess: () => setGuestRecordAccess,
212
+ settingsPatch: () => settingsPatch,
203
213
  snapshotParticipant: () => snapshotParticipant,
204
214
  unavailableFrom: () => unavailableFrom,
205
215
  useCalls: () => useCalls,
@@ -266,7 +276,7 @@ var MeetError = class extends Error {
266
276
  };
267
277
  var FORBIDDEN_CODES = /* @__PURE__ */ new Set(["42501", "PGRST301", "PGRST302"]);
268
278
  var NOT_FOUND_CODES = /* @__PURE__ */ new Set(["PGRST116", "PGRST205"]);
269
- var CONFLICT_CODES = /* @__PURE__ */ new Set(["23505"]);
279
+ var CONFLICT_CODES = /* @__PURE__ */ new Set(["23505", "40001", "55000"]);
270
280
  var SESSION_MARKERS = [
271
281
  "auth session missing",
272
282
  "jwt expired",
@@ -424,8 +434,19 @@ var MEET_ROUTES = {
424
434
  */
425
435
  record: "/api/v1/meet/record",
426
436
  /** A host chooses what guests may read of ONE meeting (MRF-1). */
427
- recordAccess: "/api/v1/meet/record/access"
437
+ recordAccess: "/api/v1/meet/record/access",
438
+ /**
439
+ * Meet wave 1 (0.8): bring every invitee up to date after a write — sends the
440
+ * invitation / update / cancellation emails with their `.ics` and re-queues
441
+ * reminders. Idempotent. `{meeting_id}` is substituted by `meetingPath`.
442
+ */
443
+ meetingAnnounce: "/api/v1/meet/meetings/{meeting_id}/announce",
444
+ /** Put a one-off meeting on the caller's own Google Calendar (gated by the server). */
445
+ meetingGoogleCalendar: "/api/v1/meet/meetings/{meeting_id}/google-calendar"
428
446
  };
447
+ function meetingPath(template, meetingId) {
448
+ return template.replace("{meeting_id}", encodeURIComponent(meetingId));
449
+ }
429
450
  var ROOM_TOKEN_HEADER = "x-meet-room-token";
430
451
  var REFRESH_RATIO = 0.8;
431
452
  function readString(source, key) {
@@ -555,6 +576,19 @@ function createTokenClient(options) {
555
576
  "The link is wrong or the meeting was deleted. Show the 'meeting not found' screen; a durable link that 404s is never a retry."
556
577
  );
557
578
  }
579
+ if (response.status === 410) {
580
+ let reason = "This meeting was cancelled.";
581
+ try {
582
+ const body = await response.json();
583
+ if (typeof body?.detail?.message === "string") reason = body.detail.message;
584
+ } catch {
585
+ }
586
+ throw new MeetError(
587
+ "cancelled",
588
+ reason,
589
+ "Nobody can join a cancelled meeting. Show the cancelled screen with this sentence; ask the host to schedule a new one."
590
+ );
591
+ }
558
592
  if (response.status === 423) {
559
593
  throw new MeetError(
560
594
  "room-locked",
@@ -756,12 +790,12 @@ function createMeetAi(options) {
756
790
  async function dispatch(route, body, operation) {
757
791
  const response = await send({ path: route, body, operation, requireAuth: true });
758
792
  if (!response.ok) {
759
- const refusal = await refusalFor(response, operation);
760
- options.store.patch({ noteTakerRefusal: refusal });
793
+ const refusal2 = await refusalFor(response, operation);
794
+ options.store.patch({ noteTakerRefusal: refusal2 });
761
795
  throw new MeetError(
762
796
  response.status === 403 ? "forbidden" : "transport",
763
- refusal.reason,
764
- refusal.remedy
797
+ refusal2.reason,
798
+ refusal2.remedy
765
799
  );
766
800
  }
767
801
  options.store.patch({ noteTakerRefusal: null });
@@ -799,11 +833,11 @@ function createMeetAi(options) {
799
833
  ...signal !== void 0 ? { signal } : {}
800
834
  });
801
835
  if (!response.ok) {
802
- const refusal = await refusalFor(response, "meet.ai.ask");
836
+ const refusal2 = await refusalFor(response, "meet.ai.ask");
803
837
  throw new MeetError(
804
838
  response.status === 403 || response.status === 401 ? "forbidden" : response.status === 404 ? "not-found" : "transport",
805
- refusal.reason,
806
- refusal.remedy
839
+ refusal2.reason,
840
+ refusal2.remedy
807
841
  );
808
842
  }
809
843
  const body = response.body;
@@ -1947,6 +1981,15 @@ function createMeetingSession(options) {
1947
1981
  const { manager, repository, store, identity } = options;
1948
1982
  let channel = null;
1949
1983
  let meetingChannel = null;
1984
+ async function readMeeting(meetingId) {
1985
+ try {
1986
+ return await repository.meeting(meetingId);
1987
+ } catch (error) {
1988
+ const slug = store.snapshot().meeting?.slug;
1989
+ if (slug === void 0) throw error;
1990
+ return repository.meetingBySlug(slug);
1991
+ }
1992
+ }
1950
1993
  function requireHostUser(operation) {
1951
1994
  if (identity.userId === null || !isHostRole(effectiveRole(store.snapshot()))) {
1952
1995
  throw new MeetError(
@@ -2027,7 +2070,7 @@ function createMeetingSession(options) {
2027
2070
  onBackfill: async () => {
2028
2071
  const meeting = store.snapshot().meeting;
2029
2072
  if (meeting === null) return;
2030
- const fresh = isGuest(identity) ? await repository.meetingBySlug(meeting.slug) : await repository.meeting(meeting.id);
2073
+ const fresh = isGuest(identity) ? await repository.meetingBySlug(meeting.slug) : await readMeeting(meeting.id);
2031
2074
  store.patch({ meeting: fresh, locked: fresh.locked });
2032
2075
  }
2033
2076
  });
@@ -2122,7 +2165,7 @@ function createMeetingSession(options) {
2122
2165
  // a recovered surface from looking healthy and being wrong.
2123
2166
  onBackfill: async () => {
2124
2167
  const [meeting, transcript, notes, recordings, attendees] = await Promise.all([
2125
- repository.meeting(meetingId),
2168
+ readMeeting(meetingId),
2126
2169
  repository.transcript(meetingId),
2127
2170
  repository.notes(meetingId),
2128
2171
  repository.recordings(meetingId),
@@ -2234,11 +2277,11 @@ function createMeetingSession(options) {
2234
2277
  } catch {
2235
2278
  body = null;
2236
2279
  }
2237
- const refusal = readServerRefusal(body);
2280
+ const refusal2 = readServerRefusal(body);
2238
2281
  throw new MeetError(
2239
2282
  response.status === 403 ? "forbidden" : "transport",
2240
- refusal?.reason ?? `meet.endForEveryone: the server answered ${response.status}`,
2241
- 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."
2283
+ refusal2?.reason ?? `meet.endForEveryone: the server answered ${response.status}`,
2284
+ 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."
2242
2285
  );
2243
2286
  }
2244
2287
  let payload = null;
@@ -2281,7 +2324,11 @@ var TABLES = {
2281
2324
  invites: "meet_call_invites",
2282
2325
  recordings: "meet_recordings",
2283
2326
  transcripts: "meet_transcript_segments",
2284
- notes: "meet_notes"
2327
+ notes: "meet_notes",
2328
+ /** Wave 1 (0.8): who a meeting was sent to, and what they answered. */
2329
+ invitees: "meet_invitees",
2330
+ /** Wave 1 (0.8): one occurrence of a series cancelled or moved. */
2331
+ occurrenceOverrides: "meet_occurrence_overrides"
2285
2332
  };
2286
2333
  var RPCS = {
2287
2334
  /** Atomic: create the invite row and stamp its expiry in one statement. */
@@ -2297,10 +2344,41 @@ var RPCS = {
2297
2344
  admit: "meet_admit_participant",
2298
2345
  setLock: "meet_set_lock",
2299
2346
  endMeeting: "meet_end_meeting",
2300
- recordConsent: "meet_record_consent"
2347
+ recordConsent: "meet_record_consent",
2348
+ /*
2349
+ * ── MEET WAVE 1 — management (0.8). Every write is a SECURITY DEFINER door
2350
+ * authorized by the meeting's host or a real iam grant (editor to change,
2351
+ * admin = co-host to archive or name a co-host) — never an organization
2352
+ * match. After any of these writes, call `announceMeeting` so the server
2353
+ * sends invitations / updates / cancellations and re-queues reminders.
2354
+ */
2355
+ /** Schedule with every setting at create; unset settings come from the knobs. */
2356
+ scheduleMeeting: "meet_schedule_meeting",
2357
+ /** Edit / reschedule — a JSON patch with a version compare-and-swap. */
2358
+ updateMeeting: "meet_update_meeting",
2359
+ /** Cancel is a state (`cancelled_at`); the link keeps resolving. */
2360
+ cancelMeeting: "meet_cancel_meeting",
2361
+ /** Archive (soft delete) — the invitations and exceptions cascade. */
2362
+ archiveMeeting: "meet_archive_meeting",
2363
+ restoreMeeting: "meet_restore_meeting",
2364
+ /** Cancel, move or restore ONE occurrence of a series by its original start. */
2365
+ setOccurrence: "meet_set_occurrence",
2366
+ addInvitees: "meet_add_invitees",
2367
+ setInviteeRole: "meet_set_invitee_role",
2368
+ removeInvitee: "meet_remove_invitee",
2369
+ /** A signed-in invitee answers for themselves. */
2370
+ respond: "meet_respond",
2371
+ /** The emailed RSVP link (no account): read and answer. */
2372
+ invitationByToken: "meet_invitation_by_token",
2373
+ respondByToken: "meet_respond_by_token",
2374
+ /** One meeting's occurrences between two instants (INVOKER — the reader's RLS). */
2375
+ meetingOccurrences: "meet_meeting_occurrences",
2376
+ /** Every occurrence the reader can see between two instants — the Upcoming list. */
2377
+ occurrencesBetween: "meet_occurrences_between"
2301
2378
  };
2302
2379
  var VOID_RPCS = [RPCS.admit, RPCS.recordConsent];
2303
- 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";
2380
+ 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";
2381
+ 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";
2304
2382
  var RECORDING_COLUMNS = "id,meeting_id,state,started_at,ended_at,started_by,file_id,failure_reason";
2305
2383
  var SEGMENT_COLUMNS = "id,meeting_id,identity,speaker,text,started_at,is_final,metadata";
2306
2384
  var NOTE_COLUMNS = "id,meeting_id,text,kind,assignee_user_id,created_at,metadata";
@@ -2398,6 +2476,138 @@ function projectNote(row) {
2398
2476
  source: "record"
2399
2477
  };
2400
2478
  }
2479
+ var RSVP_STATES = ["needs_action", "accepted", "declined", "tentative"];
2480
+ var OCCURRENCE_STATES = ["scheduled", "moved", "cancelled"];
2481
+ function rsvpState(value) {
2482
+ return RSVP_STATES.includes(value) ? value : "needs_action";
2483
+ }
2484
+ function num(row, key) {
2485
+ const value = row[key];
2486
+ return typeof value === "number" ? value : null;
2487
+ }
2488
+ function when(row, key) {
2489
+ const value = row[key];
2490
+ if (value instanceof Date) return value.toISOString();
2491
+ return text(row, key);
2492
+ }
2493
+ function projectInvitee(row) {
2494
+ const operation = "meet.projectInvitee";
2495
+ const userId = text(row, "invitee_user_id");
2496
+ return {
2497
+ id: requireText(row, "id", operation),
2498
+ meetingId: asMeetingId(requireText(row, "meeting_id", operation)),
2499
+ userId: userId === null ? null : asUserId(userId),
2500
+ email: text(row, "email"),
2501
+ displayName: text(row, "display_name"),
2502
+ role: text(row, "role") === "cohost" ? "cohost" : "invitee",
2503
+ rsvpState: rsvpState(text(row, "rsvp_state")),
2504
+ respondedAt: when(row, "responded_at"),
2505
+ rsvpNote: text(row, "rsvp_note"),
2506
+ lastSentSequence: num(row, "last_sent_sequence"),
2507
+ lastSentAt: when(row, "last_sent_at")
2508
+ };
2509
+ }
2510
+ function projectOccurrence(row) {
2511
+ const operation = "meet.projectOccurrence";
2512
+ const state = text(row, "state");
2513
+ return {
2514
+ meetingId: asMeetingId(requireText(row, "meeting_id", operation)),
2515
+ originalStart: when(row, "original_start") ?? requireText(row, "original_start", operation),
2516
+ occurrenceStart: when(row, "occurrence_start") ?? requireText(row, "occurrence_start", operation),
2517
+ durationMinutes: num(row, "duration_minutes"),
2518
+ // An unknown state is shown as scheduled, never dropped: the row is real.
2519
+ state: OCCURRENCE_STATES.includes(state) ? state : "scheduled",
2520
+ sequence: num(row, "sequence") ?? 0
2521
+ };
2522
+ }
2523
+ function projectUpcoming(row) {
2524
+ const operation = "meet.projectUpcoming";
2525
+ const kind = text(row, "kind");
2526
+ const myRole = text(row, "my_role");
2527
+ const myRsvp = text(row, "my_rsvp");
2528
+ return {
2529
+ ...projectOccurrence(row),
2530
+ organizationId: asOrganizationId(requireText(row, "organization_id", operation)),
2531
+ title: text(row, "title") ?? "Meeting",
2532
+ slug: requireText(row, "slug", operation),
2533
+ kind: kind === "scheduled" || kind === "recurring" ? kind : "instant",
2534
+ hostUserId: asUserId(requireText(row, "host_user_id", operation)),
2535
+ timeZone: text(row, "time_zone") ?? "UTC",
2536
+ meetingCancelled: bool(row, "meeting_cancelled", false),
2537
+ myRsvp: myRsvp === null ? null : rsvpState(myRsvp),
2538
+ myRole: myRole === "host" || myRole === "cohost" || myRole === "invitee" ? myRole : null
2539
+ };
2540
+ }
2541
+ function projectOverride(row) {
2542
+ const operation = "meet.projectOverride";
2543
+ return {
2544
+ id: requireText(row, "id", operation),
2545
+ meetingId: asMeetingId(requireText(row, "meeting_id", operation)),
2546
+ originalStart: when(row, "original_start") ?? requireText(row, "original_start", operation),
2547
+ state: text(row, "state") === "moved" ? "moved" : "cancelled",
2548
+ newStart: when(row, "new_start"),
2549
+ newDurationMinutes: num(row, "new_duration_minutes"),
2550
+ reason: text(row, "reason"),
2551
+ calendarSequence: num(row, "calendar_sequence") ?? 0,
2552
+ deletedAt: when(row, "deleted_at")
2553
+ };
2554
+ }
2555
+ function projectRsvpInvitation(body) {
2556
+ const operation = "meet.projectRsvpInvitation";
2557
+ if (typeof body !== "object" || body === null) {
2558
+ throw invalidResponse(operation, "the RSVP door returned no object");
2559
+ }
2560
+ const row = body;
2561
+ if (row["ok"] !== true) {
2562
+ return {
2563
+ ok: false,
2564
+ message: text(row, "message") ?? "This invitation link is no longer valid \u2014 ask the host to send it again."
2565
+ };
2566
+ }
2567
+ const meeting = row["meeting"] ?? {};
2568
+ const invitee = row["invitee"] ?? {};
2569
+ return {
2570
+ ok: true,
2571
+ meeting: {
2572
+ title: text(meeting, "title") ?? "Meeting",
2573
+ slug: requireText(meeting, "slug", operation),
2574
+ scheduledFor: when(meeting, "scheduled_for"),
2575
+ durationMinutes: num(meeting, "duration_minutes"),
2576
+ timeZone: text(meeting, "time_zone") ?? "UTC",
2577
+ agenda: text(meeting, "agenda"),
2578
+ recurrenceRule: text(meeting, "recurrence_rule"),
2579
+ cancelledAt: when(meeting, "cancelled_at"),
2580
+ cancellationReason: text(meeting, "cancellation_reason"),
2581
+ endedAt: when(meeting, "ended_at")
2582
+ },
2583
+ invitee: {
2584
+ displayName: text(invitee, "display_name"),
2585
+ email: text(invitee, "email"),
2586
+ rsvpState: rsvpState(text(invitee, "rsvp_state")),
2587
+ respondedAt: when(invitee, "responded_at")
2588
+ }
2589
+ };
2590
+ }
2591
+ function settingsPatch(settings) {
2592
+ const patch = {};
2593
+ if (settings.lobbyEnabled !== void 0) patch["lobby_enabled"] = settings.lobbyEnabled;
2594
+ if (settings.recordingPolicy !== void 0) patch["recording_policy"] = settings.recordingPolicy;
2595
+ if (settings.aiEnabled !== void 0) patch["ai_enabled"] = settings.aiEnabled;
2596
+ if (settings.joinBeforeHost !== void 0) patch["join_before_host"] = settings.joinBeforeHost;
2597
+ return patch;
2598
+ }
2599
+ function changesPatch(changes) {
2600
+ const patch = settingsPatch(changes);
2601
+ if (changes.title !== void 0) patch["title"] = changes.title;
2602
+ if (changes.agenda !== void 0) patch["agenda"] = changes.agenda;
2603
+ if (changes.timeZone !== void 0) patch["time_zone"] = changes.timeZone;
2604
+ if (changes.scheduledFor !== void 0) patch["scheduled_for"] = changes.scheduledFor;
2605
+ if (changes.scheduledDurationMinutes !== void 0) {
2606
+ patch["scheduled_duration_minutes"] = changes.scheduledDurationMinutes;
2607
+ }
2608
+ if (changes.recurrenceRule !== void 0) patch["recurrence_rule"] = changes.recurrenceRule;
2609
+ return patch;
2610
+ }
2401
2611
  function createMeetRepository(options) {
2402
2612
  const db = options.client.schema(MEET_SCHEMA);
2403
2613
  async function guardVoid(operation, run) {
@@ -2467,7 +2677,16 @@ function createMeetRepository(options) {
2467
2677
  lobbyEnabled: bool(row, "lobby_enabled", true),
2468
2678
  recordingPolicy: policy === "disabled" || policy === "always-on" ? policy : "host-controlled",
2469
2679
  aiEnabled: bool(row, "ai_enabled", true),
2470
- metadata: typeof metadata === "object" && metadata !== null && !Array.isArray(metadata) ? metadata : {}
2680
+ metadata: typeof metadata === "object" && metadata !== null && !Array.isArray(metadata) ? metadata : {},
2681
+ timeZone: text(row, "time_zone") ?? "UTC",
2682
+ agenda: text(row, "agenda"),
2683
+ recurrenceRule: text(row, "recurrence_rule"),
2684
+ joinBeforeHost: bool(row, "join_before_host", true),
2685
+ cancelledAt: text(row, "cancelled_at"),
2686
+ cancellationReason: text(row, "cancellation_reason"),
2687
+ deletedAt: text(row, "deleted_at"),
2688
+ calendarSequence: typeof row["calendar_sequence"] === "number" ? row["calendar_sequence"] : 0,
2689
+ version: typeof row["version"] === "number" ? row["version"] : 1
2471
2690
  };
2472
2691
  }
2473
2692
  function projectRecording(row) {
@@ -2554,6 +2773,170 @@ function createMeetRepository(options) {
2554
2773
  projectNote,
2555
2774
  projectRecording,
2556
2775
  projectAttendee,
2776
+ projectInvitee,
2777
+ async scheduleMeeting(input) {
2778
+ requireOrg(input.organizationId, "meet.scheduleMeeting");
2779
+ const row = await guard(
2780
+ "meet.scheduleMeeting",
2781
+ () => db.rpc(RPCS.scheduleMeeting, {
2782
+ p_organization_id: input.organizationId,
2783
+ p_host_user_id: input.hostUserId,
2784
+ p_title: input.title,
2785
+ p_scheduled_for: input.scheduledFor,
2786
+ p_time_zone: input.timeZone,
2787
+ p_duration_minutes: input.durationMinutes ?? null,
2788
+ p_agenda: input.agenda ?? null,
2789
+ p_recurrence_rule: input.recurrenceRule ?? null,
2790
+ p_settings: settingsPatch(input.settings ?? {})
2791
+ })
2792
+ );
2793
+ return projectMeeting(row);
2794
+ },
2795
+ async updateMeeting(meetingId, changes, options2 = {}) {
2796
+ const row = await guard(
2797
+ "meet.updateMeeting",
2798
+ () => db.rpc(RPCS.updateMeeting, {
2799
+ p_meeting_id: meetingId,
2800
+ p_changes: changesPatch(changes),
2801
+ p_expected_version: options2.expectedVersion ?? null,
2802
+ p_by_user_id: options2.byUserId ?? null
2803
+ })
2804
+ );
2805
+ return projectMeeting(row);
2806
+ },
2807
+ async cancelMeeting(meetingId, reason, byUserId = null) {
2808
+ const row = await guard(
2809
+ "meet.cancelMeeting",
2810
+ () => db.rpc(RPCS.cancelMeeting, {
2811
+ p_meeting_id: meetingId,
2812
+ p_reason: reason,
2813
+ p_by_user_id: byUserId
2814
+ })
2815
+ );
2816
+ return projectMeeting(row);
2817
+ },
2818
+ async archiveMeeting(meetingId, byUserId = null) {
2819
+ const row = await guard(
2820
+ "meet.archiveMeeting",
2821
+ () => db.rpc(RPCS.archiveMeeting, { p_meeting_id: meetingId, p_by_user_id: byUserId })
2822
+ );
2823
+ return projectMeeting(row);
2824
+ },
2825
+ async restoreMeeting(meetingId, byUserId = null) {
2826
+ const row = await guard(
2827
+ "meet.restoreMeeting",
2828
+ () => db.rpc(RPCS.restoreMeeting, { p_meeting_id: meetingId, p_by_user_id: byUserId })
2829
+ );
2830
+ return projectMeeting(row);
2831
+ },
2832
+ async setOccurrence(args) {
2833
+ const row = await guard(
2834
+ "meet.setOccurrence",
2835
+ () => db.rpc(RPCS.setOccurrence, {
2836
+ p_meeting_id: args.meetingId,
2837
+ p_original_start: args.originalStart,
2838
+ p_action: args.action,
2839
+ p_new_start: args.newStart ?? null,
2840
+ p_new_duration_minutes: args.newDurationMinutes ?? null,
2841
+ p_reason: args.reason ?? null,
2842
+ p_by_user_id: args.byUserId ?? null
2843
+ })
2844
+ );
2845
+ return projectOverride(row);
2846
+ },
2847
+ async meetingOccurrences(meetingId, { from, to, limit = 200 }) {
2848
+ const rows = await guard(
2849
+ "meet.meetingOccurrences",
2850
+ () => db.rpc(RPCS.meetingOccurrences, {
2851
+ p_meeting_id: meetingId,
2852
+ p_from: from,
2853
+ p_to: to,
2854
+ p_limit: limit
2855
+ })
2856
+ );
2857
+ return rows.map(projectOccurrence);
2858
+ },
2859
+ async occurrencesBetween({ from, to, organizationId = null, limit = 500 }) {
2860
+ const rows = await guard(
2861
+ "meet.occurrencesBetween",
2862
+ () => db.rpc(RPCS.occurrencesBetween, {
2863
+ p_from: from,
2864
+ p_to: to,
2865
+ p_organization_id: organizationId,
2866
+ p_limit: limit
2867
+ })
2868
+ );
2869
+ return rows.map(projectUpcoming);
2870
+ },
2871
+ async invitees(meetingId) {
2872
+ 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 });
2873
+ if (response.error !== null) throw normalizeMeetError(response.error, "meet.invitees");
2874
+ return (response.data ?? []).map(projectInvitee);
2875
+ },
2876
+ async addInvitees(meetingId, people, byUserId = null) {
2877
+ const rows = await guard(
2878
+ "meet.addInvitees",
2879
+ () => db.rpc(RPCS.addInvitees, {
2880
+ p_meeting_id: meetingId,
2881
+ p_invitees: people.map((person) => ({
2882
+ ...person.userId !== void 0 ? { user_id: person.userId } : {},
2883
+ ...person.email !== void 0 ? { email: person.email } : {},
2884
+ ...person.displayName !== void 0 ? { display_name: person.displayName } : {},
2885
+ ...person.role !== void 0 ? { role: person.role } : {}
2886
+ })),
2887
+ p_by_user_id: byUserId
2888
+ })
2889
+ );
2890
+ return rows.map(projectInvitee);
2891
+ },
2892
+ async setInviteeRole(inviteeId, role, byUserId = null) {
2893
+ const row = await guard(
2894
+ "meet.setInviteeRole",
2895
+ () => db.rpc(RPCS.setInviteeRole, {
2896
+ p_invitee_id: inviteeId,
2897
+ p_role: role,
2898
+ p_by_user_id: byUserId
2899
+ })
2900
+ );
2901
+ return projectInvitee(row);
2902
+ },
2903
+ async removeInvitee(inviteeId, byUserId = null) {
2904
+ const row = await guard(
2905
+ "meet.removeInvitee",
2906
+ () => db.rpc(RPCS.removeInvitee, { p_invitee_id: inviteeId, p_by_user_id: byUserId })
2907
+ );
2908
+ return projectInvitee(row);
2909
+ },
2910
+ async respond(meetingId, answer, note = null) {
2911
+ const row = await guard(
2912
+ "meet.respond",
2913
+ () => db.rpc(RPCS.respond, {
2914
+ p_meeting_id: meetingId,
2915
+ p_response: answer,
2916
+ p_note: note,
2917
+ p_by_user_id: null
2918
+ })
2919
+ );
2920
+ return projectInvitee(row);
2921
+ },
2922
+ async invitationByToken(secret) {
2923
+ const body = await guard(
2924
+ "meet.invitationByToken",
2925
+ () => db.rpc(RPCS.invitationByToken, { p_secret: secret })
2926
+ );
2927
+ return projectRsvpInvitation(body);
2928
+ },
2929
+ async respondByToken(secret, answer, note = null) {
2930
+ const body = await guard(
2931
+ "meet.respondByToken",
2932
+ () => db.rpc(RPCS.respondByToken, {
2933
+ p_secret: secret,
2934
+ p_response: answer,
2935
+ p_note: note
2936
+ })
2937
+ );
2938
+ return projectRsvpInvitation(body);
2939
+ },
2557
2940
  async attendees(meetingId) {
2558
2941
  const response = await db.from(TABLES.participants).select(ATTENDEE_COLUMNS).eq("meeting_id", meetingId).order("joined_at", { ascending: true }).order("id", { ascending: true });
2559
2942
  if (response.error !== null) throw normalizeMeetError(response.error, "meet.attendees");
@@ -2981,14 +3364,14 @@ function noteTakerStatus(args) {
2981
3364
  const row = typeof raw === "object" && raw !== null && !Array.isArray(raw) ? raw : {};
2982
3365
  const recorded = readState(row["state"]);
2983
3366
  const detail = typeof row["reason"] === "string" && row["reason"].length > 0 ? row["reason"] : null;
2984
- const refusal = args.refusal ?? null;
3367
+ const refusal2 = args.refusal ?? null;
2985
3368
  if (recorded === null) {
2986
- return { state: "absent", detail, present, refusal };
3369
+ return { state: "absent", detail, present, refusal: refusal2 };
2987
3370
  }
2988
3371
  if (recorded === "transcribing" && !present) {
2989
- return { state: "handoff_pending", detail, present, refusal };
3372
+ return { state: "handoff_pending", detail, present, refusal: refusal2 };
2990
3373
  }
2991
- return { state: recorded, detail, present, refusal };
3374
+ return { state: recorded, detail, present, refusal: refusal2 };
2992
3375
  }
2993
3376
  function noteTakerLabel(status) {
2994
3377
  switch (status.state) {
@@ -4013,11 +4396,11 @@ function computeStage(args) {
4013
4396
  presenting: sharer !== null
4014
4397
  };
4015
4398
  }
4016
- function gridColumns(count) {
4017
- if (count <= 1) return 1;
4018
- if (count <= 4) return 2;
4019
- if (count <= 9) return 3;
4020
- if (count <= 16) return 4;
4399
+ function gridColumns(count2) {
4400
+ if (count2 <= 1) return 1;
4401
+ if (count2 <= 4) return 2;
4402
+ if (count2 <= 9) return 3;
4403
+ if (count2 <= 16) return 4;
4021
4404
  return 5;
4022
4405
  }
4023
4406
 
@@ -4191,7 +4574,7 @@ function meetingWhen(meeting, locale) {
4191
4574
  if (start === null) return "Time not recorded";
4192
4575
  const startedAt = new Date(start);
4193
4576
  if (Number.isNaN(startedAt.getTime())) return "Time not recorded";
4194
- const when = startedAt.toLocaleString(locale, {
4577
+ const when2 = startedAt.toLocaleString(locale, {
4195
4578
  weekday: "long",
4196
4579
  year: "numeric",
4197
4580
  month: "long",
@@ -4199,11 +4582,11 @@ function meetingWhen(meeting, locale) {
4199
4582
  hour: "numeric",
4200
4583
  minute: "2-digit"
4201
4584
  });
4202
- if (meeting.endedAt === null || meeting.startedAt === null) return when;
4585
+ if (meeting.endedAt === null || meeting.startedAt === null) return when2;
4203
4586
  const endedAt = new Date(meeting.endedAt);
4204
- if (Number.isNaN(endedAt.getTime())) return when;
4587
+ if (Number.isNaN(endedAt.getTime())) return when2;
4205
4588
  const elapsedMs = endedAt.getTime() - startedAt.getTime();
4206
- return `${when} \xB7 ${(0, import_format.formatDurationMs)(elapsedMs, { style: "long" })}`;
4589
+ return `${when2} \xB7 ${(0, import_format.formatDurationMs)(elapsedMs, { style: "long" })}`;
4207
4590
  }
4208
4591
  var GUEST_RECORD_ACCESS_LEVELS = [
4209
4592
  "none",
@@ -4310,8 +4693,8 @@ async function loadGuestMeetingRecord(args) {
4310
4693
  );
4311
4694
  }
4312
4695
  if (!response.ok) {
4313
- const refusal = await readRefusal(response);
4314
- return refusedBundle(meeting, refusal.message, refusal.remedy);
4696
+ const refusal2 = await readRefusal(response);
4697
+ return refusedBundle(meeting, refusal2.message, refusal2.remedy);
4315
4698
  }
4316
4699
  const body = await response.json();
4317
4700
  const payload = typeof body["data"] === "object" && body["data"] !== null ? body["data"] : body;
@@ -4402,8 +4785,8 @@ async function setGuestRecordAccess(args) {
4402
4785
  }
4403
4786
  });
4404
4787
  if (!response.ok) {
4405
- const refusal = await readRefusal(response);
4406
- throw new MeetError("forbidden", refusal.message, refusal.remedy);
4788
+ const refusal2 = await readRefusal(response);
4789
+ throw new MeetError("forbidden", refusal2.message, refusal2.remedy);
4407
4790
  }
4408
4791
  const body = await response.json();
4409
4792
  const payload = typeof body["data"] === "object" && body["data"] !== null ? body["data"] : body;
@@ -5070,8 +5453,8 @@ function callStateLabel(state, outgoing) {
5070
5453
  return "Call";
5071
5454
  }
5072
5455
  }
5073
- function countLabel(count, singular, plural = `${singular}s`) {
5074
- return `${count} ${count === 1 ? singular : plural}`;
5456
+ function countLabel(count2, singular, plural = `${singular}s`) {
5457
+ return `${count2} ${count2 === 1 ? singular : plural}`;
5075
5458
  }
5076
5459
 
5077
5460
  // src/react/icons.tsx
@@ -6129,7 +6512,7 @@ function ScheduleMeetingBody({
6129
6512
  host
6130
6513
  }) {
6131
6514
  const [title, setTitle] = (0, import_react4.useState)("");
6132
- const [when, setWhen] = (0, import_react4.useState)("");
6515
+ const [when2, setWhen] = (0, import_react4.useState)("");
6133
6516
  const [kind, setKind] = (0, import_react4.useState)("scheduled");
6134
6517
  const [busy, setBusy] = (0, import_react4.useState)(false);
6135
6518
  const [link, setLink] = (0, import_react4.useState)(null);
@@ -6149,7 +6532,7 @@ function ScheduleMeetingBody({
6149
6532
  hostUserId: host.identity.userId,
6150
6533
  title: title.trim(),
6151
6534
  kind,
6152
- scheduledFor: when.length > 0 ? new Date(when).toISOString() : null
6535
+ scheduledFor: when2.length > 0 ? new Date(when2).toISOString() : null
6153
6536
  }).then((meeting) => {
6154
6537
  setLink(meetingLink(appBaseUrl, meeting.slug));
6155
6538
  onScheduled?.(meeting.slug);
@@ -6179,7 +6562,7 @@ function ScheduleMeetingBody({
6179
6562
  {
6180
6563
  className: "mx-meet__input",
6181
6564
  type: "datetime-local",
6182
- value: when,
6565
+ value: when2,
6183
6566
  onChange: (event) => setWhen(event.target.value)
6184
6567
  }
6185
6568
  )
@@ -6759,4 +7142,55 @@ function createMeetingInviteHandler(options) {
6759
7142
  }
6760
7143
  };
6761
7144
  }
7145
+
7146
+ // src/core/management.ts
7147
+ async function refusal(response, operation) {
7148
+ let body = null;
7149
+ try {
7150
+ body = await response.json();
7151
+ } catch {
7152
+ body = null;
7153
+ }
7154
+ const said = readServerRefusal(body);
7155
+ return new MeetError(
7156
+ response.status === 403 || response.status === 401 ? "forbidden" : response.status === 404 ? "not-found" : response.status === 409 ? "conflict" : "transport",
7157
+ `${operation}: ${said?.reason ?? `the server answered ${response.status}`}`,
7158
+ said?.remedy || "The meeting itself is saved \u2014 only the telling failed. Try again; the call is idempotent."
7159
+ );
7160
+ }
7161
+ function count(body, key) {
7162
+ const value = body[key];
7163
+ return typeof value === "number" ? value : 0;
7164
+ }
7165
+ async function announceMeeting(api, meetingId) {
7166
+ const operation = "meet.announceMeeting";
7167
+ const response = await api.request({
7168
+ path: meetingPath(MEET_ROUTES.meetingAnnounce, meetingId),
7169
+ method: "POST",
7170
+ requireAuth: true,
7171
+ operation
7172
+ });
7173
+ if (!response.ok) throw await refusal(response, operation);
7174
+ const body = await response.json();
7175
+ return {
7176
+ invitations: count(body, "invitations"),
7177
+ updates: count(body, "updates"),
7178
+ cancellations: count(body, "cancellations"),
7179
+ alreadyCurrent: count(body, "already_current"),
7180
+ remindersQueued: count(body, "reminders_queued"),
7181
+ remindersCancelled: count(body, "reminders_cancelled")
7182
+ };
7183
+ }
7184
+ async function pushMeetingToGoogleCalendar(api, meetingId, target) {
7185
+ const operation = "meet.pushMeetingToGoogleCalendar";
7186
+ const response = await api.request({
7187
+ path: meetingPath(MEET_ROUTES.meetingGoogleCalendar, meetingId),
7188
+ method: "POST",
7189
+ requireAuth: true,
7190
+ operation,
7191
+ body: { connection_id: target.connectionId, calendar_id: target.calendarId ?? "primary" }
7192
+ });
7193
+ if (!response.ok) throw await refusal(response, operation);
7194
+ return await response.json();
7195
+ }
6762
7196
  //# sourceMappingURL=react.cjs.map