Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ulixee
GitHub Repository: ulixee/secret-agent
Path: blob/main/client/lib/CoreSession.ts
1028 views
1
import ISessionMeta from '@secret-agent/interfaces/ISessionMeta';
2
import IConfigureSessionOptions from '@secret-agent/interfaces/IConfigureSessionOptions';
3
import { IJsPath } from 'awaited-dom/base/AwaitedPath';
4
import { loggerSessionIdNames } from '@secret-agent/commons/Logger';
5
import IAgentMeta from '@secret-agent/interfaces/IAgentMeta';
6
import IJsPathResult from '@secret-agent/interfaces/IJsPathResult';
7
import CoreCommandQueue from './CoreCommandQueue';
8
import CoreEventHeap from './CoreEventHeap';
9
import CoreTab from './CoreTab';
10
import IJsPathEventTarget from '../interfaces/IJsPathEventTarget';
11
import ConnectionToCore from '../connections/ConnectionToCore';
12
13
export default class CoreSession implements IJsPathEventTarget {
14
public tabsById = new Map<number, CoreTab>();
15
public frozenTabsById = new Map<number, CoreTab>();
16
public sessionId: string;
17
public sessionName: string;
18
public sessionsDataLocation: string;
19
public commandQueue: CoreCommandQueue;
20
public eventHeap: CoreEventHeap;
21
22
public get lastCommandId(): number {
23
return this.commandId;
24
}
25
26
public get nextCommandId(): number {
27
this.commandId += 1;
28
return this.commandId;
29
}
30
31
public get firstTab(): CoreTab {
32
return [...this.tabsById.values()][0];
33
}
34
35
public get replayApiUrl(): Promise<string> {
36
return this.connection.hostOrError.then(x => {
37
if (x instanceof Error) {
38
throw x;
39
}
40
return `${x}/replay`;
41
});
42
}
43
44
protected readonly meta: ISessionMeta;
45
private readonly connection: ConnectionToCore;
46
private commandId = 0;
47
48
constructor(sessionMeta: ISessionMeta & { sessionName: string }, connection: ConnectionToCore) {
49
const { sessionId, sessionsDataLocation, sessionName } = sessionMeta;
50
this.sessionId = sessionId;
51
this.sessionName = sessionName;
52
this.sessionsDataLocation = sessionsDataLocation;
53
this.meta = {
54
sessionId,
55
};
56
this.connection = connection;
57
loggerSessionIdNames.set(sessionId, sessionName);
58
this.commandQueue = new CoreCommandQueue({ sessionId, sessionName }, connection, this);
59
this.eventHeap = new CoreEventHeap(this.meta, connection);
60
61
this.addTab(sessionMeta);
62
}
63
64
public onEvent(meta: ISessionMeta, listenerId: string, eventData: any): void {
65
if (meta.tabId) {
66
const coreTab = this.tabsById.get(meta.tabId);
67
coreTab?.eventHeap?.incomingEvent(meta, listenerId, eventData);
68
} else {
69
this.eventHeap.incomingEvent(meta, listenerId, eventData);
70
}
71
}
72
73
public recordOutput(
74
changes: { type: string; value: any; path: string; timestamp: Date }[],
75
): void {
76
for (const change of changes) (change as any).lastCommandId = this.lastCommandId;
77
this.commandQueue.record({ command: 'Session.recordOutput', args: changes });
78
}
79
80
public getAgentMeta(): Promise<IAgentMeta> {
81
return this.commandQueue.run('Session.getAgentMeta');
82
}
83
84
public async configure(options?: Partial<IConfigureSessionOptions>): Promise<void> {
85
await this.commandQueue.run('Session.configure', options);
86
}
87
88
public async getTabs(): Promise<CoreTab[]> {
89
const tabSessionMetas = await this.commandQueue.run<ISessionMeta[]>('Session.getTabs');
90
for (const tabMeta of tabSessionMetas) {
91
this.addTab(tabMeta);
92
}
93
return [...this.tabsById.values()];
94
}
95
96
public addTab(tabMeta: ISessionMeta): void {
97
if (!this.tabsById.has(tabMeta.tabId)) {
98
this.tabsById.set(
99
tabMeta.tabId,
100
new CoreTab({ ...tabMeta, sessionName: this.sessionName }, this.connection, this),
101
);
102
}
103
}
104
105
public removeTab(tab: CoreTab): void {
106
this.tabsById.delete(tab.tabId);
107
}
108
109
public async detachTab(
110
tab: CoreTab,
111
callSitePath: string,
112
key?: string,
113
): Promise<{ coreTab: CoreTab; prefetchedJsPaths: IJsPathResult[] }> {
114
const { meta, prefetchedJsPaths } = await this.commandQueue.run<{
115
meta: ISessionMeta;
116
prefetchedJsPaths: IJsPathResult[];
117
}>('Session.detachTab', tab.tabId, callSitePath, key);
118
const coreTab = new CoreTab({ ...meta, sessionName: this.sessionName }, this.connection, this);
119
this.frozenTabsById.set(meta.tabId, coreTab);
120
return {
121
coreTab,
122
prefetchedJsPaths,
123
};
124
}
125
126
public async close(): Promise<void> {
127
try {
128
await this.commandQueue.flush();
129
for (const tab of this.tabsById.values()) {
130
await tab.flush();
131
}
132
for (const tab of this.frozenTabsById.values()) {
133
await tab.flush();
134
}
135
await this.commandQueue.run('Session.close');
136
} finally {
137
process.nextTick(() => this.connection.closeSession(this));
138
loggerSessionIdNames.delete(this.sessionId);
139
}
140
}
141
142
public async addEventListener(
143
jsPath: IJsPath | null,
144
eventType: string,
145
listenerFn: (...args: any[]) => void,
146
options?,
147
): Promise<void> {
148
await this.eventHeap.addListener(jsPath, eventType, listenerFn, options);
149
}
150
151
public async removeEventListener(
152
jsPath: IJsPath | null,
153
eventType: string,
154
listenerFn: (...args: any[]) => void,
155
): Promise<void> {
156
await this.eventHeap.removeListener(jsPath, eventType, listenerFn);
157
}
158
}
159
160