Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
ulixee
GitHub Repository: ulixee/secret-agent
Path: blob/main/core/models/DomChangesTable.ts
1028 views
1
import { Database as SqliteDatabase } from 'better-sqlite3';
2
import SqliteTable from '@secret-agent/commons/SqliteTable';
3
import { DomActionType, IDomChangeEvent } from '@secret-agent/interfaces/IDomChangeEvent';
4
5
export declare type IFrontendDomChangeEvent = Omit<
6
IDomChangeRecord,
7
'frameId' | 'tabId' | 'commandId' | 'timestamp'
8
> & {
9
frameIdPath?: string;
10
};
11
12
export default class DomChangesTable extends SqliteTable<IDomChangeRecord> {
13
constructor(readonly db: SqliteDatabase) {
14
super(db, 'DomChanges', [
15
['frameId', 'INTEGER'],
16
['eventIndex', 'INTEGER'],
17
['action', 'INTEGER'],
18
['nodeId', 'INTEGER'],
19
['nodeType', 'INTEGER'],
20
['tagName', 'TEXT'],
21
['previousSiblingId', 'INTEGER'],
22
['parentNodeId', 'INTEGER'],
23
['textContent', 'TEXT'],
24
['attributes', 'TEXT'],
25
['attributeNamespaces', 'TEXT'],
26
['properties', 'TEXT'],
27
['namespaceUri', 'TEXT'],
28
['commandId', 'INTEGER'],
29
['tabId', 'INTEGER'],
30
['timestamp', 'INTEGER'],
31
]);
32
this.defaultSortOrder = 'timestamp ASC';
33
}
34
35
public insert(tabId: number, frameId: number, commandId: number, change: IDomChangeEvent) {
36
const [action, nodeData, timestamp, eventIndex] = change;
37
const record = [
38
frameId,
39
eventIndex,
40
action,
41
nodeData.id,
42
nodeData.nodeType,
43
nodeData.tagName,
44
nodeData.previousSiblingId,
45
nodeData.parentNodeId,
46
nodeData.textContent,
47
nodeData.attributes ? JSON.stringify(nodeData.attributes) : undefined,
48
nodeData.attributeNamespaces ? JSON.stringify(nodeData.attributeNamespaces) : undefined,
49
nodeData.properties ? JSON.stringify(nodeData.properties) : undefined,
50
nodeData.namespaceUri,
51
commandId,
52
tabId,
53
timestamp,
54
];
55
this.queuePendingInsert(record);
56
}
57
58
public getFrameChanges(frameId: number, sinceCommandId?: number): IDomChangeRecord[] {
59
const query = this.db.prepare(
60
`select * from ${this.tableName} where frameId =? and commandId > ?`,
61
);
62
63
return query.all(frameId, sinceCommandId ?? 0).map(DomChangesTable.inflateRecord);
64
}
65
66
public static inflateRecord(record: IDomChangeRecord): IDomChangeRecord {
67
for (const [key, value] of Object.entries(record)) {
68
if (value === null) record[key] = undefined;
69
}
70
record.attributes = record.attributes ? JSON.parse(record.attributes as any) : undefined;
71
record.attributeNamespaces = record.attributeNamespaces
72
? JSON.parse(record.attributeNamespaces as any)
73
: undefined;
74
record.properties = record.properties ? JSON.parse(record.properties as any) : undefined;
75
return record;
76
}
77
78
public static toFrontendRecord(
79
record: IDomChangeRecord,
80
frameIdToNodePath: Map<number, string>,
81
): IFrontendDomChangeEvent {
82
return {
83
action: record.action,
84
eventIndex: record.eventIndex,
85
nodeId: record.nodeId,
86
parentNodeId: record.parentNodeId,
87
previousSiblingId: record.previousSiblingId,
88
properties: record.properties,
89
nodeType: record.nodeType,
90
tagName: record.tagName,
91
attributeNamespaces: record.attributeNamespaces,
92
attributes: record.attributes,
93
frameIdPath: frameIdToNodePath.get(record.frameId),
94
textContent: record.textContent,
95
namespaceUri: record.namespaceUri,
96
};
97
}
98
}
99
100
export interface IDomChangeRecord {
101
commandId: number;
102
tabId: number;
103
frameId: number;
104
nodeId: number;
105
timestamp: number;
106
eventIndex: number;
107
action: DomActionType;
108
nodeType: number;
109
tagName: string;
110
namespaceUri: string;
111
textContent: string;
112
previousSiblingId: number;
113
parentNodeId: number;
114
attributes: Record<string, string> | undefined;
115
attributeNamespaces: Record<string, string> | undefined;
116
properties: Record<string, any> | undefined;
117
}
118
119