forked from AdguardTeam/Scriptlets
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscriptlets.ts
More file actions
143 lines (128 loc) · 4.15 KB
/
scriptlets.ts
File metadata and controls
143 lines (128 loc) · 4.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// Ignore this module, because it appears only during build
// @ts-ignore
// eslint-disable-next-line import/order
import { getScriptletFunction } from 'scriptlets-func';
import { passSourceAndProps, wrapInNonameFunc } from '../helpers/injector';
import { type PolicyApi } from '../helpers/trusted-types-utils';
/**
* Interface of content script provided API.
*
* This API is used to provide a set of utilities and shared state
* for scriptlets running in the context of a web page. Particularly,
* it includes:
* - Trusted Types Policy API utilities.
* - Shared state between different scriptlet rules.
*
* NOTE: Currently only CoreLibs provides this API.
*/
export interface ContentScriptApi {
/**
* Trusted Types Policy API utilities.
*
* @see {@link PolicyApi} for more information.
*/
readonly policy: PolicyApi;
/**
* Shared state between different script and scriptlet rules.
*
* This object acts as a centralized repository for shared data.
* - Keys represent the unique identifiers or names of the shared data.
* - Values can be of any type and should correspond to the specific data shared across script rules.
*
* @example
* ```adguard
* ! Modify in one script rule
* #%#api.shared.testKey = 'testValue'
*
* ! Access in another (logs 'testValue')
* #%#console.log(api.shared.testKey)
* ```
*/
readonly shared: Record<string, unknown>;
}
/**
* Scriptlet properties
*/
export interface Source {
/**
* Scriptlet name
*/
name: string;
/**
* Arguments for scriptlet function
*/
args: string[];
/**
* {'extension'|'corelibs'} engine Defines the final form of scriptlet string presentation
*/
engine: string;
/**
* Version
*/
version: string;
/**
* flag to enable printing to console debug information
*/
verbose: boolean;
/**
* Source rule text is used for debugging purposes.
*
* @deprecated since it is not used in the code anymore.
*/
ruleText?: string;
/**
* Domain name, used to improve logging
*/
domainName?: string;
/**
* Optional unique identifier for a scriptlet instance.
*
* This identifier is used to prevent multiple executions of the same scriptlet on the page.
* If provided, this `uniqueId` will be combined with the scriptlet's `name` and `args`
* to create a unique identifier for the scriptlet call. This identifier is
* stored in the `Window.prototype.toString` object to ensure the scriptlet
* is not executed more than once in the same context.
*
* By avoiding multiple executions, it helps in reducing redundant operations and
* potential side effects that might occur if the same scriptlet is called multiple times.
*
* If `uniqueId` is not specified, no such unique identifier is created, and the
* scriptlet can be called multiple times.
*/
uniqueId?: string;
/**
* Instance of content script provided API.
*
* Property optional because:
* - for backwards compatibility,
* - currently only CoreLibs provides this API.
*
* @see {@link ContentScriptApi} for more information.
*/
api?: ContentScriptApi;
}
/**
* Returns scriptlet code by `source`.
*
* @param source Scriptlet properties.
*
* @returns Scriptlet code.
* @throws An error on unknown scriptlet name.
*/
function getScriptletCode(source: Source): string {
const scriptletFunction = getScriptletFunction(source.name);
// In case isValidScriptletName check will pass invalid scriptlet name,
// for example when there is a bad alias
if (typeof scriptletFunction !== 'function') {
throw new Error(`Error: cannot invoke scriptlet with name: '${source.name}'`);
}
const scriptletFunctionString = scriptletFunction.toString();
const result = source.engine === 'corelibs' || source.engine === 'test'
? wrapInNonameFunc(scriptletFunctionString)
: passSourceAndProps(source, scriptletFunctionString);
return result;
}
export const scriptlets = {
invoke: getScriptletCode,
getScriptletFunction,
};