-
Notifications
You must be signed in to change notification settings - Fork 0
/
info.ts
200 lines (169 loc) · 4.44 KB
/
info.ts
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
import crypto from 'node:crypto';
import {PartialDeep} from 'type-fest';
import {BetterMap} from '../utils';
import {getUserscriptId} from '../violentmonkey-context';
type ScriptInfo = {
/** A unique ID of the script. */
uuid: string;
/** The meta block of the script. */
scriptMetaStr: string;
/** Whether the script will be updated automatically. */
scriptWillUpdate: boolean;
/** The name of userscript manager, which should be the string Violentmonkey. */
scriptHandler: string;
/** Version of Violentmonkey. */
version: string;
/** Unlike navigator.userAgent, which can be overriden by other extensions/userscripts or by devtools in device-emulation mode, GM_info.platform is more reliable as the data is obtained in the background page of Violentmonkey using a specialized extension API (browser.runtime.getPlatformInfo and getBrowserInfo). */
platform: {
/** One of "arm", "mips", "mips64", "x86-32", "x86-64". */
arch: string;
/** "chrome", "firefox" or whatever was returned by the API. */
browserName: string;
browserVersion: string;
/** One of "android", "cros", "linux", "mac", "openbsd", "win". */
os: string;
};
/** Contains structured fields from the Metadata Block: */
script: {
description: string;
excludes: string[];
includes: string[];
matches: string[];
name: string;
namespace: string;
resources: Array<{name: string; url: string}>;
runAt: string;
version: string;
};
/** The injection mode of current script */
injectInto: 'page' | 'content';
};
const generateInfo = (): ScriptInfo => ({
uuid: crypto.randomUUID(),
scriptMetaStr: '',
scriptWillUpdate: true,
scriptHandler: 'Violentmonkey',
version: '2.13.0',
platform: {
arch: 'x86-64',
browserName: 'firefox',
browserVersion: '93',
os: 'linux',
},
script: {
description: '',
excludes: [],
includes: [],
matches: [],
name: '',
namespace: '',
resources: [],
runAt: 'document-start',
version: '1.0',
},
injectInto: 'page',
});
const cachedInfos = new BetterMap<number, ScriptInfo>();
/**
* Returns with information about the userscript
*
* The object is modifiable like with Violentmonkey
*
* If this function is imported, it is a function, if it used from global, it is a getter.
*
* @defaultValue
* ```
* {
* uuid: {randomly generated},
* scriptMetaStr: '',
* scriptWillUpdate: true,
* scriptHandler: 'Violentmonkey',
* version: '2.13.0',
* platform: {
* arch: 'x86-64',
* browserName: 'firefox',
* browserVersion: '93',
* os: 'linux',
* },
* script: {
* description: '',
* excludes: [],
* includes: [],
* matches: [],
* name: '',
* namespace: '',
* resources: [],
* runAt: 'document-start',
* version: '1.0',
* },
* injectInto: 'page',
* }
* ```
*/
const getInfo = () => cachedInfos.get(getUserscriptId(), () => generateInfo());
/* #resources may be undefined
But not #url or #name in resources
*/
type PartialScriptInfo = PartialDeep<ScriptInfo> & {
script?: {
resources?: ScriptInfo['script']['resources'];
};
};
type NonOptional<T> = T extends undefined ? never : T;
type PartialScriptInfo_Script = NonOptional<PartialScriptInfo['script']>;
/**
* Update GM_info#script by passing a DeepPartial of GM_info#script
*/
const updateInfoScript = (newScript: PartialScriptInfo_Script) => {
const {script} = getInfo();
for (const [key, value] of Object.entries(newScript)) {
switch (key) {
case 'excludes':
case 'includes':
case 'matches':
case 'resources': {
// @ts-expect-error It will always have the right type, since key and value are connected
script[key].push(...value);
break;
}
default: {
// @ts-expect-error It will always have the right type, since key and value are connected
script[key] = value;
}
}
}
};
/**
* Update the GM_info object by passing a DeepPartial of GM_info
*/
const updateInfo = (newInfo: PartialScriptInfo) => {
const info = getInfo();
for (const [key, value] of Object.entries(newInfo)) {
switch (key) {
case 'script': {
updateInfoScript(value as PartialScriptInfo_Script);
break;
}
case 'platform': {
Object.assign(info[key], value);
break;
}
default: {
// @ts-expect-error It will always have the right type, since key and value are connected
info[key] = value;
}
}
}
return info;
};
export {
getInfo as GM_info,
updateInfo as update_GM_info,
ScriptInfo,
PartialScriptInfo,
};
Object.defineProperties(global, {
GM_info: {
get: getInfo,
},
});