Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ulixee
GitHub Repository: ulixee/secret-agent
Path: blob/main/core/lib/CommandRecorder.ts
1029 views
1
import { IBoundLog } from '@secret-agent/interfaces/ILog';
2
import Log from '@secret-agent/commons/Logger';
3
import ICommandMeta from '@secret-agent/interfaces/ICommandMeta';
4
import TypeSerializer from '@secret-agent/commons/TypeSerializer';
5
import Session from './Session';
6
import Tab from './Tab';
7
8
const { log } = Log(module);
9
type AsyncFunc = (...args: any[]) => Promise<any>;
10
11
export default class CommandRecorder {
12
public readonly fnNames = new Set<string>();
13
private logger: IBoundLog;
14
constructor(
15
private owner: any,
16
private session: Session,
17
readonly tabId: number,
18
readonly frameId: number,
19
fns: AsyncFunc[],
20
) {
21
for (const fn of fns) {
22
owner[fn.name] = ((...args) => this.runCommandFn(fn, ...args)) as any;
23
this.fnNames.add(fn.name);
24
}
25
this.logger = log.createChild(module, {
26
tabId,
27
sessionId: session.id,
28
frameId,
29
});
30
}
31
32
public clear(): void {
33
this.session = null;
34
this.owner = null;
35
}
36
37
private async runCommandFn<T>(commandFn: AsyncFunc, ...args: any[]): Promise<T> {
38
if (!this.fnNames.has(commandFn.name))
39
throw new Error(`Unsupported function requested ${commandFn.name}`);
40
41
const { session } = this;
42
const sessionState = session.sessionState;
43
const commandHistory = sessionState.commands;
44
let tabId = this.tabId;
45
const frameId = this.frameId;
46
47
if (!tabId && args.length && args[0] instanceof Tab) {
48
tabId = args[0].id;
49
}
50
51
const commandMeta = {
52
id: commandHistory.length + 1,
53
tabId,
54
frameId,
55
name: commandFn.name,
56
args: args.length ? TypeSerializer.stringify(args) : undefined,
57
} as ICommandMeta;
58
59
if (sessionState.nextCommandMeta) {
60
const { commandId, sendDate, startDate } = sessionState.nextCommandMeta;
61
sessionState.nextCommandMeta = null;
62
commandMeta.id = commandId;
63
commandMeta.clientSendDate = sendDate?.getTime();
64
commandMeta.clientStartDate = startDate?.getTime();
65
}
66
67
if (frameId) {
68
const tab = session.getTab(tabId);
69
const frame = tab.frameEnvironmentsById.get(frameId);
70
frame.navigationsObserver.willRunCommand(commandMeta, commandHistory);
71
}
72
const id = this.logger.info('Command.run', commandMeta);
73
74
let result: T;
75
try {
76
commandMeta.runStartDate = new Date().getTime();
77
sessionState.recordCommandStart(commandMeta);
78
79
result = await commandFn.call(this.owner, ...args);
80
return result;
81
} catch (err) {
82
result = err;
83
throw err;
84
} finally {
85
commandMeta.endDate = new Date().getTime();
86
commandMeta.result = result;
87
// NOTE: second insert on purpose -- it will do an update
88
sessionState.recordCommandFinished(commandMeta);
89
this.logger.stats('Command.done', { result, parentLogId: id });
90
}
91
}
92
}
93
94