diff --git a/browser-extensions/chrome/package-lock.json b/browser-extensions/chrome/package-lock.json index 27db1a348..40f93ee3a 100644 --- a/browser-extensions/chrome/package-lock.json +++ b/browser-extensions/chrome/package-lock.json @@ -18,7 +18,8 @@ "react-dom": "^19.0.0", "react-router-dom": "^7.1.4", "secure-remote-password": "github:LinusU/secure-remote-password#73e5f732b6ca0cdbdc19da1a0c5f48cdbad2cbf0", - "sql.js": "^1.12.0" + "sql.js": "^1.12.0", + "vitest": "^3.0.4" }, "devDependencies": { "@types/react": "^19.0.7", @@ -366,7 +367,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -383,7 +383,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -400,7 +399,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -417,7 +415,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -434,7 +431,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -451,7 +447,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -468,7 +463,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -485,7 +479,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -502,7 +495,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -519,7 +511,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -536,7 +527,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -553,7 +543,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -570,7 +559,6 @@ "cpu": [ "mips64el" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -587,7 +575,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -604,7 +591,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -621,7 +607,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -638,7 +623,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -655,7 +639,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -672,7 +655,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -689,7 +671,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -706,7 +687,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -723,7 +703,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -740,7 +719,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -757,7 +735,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -774,7 +751,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1065,7 +1041,6 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { @@ -1159,7 +1134,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1173,7 +1147,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1187,7 +1160,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1201,7 +1173,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1215,7 +1186,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1229,7 +1199,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1243,7 +1212,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1257,7 +1225,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1271,7 +1238,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1285,7 +1251,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1299,7 +1264,6 @@ "cpu": [ "loong64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1313,7 +1277,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1327,7 +1290,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1341,7 +1303,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1355,7 +1316,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1369,7 +1329,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1383,7 +1342,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1397,7 +1355,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1411,7 +1368,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -1528,7 +1484,7 @@ "version": "22.10.10", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.10.tgz", "integrity": "sha512-X47y/mPNzxviAGY5TcYPtYL8JsY3kAq2n8fMmKoRCxq/c4v4pyGNCzM2R6+M5/umG4ZfHuT+sgqDYqWc9rJ6ww==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "undici-types": "~6.20.0" @@ -1791,6 +1747,112 @@ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" } }, + "node_modules/@vitest/expect": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.4.tgz", + "integrity": "sha512-Nm5kJmYw6P2BxhJPkO3eKKhGYKRsnqJqf+r0yOGRKpEP+bSCBDsjXgiu1/5QFrnPMEgzfC38ZEjvCFgaNBC0Eg==", + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.0.4", + "@vitest/utils": "3.0.4", + "chai": "^5.1.2", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.4.tgz", + "integrity": "sha512-gEef35vKafJlfQbnyOXZ0Gcr9IBUsMTyTLXsEQwuyYAerpHqvXhzdBnDFuHLpFqth3F7b6BaFr4qV/Cs1ULx5A==", + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.0.4", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.4.tgz", + "integrity": "sha512-ts0fba+dEhK2aC9PFuZ9LTpULHpY/nd6jhAQ5IMU7Gaj7crPCTdCFfgvXxruRBLFS+MLraicCuFXxISEq8C93g==", + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.4.tgz", + "integrity": "sha512-dKHzTQ7n9sExAcWH/0sh1elVgwc7OJ2lMOBrAm73J7AH6Pf9T12Zh3lNE1TETZaqrWFXtLlx3NVrLRb5hCK+iw==", + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.0.4", + "pathe": "^2.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.4.tgz", + "integrity": "sha512-+p5knMLwIk7lTQkM3NonZ9zBewzVp9EVkVpvNta0/PlFWpiqLaRcF4+33L1it3uRUCh0BGLOaXPPGEjNKfWb4w==", + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.0.4", + "magic-string": "^0.30.17", + "pathe": "^2.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.4.tgz", + "integrity": "sha512-sXIMF0oauYyUy2hN49VFTYodzEAu744MmGcPR3ZBsPM20G+1/cSW/n1U+3Yu/zHxX2bIDe1oJASOkml+osTU6Q==", + "license": "MIT", + "dependencies": { + "tinyspy": "^3.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.4.tgz", + "integrity": "sha512-8BqC1ksYsHtbWH+DfpOAKrFw3jl3Uf9J7yeFh85Pz52IWuh1hBBtyfEbRNNZNjl8H8A5yMLH9/t+k7HIKzQcZQ==", + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.0.4", + "loupe": "^3.1.2", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/acorn": { "version": "8.14.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", @@ -2066,6 +2128,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/async-function": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", @@ -2255,6 +2326,15 @@ "ieee754": "^1.2.1" } }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/call-bind": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", @@ -2357,6 +2437,22 @@ "node": ">= 10" } }, + "node_modules/chai": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.2.tgz", + "integrity": "sha512-aGtmf24DW6MLHHG5gCx4zaI3uBq3KRtxeVs0DjFH6Z0rDNbsvTxFASFvdj79pxjxZ8/5u3PIiN3IwEIQkiiuPw==", + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -2388,6 +2484,15 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/check-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -2741,6 +2846,15 @@ } } }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -3045,7 +3159,6 @@ "version": "0.24.2", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz", "integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==", - "dev": true, "hasInstallScript": true, "license": "MIT", "bin": { @@ -3530,6 +3643,15 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -3539,6 +3661,15 @@ "node": ">=0.10.0" } }, + "node_modules/expect-type": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.1.0.tgz", + "integrity": "sha512-bFi65yM+xZgk+u/KRIpekdSYkTB5W1pEf0Lt8Q8Msh7b+eQ7LXVtIB1Bkm4fvclDEL1b2CZkMhv2mOeF8tMdkA==", + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3723,7 +3854,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -4818,6 +4948,12 @@ "loose-envify": "cli.js" } }, + "node_modules/loupe": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", + "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", + "license": "MIT" + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -4828,6 +4964,15 @@ "yallist": "^3.0.2" } }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, "node_modules/markdown-it": { "version": "14.1.0", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", @@ -4977,7 +5122,6 @@ "version": "3.3.8", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", - "dev": true, "funding": [ { "type": "github", @@ -5309,11 +5453,25 @@ "dev": true, "license": "ISC" }, + "node_modules/pathe": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.2.tgz", + "integrity": "sha512-15Ztpk+nov8DR524R4BF7uEuzESgzUEAV4Ah7CUMNGXdE5ELuvxElxGXndBl32vMSsWa1jpNf22Z+Er3sKwq+w==", + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", + "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, "license": "ISC" }, "node_modules/picomatch": { @@ -5363,7 +5521,6 @@ "version": "8.5.1", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", - "dev": true, "funding": [ { "type": "opencollective", @@ -5778,7 +5935,6 @@ "version": "4.31.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.31.0.tgz", "integrity": "sha512-9cCE8P4rZLx9+PjoyqHLs31V9a9Vpvfo4qNcs6JCiGWYhw2gijSetFbH6SSy1whnkgcefnUwr8sad7tgqsGvnw==", - "dev": true, "license": "MIT", "dependencies": { "@types/estree": "1.0.6" @@ -6098,6 +6254,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "license": "ISC" + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -6121,7 +6283,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -6155,6 +6316,18 @@ "integrity": "sha512-Bi+43yMx/tUFZVYD4AUscmdL6NHn3gYQ+CM+YheFWLftOmrEC/Mz6Yh7E96Y2WDHYz3COSqT+LP6Z79zgrwJlA==", "license": "MIT" }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.8.0.tgz", + "integrity": "sha512-Bc3YwwCB+OzldMxOXJIIvC6cPRWr/LxOp48CdQTOkPyk/t4JWWJbrilwBd7RJzKV8QW7tJkcgAmeuLLJugl5/w==", + "license": "MIT" + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -6504,6 +6677,45 @@ "node": ">=0.8" } }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "license": "MIT" + }, + "node_modules/tinypool": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", + "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -6724,7 +6936,7 @@ "version": "6.20.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/universalify": { @@ -6788,7 +7000,6 @@ "version": "6.0.11", "resolved": "https://registry.npmjs.org/vite/-/vite-6.0.11.tgz", "integrity": "sha512-4VL9mQPKoHy4+FE0NnRE/kbY51TOfaknxAjt3fJbGJxhIpBZiqVzlZDEesWWsuREXHwNdAoOFZ9MkPEVXczHwg==", - "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.24.2", @@ -6856,6 +7067,28 @@ } } }, + "node_modules/vite-node": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.4.tgz", + "integrity": "sha512-7JZKEzcYV2Nx3u6rlvN8qdo3QV7Fxyt6hx+CCKz9fbWxdX5IvUOmTWEAxMrWxaiSf7CKGLJQ5rFu8prb/jBjOA==", + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.0", + "es-module-lexer": "^1.6.0", + "pathe": "^2.0.2", + "vite": "^5.0.0 || ^6.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, "node_modules/vite-plugin-static-copy": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/vite-plugin-static-copy/-/vite-plugin-static-copy-2.2.0.tgz", @@ -6875,6 +7108,75 @@ "vite": "^5.0.0 || ^6.0.0" } }, + "node_modules/vitest": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.4.tgz", + "integrity": "sha512-6XG8oTKy2gnJIFTHP6LD7ExFeNLxiTkK3CfMvT7IfR8IN+BYICCf0lXUQmX7i7JoxUP8QmeP4mTnWXgflu4yjw==", + "license": "MIT", + "dependencies": { + "@vitest/expect": "3.0.4", + "@vitest/mocker": "3.0.4", + "@vitest/pretty-format": "^3.0.4", + "@vitest/runner": "3.0.4", + "@vitest/snapshot": "3.0.4", + "@vitest/spy": "3.0.4", + "@vitest/utils": "3.0.4", + "chai": "^5.1.2", + "debug": "^4.4.0", + "expect-type": "^1.1.0", + "magic-string": "^0.30.17", + "pathe": "^2.0.2", + "std-env": "^3.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinypool": "^1.0.2", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0", + "vite-node": "3.0.4", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.0.4", + "@vitest/ui": "3.0.4", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -6978,6 +7280,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -7112,7 +7430,7 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", - "dev": true, + "devOptional": true, "license": "ISC", "bin": { "yaml": "bin.mjs" diff --git a/browser-extensions/chrome/package.json b/browser-extensions/chrome/package.json index abe1f14d2..3206b141a 100644 --- a/browser-extensions/chrome/package.json +++ b/browser-extensions/chrome/package.json @@ -4,6 +4,7 @@ "main": "index.tsx", "type": "module", "scripts": { + "test": "vitest", "dev": "vite", "preview": "vite preview", "lint": "eslint src", @@ -26,7 +27,8 @@ "react-dom": "^19.0.0", "react-router-dom": "^7.1.4", "secure-remote-password": "github:LinusU/secure-remote-password#73e5f732b6ca0cdbdc19da1a0c5f48cdbad2cbf0", - "sql.js": "^1.12.0" + "sql.js": "^1.12.0", + "vitest": "^3.0.4" }, "devDependencies": { "@types/react": "^19.0.7", diff --git a/browser-extensions/chrome/src/generators/Identity/UsernameEmailGenerator.ts b/browser-extensions/chrome/src/generators/Identity/UsernameEmailGenerator.ts new file mode 100644 index 000000000..075b3e548 --- /dev/null +++ b/browser-extensions/chrome/src/generators/Identity/UsernameEmailGenerator.ts @@ -0,0 +1,102 @@ +import { Identity } from "./types/Identity"; + +export class UsernameEmailGenerator { + private static readonly MIN_LENGTH = 6; + private static readonly MAX_LENGTH = 20; + private readonly symbols: string[] = ['.', '-']; + + public generateUsername(identity: Identity): string { + // Generate username based on email prefix but strip all non-alphanumeric characters + let username = this.generateEmailPrefix(identity); + username = username.replace(/[^a-zA-Z0-9]/g, ''); + + // Adjust length + if (username.length < UsernameEmailGenerator.MIN_LENGTH) { + username += this.generateRandomString(UsernameEmailGenerator.MIN_LENGTH - username.length); + } else if (username.length > UsernameEmailGenerator.MAX_LENGTH) { + username = username.substring(0, UsernameEmailGenerator.MAX_LENGTH); + } + + return username; + } + + public generateEmailPrefix(identity: Identity): string { + const parts: string[] = []; + + switch (Math.floor(Math.random() * 4)) { + case 0: + // First initial + last name + parts.push(identity.firstName.substring(0, 1).toLowerCase() + identity.lastName.toLowerCase()); + break; + case 1: + // Full name + parts.push((identity.firstName + identity.lastName).toLowerCase()); + break; + case 2: + // First name + last initial + parts.push(identity.firstName.toLowerCase() + identity.lastName.substring(0, 1).toLowerCase()); + break; + case 3: + // First 3 chars of first name + last name + parts.push(identity.firstName.substring(0, Math.min(3, identity.firstName.length)).toLowerCase() + identity.lastName.toLowerCase()); + break; + } + + // Add birth year variations + if (Math.floor(Math.random() * 3) !== 0) { + switch (Math.floor(Math.random() * 2)) { + case 0: + parts.push(identity.birthDate.getFullYear().toString().substring(2)); + break; + case 1: + parts.push(identity.birthDate.getFullYear().toString()); + break; + } + } else if (Math.floor(Math.random() * 2) === 0) { + // Add random numbers for more uniqueness + parts.push((Math.floor(Math.random() * 990) + 10).toString()); + } + + // Join parts with random symbols, possibly multiple + let emailPrefix = parts.join(this.getRandomSymbol()); + + // Add extra random symbol at random position + if (Math.floor(Math.random() * 2) === 0) { + const position = Math.floor(Math.random() * emailPrefix.length); + emailPrefix = emailPrefix.slice(0, position) + this.getRandomSymbol() + emailPrefix.slice(position); + } + + emailPrefix = this.sanitizeEmailPrefix(emailPrefix); + + // Adjust length + if (emailPrefix.length < UsernameEmailGenerator.MIN_LENGTH) { + emailPrefix += this.generateRandomString(UsernameEmailGenerator.MIN_LENGTH - emailPrefix.length); + } else if (emailPrefix.length > UsernameEmailGenerator.MAX_LENGTH) { + emailPrefix = emailPrefix.substring(0, UsernameEmailGenerator.MAX_LENGTH); + } + + return emailPrefix; + } + + private sanitizeEmailPrefix(input: string): string { + // Remove any character that's not a letter, number, dot, underscore, or hyphen including special characters + let sanitized = input.replace(/[^a-zA-Z0-9._-]/g, ''); + + // Remove consecutive dots, underscores, or hyphens + sanitized = sanitized.replace(/[-_.]{2,}/g, (match) => match[0]); + + // Ensure it doesn't start or end with a dot, underscore, or hyphen + sanitized = sanitized.replace(/^[-._]+|[-._]+$/g, ''); + + return sanitized; + } + + private getRandomSymbol(): string { + return Math.floor(Math.random() * 3) === 0 ? this.symbols[Math.floor(Math.random() * this.symbols.length)] : ''; + } + + private generateRandomString(length: number): string { + const chars = 'abcdefghijklmnopqrstuvwxyz0123456789'; + return Array.from({ length }, () => chars.charAt(Math.floor(Math.random() * chars.length))).join(''); + } +} diff --git a/browser-extensions/chrome/src/generators/Identity/implementations/IdentityGeneratorEn.ts b/browser-extensions/chrome/src/generators/Identity/implementations/IdentityGeneratorEn.ts index 9eea6c0a8..a77677610 100644 --- a/browser-extensions/chrome/src/generators/Identity/implementations/IdentityGeneratorEn.ts +++ b/browser-extensions/chrome/src/generators/Identity/implementations/IdentityGeneratorEn.ts @@ -3,14 +3,14 @@ import * as path from 'path'; export class IdentityGeneratorEn extends BaseIdentityGenerator { protected getFirstNamesMaleFilePath(): string { - return path.join(__dirname, '../dictionaries/en/firstnames_male.txt'); + return path.join(__dirname, 'dictionaries/en/firstnames_male'); } protected getFirstNamesFemaleFilePath(): string { - return path.join(__dirname, '../dictionaries/en/firstnames_female.txt'); + return path.join(__dirname, 'dictionaries/en/firstnames_female'); } protected getLastNamesFilePath(): string { - return path.join(__dirname, '../dictionaries/en/lastnames.txt'); + return path.join(__dirname, 'dictionaries/en/lastnames'); } } diff --git a/browser-extensions/chrome/src/generators/Identity/implementations/IdentityGeneratorNl.ts b/browser-extensions/chrome/src/generators/Identity/implementations/IdentityGeneratorNl.ts new file mode 100644 index 000000000..8237b2e38 --- /dev/null +++ b/browser-extensions/chrome/src/generators/Identity/implementations/IdentityGeneratorNl.ts @@ -0,0 +1,16 @@ +import { BaseIdentityGenerator } from "./base/BaseIdentityGenerator"; +import * as path from 'path'; + +export class IdentityGeneratorNl extends BaseIdentityGenerator { + protected getFirstNamesMaleFilePath(): string { + return path.join(__dirname, 'dictionaries/nl/firstnames_male'); + } + + protected getFirstNamesFemaleFilePath(): string { + return path.join(__dirname, 'dictionaries/nl/firstnames_female'); + } + + protected getLastNamesFilePath(): string { + return path.join(__dirname, 'dictionaries/nl/lastnames'); + } +} diff --git a/browser-extensions/chrome/src/generators/Identity/implementations/__tests__/IdentityGenerator.test.ts b/browser-extensions/chrome/src/generators/Identity/implementations/__tests__/IdentityGenerator.test.ts new file mode 100644 index 000000000..7e72b565a --- /dev/null +++ b/browser-extensions/chrome/src/generators/Identity/implementations/__tests__/IdentityGenerator.test.ts @@ -0,0 +1,47 @@ +import { IdentityGeneratorEn } from '../IdentityGeneratorEn'; +import { IdentityGeneratorNl } from '../IdentityGeneratorNl'; +import { describe, it, expect } from 'vitest'; +import { IIdentityGenerator } from '../../types/IIdentityGenerator'; + +// Test factory function to run tests for each language implementation +const testIdentityGenerator = ( + language: string, + generator: IIdentityGenerator +) => { + describe(`IdentityGenerator${language}`, () => { + describe('generateRandomIdentity', () => { + it('should generate a random gender identity when no gender is specified', async () => { + const identity = await generator.generateRandomIdentity(); + + expect(identity).toBeDefined(); + expect(identity.firstName).toBeTruthy(); + expect(identity.lastName).toBeTruthy(); + expect(['Male', 'Female']).toContain(identity.gender); + }); + + it('should generate unique identities on subsequent calls', async () => { + const identity1 = await generator.generateRandomIdentity(); + const identity2 = await generator.generateRandomIdentity(); + + expect(identity1).not.toEqual(identity2); + }); + + it('should generate an identity with all non-empty fields', async () => { + const identity = await generator.generateRandomIdentity(); + + Object.entries(identity).forEach(([key, value]) => { + expect(value).toBeTruthy(); + expect(value).not.toBe(''); + expect(value).not.toBeNull(); + expect(value).not.toBeUndefined(); + }); + }); + }); + }); +}; + +// Run tests for each language implementation +describe('Identity Generators', () => { + testIdentityGenerator('En', new IdentityGeneratorEn()); + testIdentityGenerator('Nl', new IdentityGeneratorNl()); +}); \ No newline at end of file diff --git a/browser-extensions/chrome/src/generators/Identity/implementations/base/BaseIdentityGenerator.ts b/browser-extensions/chrome/src/generators/Identity/implementations/base/BaseIdentityGenerator.ts index 2635efa7e..de0859be0 100644 --- a/browser-extensions/chrome/src/generators/Identity/implementations/base/BaseIdentityGenerator.ts +++ b/browser-extensions/chrome/src/generators/Identity/implementations/base/BaseIdentityGenerator.ts @@ -1,8 +1,8 @@ +import { UsernameEmailGenerator } from '../../UsernameEmailGenerator'; import { Gender } from '../../types/Gender'; import { IIdentityGenerator } from '../../types/IIdentityGenerator'; import { Identity } from '../../types/Identity'; import * as fs from 'fs'; -import * as path from 'path'; export abstract class BaseIdentityGenerator implements IIdentityGenerator { private firstNamesMale: string[] = [];