@ai-matrx/meet 0.7.78 → 0.7.80
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/dist/index.cjs +230 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +120 -1
- package/dist/index.d.ts +120 -1
- package/dist/index.js +230 -20
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +489 -219
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +4 -3
- package/dist/react.d.ts +4 -3
- package/dist/react.js +384 -114
- package/dist/react.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.cts
CHANGED
|
@@ -2938,6 +2938,21 @@ interface MeetingRecordBundle {
|
|
|
2938
2938
|
* therefore make the same distinction as decisions and action items.
|
|
2939
2939
|
*/
|
|
2940
2940
|
declare function activeWrapUpSummary(notes: readonly MeetingNote[]): MeetingNote | null;
|
|
2941
|
+
/**
|
|
2942
|
+
* The four durable reads behind a record, as the database returned them, plus
|
|
2943
|
+
* what the reader could not see. `assembleMeetingRecord` turns them into the
|
|
2944
|
+
* bundle; a cache that keeps them can apply a realtime row to one part and
|
|
2945
|
+
* assemble again, without reading the other three (0.7.79).
|
|
2946
|
+
*/
|
|
2947
|
+
interface MeetingRecordParts {
|
|
2948
|
+
readonly attendees: readonly MeetingAttendee[];
|
|
2949
|
+
/** Every note row (all kinds, superseded included), oldest first. */
|
|
2950
|
+
readonly notes: readonly MeetingNote[];
|
|
2951
|
+
readonly transcript: readonly TranscriptSegment[];
|
|
2952
|
+
/** Newest first; archived recordings are not here. */
|
|
2953
|
+
readonly recordings: readonly RecordingRecord[];
|
|
2954
|
+
readonly restrictions: readonly MeetingRecordRestriction[];
|
|
2955
|
+
}
|
|
2941
2956
|
/**
|
|
2942
2957
|
* Load the record parts the reader may request; state all restrictions.
|
|
2943
2958
|
*
|
|
@@ -2946,6 +2961,10 @@ declare function activeWrapUpSummary(notes: readonly MeetingNote[]): MeetingNote
|
|
|
2946
2961
|
* fail for exactly the reader who most needs the page to work.
|
|
2947
2962
|
*/
|
|
2948
2963
|
declare function loadMeetingRecord(repository: MeetRepository, meetingId: MeetingId, meeting: MeetingRecord, reader?: Pick<MeetIdentity, "userId">): Promise<MeetingRecordBundle>;
|
|
2964
|
+
/** The four reads, each with its own verdict (see `loadMeetingRecord`). */
|
|
2965
|
+
declare function loadMeetingRecordParts(repository: MeetRepository, meetingId: MeetingId, reader?: Pick<MeetIdentity, "userId">): Promise<MeetingRecordParts>;
|
|
2966
|
+
/** The bundle a reader sees, from the parts. Grouping and ordering only (rule 3). */
|
|
2967
|
+
declare function assembleMeetingRecord(meeting: MeetingRecord, parts: MeetingRecordParts): MeetingRecordBundle;
|
|
2949
2968
|
/**
|
|
2950
2969
|
* 🚨 **A READ THAT WAS REFUSED IS NOT AN EMPTY RESULT (MRI-A16).**
|
|
2951
2970
|
*
|
|
@@ -3594,4 +3613,104 @@ interface InRoomClientOptions {
|
|
|
3594
3613
|
}
|
|
3595
3614
|
declare function createInRoomClient(options: InRoomClientOptions): InRoomClient;
|
|
3596
3615
|
|
|
3597
|
-
|
|
3616
|
+
/**
|
|
3617
|
+
* ONE MEETING RECORD PER READER PER TAB — read once, kept current by one
|
|
3618
|
+
* ref-counted channel (0.7.79).
|
|
3619
|
+
*
|
|
3620
|
+
* 🚨 THE DEFECT THIS CLOSES. `useMeetingRecord` held the bundle in its own
|
|
3621
|
+
* component state and read all four parts (`meet_participants`, `meet_notes`,
|
|
3622
|
+
* `meet_transcript_segments`, `meet_recordings`) in a mount effect. A host that
|
|
3623
|
+
* hides a view without unmounting its data — a board tile asleep under React
|
|
3624
|
+
* `<Activity>`, which tears every effect down and runs it again on wake — read
|
|
3625
|
+
* the whole record again on every wake, on a Remove + Undo, and once more for
|
|
3626
|
+
* every second view of the same meeting (the record tab, the meeting's agent
|
|
3627
|
+
* surface, five "meeting notes" tiles). The view owned the data.
|
|
3628
|
+
*
|
|
3629
|
+
* THE SHAPE. One entry per (reader, meeting). The first view reads; every later
|
|
3630
|
+
* view — the same view after a wake or a remount included — renders the entry
|
|
3631
|
+
* and reads nothing. A read already in flight is joined. A forced read (Retry,
|
|
3632
|
+
* a reconnect) supersedes it, and an answer that lands after a newer read was
|
|
3633
|
+
* asked is DROPPED (the late-answer guard), so an old answer can never paint
|
|
3634
|
+
* over a newer one.
|
|
3635
|
+
*
|
|
3636
|
+
* LIVE WHILE HELD. A member's entry (a direct read, kept as its four parts) is
|
|
3637
|
+
* patched row by row from the database's change feed for the four tables, then
|
|
3638
|
+
* assembled again — one row, never four reads. The channel opens on the first
|
|
3639
|
+
* holder and closes `MEETING_RECORD_GRACE_MS` after the last one lets go, so a
|
|
3640
|
+
* wake or an Undo inside the grace costs nothing. Past the grace nothing
|
|
3641
|
+
* watched the meeting, so the entry is marked stale and the next view reads it
|
|
3642
|
+
* once. A guest's record arrives through the server as a finished bundle; it
|
|
3643
|
+
* has no change feed and is kept as it is.
|
|
3644
|
+
*
|
|
3645
|
+
* Framework-free on purpose: `react/hooks.ts` is one reader of it.
|
|
3646
|
+
*/
|
|
3647
|
+
|
|
3648
|
+
/** How long a meeting's channel and kept record outlive the last view of it. */
|
|
3649
|
+
declare const MEETING_RECORD_GRACE_MS = 60000;
|
|
3650
|
+
interface MeetingRecordFailure {
|
|
3651
|
+
readonly message: string;
|
|
3652
|
+
readonly remedy: string;
|
|
3653
|
+
}
|
|
3654
|
+
interface MeetingRecordState {
|
|
3655
|
+
readonly loading: boolean;
|
|
3656
|
+
readonly record: MeetingRecordBundle | null;
|
|
3657
|
+
/** A failure that stopped the WHOLE record loading (never kept as an answer). */
|
|
3658
|
+
readonly failure: MeetingRecordFailure | null;
|
|
3659
|
+
}
|
|
3660
|
+
/** What a read produced: the four parts (patchable), or a finished bundle (a guest's). */
|
|
3661
|
+
type MeetingRecordAnswer = {
|
|
3662
|
+
readonly kind: "parts";
|
|
3663
|
+
readonly meeting: MeetingRecord;
|
|
3664
|
+
readonly parts: MeetingRecordParts;
|
|
3665
|
+
} | {
|
|
3666
|
+
readonly kind: "bundle";
|
|
3667
|
+
readonly bundle: MeetingRecordBundle;
|
|
3668
|
+
};
|
|
3669
|
+
/** One changed row, already projected by the repository. `null` + `removedId` = gone. */
|
|
3670
|
+
type MeetingRecordChange = {
|
|
3671
|
+
readonly part: "transcript";
|
|
3672
|
+
readonly row: MeetingRecordParts["transcript"][number] | null;
|
|
3673
|
+
readonly removedId?: string | undefined;
|
|
3674
|
+
} | {
|
|
3675
|
+
readonly part: "notes";
|
|
3676
|
+
readonly row: MeetingRecordParts["notes"][number] | null;
|
|
3677
|
+
readonly removedId?: string | undefined;
|
|
3678
|
+
} | {
|
|
3679
|
+
readonly part: "attendance";
|
|
3680
|
+
readonly row: MeetingRecordParts["attendees"][number] | null;
|
|
3681
|
+
readonly removedId?: string | undefined;
|
|
3682
|
+
} | {
|
|
3683
|
+
readonly part: "recording";
|
|
3684
|
+
readonly row: MeetingRecordParts["recordings"][number] | null;
|
|
3685
|
+
readonly removedId?: string | undefined;
|
|
3686
|
+
};
|
|
3687
|
+
interface MeetingRecordCache {
|
|
3688
|
+
/** The kept state for `key` (stable until it changes). */
|
|
3689
|
+
state(key: string | null): MeetingRecordState;
|
|
3690
|
+
subscribe(listener: () => void): () => void;
|
|
3691
|
+
/**
|
|
3692
|
+
* Read unless the entry is kept and current (or a read is in flight — joined).
|
|
3693
|
+
* `force` reads anyway and supersedes a read in flight. Never rejects: a
|
|
3694
|
+
* failure is in the state.
|
|
3695
|
+
*/
|
|
3696
|
+
ensure(key: string, read: () => Promise<MeetingRecordAnswer>, options?: {
|
|
3697
|
+
force?: boolean;
|
|
3698
|
+
}): Promise<void>;
|
|
3699
|
+
/** The meeting row moved (a rename, it ended): assemble again — no read. */
|
|
3700
|
+
setMeeting(key: string, meeting: MeetingRecord): void;
|
|
3701
|
+
/** Apply one changed row from the change feed — no read. */
|
|
3702
|
+
apply(key: string, change: MeetingRecordChange): void;
|
|
3703
|
+
/**
|
|
3704
|
+
* Hold the live half: `open` on the first holder, its close a grace after the
|
|
3705
|
+
* last. `owner` is what the channel runs on (the realtime manager): a new
|
|
3706
|
+
* owner means the old one's channels are gone, so it opens again.
|
|
3707
|
+
*/
|
|
3708
|
+
hold(key: string, open: () => () => void, owner?: unknown): () => void;
|
|
3709
|
+
/** Tests only. */
|
|
3710
|
+
reset(): void;
|
|
3711
|
+
}
|
|
3712
|
+
declare function createMeetingRecordCache(options?: {
|
|
3713
|
+
graceMs?: number;
|
|
3714
|
+
}): MeetingRecordCache;
|
|
3715
|
+
|
|
3716
|
+
export { ATTENDANCE_CSV_HEADER, type AccessTokenSource, type AdmissionState, type AnnounceResult, type ApplyRoomEventArgs, type AskArgs, type AttendanceKind, type AttendanceRow, type AudioProcessing, CALL_INVITE_KIND, CALL_INVITE_VERSION, CAPTION_LANGUAGES, CONSENT_NOTICE_ATTRIBUTE, type CallCenter, type CallCenterOptions, type CallId, type CallInvite, type CallInviteHandlerOptions, type CallInvitePayload, type CallMode, type CallState, type CallablePerson, type CollabAction, type CollabPanel, type CollabRequest, type CollabResult, type CollabSnapshot, type ConnectionQuality, type CreateCallInviteInput, type CreateMeetingInput, DATA_TOPICS, DEFAULT_EFFECTS, DEFAULT_PAGE_SIZE, type DataTopic, type DeviceKind, type DeviceManager, type DeviceManagerOptions, type DeviceSnapshot, type DeviceStorage, type DriverConnectionState, type DriverParticipant, type DriverTrackKind, type DriverTranscription, EMPTY_COLLAB, END_MEETING_NOT_SERVED, type EffectsStorage, type EndMeetingResult, FILE_SESSION_PATH, type FollowUpDraft, type FollowUpRecipient, type FollowUpSendResult, GUEST_RECORD_ACCESS_CHOICES, GUEST_RECORD_ACCESS_LEVELS, GUEST_RECORD_ACCESS_METADATA_KEY, GUEST_RECORD_NOT_SHARED, type GuestRecordAccess, type GuestRecordAccessResolution, type GuestRecordAccessSetting, IDLE_MEDIA, IDLE_TRACK, type InRoomClient, type InRoomClientOptions, type InviteeInput, type InviteeRole, type JoinRequest, type JsonObject, type JsonValue, LISTEN_ONLY_NOTICE, type LibraryRecording, type LiveKitDriverOptions, type LobbyEntry, type LocalMediaState, type LocalTrackState, MEETING_INVITE_KIND, MEETING_INVITE_VERSION, MEETING_RECORD_GRACE_MS, MEET_ANSWER_CONTEXT_EVENT, MEET_ANSWER_EVENTS, MEET_EVENTS, MEET_ROUTES, MEET_SCHEMA, MODERATION_ACTIONS, type MediaDevicesLike, type MediaEffects, type MediaUnavailable, type MeetAi, type MeetAiAnswer, type MeetAiOptions, type MeetAnswerCoverage, type MeetApi, type MeetApiOptions, type MeetApiRequest, type MeetBreakoutBroadcast, type MeetBreakoutPlan, type MeetBreakoutRoom, type MeetCollabSettings, type MeetDevice, MeetError, type MeetErrorCode, type MeetEvent, type MeetFileMedia, type MeetGrants, type MeetIdentity, type MeetParticipant, type MeetPoll, type MeetPollOption, type MeetPollState, type MeetQuestion, type MeetQuestionState, type MeetRepository, type MeetRoomDriver, type MeetSnapshot, type MeetStore, type MeetStroke, type MeetingActivities, type MeetingAttendee, type MeetingChanges, type MeetingChatHistory, type MeetingId, type MeetingInviteHandlerOptions, type MeetingInvitePayload, type MeetingInvitee, type MeetingKind, type MeetingNote, type MeetingNoteKind, type MeetingOccurrence, type MeetingRecord, type MeetingRecordAnswer, type MeetingRecordBundle, type MeetingRecordCache, type MeetingRecordChange, type MeetingRecordFailure, type MeetingRecordParts, type MeetingRecordRestriction, type MeetingRecordRestrictionNotice, type MeetingRecordState, type MeetingSearchHit, type MeetingSearchSource, type MeetingSession, type MeetingSessionOptions, type MeetingSettings, type ModerationAction, type ModerationNotice, type ModerationRequest, type ModerationResult, NOTE_TAKER_IDENTITY_PREFIX, NOTE_TAKER_METADATA_KEY, NO_GRANTS, type NameBook, type NoteTakerDispatch, type NoteTakerRefusal, type NoteTakerState, type NoteTakerStatus, ORGANIZATION_HEADER, type OccurrenceOverride, type OccurrenceState, type OrganizationId, PARTICIPANT_CAN_RECORD_ATTRIBUTE, PARTICIPANT_ROLE_ATTRIBUTE, type ParticipantIdentity, type ParticipantRole, REACTION_TTL_MS, REFUSED_ABSENCE, ROLE_RANK, ROOM_EVENTS, ROOM_EVENT_PREFIX, ROOM_TOKEN_HEADER, RPCS, type RecordingController, type RecordingControllerOptions, type RecordingPolicy, type RecordingRecord, type RecordingState, type RepositoryOptions, type RoomChatMessage, type RoomDriverFactory, type RoomDriverHandlers, type RoomEngine, type RoomEngineOptions, type RoomEventOutcome, type RoomEventTopic, type RoomName, type RoomPhase, type RoomReaction, type RoomToken, type RoomTokenStorage, type RsvpAnswer, type RsvpInvitation, type RsvpState, type ScheduleMeetingInput, type Stage, type StageArgs, type StageLayout, TABLES, TRANSCRIPTION_ATTRIBUTES, TRANSCRIPTION_TOPIC, type TokenClient, type TokenClientOptions, type TokenRequest, type TranscriptBlock, type TranscriptFormat, type TranscriptMatch, type TranscriptSegment, type TranscriptSource, type UpcomingOccurrence, type UserId, type VideoEffect, type WrapUpAnnouncement, type WrapUpStatus, absenceSentence, activeWrapUpSummary, announceMeeting, applyCollabPacket, applyRoomEvent, asCallId, asMeetingId, asOrganizationId, asParticipantIdentity, asRoomName, asUserId, assembleMeetingRecord, assignedRoom, attendanceCsv, attendanceReport, attendanceTotals, attendeeKey, autoAssign, breakoutRemainingMs, callInviteAction, callInviteText, callStateLabel, canModerate, canModerateParticipant, canTransition, captionSegment, changesPatch, clockPosition, collabFromState, computeStage, countLabel, createCallCenter, createCallInviteHandler, createDeviceManager, createInRoomClient, createLiveKitDriver, createMeetAi, createMeetApi, createMeetRepository, createMeetStore, createMeetingInviteHandler, createMeetingRecordCache, createMeetingSession, createMemoryDeviceStorage, createRecordingController, createRoomEngine, createTokenClient, createWebDeviceStorage, createWebEffectsStorage, createWebRoomTokenStorage, defaultCaptionLanguage, displayNameFor, fileDownloadPath, gridColumns, groupTranscript, guestRecordAccessExplanation, guestRecordAccessLabel, guestRecordAccessSetting, hasEnded, hostUserIds, invalidResponse, isGuest, isGuestIdentity, isHostRole, isListenOnly, isNoteTaker, isRealName, isRoomEventTopic, learnName, learnNames, listenOnlyNotice, loadFollowUpDraft, loadGuestMeetingRecord, loadMeetingActivities, loadMeetingChat, loadMeetingRecord, loadMeetingRecordParts, mapConnectionState, mapQuality, meetingInviteAction, meetingLength, meetingLink, meetingPath, meetingSlug, meetingTopic, meetingWhen, mergeCaption, mergeRecord, normalizeDeviceError, normalizeMeetError, normalizeSpoken, noteTakerIdentity, noteTakerLabel, noteTakerParticipant, noteTakerStatus, notesDocument, orderParticipants, parseRoomToken, participantSummary, playableRecordingFileId, projectInvitee, projectLibraryRecording, projectNote, projectOccurrence, projectOverride, projectParticipant, projectRsvpInvitation, projectSearchHit, projectSegment, projectUpcoming, pushMeetingToGoogleCalendar, readServerRefusal, readableTranscript, recordingLabel, recordingNotice, recordingOffsetMs, roleOf, roomTopic, searchTranscript, sendFollowUp, setGuestRecordAccess, settingsPatch, snapshotParticipant, snippetRuns, summarizeMeetingRecordRestrictions, transcriptDocument, unavailableFrom, uniqueAttendees, userTopic, wasRefused, withItem, wrapUpStatus };
|
package/dist/index.d.ts
CHANGED
|
@@ -2938,6 +2938,21 @@ interface MeetingRecordBundle {
|
|
|
2938
2938
|
* therefore make the same distinction as decisions and action items.
|
|
2939
2939
|
*/
|
|
2940
2940
|
declare function activeWrapUpSummary(notes: readonly MeetingNote[]): MeetingNote | null;
|
|
2941
|
+
/**
|
|
2942
|
+
* The four durable reads behind a record, as the database returned them, plus
|
|
2943
|
+
* what the reader could not see. `assembleMeetingRecord` turns them into the
|
|
2944
|
+
* bundle; a cache that keeps them can apply a realtime row to one part and
|
|
2945
|
+
* assemble again, without reading the other three (0.7.79).
|
|
2946
|
+
*/
|
|
2947
|
+
interface MeetingRecordParts {
|
|
2948
|
+
readonly attendees: readonly MeetingAttendee[];
|
|
2949
|
+
/** Every note row (all kinds, superseded included), oldest first. */
|
|
2950
|
+
readonly notes: readonly MeetingNote[];
|
|
2951
|
+
readonly transcript: readonly TranscriptSegment[];
|
|
2952
|
+
/** Newest first; archived recordings are not here. */
|
|
2953
|
+
readonly recordings: readonly RecordingRecord[];
|
|
2954
|
+
readonly restrictions: readonly MeetingRecordRestriction[];
|
|
2955
|
+
}
|
|
2941
2956
|
/**
|
|
2942
2957
|
* Load the record parts the reader may request; state all restrictions.
|
|
2943
2958
|
*
|
|
@@ -2946,6 +2961,10 @@ declare function activeWrapUpSummary(notes: readonly MeetingNote[]): MeetingNote
|
|
|
2946
2961
|
* fail for exactly the reader who most needs the page to work.
|
|
2947
2962
|
*/
|
|
2948
2963
|
declare function loadMeetingRecord(repository: MeetRepository, meetingId: MeetingId, meeting: MeetingRecord, reader?: Pick<MeetIdentity, "userId">): Promise<MeetingRecordBundle>;
|
|
2964
|
+
/** The four reads, each with its own verdict (see `loadMeetingRecord`). */
|
|
2965
|
+
declare function loadMeetingRecordParts(repository: MeetRepository, meetingId: MeetingId, reader?: Pick<MeetIdentity, "userId">): Promise<MeetingRecordParts>;
|
|
2966
|
+
/** The bundle a reader sees, from the parts. Grouping and ordering only (rule 3). */
|
|
2967
|
+
declare function assembleMeetingRecord(meeting: MeetingRecord, parts: MeetingRecordParts): MeetingRecordBundle;
|
|
2949
2968
|
/**
|
|
2950
2969
|
* 🚨 **A READ THAT WAS REFUSED IS NOT AN EMPTY RESULT (MRI-A16).**
|
|
2951
2970
|
*
|
|
@@ -3594,4 +3613,104 @@ interface InRoomClientOptions {
|
|
|
3594
3613
|
}
|
|
3595
3614
|
declare function createInRoomClient(options: InRoomClientOptions): InRoomClient;
|
|
3596
3615
|
|
|
3597
|
-
|
|
3616
|
+
/**
|
|
3617
|
+
* ONE MEETING RECORD PER READER PER TAB — read once, kept current by one
|
|
3618
|
+
* ref-counted channel (0.7.79).
|
|
3619
|
+
*
|
|
3620
|
+
* 🚨 THE DEFECT THIS CLOSES. `useMeetingRecord` held the bundle in its own
|
|
3621
|
+
* component state and read all four parts (`meet_participants`, `meet_notes`,
|
|
3622
|
+
* `meet_transcript_segments`, `meet_recordings`) in a mount effect. A host that
|
|
3623
|
+
* hides a view without unmounting its data — a board tile asleep under React
|
|
3624
|
+
* `<Activity>`, which tears every effect down and runs it again on wake — read
|
|
3625
|
+
* the whole record again on every wake, on a Remove + Undo, and once more for
|
|
3626
|
+
* every second view of the same meeting (the record tab, the meeting's agent
|
|
3627
|
+
* surface, five "meeting notes" tiles). The view owned the data.
|
|
3628
|
+
*
|
|
3629
|
+
* THE SHAPE. One entry per (reader, meeting). The first view reads; every later
|
|
3630
|
+
* view — the same view after a wake or a remount included — renders the entry
|
|
3631
|
+
* and reads nothing. A read already in flight is joined. A forced read (Retry,
|
|
3632
|
+
* a reconnect) supersedes it, and an answer that lands after a newer read was
|
|
3633
|
+
* asked is DROPPED (the late-answer guard), so an old answer can never paint
|
|
3634
|
+
* over a newer one.
|
|
3635
|
+
*
|
|
3636
|
+
* LIVE WHILE HELD. A member's entry (a direct read, kept as its four parts) is
|
|
3637
|
+
* patched row by row from the database's change feed for the four tables, then
|
|
3638
|
+
* assembled again — one row, never four reads. The channel opens on the first
|
|
3639
|
+
* holder and closes `MEETING_RECORD_GRACE_MS` after the last one lets go, so a
|
|
3640
|
+
* wake or an Undo inside the grace costs nothing. Past the grace nothing
|
|
3641
|
+
* watched the meeting, so the entry is marked stale and the next view reads it
|
|
3642
|
+
* once. A guest's record arrives through the server as a finished bundle; it
|
|
3643
|
+
* has no change feed and is kept as it is.
|
|
3644
|
+
*
|
|
3645
|
+
* Framework-free on purpose: `react/hooks.ts` is one reader of it.
|
|
3646
|
+
*/
|
|
3647
|
+
|
|
3648
|
+
/** How long a meeting's channel and kept record outlive the last view of it. */
|
|
3649
|
+
declare const MEETING_RECORD_GRACE_MS = 60000;
|
|
3650
|
+
interface MeetingRecordFailure {
|
|
3651
|
+
readonly message: string;
|
|
3652
|
+
readonly remedy: string;
|
|
3653
|
+
}
|
|
3654
|
+
interface MeetingRecordState {
|
|
3655
|
+
readonly loading: boolean;
|
|
3656
|
+
readonly record: MeetingRecordBundle | null;
|
|
3657
|
+
/** A failure that stopped the WHOLE record loading (never kept as an answer). */
|
|
3658
|
+
readonly failure: MeetingRecordFailure | null;
|
|
3659
|
+
}
|
|
3660
|
+
/** What a read produced: the four parts (patchable), or a finished bundle (a guest's). */
|
|
3661
|
+
type MeetingRecordAnswer = {
|
|
3662
|
+
readonly kind: "parts";
|
|
3663
|
+
readonly meeting: MeetingRecord;
|
|
3664
|
+
readonly parts: MeetingRecordParts;
|
|
3665
|
+
} | {
|
|
3666
|
+
readonly kind: "bundle";
|
|
3667
|
+
readonly bundle: MeetingRecordBundle;
|
|
3668
|
+
};
|
|
3669
|
+
/** One changed row, already projected by the repository. `null` + `removedId` = gone. */
|
|
3670
|
+
type MeetingRecordChange = {
|
|
3671
|
+
readonly part: "transcript";
|
|
3672
|
+
readonly row: MeetingRecordParts["transcript"][number] | null;
|
|
3673
|
+
readonly removedId?: string | undefined;
|
|
3674
|
+
} | {
|
|
3675
|
+
readonly part: "notes";
|
|
3676
|
+
readonly row: MeetingRecordParts["notes"][number] | null;
|
|
3677
|
+
readonly removedId?: string | undefined;
|
|
3678
|
+
} | {
|
|
3679
|
+
readonly part: "attendance";
|
|
3680
|
+
readonly row: MeetingRecordParts["attendees"][number] | null;
|
|
3681
|
+
readonly removedId?: string | undefined;
|
|
3682
|
+
} | {
|
|
3683
|
+
readonly part: "recording";
|
|
3684
|
+
readonly row: MeetingRecordParts["recordings"][number] | null;
|
|
3685
|
+
readonly removedId?: string | undefined;
|
|
3686
|
+
};
|
|
3687
|
+
interface MeetingRecordCache {
|
|
3688
|
+
/** The kept state for `key` (stable until it changes). */
|
|
3689
|
+
state(key: string | null): MeetingRecordState;
|
|
3690
|
+
subscribe(listener: () => void): () => void;
|
|
3691
|
+
/**
|
|
3692
|
+
* Read unless the entry is kept and current (or a read is in flight — joined).
|
|
3693
|
+
* `force` reads anyway and supersedes a read in flight. Never rejects: a
|
|
3694
|
+
* failure is in the state.
|
|
3695
|
+
*/
|
|
3696
|
+
ensure(key: string, read: () => Promise<MeetingRecordAnswer>, options?: {
|
|
3697
|
+
force?: boolean;
|
|
3698
|
+
}): Promise<void>;
|
|
3699
|
+
/** The meeting row moved (a rename, it ended): assemble again — no read. */
|
|
3700
|
+
setMeeting(key: string, meeting: MeetingRecord): void;
|
|
3701
|
+
/** Apply one changed row from the change feed — no read. */
|
|
3702
|
+
apply(key: string, change: MeetingRecordChange): void;
|
|
3703
|
+
/**
|
|
3704
|
+
* Hold the live half: `open` on the first holder, its close a grace after the
|
|
3705
|
+
* last. `owner` is what the channel runs on (the realtime manager): a new
|
|
3706
|
+
* owner means the old one's channels are gone, so it opens again.
|
|
3707
|
+
*/
|
|
3708
|
+
hold(key: string, open: () => () => void, owner?: unknown): () => void;
|
|
3709
|
+
/** Tests only. */
|
|
3710
|
+
reset(): void;
|
|
3711
|
+
}
|
|
3712
|
+
declare function createMeetingRecordCache(options?: {
|
|
3713
|
+
graceMs?: number;
|
|
3714
|
+
}): MeetingRecordCache;
|
|
3715
|
+
|
|
3716
|
+
export { ATTENDANCE_CSV_HEADER, type AccessTokenSource, type AdmissionState, type AnnounceResult, type ApplyRoomEventArgs, type AskArgs, type AttendanceKind, type AttendanceRow, type AudioProcessing, CALL_INVITE_KIND, CALL_INVITE_VERSION, CAPTION_LANGUAGES, CONSENT_NOTICE_ATTRIBUTE, type CallCenter, type CallCenterOptions, type CallId, type CallInvite, type CallInviteHandlerOptions, type CallInvitePayload, type CallMode, type CallState, type CallablePerson, type CollabAction, type CollabPanel, type CollabRequest, type CollabResult, type CollabSnapshot, type ConnectionQuality, type CreateCallInviteInput, type CreateMeetingInput, DATA_TOPICS, DEFAULT_EFFECTS, DEFAULT_PAGE_SIZE, type DataTopic, type DeviceKind, type DeviceManager, type DeviceManagerOptions, type DeviceSnapshot, type DeviceStorage, type DriverConnectionState, type DriverParticipant, type DriverTrackKind, type DriverTranscription, EMPTY_COLLAB, END_MEETING_NOT_SERVED, type EffectsStorage, type EndMeetingResult, FILE_SESSION_PATH, type FollowUpDraft, type FollowUpRecipient, type FollowUpSendResult, GUEST_RECORD_ACCESS_CHOICES, GUEST_RECORD_ACCESS_LEVELS, GUEST_RECORD_ACCESS_METADATA_KEY, GUEST_RECORD_NOT_SHARED, type GuestRecordAccess, type GuestRecordAccessResolution, type GuestRecordAccessSetting, IDLE_MEDIA, IDLE_TRACK, type InRoomClient, type InRoomClientOptions, type InviteeInput, type InviteeRole, type JoinRequest, type JsonObject, type JsonValue, LISTEN_ONLY_NOTICE, type LibraryRecording, type LiveKitDriverOptions, type LobbyEntry, type LocalMediaState, type LocalTrackState, MEETING_INVITE_KIND, MEETING_INVITE_VERSION, MEETING_RECORD_GRACE_MS, MEET_ANSWER_CONTEXT_EVENT, MEET_ANSWER_EVENTS, MEET_EVENTS, MEET_ROUTES, MEET_SCHEMA, MODERATION_ACTIONS, type MediaDevicesLike, type MediaEffects, type MediaUnavailable, type MeetAi, type MeetAiAnswer, type MeetAiOptions, type MeetAnswerCoverage, type MeetApi, type MeetApiOptions, type MeetApiRequest, type MeetBreakoutBroadcast, type MeetBreakoutPlan, type MeetBreakoutRoom, type MeetCollabSettings, type MeetDevice, MeetError, type MeetErrorCode, type MeetEvent, type MeetFileMedia, type MeetGrants, type MeetIdentity, type MeetParticipant, type MeetPoll, type MeetPollOption, type MeetPollState, type MeetQuestion, type MeetQuestionState, type MeetRepository, type MeetRoomDriver, type MeetSnapshot, type MeetStore, type MeetStroke, type MeetingActivities, type MeetingAttendee, type MeetingChanges, type MeetingChatHistory, type MeetingId, type MeetingInviteHandlerOptions, type MeetingInvitePayload, type MeetingInvitee, type MeetingKind, type MeetingNote, type MeetingNoteKind, type MeetingOccurrence, type MeetingRecord, type MeetingRecordAnswer, type MeetingRecordBundle, type MeetingRecordCache, type MeetingRecordChange, type MeetingRecordFailure, type MeetingRecordParts, type MeetingRecordRestriction, type MeetingRecordRestrictionNotice, type MeetingRecordState, type MeetingSearchHit, type MeetingSearchSource, type MeetingSession, type MeetingSessionOptions, type MeetingSettings, type ModerationAction, type ModerationNotice, type ModerationRequest, type ModerationResult, NOTE_TAKER_IDENTITY_PREFIX, NOTE_TAKER_METADATA_KEY, NO_GRANTS, type NameBook, type NoteTakerDispatch, type NoteTakerRefusal, type NoteTakerState, type NoteTakerStatus, ORGANIZATION_HEADER, type OccurrenceOverride, type OccurrenceState, type OrganizationId, PARTICIPANT_CAN_RECORD_ATTRIBUTE, PARTICIPANT_ROLE_ATTRIBUTE, type ParticipantIdentity, type ParticipantRole, REACTION_TTL_MS, REFUSED_ABSENCE, ROLE_RANK, ROOM_EVENTS, ROOM_EVENT_PREFIX, ROOM_TOKEN_HEADER, RPCS, type RecordingController, type RecordingControllerOptions, type RecordingPolicy, type RecordingRecord, type RecordingState, type RepositoryOptions, type RoomChatMessage, type RoomDriverFactory, type RoomDriverHandlers, type RoomEngine, type RoomEngineOptions, type RoomEventOutcome, type RoomEventTopic, type RoomName, type RoomPhase, type RoomReaction, type RoomToken, type RoomTokenStorage, type RsvpAnswer, type RsvpInvitation, type RsvpState, type ScheduleMeetingInput, type Stage, type StageArgs, type StageLayout, TABLES, TRANSCRIPTION_ATTRIBUTES, TRANSCRIPTION_TOPIC, type TokenClient, type TokenClientOptions, type TokenRequest, type TranscriptBlock, type TranscriptFormat, type TranscriptMatch, type TranscriptSegment, type TranscriptSource, type UpcomingOccurrence, type UserId, type VideoEffect, type WrapUpAnnouncement, type WrapUpStatus, absenceSentence, activeWrapUpSummary, announceMeeting, applyCollabPacket, applyRoomEvent, asCallId, asMeetingId, asOrganizationId, asParticipantIdentity, asRoomName, asUserId, assembleMeetingRecord, assignedRoom, attendanceCsv, attendanceReport, attendanceTotals, attendeeKey, autoAssign, breakoutRemainingMs, callInviteAction, callInviteText, callStateLabel, canModerate, canModerateParticipant, canTransition, captionSegment, changesPatch, clockPosition, collabFromState, computeStage, countLabel, createCallCenter, createCallInviteHandler, createDeviceManager, createInRoomClient, createLiveKitDriver, createMeetAi, createMeetApi, createMeetRepository, createMeetStore, createMeetingInviteHandler, createMeetingRecordCache, createMeetingSession, createMemoryDeviceStorage, createRecordingController, createRoomEngine, createTokenClient, createWebDeviceStorage, createWebEffectsStorage, createWebRoomTokenStorage, defaultCaptionLanguage, displayNameFor, fileDownloadPath, gridColumns, groupTranscript, guestRecordAccessExplanation, guestRecordAccessLabel, guestRecordAccessSetting, hasEnded, hostUserIds, invalidResponse, isGuest, isGuestIdentity, isHostRole, isListenOnly, isNoteTaker, isRealName, isRoomEventTopic, learnName, learnNames, listenOnlyNotice, loadFollowUpDraft, loadGuestMeetingRecord, loadMeetingActivities, loadMeetingChat, loadMeetingRecord, loadMeetingRecordParts, mapConnectionState, mapQuality, meetingInviteAction, meetingLength, meetingLink, meetingPath, meetingSlug, meetingTopic, meetingWhen, mergeCaption, mergeRecord, normalizeDeviceError, normalizeMeetError, normalizeSpoken, noteTakerIdentity, noteTakerLabel, noteTakerParticipant, noteTakerStatus, notesDocument, orderParticipants, parseRoomToken, participantSummary, playableRecordingFileId, projectInvitee, projectLibraryRecording, projectNote, projectOccurrence, projectOverride, projectParticipant, projectRsvpInvitation, projectSearchHit, projectSegment, projectUpcoming, pushMeetingToGoogleCalendar, readServerRefusal, readableTranscript, recordingLabel, recordingNotice, recordingOffsetMs, roleOf, roomTopic, searchTranscript, sendFollowUp, setGuestRecordAccess, settingsPatch, snapshotParticipant, snippetRuns, summarizeMeetingRecordRestrictions, transcriptDocument, unavailableFrom, uniqueAttendees, userTopic, wasRefused, withItem, wrapUpStatus };
|
package/dist/index.js
CHANGED
|
@@ -826,13 +826,13 @@ function mergeCaption(list, caption) {
|
|
|
826
826
|
return list.map((entry, position) => position === index ? caption : entry);
|
|
827
827
|
}
|
|
828
828
|
function mergeRecord(list, record) {
|
|
829
|
-
const
|
|
830
|
-
if (
|
|
831
|
-
const existing = list[
|
|
829
|
+
const byId2 = list.findIndex((entry) => entry.source === "record" && entry.id === record.id);
|
|
830
|
+
if (byId2 !== -1) {
|
|
831
|
+
const existing = list[byId2];
|
|
832
832
|
if (existing !== void 0 && existing.text === record.text && existing.final === record.final) {
|
|
833
833
|
return list;
|
|
834
834
|
}
|
|
835
|
-
return list.map((entry, position) => position ===
|
|
835
|
+
return list.map((entry, position) => position === byId2 ? record : entry);
|
|
836
836
|
}
|
|
837
837
|
const captionIndex = list.findIndex((entry) => supersedes(record, entry));
|
|
838
838
|
if (captionIndex === -1) return [...list, record];
|
|
@@ -4340,6 +4340,7 @@ function fileDownloadPath(fileId) {
|
|
|
4340
4340
|
}
|
|
4341
4341
|
|
|
4342
4342
|
// src/core/after.ts
|
|
4343
|
+
import { toDelimited } from "@ai-matrx/kit/delimited";
|
|
4343
4344
|
import { formatDurationMs } from "@ai-matrx/kit/format";
|
|
4344
4345
|
function kindOf(person) {
|
|
4345
4346
|
if (person.isAgent) return "assistant";
|
|
@@ -4478,11 +4479,6 @@ function attendanceTotals(rows) {
|
|
|
4478
4479
|
averageMs: timed.length === 0 ? null : Math.round(timed.reduce((sum, row) => sum + (row.durationMs ?? 0), 0) / timed.length)
|
|
4479
4480
|
};
|
|
4480
4481
|
}
|
|
4481
|
-
function csvCell(value) {
|
|
4482
|
-
if (value === null) return "";
|
|
4483
|
-
const text3 = String(value);
|
|
4484
|
-
return /[",\n\r]/.test(text3) ? `"${text3.replace(/"/g, '""')}"` : text3;
|
|
4485
|
-
}
|
|
4486
4482
|
var ATTENDANCE_CSV_HEADER = [
|
|
4487
4483
|
"Meeting",
|
|
4488
4484
|
"Meeting started",
|
|
@@ -4494,11 +4490,11 @@ var ATTENDANCE_CSV_HEADER = [
|
|
|
4494
4490
|
"Admission"
|
|
4495
4491
|
];
|
|
4496
4492
|
function attendanceCsv(reports) {
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
[
|
|
4493
|
+
return toDelimited(
|
|
4494
|
+
[
|
|
4495
|
+
ATTENDANCE_CSV_HEADER,
|
|
4496
|
+
...reports.flatMap(
|
|
4497
|
+
({ meeting, rows }) => rows.map((row) => [
|
|
4502
4498
|
meeting.title,
|
|
4503
4499
|
meeting.startedAt ?? meeting.scheduledFor,
|
|
4504
4500
|
row.name,
|
|
@@ -4507,12 +4503,11 @@ function attendanceCsv(reports) {
|
|
|
4507
4503
|
row.leftAt,
|
|
4508
4504
|
row.durationMs === null ? null : Math.round(row.durationMs / 6e3) / 10,
|
|
4509
4505
|
row.admission
|
|
4510
|
-
]
|
|
4511
|
-
)
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
`;
|
|
4506
|
+
])
|
|
4507
|
+
)
|
|
4508
|
+
],
|
|
4509
|
+
{ lineEnding: "\r\n", trailingNewline: true }
|
|
4510
|
+
);
|
|
4516
4511
|
}
|
|
4517
4512
|
function recordingOffsetMs(at, recording) {
|
|
4518
4513
|
if (recording === null || recording.state !== "available") return null;
|
|
@@ -5662,6 +5657,9 @@ function restrictionFor(part, error) {
|
|
|
5662
5657
|
};
|
|
5663
5658
|
}
|
|
5664
5659
|
async function loadMeetingRecord(repository, meetingId, meeting, reader = { userId: null }) {
|
|
5660
|
+
return assembleMeetingRecord(meeting, await loadMeetingRecordParts(repository, meetingId, reader));
|
|
5661
|
+
}
|
|
5662
|
+
async function loadMeetingRecordParts(repository, meetingId, reader = { userId: null }) {
|
|
5665
5663
|
const restrictions = [];
|
|
5666
5664
|
async function attempt(part, run, empty) {
|
|
5667
5665
|
if (reader.userId === null) {
|
|
@@ -5689,6 +5687,10 @@ async function loadMeetingRecord(repository, meetingId, meeting, reader = { user
|
|
|
5689
5687
|
[]
|
|
5690
5688
|
)
|
|
5691
5689
|
]);
|
|
5690
|
+
return { attendees, notes, transcript, recordings, restrictions };
|
|
5691
|
+
}
|
|
5692
|
+
function assembleMeetingRecord(meeting, parts) {
|
|
5693
|
+
const { attendees, notes, transcript, recordings, restrictions } = parts;
|
|
5692
5694
|
return {
|
|
5693
5695
|
meeting,
|
|
5694
5696
|
attendees,
|
|
@@ -6033,6 +6035,210 @@ async function setGuestRecordAccess(args) {
|
|
|
6033
6035
|
};
|
|
6034
6036
|
}
|
|
6035
6037
|
var GUEST_RECORD_NOT_SHARED = NOT_SHARED;
|
|
6038
|
+
|
|
6039
|
+
// src/core/record-cache.ts
|
|
6040
|
+
var MEETING_RECORD_GRACE_MS = 6e4;
|
|
6041
|
+
var INITIAL = { loading: true, record: null, failure: null };
|
|
6042
|
+
function upsert2(rows, row, removedId, order) {
|
|
6043
|
+
const id = row?.id ?? removedId;
|
|
6044
|
+
if (id === void 0) return rows;
|
|
6045
|
+
const rest = rows.filter((existing) => existing.id !== id);
|
|
6046
|
+
return row === null ? rest : [...rest, row].sort(order);
|
|
6047
|
+
}
|
|
6048
|
+
var byId = (a, b) => a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
6049
|
+
var ascending = (a, b) => a === b ? 0 : a === null ? 1 : b === null ? -1 : a < b ? -1 : 1;
|
|
6050
|
+
function createMeetingRecordCache(options = {}) {
|
|
6051
|
+
const graceMs = options.graceMs ?? MEETING_RECORD_GRACE_MS;
|
|
6052
|
+
const entries = /* @__PURE__ */ new Map();
|
|
6053
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
6054
|
+
const notify = () => {
|
|
6055
|
+
for (const listener of listeners) listener();
|
|
6056
|
+
};
|
|
6057
|
+
const entryOf = (key) => {
|
|
6058
|
+
let entry = entries.get(key);
|
|
6059
|
+
if (!entry) {
|
|
6060
|
+
entry = {
|
|
6061
|
+
state: INITIAL,
|
|
6062
|
+
meeting: null,
|
|
6063
|
+
parts: null,
|
|
6064
|
+
fresh: false,
|
|
6065
|
+
seq: 0,
|
|
6066
|
+
inflight: null,
|
|
6067
|
+
holders: 0,
|
|
6068
|
+
close: null,
|
|
6069
|
+
owner: null,
|
|
6070
|
+
grace: null
|
|
6071
|
+
};
|
|
6072
|
+
entries.set(key, entry);
|
|
6073
|
+
}
|
|
6074
|
+
return entry;
|
|
6075
|
+
};
|
|
6076
|
+
const assemble = (entry) => {
|
|
6077
|
+
if (entry.meeting === null || entry.parts === null) return;
|
|
6078
|
+
entry.state = { loading: false, failure: null, record: assembleMeetingRecord(entry.meeting, entry.parts) };
|
|
6079
|
+
notify();
|
|
6080
|
+
};
|
|
6081
|
+
return {
|
|
6082
|
+
state: (key) => key === null ? INITIAL : entries.get(key)?.state ?? INITIAL,
|
|
6083
|
+
subscribe(listener) {
|
|
6084
|
+
listeners.add(listener);
|
|
6085
|
+
return () => {
|
|
6086
|
+
listeners.delete(listener);
|
|
6087
|
+
};
|
|
6088
|
+
},
|
|
6089
|
+
ensure(key, read, { force = false } = {}) {
|
|
6090
|
+
const entry = entryOf(key);
|
|
6091
|
+
if (!force) {
|
|
6092
|
+
if (entry.inflight !== null) return entry.inflight;
|
|
6093
|
+
if (entry.fresh) return Promise.resolve();
|
|
6094
|
+
}
|
|
6095
|
+
const seq = ++entry.seq;
|
|
6096
|
+
if (entry.state.failure !== null) {
|
|
6097
|
+
entry.state = { ...entry.state, loading: true, failure: null };
|
|
6098
|
+
notify();
|
|
6099
|
+
}
|
|
6100
|
+
const request = read().then(
|
|
6101
|
+
(answer) => {
|
|
6102
|
+
if (seq !== entry.seq) return;
|
|
6103
|
+
entry.fresh = true;
|
|
6104
|
+
if (answer.kind === "parts") {
|
|
6105
|
+
entry.meeting = answer.meeting;
|
|
6106
|
+
entry.parts = answer.parts;
|
|
6107
|
+
assemble(entry);
|
|
6108
|
+
} else {
|
|
6109
|
+
entry.meeting = answer.bundle.meeting;
|
|
6110
|
+
entry.parts = null;
|
|
6111
|
+
entry.state = { loading: false, failure: null, record: answer.bundle };
|
|
6112
|
+
notify();
|
|
6113
|
+
}
|
|
6114
|
+
},
|
|
6115
|
+
(thrown) => {
|
|
6116
|
+
if (seq !== entry.seq) return;
|
|
6117
|
+
entry.fresh = false;
|
|
6118
|
+
entry.state = {
|
|
6119
|
+
loading: false,
|
|
6120
|
+
record: entry.state.record,
|
|
6121
|
+
failure: {
|
|
6122
|
+
message: thrown?.message ?? String(thrown),
|
|
6123
|
+
remedy: thrown?.remedy ?? "The meeting record could not be read. Reload the page; nothing was changed."
|
|
6124
|
+
}
|
|
6125
|
+
};
|
|
6126
|
+
notify();
|
|
6127
|
+
}
|
|
6128
|
+
);
|
|
6129
|
+
const tracked = request.finally(() => {
|
|
6130
|
+
if (entry.inflight === tracked) entry.inflight = null;
|
|
6131
|
+
});
|
|
6132
|
+
entry.inflight = tracked;
|
|
6133
|
+
return tracked;
|
|
6134
|
+
},
|
|
6135
|
+
setMeeting(key, meeting) {
|
|
6136
|
+
const entry = entries.get(key);
|
|
6137
|
+
if (!entry || entry.meeting === meeting) return;
|
|
6138
|
+
if (entry.parts !== null) {
|
|
6139
|
+
entry.meeting = meeting;
|
|
6140
|
+
assemble(entry);
|
|
6141
|
+
} else if (entry.state.record !== null) {
|
|
6142
|
+
entry.meeting = meeting;
|
|
6143
|
+
entry.state = { ...entry.state, record: { ...entry.state.record, meeting } };
|
|
6144
|
+
notify();
|
|
6145
|
+
}
|
|
6146
|
+
},
|
|
6147
|
+
apply(key, change) {
|
|
6148
|
+
const entry = entries.get(key);
|
|
6149
|
+
const parts = entry?.parts;
|
|
6150
|
+
if (!entry || !parts) return;
|
|
6151
|
+
const refused = parts.restrictions.some((r) => r.part === change.part);
|
|
6152
|
+
if (refused) return;
|
|
6153
|
+
switch (change.part) {
|
|
6154
|
+
case "transcript":
|
|
6155
|
+
entry.parts = {
|
|
6156
|
+
...parts,
|
|
6157
|
+
transcript: upsert2(
|
|
6158
|
+
parts.transcript,
|
|
6159
|
+
change.row,
|
|
6160
|
+
change.removedId,
|
|
6161
|
+
(a, b) => ascending(a.startedAt, b.startedAt) || byId(a, b)
|
|
6162
|
+
)
|
|
6163
|
+
};
|
|
6164
|
+
break;
|
|
6165
|
+
case "notes":
|
|
6166
|
+
entry.parts = {
|
|
6167
|
+
...parts,
|
|
6168
|
+
notes: upsert2(
|
|
6169
|
+
parts.notes,
|
|
6170
|
+
change.row,
|
|
6171
|
+
change.removedId,
|
|
6172
|
+
(a, b) => ascending(a.createdAt, b.createdAt) || byId(a, b)
|
|
6173
|
+
)
|
|
6174
|
+
};
|
|
6175
|
+
break;
|
|
6176
|
+
case "attendance":
|
|
6177
|
+
entry.parts = {
|
|
6178
|
+
...parts,
|
|
6179
|
+
attendees: upsert2(
|
|
6180
|
+
parts.attendees,
|
|
6181
|
+
change.row,
|
|
6182
|
+
change.removedId,
|
|
6183
|
+
(a, b) => ascending(a.joinedAt, b.joinedAt) || byId(a, b)
|
|
6184
|
+
)
|
|
6185
|
+
};
|
|
6186
|
+
break;
|
|
6187
|
+
case "recording":
|
|
6188
|
+
entry.parts = {
|
|
6189
|
+
...parts,
|
|
6190
|
+
// Newest first, as the read orders them.
|
|
6191
|
+
recordings: upsert2(
|
|
6192
|
+
parts.recordings,
|
|
6193
|
+
change.row,
|
|
6194
|
+
change.removedId,
|
|
6195
|
+
(a, b) => ascending(b.startedAt, a.startedAt) || byId(a, b)
|
|
6196
|
+
)
|
|
6197
|
+
};
|
|
6198
|
+
break;
|
|
6199
|
+
}
|
|
6200
|
+
assemble(entry);
|
|
6201
|
+
},
|
|
6202
|
+
hold(key, open, owner = null) {
|
|
6203
|
+
const entry = entryOf(key);
|
|
6204
|
+
if (entry.grace !== null) {
|
|
6205
|
+
clearTimeout(entry.grace);
|
|
6206
|
+
entry.grace = null;
|
|
6207
|
+
}
|
|
6208
|
+
if (entry.close !== null && entry.owner !== owner) {
|
|
6209
|
+
entry.close();
|
|
6210
|
+
entry.close = null;
|
|
6211
|
+
}
|
|
6212
|
+
if (entry.close === null) {
|
|
6213
|
+
entry.close = open();
|
|
6214
|
+
entry.owner = owner;
|
|
6215
|
+
}
|
|
6216
|
+
entry.holders += 1;
|
|
6217
|
+
let released = false;
|
|
6218
|
+
return () => {
|
|
6219
|
+
if (released) return;
|
|
6220
|
+
released = true;
|
|
6221
|
+
entry.holders -= 1;
|
|
6222
|
+
if (entry.holders > 0) return;
|
|
6223
|
+
entry.grace = setTimeout(() => {
|
|
6224
|
+
entry.grace = null;
|
|
6225
|
+
if (entry.holders > 0) return;
|
|
6226
|
+
entry.close?.();
|
|
6227
|
+
entry.close = null;
|
|
6228
|
+
entry.fresh = false;
|
|
6229
|
+
}, graceMs);
|
|
6230
|
+
};
|
|
6231
|
+
},
|
|
6232
|
+
reset() {
|
|
6233
|
+
for (const entry of entries.values()) {
|
|
6234
|
+
if (entry.grace !== null) clearTimeout(entry.grace);
|
|
6235
|
+
entry.close?.();
|
|
6236
|
+
}
|
|
6237
|
+
entries.clear();
|
|
6238
|
+
notify();
|
|
6239
|
+
}
|
|
6240
|
+
};
|
|
6241
|
+
}
|
|
6036
6242
|
export {
|
|
6037
6243
|
ATTENDANCE_CSV_HEADER,
|
|
6038
6244
|
CALL_INVITE_KIND,
|
|
@@ -6054,6 +6260,7 @@ export {
|
|
|
6054
6260
|
LISTEN_ONLY_NOTICE,
|
|
6055
6261
|
MEETING_INVITE_KIND,
|
|
6056
6262
|
MEETING_INVITE_VERSION,
|
|
6263
|
+
MEETING_RECORD_GRACE_MS,
|
|
6057
6264
|
MEET_ANSWER_CONTEXT_EVENT,
|
|
6058
6265
|
MEET_ANSWER_EVENTS,
|
|
6059
6266
|
MEET_EVENTS,
|
|
@@ -6088,6 +6295,7 @@ export {
|
|
|
6088
6295
|
asParticipantIdentity,
|
|
6089
6296
|
asRoomName,
|
|
6090
6297
|
asUserId,
|
|
6298
|
+
assembleMeetingRecord,
|
|
6091
6299
|
assignedRoom,
|
|
6092
6300
|
attendanceCsv,
|
|
6093
6301
|
attendanceReport,
|
|
@@ -6117,6 +6325,7 @@ export {
|
|
|
6117
6325
|
createMeetRepository,
|
|
6118
6326
|
createMeetStore,
|
|
6119
6327
|
createMeetingInviteHandler,
|
|
6328
|
+
createMeetingRecordCache,
|
|
6120
6329
|
createMeetingSession,
|
|
6121
6330
|
createMemoryDeviceStorage,
|
|
6122
6331
|
createRecordingController,
|
|
@@ -6151,6 +6360,7 @@ export {
|
|
|
6151
6360
|
loadMeetingActivities,
|
|
6152
6361
|
loadMeetingChat,
|
|
6153
6362
|
loadMeetingRecord,
|
|
6363
|
+
loadMeetingRecordParts,
|
|
6154
6364
|
mapConnectionState,
|
|
6155
6365
|
mapQuality,
|
|
6156
6366
|
meetingInviteAction,
|