vite-userscript-plugin 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -12,6 +12,7 @@
12
12
  - 🔧 Configure Tampermonkey's Userscript header.
13
13
  - 💨 Import all `grant` by default in development mode.
14
14
  - 📝 Automatically add used `grant` when building for production.
15
+ - 📦 Built-in Tampermonkey's TypeScript type difinition.
15
16
 
16
17
  ## Install
17
18
 
@@ -36,21 +37,23 @@ import { defineConfig } from 'vite'
36
37
  import Userscript from 'vite-userscript-plugin'
37
38
  import { name, version } from './package.json'
38
39
 
39
- export default defineConfig({
40
- plugins: [
41
- Userscript({
42
- entry: 'src/index.ts',
43
- header: {
44
- name,
45
- version,
46
- match: [
47
- 'https://example.com',
48
- 'https://example.org',
49
- 'https://example.edu'
50
- ]
51
- }
52
- })
53
- ]
40
+ export default defineConfig((config) => {
41
+ return {
42
+ plugins: [
43
+ Userscript({
44
+ entry: 'src/index.ts',
45
+ header: {
46
+ name,
47
+ version,
48
+ match: [
49
+ '*://example.com',
50
+ '*://example.org',
51
+ '*://example.edu'
52
+ ]
53
+ }
54
+ })
55
+ ]
56
+ }
54
57
  })
55
58
  ```
56
59
 
@@ -65,6 +68,18 @@ export default defineConfig({
65
68
  }
66
69
  ```
67
70
 
71
+ ### `tsconfig.json`
72
+
73
+ ```json
74
+ {
75
+ "compilerOptions": {
76
+ "types": [
77
+ "vite-userscript-plugin/tampermonkey"
78
+ ]
79
+ }
80
+ }
81
+ ```
82
+
68
83
  ## Plugin Configuration
69
84
 
70
85
  ```ts
package/dist/index.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  "use strict";var Z=Object.create;var b=Object.defineProperty;var ee=Object.getOwnPropertyDescriptor;var te=Object.getOwnPropertyNames;var re=Object.getPrototypeOf,ne=Object.prototype.hasOwnProperty;var oe=(e,r)=>{for(var t in r)b(e,t,{get:r[t],enumerable:!0})},T=(e,r,t,n)=>{if(r&&typeof r=="object"||typeof r=="function")for(let s of te(r))!ne.call(e,s)&&s!==t&&b(e,s,{get:()=>r[s],enumerable:!(n=ee(r,s))||n.enumerable});return e};var u=(e,r,t)=>(t=e!=null?Z(re(e)):{},T(r||!e||!e.__esModule?b(t,"default",{value:e,enumerable:!0}):t,e)),se=e=>T(b({},"__esModule",{value:!0}),e);var le={};oe(le,{default:()=>N});module.exports=se(le);var V=u(require("get-port"),1),c=require("fs"),D=require("http"),a=require("path"),K=require("url"),A=u(require("open"),1),M=u(require("picocolors"),1),H=u(require("sanitize-filename"),1),W=u(require("serve-handler"),1),q=require("vite"),B=require("websocket");var g=class{constructor(r){this.config=r;this.downloadMeta(),this.configKeys=Object.keys(this.config),this.maxKeyLength=Math.max(...this.configKeys.map(t=>t.length))+1}header=[];configKeys;maxKeyLength;downloadMeta(){let{name:r,homepage:t,updateURL:n,downloadURL:s}=this.config;t&&!n&&!s&&(this.config.updateURL=new URL(`${r}.meta.js`,t).href,this.config.downloadURL=new URL(`${r}.user.js`,t).href)}addSpaces(r){return" ".repeat(this.maxKeyLength-r.length)}addMetadata(r,t){let n=typeof t=="boolean";n&&!t||(t=n?"":`${this.addSpaces(r)}${t}`,this.header.push(`// @${r}${t}`))}generate(){for(let[r,t]of Object.entries(this.config))if(Array.isArray(t))t.forEach(n=>this.addMetadata(r,n));else{if(t===void 0)continue;this.addMetadata(r,t)}return["// ==UserScript==",...this.header,"// ==/UserScript=="].join(`
2
- `)}};function E({entry:e,header:r}){return{build:{lib:{entry:e,name:r.name,formats:["iife"],fileName:()=>`${r.name}.js`},rollupOptions:{output:{extend:!0}}}}}var F=new RegExp(/.(t|j)sx?/),L=new RegExp(/\.(s?css|sass)$/),ie=["setValue","getValue","deleteValue","listValues","setClipboard","addStyle","addElement","addValueChangeListener","removeValueChangeListener","registerMenuCommand","unregisterMenuCommand","download","getTab","getTabs","saveTab","openInTab","notification","getResourceURL","getResourceText","xmlhttpRequest","log","info"],ae=["unsafeWindow","window.onurlchange","window.focus","window.close"],h=ie.map(e=>[`GM_${e}`,`GM.${e}`]).flat();h.push(...ae);var O=require("vite");function U(e){return[...new Set(Array.isArray(e)?e:e?[e]:[])]}async function y({file:e,name:r,loader:t}){let{code:n}=await(0,O.transformWithEsbuild)(e,r,{loader:t,minify:!0});return n}function k(e){let r=[];for(let t of h)e.indexOf(t)!==-1&&r.push(t);return r}var _=class{styles=new Map;async add(r,t){let n=await y({file:r,name:t,loader:"css"});return this.styles.set(t,n.replace(`
2
+ `)}};function E({entry:e,header:r}){return{build:{lib:{entry:e,name:r.name,formats:["iife"],fileName:()=>`${r.name}.js`},rollupOptions:{output:{extend:!0}}}}}var F=new RegExp(/.(t|j)sx?/),L=new RegExp(/\.(s?css|sass)$/),ie=["setValue","getValue","deleteValue","listValues","setClipboard","addStyle","addElement","addValueChangeListener","removeValueChangeListener","registerMenuCommand","unregisterMenuCommand","download","getTab","getTabs","saveTab","openInTab","notification","getResourceURL","getResourceText","xmlhttpRequest","log","info"],ae=["unsafeWindow","window.onurlchange","window.focus","window.close"],h=ie.map(e=>[`GM_${e}`,`GM.${e}`]).flat();h.push(...ae);var O=require("vite");function U(e){return[...new Set(Array.isArray(e)?e:e?[e]:[])]}async function y({file:e,name:r,loader:t}){let{code:n}=await(0,O.transformWithEsbuild)(e,r,{loader:t,minify:!0,sourcemap:!1,legalComments:"none"});return n}function k(e){let r=[];for(let t of h)e.indexOf(t)!==-1&&r.push(t);return r}var _=class{styles=new Map;async add(r,t){let n=await y({file:r,name:t,loader:"css"});return this.styles.set(t,n.replace(`
3
3
  `,"")),""}inject(){return this.styles.size?`GM_addStyle(\`${[...this.styles.values()].join("")}\`)`:void 0}merge(r){let t=[];for(let n of r){let s=this.styles.get(n);!s||t.push([n,s])}this.styles.clear(),t.forEach(n=>this.styles.set(...n))}},j=new _;var ce={};function N(e){let r,t,n=null,s=(0,a.dirname)((0,K.fileURLToPath)(ce.url)),S=(0,q.createLogger)("info",{prefix:"[vite-userscript-plugin]",allowClearScreen:!0}),w=(0,D.createServer)((o,i)=>(0,W.default)(o,i,{public:r.build.outDir})),z=B.server;return new z({httpServer:w}).on("request",o=>{n=o.accept(null,o.origin)}),{name:"vite-userscript-plugin",apply:"build",config(){return E(e)},async configResolved(o){r=o,t=o.build.watch??!1,e.entry=(0,a.resolve)(o.root,e.entry),e.header.name=(0,H.default)(e.header.name),Array.from(["match","require","include","exclude","resource","connect"]).forEach(i=>{let p=e.header[i];e.header[i]=U(p)}),e.server={port:await(0,V.default)(),open:!1,...e.server}},async transform(o,i){let p=o;return L.test(i)&&(p=await j.add(o,i)),i.includes(e.entry)&&(p=o+"__STYLE__"),{code:p,map:null}},generateBundle(o,i){for(let[p,m]of Object.entries(i)){let f=Object.keys(m.modules).filter($=>L.test($));f.length&&j.merge(f)}},async writeBundle(o,i){let{open:p,port:m}=e.server,P=`${e.header.name}.user.js`,f=`${e.header.name}.proxy.user.js`,$=`${e.header.name}.meta.js`;for(let[d]of Object.entries(i))if(F.test(d)){let x=r.root,v=r.build.outDir,C=(0,a.resolve)(x,v,d),Y=(0,a.resolve)(x,v,P),I=(0,a.resolve)(x,v,f),J=(0,a.resolve)(x,v,$),R=(0,a.resolve)(s,`hot-reload-${e.header.name}.js`);try{let l=(0,c.readFileSync)(C,"utf8");if(l=l.replace("__STYLE__",`${j.inject()}`),l=await y({file:l,name:d,loader:"js"}),e.header.grant=U(t?h:[...k(l),...e.header.grant??[]]),t){let Q=(0,c.readFileSync)((0,a.resolve)(s,"hot-reload.js"),"utf8"),X=await y({file:Q.replace("__WS__",`ws://localhost:${m}`),name:R,loader:"js"});(0,c.writeFileSync)(R,X),(0,c.writeFileSync)(I,new g({...e.header,require:[...e.header.require,"file://"+R,"file://"+C]}).generate())}let G=new g(e.header).generate();(0,c.writeFileSync)(C,l),(0,c.writeFileSync)(J,G),(0,c.writeFileSync)(Y,`${G}
4
4
 
5
5
  ${l}`)}catch(l){console.log(l)}}if(t&&!w.listening){let d=`http://localhost:${m}`;w.listen(m,()=>{S.clearScreen("info"),S.info(M.default.blue(`Running at: ${M.default.gray(d)}`))}),p&&await(0,A.default)(`${d}/${f}`)}else t||(w.close(),process.exit(0))},buildEnd(){t&&(S.clearScreen("info"),n&&n.sendUTF(JSON.stringify({message:"reload"})))}}}0&&(module.exports={});
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import q from"get-port";import{readFileSync as T,writeFileSync as h}from"fs";import{createServer as B}from"http";import{dirname as N,resolve as c}from"path";import{fileURLToPath as z}from"url";import Y from"open";import E from"picocolors";import I from"sanitize-filename";import J from"serve-handler";import{createLogger as Q}from"vite";import{server as X}from"websocket";var f=class{constructor(r){this.config=r;this.downloadMeta(),this.configKeys=Object.keys(this.config),this.maxKeyLength=Math.max(...this.configKeys.map(t=>t.length))+1}header=[];configKeys;maxKeyLength;downloadMeta(){let{name:r,homepage:t,updateURL:n,downloadURL:l}=this.config;t&&!n&&!l&&(this.config.updateURL=new URL(`${r}.meta.js`,t).href,this.config.downloadURL=new URL(`${r}.user.js`,t).href)}addSpaces(r){return" ".repeat(this.maxKeyLength-r.length)}addMetadata(r,t){let n=typeof t=="boolean";n&&!t||(t=n?"":`${this.addSpaces(r)}${t}`,this.header.push(`// @${r}${t}`))}generate(){for(let[r,t]of Object.entries(this.config))if(Array.isArray(t))t.forEach(n=>this.addMetadata(r,n));else{if(t===void 0)continue;this.addMetadata(r,t)}return["// ==UserScript==",...this.header,"// ==/UserScript=="].join(`
2
- `)}};function M({entry:e,header:r}){return{build:{lib:{entry:e,name:r.name,formats:["iife"],fileName:()=>`${r.name}.js`},rollupOptions:{output:{extend:!0}}}}}var P=new RegExp(/.(t|j)sx?/),C=new RegExp(/\.(s?css|sass)$/),A=["setValue","getValue","deleteValue","listValues","setClipboard","addStyle","addElement","addValueChangeListener","removeValueChangeListener","registerMenuCommand","unregisterMenuCommand","download","getTab","getTabs","saveTab","openInTab","notification","getResourceURL","getResourceText","xmlhttpRequest","log","info"],H=["unsafeWindow","window.onurlchange","window.focus","window.close"],u=A.map(e=>[`GM_${e}`,`GM.${e}`]).flat();u.push(...H);import{transformWithEsbuild as W}from"vite";function R(e){return[...new Set(Array.isArray(e)?e:e?[e]:[])]}async function g({file:e,name:r,loader:t}){let{code:n}=await W(e,r,{loader:t,minify:!0});return n}function G(e){let r=[];for(let t of u)e.indexOf(t)!==-1&&r.push(t);return r}var L=class{styles=new Map;async add(r,t){let n=await g({file:r,name:t,loader:"css"});return this.styles.set(t,n.replace(`
2
+ `)}};function M({entry:e,header:r}){return{build:{lib:{entry:e,name:r.name,formats:["iife"],fileName:()=>`${r.name}.js`},rollupOptions:{output:{extend:!0}}}}}var P=new RegExp(/.(t|j)sx?/),C=new RegExp(/\.(s?css|sass)$/),A=["setValue","getValue","deleteValue","listValues","setClipboard","addStyle","addElement","addValueChangeListener","removeValueChangeListener","registerMenuCommand","unregisterMenuCommand","download","getTab","getTabs","saveTab","openInTab","notification","getResourceURL","getResourceText","xmlhttpRequest","log","info"],H=["unsafeWindow","window.onurlchange","window.focus","window.close"],u=A.map(e=>[`GM_${e}`,`GM.${e}`]).flat();u.push(...H);import{transformWithEsbuild as W}from"vite";function R(e){return[...new Set(Array.isArray(e)?e:e?[e]:[])]}async function g({file:e,name:r,loader:t}){let{code:n}=await W(e,r,{loader:t,minify:!0,sourcemap:!1,legalComments:"none"});return n}function G(e){let r=[];for(let t of u)e.indexOf(t)!==-1&&r.push(t);return r}var L=class{styles=new Map;async add(r,t){let n=await g({file:r,name:t,loader:"css"});return this.styles.set(t,n.replace(`
3
3
  `,"")),""}inject(){return this.styles.size?`GM_addStyle(\`${[...this.styles.values()].join("")}\`)`:void 0}merge(r){let t=[];for(let n of r){let l=this.styles.get(n);!l||t.push([n,l])}this.styles.clear(),t.forEach(n=>this.styles.set(...n))}},v=new L;function Z(e){let r,t,n=null,l=N(z(import.meta.url)),b=Q("info",{prefix:"[vite-userscript-plugin]",allowClearScreen:!0}),y=B((o,s)=>J(o,s,{public:r.build.outDir})),F=X;return new F({httpServer:y}).on("request",o=>{n=o.accept(null,o.origin)}),{name:"vite-userscript-plugin",apply:"build",config(){return M(e)},async configResolved(o){r=o,t=o.build.watch??!1,e.entry=c(o.root,e.entry),e.header.name=I(e.header.name),Array.from(["match","require","include","exclude","resource","connect"]).forEach(s=>{let a=e.header[s];e.header[s]=R(a)}),e.server={port:await q(),open:!1,...e.server}},async transform(o,s){let a=o;return C.test(s)&&(a=await v.add(o,s)),s.includes(e.entry)&&(a=o+"__STYLE__"),{code:a,map:null}},generateBundle(o,s){for(let[a,d]of Object.entries(s)){let m=Object.keys(d.modules).filter(j=>C.test(j));m.length&&v.merge(m)}},async writeBundle(o,s){let{open:a,port:d}=e.server,U=`${e.header.name}.user.js`,m=`${e.header.name}.proxy.user.js`,j=`${e.header.name}.meta.js`;for(let[p]of Object.entries(s))if(P.test(p)){let w=r.root,x=r.build.outDir,S=c(w,x,p),O=c(w,x,U),k=c(w,x,m),V=c(w,x,j),$=c(l,`hot-reload-${e.header.name}.js`);try{let i=T(S,"utf8");if(i=i.replace("__STYLE__",`${v.inject()}`),i=await g({file:i,name:p,loader:"js"}),e.header.grant=R(t?u:[...G(i),...e.header.grant??[]]),t){let D=T(c(l,"hot-reload.js"),"utf8"),K=await g({file:D.replace("__WS__",`ws://localhost:${d}`),name:$,loader:"js"});h($,K),h(k,new f({...e.header,require:[...e.header.require,"file://"+$,"file://"+S]}).generate())}let _=new f(e.header).generate();h(S,i),h(V,_),h(O,`${_}
4
4
 
5
5
  ${i}`)}catch(i){console.log(i)}}if(t&&!y.listening){let p=`http://localhost:${d}`;y.listen(d,()=>{b.clearScreen("info"),b.info(E.blue(`Running at: ${E.gray(p)}`))}),a&&await Y(`${p}/${m}`)}else t||(y.close(),process.exit(0))},buildEnd(){t&&(b.clearScreen("info"),n&&n.sendUTF(JSON.stringify({message:"reload"})))}}}export{Z as default};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-userscript-plugin",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "type": "module",
5
5
  "types": "./dist/index.d.ts",
6
6
  "main": "./dist/index.cjs",
@@ -10,7 +10,8 @@
10
10
  "import": "./dist/index.js"
11
11
  },
12
12
  "files": [
13
- "dist"
13
+ "dist",
14
+ "tampermonkey.d.ts"
14
15
  ],
15
16
  "repository": {
16
17
  "type": "git",
@@ -34,7 +35,6 @@
34
35
  "url": "https://github.com/crashmax-dev/vite-userscript-plugin/issues"
35
36
  },
36
37
  "dependencies": {
37
- "@types/tampermonkey": "^4.0.5",
38
38
  "get-port": "^6.1.2",
39
39
  "open": "^8.4.0",
40
40
  "picocolors": "^1.0.0",
@@ -50,14 +50,13 @@
50
50
  "@types/websocket": "^1.0.5",
51
51
  "@vitest/ui": "^0.23.2",
52
52
  "tsup": "^6.2.3",
53
- "turbo": "^1.4.6",
53
+ "turbo": "^1.4.7",
54
54
  "typescript": "^4.8.3",
55
- "vite": "^3.1.1",
55
+ "vite": "^3.1.2",
56
56
  "vite-plugin-dts": "^1.5.0",
57
57
  "vitest": "^0.23.2"
58
58
  },
59
59
  "peerDependencies": {
60
- "@types/tampermonkey": ">=4.0.0",
61
60
  "vite": ">=2.9.0"
62
61
  },
63
62
  "scripts": {
@@ -0,0 +1,711 @@
1
+ // Type definitions for non-npm package Tampermonkey 4.x
2
+ // Project: https://tampermonkey.net
3
+ // Definitions by: Steven Wang <https://github.com/silverwzw>
4
+ // Nikolay Borzov <https://github.com/nikolay-borzov>
5
+ // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
6
+
7
+ // This definition is based on the API reference of Tampermonkey
8
+ // https://tampermonkey.net/documentation.php
9
+ // TypeScript Version: 3.3
10
+
11
+ declare namespace Tampermonkey {
12
+ type ValueChangeListener = (
13
+ name: string,
14
+ oldValue: any,
15
+ newValue: any,
16
+ remote: boolean
17
+ ) => void
18
+
19
+ // Response
20
+
21
+ enum ReadyState {
22
+ Unsent = 0,
23
+ Opened = 1,
24
+ HeadersReceived = 2,
25
+ Loading = 3,
26
+ Done = 4
27
+ }
28
+
29
+ interface ResponseBase {
30
+ readonly responseHeaders: string
31
+ readonly readyState: ReadyState
32
+ readonly response: any
33
+ readonly responseText: string
34
+ readonly responseXML: Document | null
35
+ readonly status: number
36
+ readonly statusText: string
37
+ }
38
+
39
+ interface ProgressResponseBase {
40
+ done: number
41
+ lengthComputable: boolean
42
+ loaded: number
43
+ position: number
44
+ total: number
45
+ totalSize: number
46
+ }
47
+
48
+ interface ErrorResponse extends ResponseBase {
49
+ readonly error: string
50
+ }
51
+
52
+ interface Response<TContext> extends ResponseBase {
53
+ readonly finalUrl: string
54
+ readonly context: TContext
55
+ }
56
+
57
+ interface ProgressResponse<TContext>
58
+ extends Response<TContext>,
59
+ ProgressResponseBase {}
60
+
61
+ // Request
62
+
63
+ interface RequestHeaders {
64
+ readonly [header: string]: string
65
+ }
66
+
67
+ type RequestEventListener<TResponse> = (
68
+ this: TResponse,
69
+ response: TResponse
70
+ ) => void
71
+
72
+ type RequestMethods =
73
+ | 'POST'
74
+ | 'GET'
75
+ | 'PUT'
76
+ | 'PATCH'
77
+ | 'DELETE'
78
+ | 'HEAD'
79
+ | 'TRACE'
80
+ | 'OPTIONS'
81
+ | 'CONNECT'
82
+
83
+ interface Request<TContext = object> {
84
+ method?: RequestMethods
85
+ /** Destination URL */
86
+ url: string
87
+ /**
88
+ * i.e. user-agent, referer... (some special headers are not supported
89
+ * by Safari and Android browsers)
90
+ */
91
+ headers?: RequestHeaders | undefined
92
+ /** String to send via a POST request */
93
+ data?: string | undefined
94
+ /** A cookie to be patched into the sent cookie set */
95
+ cookie?: string | undefined
96
+ /** Send the data string in binary mode */
97
+ binary?: boolean | undefined
98
+ /** Don't cache the resource */
99
+ nocache?: boolean | undefined
100
+ /** Revalidate maybe cached content */
101
+ revalidate?: boolean | undefined
102
+ /** Timeout in ms */
103
+ timeout?: number | undefined
104
+ /** Property which will be added to the response object */
105
+ context?: TContext | undefined
106
+ responseType?: 'arraybuffer' | 'blob' | 'json' | undefined
107
+ /** MIME type for the request */
108
+ overrideMimeType?: string | undefined
109
+ /** Don't send cookies with the requests (please see the fetch notes) */
110
+ anonymous?: boolean | undefined
111
+ /**
112
+ * (Beta) Use a fetch instead of a xhr request(at Chrome this causes
113
+ * `xhr.abort`, `details.timeout` and `xhr.onprogress` to not work and
114
+ * makes `xhr.onreadystatechange` receive only readyState 4 events)
115
+ */
116
+ fetch?: boolean | undefined
117
+ /** Username for authentication */
118
+ user?: string | undefined
119
+ password?: string | undefined
120
+
121
+ // Events
122
+
123
+ /** Callback to be executed if the request was aborted */
124
+ onabort?(): void
125
+ /** Callback to be executed if the request ended up with an error */
126
+ onerror?: RequestEventListener<ErrorResponse> | undefined
127
+ /** Callback to be executed if the request started to load */
128
+ onloadstart?: RequestEventListener<Response<TContext>> | undefined
129
+ /** Callback to be executed if the request made some progress */
130
+ onprogress?: RequestEventListener<ProgressResponse<TContext>> | undefined
131
+ /** Callback to be executed if the request's ready state changed */
132
+ onreadystatechange?: RequestEventListener<Response<TContext>> | undefined
133
+ /** Callback to be executed if the request failed due to a timeout */
134
+ ontimeout?(): void
135
+ /** Callback to be executed if the request was loaded */
136
+ onload?: RequestEventListener<Response<TContext>> | undefined
137
+ }
138
+
139
+ // Download Response
140
+
141
+ interface DownloadProgressResponse extends ProgressResponseBase {
142
+ readonly finalUrl: string
143
+ }
144
+
145
+ interface DownloadErrorResponse {
146
+ /**
147
+ * Error reason
148
+ * - `not_enabled` - the download feature isn't enabled by the user
149
+ * - `not_whitelisted` - the requested file extension is not
150
+ * whitelisted
151
+ * - `not_permitted` - the user enabled the download feature, but did
152
+ * not give the downloads permission
153
+ * - `not_supported` - the download feature isn't supported by the
154
+ * browser/version
155
+ * - `not_succeeded` - the download wasn't started or failed, the
156
+ * details attribute may provide more information
157
+ */
158
+ error:
159
+ | 'not_enabled'
160
+ | 'not_whitelisted'
161
+ | 'not_permitted'
162
+ | 'not_supported'
163
+ | 'not_succeeded'
164
+ /** Detail about that error */
165
+ details?: string | undefined
166
+ }
167
+
168
+ // Download Request
169
+
170
+ interface DownloadRequest {
171
+ /** URL from where the data should be downloaded */
172
+ url: string
173
+ /**
174
+ * Filename - for security reasons the file extension needs to be
175
+ * whitelisted at Tampermonkey options page
176
+ */
177
+ name: string
178
+ headers?: RequestHeaders | undefined
179
+ /** Show 'Save As' dialog */
180
+ saveAs?: boolean | undefined
181
+ timeout?: number | undefined
182
+ /** Callback to be executed if this download ended up with an error */
183
+ onerror?: RequestEventListener<DownloadErrorResponse> | undefined
184
+ /** Callback to be executed if this download finished */
185
+ ontimeout?(): void
186
+ /** Callback to be executed if this download finished */
187
+ onload?(): void
188
+ /** Callback to be executed if this download failed due to a timeout */
189
+ onprogress?: RequestEventListener<DownloadProgressResponse> | undefined
190
+ }
191
+
192
+ interface AbortHandle<TReturn> {
193
+ abort(): TReturn
194
+ }
195
+
196
+ interface OpenTabOptions {
197
+ /** Decides whether the new tab should be focused */
198
+ active?: boolean | undefined
199
+ /** Inserts the new tab after the current one */
200
+ insert?: boolean | undefined
201
+ /** Makes the browser re-focus the current tab on close */
202
+ setParent?: boolean | undefined
203
+ }
204
+
205
+ interface OpenTabObject {
206
+ /** Closes tab */
207
+ close(): void
208
+ /** Set closed listener */
209
+ onclose?(): void
210
+ closed: boolean
211
+ }
212
+
213
+ interface NotificationThis extends Notification {
214
+ id: string
215
+ }
216
+
217
+ type NotificationOnClick = (this: NotificationThis) => void
218
+ /** `clicked` is `true` when `text` was set */
219
+ type NotificationOnDone = (this: NotificationThis, clicked: boolean) => void
220
+
221
+ interface Notification {
222
+ /** Text of the notification (optional if highlight is set) */
223
+ text?: string | undefined
224
+ /** Notification title. If not specified the script name is used */
225
+ title?: string | undefined
226
+ image?: string | undefined
227
+ /** Flag whether to highlight the tab that sends the notification */
228
+ highlight?: boolean | undefined
229
+ /** Whether to play or not play a sound */
230
+ silent?: boolean | undefined
231
+ /** Time after that the notification will be hidden. `0` = disabled */
232
+ timeout?: number | undefined
233
+ /**
234
+ * Called when the notification is closed (no matter if this was
235
+ * triggered by a timeout or a click) or the tab was highlighted
236
+ */
237
+ onclick?: NotificationOnClick | undefined
238
+ /** Called in case the user clicks the notification */
239
+ ondone?: NotificationOnDone | undefined
240
+ }
241
+
242
+ interface TextNotification extends Notification {
243
+ /** Text of the notification (optional if highlight is set) */
244
+ text: string
245
+ }
246
+
247
+ interface HighlightNotification extends Notification {
248
+ text?: undefined
249
+ highlight: true
250
+ }
251
+
252
+ type NotificationDetails = TextNotification | HighlightNotification
253
+
254
+ // Interfaces for GM_info
255
+
256
+ /**
257
+ * The metadata that the user can override in the settings
258
+ * for example run-at or excludes
259
+ */
260
+ interface ScriptMetadataOverrides {
261
+ merge_connects: boolean
262
+ merge_excludes: boolean
263
+ merge_includes: boolean
264
+ merge_matches: boolean
265
+ orig_connects: string[]
266
+ orig_excludes: string[]
267
+ orig_includes: string[]
268
+ orig_matches: string[]
269
+ orig_noframes: string | null
270
+ orig_run_at: string | null
271
+ use_blockers: string[]
272
+ use_connects: string[]
273
+ use_excludes: string[]
274
+ use_includes: string[]
275
+ use_matches: string[]
276
+ }
277
+
278
+ /**
279
+ * The options that the user of the userscript
280
+ * can set in the settings (!== overrides)
281
+ */
282
+ interface ScriptSettings {
283
+ check_for_updates: boolean
284
+ comment: string | null
285
+ compat_foreach: boolean
286
+ compat_metadata: boolean
287
+ compat_prototypes: boolean
288
+ compat_wrappedjsobject: boolean
289
+ compatopts_for_requires: boolean
290
+ noframes: boolean | null
291
+ run_at: string
292
+ override: ScriptMetadataOverrides
293
+ }
294
+
295
+ /**
296
+ * The resources from the metadata block (@resources)
297
+ * that tampermonkey should preload
298
+ */
299
+ interface ScriptResource {
300
+ name: string
301
+ url: string
302
+ content: string
303
+ meta: string
304
+ }
305
+
306
+ /**
307
+ * `.. | null` means if it was not explicitely set in the metadata
308
+ * block it is null
309
+ */
310
+ interface ScriptMetadata {
311
+ antifeatures: Record<string, Record<string, string>>
312
+ author: string | null
313
+
314
+ /**
315
+ * Idk what this is, nothing I did changed this from any empty array
316
+ * and it's not documented anywhere
317
+ */
318
+ blockers: any[]
319
+
320
+ copyright: string | null
321
+ description: string | null
322
+ description_i18n: Record<string, string>
323
+ downloadURL: string | null
324
+ evilness: number
325
+ excludes: string[]
326
+ grant: string[]
327
+ header: string
328
+ homepage: string | null
329
+ icon: string | null
330
+ icon64: string | null
331
+ includes: string[]
332
+ lastModified: number
333
+ matches: string[]
334
+ name: string
335
+ name_i18n: Record<string, string>
336
+ namespace: string | null
337
+ options: ScriptSettings
338
+
339
+ position: number
340
+ resources: ScriptResource[]
341
+
342
+ /**
343
+ * Never null, defaults to document-idle
344
+ */
345
+ 'run-at': string
346
+
347
+ supportURL: string | null
348
+ sync: {
349
+ imported: boolean
350
+ }
351
+ unwrap: boolean
352
+ updateURL: string | null
353
+ uuid: string
354
+ version: string
355
+ webRequest: string[]
356
+ }
357
+
358
+ interface ScriptInfo {
359
+ downloadMode: 'native' | 'browser' | 'disabled'
360
+ isFirstPartyIsolation: boolean | undefined
361
+ isIncognito: boolean
362
+ script: ScriptMetadata
363
+
364
+ /**
365
+ * In tampermonkey it's "Tampermonkey"
366
+ * but I'll leave it as string so this can be used
367
+ * for other managers
368
+ */
369
+ scriptHandler: string
370
+
371
+ scriptMetaStr: string
372
+ scriptSource: string
373
+ scriptUpdateURL: string | undefined
374
+ scriptWillUpdate: boolean
375
+
376
+ /** This refers to tampermonkey's version */
377
+ version: string
378
+ }
379
+
380
+ type ContentType =
381
+ | string
382
+ | { type?: string | undefined; mimetype?: string | undefined }
383
+ }
384
+
385
+ /**
386
+ * The unsafeWindow object provides full access to the pages javascript
387
+ * functions and variables
388
+ */
389
+ declare var unsafeWindow: Window
390
+
391
+ // Styles
392
+
393
+ /**
394
+ * Adds the given style to the document and returns the injected style element.
395
+ */
396
+ declare function GM_addStyle(css: string): HTMLStyleElement
397
+
398
+ // Storage
399
+
400
+ /** Sets the value of `name` to the storage */
401
+ declare function GM_setValue(name: string, value: any): void
402
+
403
+ /**
404
+ * Adds a change listener to the storage and returns the listener ID.
405
+ * The `remote` argument of the callback function shows whether this value was
406
+ * modified from the instance of another tab (`true`) or within this script
407
+ * instance (`false`). Therefore this functionality can be used by scripts of
408
+ * different browser tabs to communicate with each other.
409
+ * @param name Name of the observed variable
410
+ */
411
+ declare function GM_addValueChangeListener(
412
+ name: string,
413
+ listener: Tampermonkey.ValueChangeListener
414
+ ): number
415
+
416
+ /** Removes a change listener by its ID */
417
+ declare function GM_removeValueChangeListener(listenerId: number): void
418
+
419
+ /** Gets the value of 'name' from storage */
420
+ declare function GM_getValue<TValue>(
421
+ name: string,
422
+ defaultValue?: TValue
423
+ ): TValue
424
+
425
+ /** Deletes 'name' from storage */
426
+ declare function GM_deleteValue(name: string): void
427
+
428
+ /** Lists all names of the storage */
429
+ declare function GM_listValues(): string[]
430
+
431
+ // Resources
432
+
433
+ /** Get the content of a predefined `@resource` tag at the script header */
434
+ declare function GM_getResourceText(name: string): string
435
+
436
+ /**
437
+ * Get the base64 encoded URI of a predefined `@resource` tag at the script
438
+ * header
439
+ */
440
+ declare function GM_getResourceURL(name: string): string
441
+
442
+ // Menu commands
443
+
444
+ /**
445
+ * Register a menu to be displayed at the Tampermonkey menu at pages where this
446
+ * script runs and returns a menu command ID.
447
+ */
448
+ declare function GM_registerMenuCommand(
449
+ name: string,
450
+ onClick: () => void,
451
+ accessKey?: string
452
+ ): number
453
+
454
+ /**
455
+ * Unregister a menu command that was previously registered by
456
+ * `GM_registerMenuCommand` or `GM.registerMenuCommand` with the given menu command ID.
457
+ */
458
+ declare function GM_unregisterMenuCommand(menuCommandId: number): void
459
+
460
+ // Requests
461
+
462
+ /** Makes an xmlHttpRequest */
463
+ declare function GM_xmlhttpRequest<TContext = any>(
464
+ details: Tampermonkey.Request<TContext> // tslint:disable-line:no-unnecessary-generics
465
+ ): Tampermonkey.AbortHandle<void>
466
+
467
+ /** Downloads a given URL to the local disk */
468
+ declare function GM_download(
469
+ details: Tampermonkey.DownloadRequest
470
+ ): Tampermonkey.AbortHandle<boolean>
471
+ declare function GM_download(
472
+ url: string,
473
+ name: string
474
+ ): Tampermonkey.AbortHandle<boolean>
475
+
476
+ // Tabs
477
+
478
+ /** Saves the tab object to reopen it after a page unload */
479
+ declare function GM_saveTab(obj: object): void
480
+
481
+ /** Gets a object that is persistent as long as this tab is open */
482
+ declare function GM_getTab(callback: (obj: any) => void): void
483
+
484
+ /** Gets all tab objects as a hash to communicate with other script instances */
485
+ declare function GM_getTabs(
486
+ callback: (tabsMap: { [tabId: number]: any }) => void
487
+ ): void
488
+
489
+ // Utils
490
+
491
+ declare const GM_info: Tampermonkey.ScriptInfo
492
+
493
+ /** Log a message to the console */
494
+ declare function GM_log(...message: any[]): void
495
+
496
+ /**
497
+ * Opens a new tab with this url.
498
+ * The options object can have the following properties:
499
+ * - `active` decides whether the new tab should be focused,
500
+ * - `insert` that inserts the new tab after the current one and
501
+ * - `setParent` makes the browser re-focus the current tab on close.
502
+ *
503
+ * Otherwise the new tab is just appended.
504
+ * If `options` is boolean (loadInBackground) it has the opposite meaning of
505
+ * active and was added to achieve Greasemonkey 3.x compatibility.
506
+ *
507
+ * If neither active nor loadInBackground is given, then the tab will not be
508
+ * focused.
509
+ * @returns Object with the function `close`, the listener `onclose` and a flag
510
+ * called `closed`.
511
+ */
512
+ declare function GM_openInTab(
513
+ url: string,
514
+ options?: Tampermonkey.OpenTabOptions | boolean
515
+ ): Tampermonkey.OpenTabObject
516
+
517
+ /**
518
+ * Shows a HTML5 Desktop notification and/or highlight the current tab.
519
+ * @param ondone If specified used instead of `details.ondone`
520
+ */
521
+ declare function GM_notification(
522
+ details: Tampermonkey.NotificationDetails,
523
+ ondone?: Tampermonkey.NotificationOnDone
524
+ ): void
525
+
526
+ /**
527
+ * Shows a HTML5 Desktop notification and/or highlight the current tab.
528
+ * @param text Text of the notification
529
+ * @param title Notification title. If not specified the script name is used
530
+ * @param onclick Called in case the user clicks the notification
531
+ */
532
+ declare function GM_notification(
533
+ text: string,
534
+ title?: string,
535
+ image?: string,
536
+ onclick?: Tampermonkey.NotificationOnClick
537
+ ): void
538
+
539
+ /**
540
+ * Copies data into the clipboard.
541
+ * The parameter 'info' can be an object like
542
+ * `{ type: 'text', mimetype: 'text/plain'}` or just a string expressing the
543
+ * type ("text" or "html").
544
+ */
545
+ declare function GM_setClipboard(
546
+ data: string,
547
+ info?: Tampermonkey.ContentType
548
+ ): void
549
+
550
+ // GM.*
551
+
552
+ /**
553
+ * `GM` has all the `GM_*` apis in promisified form
554
+ */
555
+ declare const GM: Readonly<{
556
+ // Styles
557
+
558
+ /**
559
+ * Adds the given style to the document and returns the injected style element.
560
+ */
561
+ addStyle(css: string): Promise<HTMLStyleElement>
562
+
563
+ // Storage
564
+
565
+ /** Sets the value of `name` to the storage */
566
+ setValue(name: string, value: any): Promise<void>
567
+
568
+ /** Gets the value of 'name' from storage */
569
+ getValue<TValue>(name: string, defaultValue?: TValue): Promise<TValue>
570
+
571
+ /** Deletes 'name' from storage */
572
+ deleteValue(name: string): Promise<void>
573
+
574
+ /** Lists all names of the storage */
575
+ listValues(): Promise<string[]>
576
+
577
+ /**
578
+ * Adds a change listener to the storage and returns the listener ID.
579
+ * The `remote` argument of the callback function shows whether this value was
580
+ * modified from the instance of another tab (`true`) or within this script
581
+ * instance (`false`). Therefore this functionality can be used by scripts of
582
+ * different browser tabs to communicate with each other.
583
+ * @param name Name of the observed variable
584
+ */
585
+ addValueChangeListener(
586
+ name: string,
587
+ listener: Tampermonkey.ValueChangeListener
588
+ ): Promise<number>
589
+
590
+ /** Removes a change listener by its ID */
591
+ removeValueChangeListener(listenerId: number): Promise<void>
592
+
593
+ // Resources
594
+
595
+ /** Get the content of a predefined `@resource` tag at the script header */
596
+ getResourceText(name: string): Promise<string>
597
+
598
+ /**
599
+ * Get the base64 encoded URI of a predefined `@resource` tag at the script
600
+ * header
601
+ */
602
+ getResourceUrl(name: string): Promise<string>
603
+
604
+ // Menu commands
605
+
606
+ /**
607
+ * Register a menu to be displayed at the Tampermonkey menu at pages where this
608
+ * script runs and returns a menu command ID.
609
+ * @param accessKey The key to use for keyboard shortcuts
610
+ */
611
+ registerMenuCommand(
612
+ name: string,
613
+ onClick: () => void,
614
+ accessKey?: string
615
+ ): Promise<number>
616
+ /**
617
+ * Unregister a menu command that was previously registered by
618
+ * `GM_registerMenuCommand` or `GM.registerMenuCommand` with the given menu command ID.
619
+ */
620
+ unregisterMenuCommand(menuCommandId: number): Promise<void>
621
+
622
+ // Requests
623
+
624
+ /**
625
+ * Makes an xmlHttpRequest
626
+ *
627
+ * @throws {Tampermonkey.ErrorResponse}
628
+ */
629
+ xmlHttpRequest<TContext = any>(
630
+ // onload and the like still work
631
+ details: Tampermonkey.Request<TContext> // tslint:disable-line:no-unnecessary-generics
632
+ ): Promise<Tampermonkey.Response<TContext>>
633
+
634
+ // GM_download has two signatures, GM.download has one
635
+ /**
636
+ * Downloads a given URL to the local disk
637
+ *
638
+ * @throws {Tampermonkey.DownloadErrorResponse}
639
+ */
640
+ download(details: Tampermonkey.DownloadRequest): Promise<void>
641
+
642
+ // Tabs
643
+
644
+ /** Saves the tab object to reopen it after a page unload */
645
+ saveTab(obj: any): Promise<void>
646
+
647
+ /** Gets a object that is persistent as long as this tab is open */
648
+ getTab(): Promise<any>
649
+
650
+ /** Gets all tab objects as a hash to communicate with other script instances */
651
+ getTabs(): Promise<{ [tabId: number]: any }>
652
+
653
+ // Utils
654
+ info: Tampermonkey.ScriptInfo
655
+
656
+ /** Log a message to the console */
657
+ log(...message: any[]): Promise<void>
658
+
659
+ /**
660
+ * Opens a new tab with this url.
661
+ * The options object can have the following properties:
662
+ * - `active` decides whether the new tab should be focused,
663
+ * - `insert` that inserts the new tab after the current one and
664
+ * - `setParent` makes the browser re-focus the current tab on close.
665
+ *
666
+ * Otherwise the new tab is just appended.
667
+ * If `options` is boolean (loadInBackground) it has the opposite meaning of
668
+ * active and was added to achieve Greasemonkey 3.x compatibility.
669
+ *
670
+ * If neither active nor loadInBackground is given, then the tab will not be
671
+ * focused.
672
+ * @returns Object with the function `close`, the listener `onclose` and a flag
673
+ * called `closed`.
674
+ */
675
+ openInTab(
676
+ url: string,
677
+ options?: Tampermonkey.OpenTabOptions | boolean
678
+ ): Promise<Tampermonkey.OpenTabObject>
679
+
680
+ /**
681
+ * Shows a HTML5 Desktop notification and/or highlight the current tab.
682
+ * @param ondone If specified used instead of `details.ondone`
683
+ * @returns True if the notification was clicked
684
+ */
685
+ notification(
686
+ details: Tampermonkey.NotificationDetails,
687
+ ondone?: Tampermonkey.NotificationOnDone
688
+ ): Promise<boolean>
689
+
690
+ /**
691
+ * Shows a HTML5 Desktop notification and/or highlight the current tab.
692
+ * @param text Text of the notification
693
+ * @param title Notification title. If not specified the script name is used
694
+ * @param onclick Called in case the user clicks the notification
695
+ * @returns True if the notification was clicked
696
+ */
697
+ notification(
698
+ text: string,
699
+ title?: string,
700
+ image?: string,
701
+ onclick?: Tampermonkey.NotificationOnClick
702
+ ): Promise<boolean>
703
+
704
+ /**
705
+ * Copies data into the clipboard.
706
+ * The parameter 'info' can be an object like
707
+ * `{ type: 'text', mimetype: 'text/plain'}` or just a string expressing the
708
+ * type ("text" or "html").
709
+ */
710
+ setClipboard(data: string, info?: Tampermonkey.ContentType): Promise<void>
711
+ }>