chore: add Typescript support

Fixed: twpowertools:198
Change-Id: I00a14ab8ad93e9ff8d2a4d2e33824a9290500a8a
diff --git a/.prettierrc.yaml b/.prettierrc.yaml
new file mode 100644
index 0000000..040276d
--- /dev/null
+++ b/.prettierrc.yaml
@@ -0,0 +1,2 @@
+semi: true
+singleQuote: true
diff --git a/package-lock.json b/package-lock.json
index cafd0b0..daa055c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,6 +16,7 @@
         "@material/tooltip": "^14.0.0",
         "@material/web": "^1.0.0",
         "@stdlib/utils-escape-regexp-string": "^0.2.0",
+        "@types/chrome": "^0.0.267",
         "async-mutex": "^0.5.0",
         "dompurify": "^3.0.0",
         "google-protobuf": "^3.19.3",
@@ -41,6 +42,8 @@
         "sass": "1.77.0",
         "sass-loader": "14.2.1",
         "style-loader": "4.0.0",
+        "ts-loader": "^9.5.1",
+        "typescript": "^5.4.5",
         "webpack": "5.91.0",
         "webpack-cli": "5.1.4",
         "webpack-preprocessor-loader": "1.3.0",
@@ -1316,6 +1319,19 @@
         "lit-localize": "bin/lit-localize.js"
       }
     },
+    "node_modules/@lit/localize-tools/node_modules/typescript": {
+      "version": "5.3.3",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
+      "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
+      "dev": true,
+      "bin": {
+        "tsc": "bin/tsc",
+        "tsserver": "bin/tsserver"
+      },
+      "engines": {
+        "node": ">=14.17"
+      }
+    },
     "node_modules/@lit/reactive-element": {
       "version": "1.6.1",
       "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.6.1.tgz",
@@ -2785,6 +2801,15 @@
         "@babel/types": "^7.20.7"
       }
     },
+    "node_modules/@types/chrome": {
+      "version": "0.0.267",
+      "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.267.tgz",
+      "integrity": "sha512-vnCWPpYjazSPRMNmybRH+0q4f738F+Pbbls4ZPFsPr9/4TTNJyK1OLZDpSnghnEWb4stfmIUtq/GegnlfD4sPA==",
+      "dependencies": {
+        "@types/filesystem": "*",
+        "@types/har-format": "*"
+      }
+    },
     "node_modules/@types/eslint": {
       "version": "8.37.0",
       "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.37.0.tgz",
@@ -2811,6 +2836,19 @@
       "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
       "dev": true
     },
+    "node_modules/@types/filesystem": {
+      "version": "0.0.36",
+      "resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.36.tgz",
+      "integrity": "sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==",
+      "dependencies": {
+        "@types/filewriter": "*"
+      }
+    },
+    "node_modules/@types/filewriter": {
+      "version": "0.0.33",
+      "resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.33.tgz",
+      "integrity": "sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g=="
+    },
     "node_modules/@types/graceful-fs": {
       "version": "4.1.9",
       "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz",
@@ -2820,6 +2858,11 @@
         "@types/node": "*"
       }
     },
+    "node_modules/@types/har-format": {
+      "version": "1.2.15",
+      "resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.15.tgz",
+      "integrity": "sha512-RpQH4rXLuvTXKR0zqHq3go0RVXYv/YVqv4TnPH95VbwUxZdQlK1EtcMvQvMpDngHbt13Csh9Z4qT9AbkiQH5BA=="
+    },
     "node_modules/@types/istanbul-lib-coverage": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
@@ -7563,6 +7606,35 @@
         "node": ">=12"
       }
     },
+    "node_modules/ts-loader": {
+      "version": "9.5.1",
+      "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz",
+      "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==",
+      "dev": true,
+      "dependencies": {
+        "chalk": "^4.1.0",
+        "enhanced-resolve": "^5.0.0",
+        "micromatch": "^4.0.0",
+        "semver": "^7.3.4",
+        "source-map": "^0.7.4"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      },
+      "peerDependencies": {
+        "typescript": "*",
+        "webpack": "^5.0.0"
+      }
+    },
+    "node_modules/ts-loader/node_modules/source-map": {
+      "version": "0.7.4",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
+      "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
+      "dev": true,
+      "engines": {
+        "node": ">= 8"
+      }
+    },
     "node_modules/tslib": {
       "version": "2.4.0",
       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
@@ -7590,9 +7662,9 @@
       }
     },
     "node_modules/typescript": {
-      "version": "5.3.3",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
-      "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
+      "version": "5.4.5",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
+      "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
       "dev": true,
       "bin": {
         "tsc": "bin/tsc",
@@ -9068,6 +9140,14 @@
         "parse5": "^7.1.1",
         "source-map-support": "^0.5.19",
         "typescript": "~5.3.3"
+      },
+      "dependencies": {
+        "typescript": {
+          "version": "5.3.3",
+          "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
+          "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
+          "dev": true
+        }
       }
     },
     "@lit/reactive-element": {
@@ -10304,6 +10384,15 @@
         "@babel/types": "^7.20.7"
       }
     },
+    "@types/chrome": {
+      "version": "0.0.267",
+      "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.267.tgz",
+      "integrity": "sha512-vnCWPpYjazSPRMNmybRH+0q4f738F+Pbbls4ZPFsPr9/4TTNJyK1OLZDpSnghnEWb4stfmIUtq/GegnlfD4sPA==",
+      "requires": {
+        "@types/filesystem": "*",
+        "@types/har-format": "*"
+      }
+    },
     "@types/eslint": {
       "version": "8.37.0",
       "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.37.0.tgz",
@@ -10330,6 +10419,19 @@
       "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
       "dev": true
     },
+    "@types/filesystem": {
+      "version": "0.0.36",
+      "resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.36.tgz",
+      "integrity": "sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==",
+      "requires": {
+        "@types/filewriter": "*"
+      }
+    },
+    "@types/filewriter": {
+      "version": "0.0.33",
+      "resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.33.tgz",
+      "integrity": "sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g=="
+    },
     "@types/graceful-fs": {
       "version": "4.1.9",
       "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz",
@@ -10339,6 +10441,11 @@
         "@types/node": "*"
       }
     },
+    "@types/har-format": {
+      "version": "1.2.15",
+      "resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.15.tgz",
+      "integrity": "sha512-RpQH4rXLuvTXKR0zqHq3go0RVXYv/YVqv4TnPH95VbwUxZdQlK1EtcMvQvMpDngHbt13Csh9Z4qT9AbkiQH5BA=="
+    },
     "@types/istanbul-lib-coverage": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz",
@@ -13846,6 +13953,27 @@
         "punycode": "^2.1.1"
       }
     },
+    "ts-loader": {
+      "version": "9.5.1",
+      "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz",
+      "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==",
+      "dev": true,
+      "requires": {
+        "chalk": "^4.1.0",
+        "enhanced-resolve": "^5.0.0",
+        "micromatch": "^4.0.0",
+        "semver": "^7.3.4",
+        "source-map": "^0.7.4"
+      },
+      "dependencies": {
+        "source-map": {
+          "version": "0.7.4",
+          "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz",
+          "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==",
+          "dev": true
+        }
+      }
+    },
     "tslib": {
       "version": "2.4.0",
       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz",
@@ -13867,9 +13995,9 @@
       "dev": true
     },
     "typescript": {
-      "version": "5.3.3",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
-      "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
+      "version": "5.4.5",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
+      "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
       "dev": true
     },
     "unicorn-magic": {
diff --git a/package.json b/package.json
index c39a172..162e67a 100644
--- a/package.json
+++ b/package.json
@@ -39,6 +39,8 @@
     "sass": "1.77.0",
     "sass-loader": "14.2.1",
     "style-loader": "4.0.0",
+    "ts-loader": "^9.5.1",
+    "typescript": "^5.4.5",
     "webpack": "5.91.0",
     "webpack-cli": "5.1.4",
     "webpack-preprocessor-loader": "1.3.0",
@@ -53,6 +55,7 @@
     "@material/tooltip": "^14.0.0",
     "@material/web": "^1.0.0",
     "@stdlib/utils-escape-regexp-string": "^0.2.0",
+    "@types/chrome": "^0.0.267",
     "async-mutex": "^0.5.0",
     "dompurify": "^3.0.0",
     "google-protobuf": "^3.19.3",
diff --git a/src/common/actionApi.js b/src/common/actionApi.js
index 0cf8528..dc22691 100644
--- a/src/common/actionApi.js
+++ b/src/common/actionApi.js
@@ -1,5 +1,9 @@
+let actionApi;
+
 // #!if browser_target == 'chromium_mv3'
-export default typeof chrome !== 'undefined' ? chrome.action : undefined;
+actionApi = typeof chrome !== 'undefined' ? chrome.action : undefined;
 // #!else
-export default typeof chrome !== 'undefined' ? chrome.browserAction : undefined;
+actionApi = typeof chrome !== 'undefined' ? chrome.browserAction : undefined;
 // #!endif
+
+export default actionApi;
diff --git a/src/common/api.js b/src/common/api.js
index 11c0ad6..8e0c19b 100644
--- a/src/common/api.js
+++ b/src/common/api.js
@@ -28,13 +28,14 @@
   let authuserPart =
       authuser == '0' ? '' : '?authuser=' + encodeURIComponent(authuser);
 
+  let context;
   // #!if browser_target == 'gecko'
   // See
   // https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Content_scripts#xhr_and_fetch
   // and https://developer.mozilla.org/en-US/docs/Web/API/Window/content.
-  const context = window.content || window;
+  context = window.content || window;
   // #!else
-  const context = window;
+  context = window;
   // #!endif
 
   return context
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..3c671ac
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,12 @@
+{
+  "compilerOptions": {
+    "sourceMap": true,
+    "noImplicitAny": true,
+    "noImplicitReturns": true,
+    "noFallthroughCasesInSwitch": true,
+    "module": "es6",
+    "target": "es2019",
+    "moduleResolution": "bundler",
+    "allowJs": true
+  }
+}
diff --git a/webpack.config.js b/webpack.config.js
index 0254055..8e92f27 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -145,9 +145,26 @@
       ...getCopyPluginsForOverridenLocales(outputPath),
     ],
     devtool: (args.mode == 'production' ? 'source-map' : 'inline-source-map'),
+    resolve: {
+      extensions: ['.tsx', '.ts', '.js', '.json', '.wasm'],
+    },
     module: {
       rules: [
         {
+          test: /\.js$/i,
+          use: [
+            preprocessorLoader,
+          ],
+        },
+        {
+          test: /\.tsx?$/,
+          use: [
+            'ts-loader',
+            preprocessorLoader,
+          ],
+          exclude: /node_modules/,
+        },
+        {
           test: /\.json5$/i,
           type: 'json',
           parser: {
@@ -202,12 +219,6 @@
             },
           ],
         },
-        {
-          test: /\.js$/i,
-          use: [
-            preprocessorLoader,
-          ],
-        },
       ]
     },
     optimization: {