-
Notifications
You must be signed in to change notification settings - Fork 140
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Inject Replay client instance (via context) so no React components im…
…port the protocol directly
- Loading branch information
Showing
7 changed files
with
113 additions
and
161 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,117 +1,101 @@ | ||
import { ExecutionPoint, Message, SessionId, TimeStampedPointRange } from "@replayio/protocol"; | ||
import { client } from "protocol/socket"; | ||
import { | ||
ExecutionPoint, | ||
Message, | ||
SessionId, | ||
TimeStampedPoint, | ||
TimeStampedPointRange, | ||
} from "@replayio/protocol"; | ||
import { client, initSocket } from "protocol/socket"; | ||
import type { ThreadFront } from "protocol/thread"; | ||
|
||
import { isRangeSubset } from "./utils/time"; | ||
|
||
// TODO How should the client handle concurrent requests? | ||
// Should we force serialization? | ||
// Should we cancel in-flight requests and start new ones? | ||
|
||
export default class ReplayClient { | ||
private _focusRange: TimeStampedPointRange | null = null; | ||
private _messageClient: MessageClient; | ||
private _sessionId: SessionId | null = null; | ||
private _threadFront: typeof ThreadFront; | ||
|
||
// TODO Pass config of some sort? | ||
constructor(threadFront: typeof ThreadFront) { | ||
this._messageClient = new MessageClient(threadFront); | ||
} | ||
constructor(dispatchURL: string, threadFront: typeof ThreadFront) { | ||
this._threadFront = threadFront; | ||
|
||
get focusRange(): TimeStampedPointRange | null { | ||
return this._focusRange; | ||
if (typeof window !== "undefined") { | ||
initSocket(dispatchURL); | ||
} | ||
} | ||
|
||
get messages(): Message[] { | ||
return this._messageClient.filteredMessages; | ||
getSessionIdThrows(): SessionId { | ||
const sessionId = this._sessionId; | ||
if (sessionId === null) { | ||
throw Error("Invalid session"); | ||
} | ||
return sessionId; | ||
} | ||
|
||
// Narrow focus window for all Replay data. | ||
async setFocus(focusRange: TimeStampedPointRange | null): Promise<void> { | ||
this._focusRange = focusRange; | ||
async initialize(recordingId: string, accessToken: string | null): Promise<SessionId> { | ||
if (accessToken != null) { | ||
await client.Authentication.setAccessToken({ accessToken }); | ||
} | ||
|
||
// Update all data; fetch from the backend or filter in memory, as needed. | ||
await Promise.all([this._messageClient.setFocus(focusRange)]); | ||
} | ||
} | ||
const { sessionId } = await client.Recording.createSession({ recordingId }); | ||
|
||
class MessageClient { | ||
private _filteredMessages: Message[] = []; | ||
private _lastFetchDidOverflow: boolean = false; | ||
private _lastFetchFocusRange: TimeStampedPointRange | null = null; | ||
private _numFilteredAfterFocusRange: number = 0; | ||
private _numFilteredBeforeFocusRange: number = 0; | ||
private _unfilteredMessages: Message[] = []; | ||
private _sessionEndpoint: ExecutionPoint | null = null; | ||
private _threadFront: typeof ThreadFront; | ||
this._sessionId = sessionId; | ||
this._threadFront.setSessionId(sessionId); | ||
|
||
constructor(threadFront: typeof ThreadFront) { | ||
this._threadFront = threadFront; | ||
return sessionId; | ||
} | ||
|
||
get didOverflow(): boolean { | ||
return this._lastFetchDidOverflow; | ||
} | ||
async findMessages(focusRange: TimeStampedPointRange | null): Promise<{ | ||
messages: Message[]; | ||
overflow: boolean; | ||
}> { | ||
const sessionId = this.getSessionIdThrows(); | ||
|
||
get filteredMessages(): Message[] { | ||
return this._filteredMessages; | ||
} | ||
if (focusRange !== null) { | ||
const response = await client.Console.findMessagesInRange( | ||
{ range: { begin: focusRange.begin.point, end: focusRange.end.point } }, | ||
sessionId | ||
); | ||
|
||
get numFilteredAfterFocusRange(): number { | ||
return this._numFilteredAfterFocusRange; | ||
return { | ||
messages: response.messages, | ||
overflow: response.overflow == true, | ||
}; | ||
} else { | ||
const messages: Message[] = []; | ||
|
||
// TOOD This won't work if there are every overlapping requests. | ||
client.Console.addNewMessageListener(({ message }) => { | ||
messages.push(message); | ||
}); | ||
|
||
const response = await client.Console.findMessages({}, sessionId); | ||
|
||
client.Console.removeNewMessageListener(); | ||
|
||
return { | ||
messages, | ||
overflow: response.overflow == true, | ||
}; | ||
} | ||
} | ||
|
||
get numFilteredBeforeFocusRange(): number { | ||
return this._numFilteredBeforeFocusRange; | ||
async findSources() { | ||
await this._threadFront.findSources(() => { | ||
// The demo doesn't use these directly, but the client throws if they aren't loaded. | ||
}); | ||
} | ||
|
||
// Apply the new focus window to console logs. | ||
// | ||
// In many cases, this will be a synchronous in-memory operation. | ||
// In some cases, this will require re-fetching from the backend. | ||
async setFocus(focusRange: TimeStampedPointRange | null): Promise<void> { | ||
if ( | ||
this._unfilteredMessages === null || | ||
this._lastFetchDidOverflow || | ||
!isRangeSubset(this._lastFetchFocusRange, focusRange) | ||
) { | ||
const sessionId = this._threadFront.sessionId as SessionId; | ||
|
||
if (!this._sessionEndpoint) { | ||
this._sessionEndpoint = (await client.Session.getEndpoint({}, sessionId)).endpoint.point; | ||
} | ||
|
||
const begin = focusRange ? focusRange.begin.point : "0"; | ||
const end = focusRange ? focusRange.end.point : this._sessionEndpoint!; | ||
const { messages, overflow } = await client.Console.findMessagesInRange( | ||
{ range: { begin, end } }, | ||
sessionId | ||
); | ||
async getPointNearTime(time: number): Promise<TimeStampedPoint> { | ||
const sessionId = this.getSessionIdThrows(); | ||
const { point } = await client.Session.getPointNearTime({ time: time }, sessionId); | ||
|
||
this._lastFetchDidOverflow = overflow === true; | ||
this._unfilteredMessages = messages; | ||
} | ||
return point; | ||
} | ||
|
||
// Filter in-memory values. | ||
if (focusRange === null) { | ||
this._filteredMessages = this._unfilteredMessages; | ||
} else { | ||
const begin = focusRange.begin.time; | ||
const end = focusRange.end.time; | ||
|
||
this._numFilteredAfterFocusRange = 0; | ||
this._numFilteredBeforeFocusRange = 0; | ||
this._filteredMessages = this._unfilteredMessages.filter(message => { | ||
const time = message.point.time; | ||
if (time < begin) { | ||
this._numFilteredBeforeFocusRange++; | ||
return false; | ||
} else if (time > end) { | ||
this._numFilteredAfterFocusRange++; | ||
return false; | ||
} else { | ||
return true; | ||
} | ||
}); | ||
} | ||
async getSessionEndpoint(sessionId: SessionId): Promise<TimeStampedPoint> { | ||
const { endpoint } = await client.Session.getEndpoint({}, sessionId); | ||
|
||
return endpoint; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.