diff --git a/.gitignore b/.gitignore
index fd3dbb5..f1bc6b7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -34,3 +34,6 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
+
+# coverage
+.coverage
\ No newline at end of file
diff --git a/eslint.config.mjs b/eslint.config.mjs
index 6597839..e74d731 100644
--- a/eslint.config.mjs
+++ b/eslint.config.mjs
@@ -24,5 +24,11 @@ export default [
rules: {
"@typescript-eslint/consistent-type-assertions": "off"
}
+ },
+ {
+ files: ["test/**/*.spec.ts"],
+ rules: {
+ "max-nested-callbacks": "off"
+ }
}
]
\ No newline at end of file
diff --git a/package.json b/package.json
index e8f91f0..87091dd 100644
--- a/package.json
+++ b/package.json
@@ -7,7 +7,8 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
- "lint": "eslint src --max-warnings 0"
+ "lint": "eslint src test --max-warnings 0",
+ "test": "vitest run --coverage"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "6.6.0",
@@ -25,9 +26,12 @@
"@types/node": "22.8.4",
"@types/react": "18.3.12",
"@types/react-dom": "18.3.1",
+ "@vitest/coverage-istanbul": "2.1.4",
"eslint": "9.13.0",
+ "jsdom": "25.0.1",
"postcss": "8.4.47",
"tailwindcss": "3.4.14",
- "typescript": "5.6.3"
+ "typescript": "5.6.3",
+ "vitest": "2.1.4"
}
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 2ddfdab..7f1e355 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -22,10 +22,10 @@ importers:
version: 0.2.2(@fortawesome/fontawesome-svg-core@6.6.0)(react@18.3.1)
next:
specifier: 15.0.2
- version: 15.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ version: 15.0.2(@babel/core@7.26.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
next-plausible:
specifier: 3.12.2
- version: 3.12.2(next@15.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ version: 3.12.2(next@15.0.2(@babel/core@7.26.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react:
specifier: 18.3.1
version: 18.3.1
@@ -35,10 +35,16 @@ importers:
devDependencies:
'@nhcarrigan/eslint-config':
specifier: 5.0.0-rc2
- version: 5.0.0-rc2(@typescript-eslint/utils@8.12.2(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.13.0(jiti@1.21.6))(playwright@1.48.2)(prettier@3.3.3)(react@18.3.1)(typescript@5.6.3)(vitest@2.1.4(@types/node@22.8.4))
+ version: 5.0.0-rc2(@typescript-eslint/utils@8.12.2(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.13.0(jiti@1.21.6))(playwright@1.48.2)(prettier@3.3.3)(react@18.3.1)(typescript@5.6.3)(vitest@2.1.4(@types/node@22.8.4)(jsdom@25.0.1))
'@nhcarrigan/typescript-config':
specifier: 4.0.0
version: 4.0.0(typescript@5.6.3)
+ '@testing-library/dom':
+ specifier: 10.4.0
+ version: 10.4.0
+ '@testing-library/react':
+ specifier: 16.0.1
+ version: 16.0.1(@testing-library/dom@10.4.0)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@types/node':
specifier: 22.8.4
version: 22.8.4
@@ -48,9 +54,18 @@ importers:
'@types/react-dom':
specifier: 18.3.1
version: 18.3.1
+ '@vitejs/plugin-react':
+ specifier: 4.3.3
+ version: 4.3.3(vite@5.4.10(@types/node@22.8.4))
+ '@vitest/coverage-istanbul':
+ specifier: 2.1.4
+ version: 2.1.4(vitest@2.1.4(@types/node@22.8.4)(jsdom@25.0.1))
eslint:
specifier: 9.13.0
version: 9.13.0(jiti@1.21.6)
+ jsdom:
+ specifier: 25.0.1
+ version: 25.0.1
postcss:
specifier: 8.4.47
version: 8.4.47
@@ -60,6 +75,9 @@ importers:
typescript:
specifier: 5.6.3
version: 5.6.3
+ vitest:
+ specifier: 2.1.4
+ version: 2.1.4(@types/node@22.8.4)(jsdom@25.0.1)
packages:
@@ -67,14 +85,93 @@ packages:
resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
engines: {node: '>=10'}
+ '@ampproject/remapping@2.3.0':
+ resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==}
+ engines: {node: '>=6.0.0'}
+
'@babel/code-frame@7.26.2':
resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==}
engines: {node: '>=6.9.0'}
+ '@babel/compat-data@7.26.2':
+ resolution: {integrity: sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/core@7.26.0':
+ resolution: {integrity: sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/generator@7.26.2':
+ resolution: {integrity: sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-compilation-targets@7.25.9':
+ resolution: {integrity: sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-imports@7.25.9':
+ resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-transforms@7.26.0':
+ resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-plugin-utils@7.25.9':
+ resolution: {integrity: sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-string-parser@7.25.9':
+ resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==}
+ engines: {node: '>=6.9.0'}
+
'@babel/helper-validator-identifier@7.25.9':
resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==}
engines: {node: '>=6.9.0'}
+ '@babel/helper-validator-option@7.25.9':
+ resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helpers@7.26.0':
+ resolution: {integrity: sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/parser@7.26.2':
+ resolution: {integrity: sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/plugin-transform-react-jsx-self@7.25.9':
+ resolution: {integrity: sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-react-jsx-source@7.25.9':
+ resolution: {integrity: sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/runtime@7.26.0':
+ resolution: {integrity: sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/template@7.25.9':
+ resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/traverse@7.25.9':
+ resolution: {integrity: sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/types@7.26.0':
+ resolution: {integrity: sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==}
+ engines: {node: '>=6.9.0'}
+
'@emnapi/runtime@1.3.1':
resolution: {integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==}
@@ -409,6 +506,10 @@ packages:
resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
engines: {node: '>=12'}
+ '@istanbuljs/schema@0.1.3':
+ resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==}
+ engines: {node: '>=8'}
+
'@jridgewell/gen-mapping@0.3.5':
resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==}
engines: {node: '>=6.0.0'}
@@ -619,6 +720,40 @@ packages:
'@swc/helpers@0.5.13':
resolution: {integrity: sha512-UoKGxQ3r5kYI9dALKJapMmuK+1zWM/H17Z1+iwnNmzcJRnfFuevZs375TA5rW31pu4BS4NoSy1fRsexDXfWn5w==}
+ '@testing-library/dom@10.4.0':
+ resolution: {integrity: sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==}
+ engines: {node: '>=18'}
+
+ '@testing-library/react@16.0.1':
+ resolution: {integrity: sha512-dSmwJVtJXmku+iocRhWOUFbrERC76TX2Mnf0ATODz8brzAZrMBbzLwQixlBSanZxR6LddK3eiwpSFZgDET1URg==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@testing-library/dom': ^10.0.0
+ '@types/react': ^18.0.0
+ '@types/react-dom': ^18.0.0
+ react: ^18.0.0
+ react-dom: ^18.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@types/aria-query@5.0.4':
+ resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==}
+
+ '@types/babel__core@7.20.5':
+ resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
+
+ '@types/babel__generator@7.6.8':
+ resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==}
+
+ '@types/babel__template@7.4.4':
+ resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==}
+
+ '@types/babel__traverse@7.20.6':
+ resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==}
+
'@types/estree@1.0.6':
resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==}
@@ -754,6 +889,17 @@ packages:
resolution: {integrity: sha512-b1tx0orFCCh/THWPQa2ZwWzvOeyzzp36vkJYOpVg0u8UVOIsfVrnuC9FqAw9gRKn+rG2VmWQ/zDJZzkxUnj/XQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ '@vitejs/plugin-react@4.3.3':
+ resolution: {integrity: sha512-NooDe9GpHGqNns1i8XDERg0Vsg5SSYRhRxxyTGogUdkdNt47jal+fbuYi+Yfq6pzRCKXyoPcWisfxE6RIM3GKA==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+ peerDependencies:
+ vite: ^4.2.0 || ^5.0.0
+
+ '@vitest/coverage-istanbul@2.1.4':
+ resolution: {integrity: sha512-NLmfjzXnRSmLF/h4hYkzjvd7hZ85DRZzPUqXu0McPFCMczDfNmOjMoM3KaxjFaEmOc1YzX9HHbU/Rr9VO+35ow==}
+ peerDependencies:
+ vitest: 2.1.4
+
'@vitest/eslint-plugin@1.1.4':
resolution: {integrity: sha512-kudjgefmJJ7xQ2WfbUU6pZbm7Ou4gLYRaao/8Ynide3G0QhVKHd978sDyWX4KOH0CCMH9cyrGAkFd55eGzJ48Q==}
peerDependencies:
@@ -813,6 +959,10 @@ packages:
engines: {node: '>=0.4.0'}
hasBin: true
+ agent-base@7.1.1:
+ resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==}
+ engines: {node: '>= 14'}
+
ajv@6.12.6:
resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
@@ -828,6 +978,10 @@ packages:
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
engines: {node: '>=8'}
+ ansi-styles@5.2.0:
+ resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
+ engines: {node: '>=10'}
+
ansi-styles@6.2.1:
resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
engines: {node: '>=12'}
@@ -849,6 +1003,9 @@ packages:
argparse@2.0.1:
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+ aria-query@5.3.0:
+ resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==}
+
array-buffer-byte-length@1.0.1:
resolution: {integrity: sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg==}
engines: {node: '>= 0.4'}
@@ -889,6 +1046,9 @@ packages:
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
engines: {node: '>=12'}
+ asynckit@0.4.0:
+ resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
+
available-typed-arrays@1.0.7:
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
engines: {node: '>= 0.4'}
@@ -983,6 +1143,10 @@ packages:
resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
engines: {node: '>=12.5.0'}
+ combined-stream@1.0.8:
+ resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
+ engines: {node: '>= 0.8'}
+
commander@4.1.1:
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
engines: {node: '>= 6'}
@@ -994,6 +1158,9 @@ packages:
concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+ convert-source-map@2.0.0:
+ resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
core-js-compat@3.38.1:
resolution: {integrity: sha512-JRH6gfXxGmrzF3tZ57lFx97YARxCXPaMzPo6jELZhv88pBH5VXpQ+y0znKGlFnzuaihqhLbefxSJxWJMPtfDzw==}
@@ -1006,9 +1173,17 @@ packages:
engines: {node: '>=4'}
hasBin: true
+ cssstyle@4.1.0:
+ resolution: {integrity: sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==}
+ engines: {node: '>=18'}
+
csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+ data-urls@5.0.0:
+ resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==}
+ engines: {node: '>=18'}
+
data-view-buffer@1.0.1:
resolution: {integrity: sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA==}
engines: {node: '>= 0.4'}
@@ -1047,6 +1222,9 @@ packages:
supports-color:
optional: true
+ decimal.js@10.4.3:
+ resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==}
+
deep-eql@5.0.2:
resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
engines: {node: '>=6'}
@@ -1062,6 +1240,14 @@ packages:
resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
engines: {node: '>= 0.4'}
+ delayed-stream@1.0.0:
+ resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
+ engines: {node: '>=0.4.0'}
+
+ dequal@2.0.3:
+ resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
+ engines: {node: '>=6'}
+
detect-libc@2.0.3:
resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==}
engines: {node: '>=8'}
@@ -1080,6 +1266,9 @@ packages:
resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
engines: {node: '>=0.10.0'}
+ dom-accessibility-api@0.5.16:
+ resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==}
+
eastasianwidth@0.2.0:
resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
@@ -1092,6 +1281,10 @@ packages:
emoji-regex@9.2.2:
resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
+ entities@4.5.0:
+ resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
+ engines: {node: '>=0.12'}
+
error-ex@1.3.2:
resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
@@ -1344,6 +1537,10 @@ packages:
resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==}
engines: {node: '>=14'}
+ form-data@4.0.1:
+ resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==}
+ engines: {node: '>= 6'}
+
fsevents@2.3.2:
resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
@@ -1364,6 +1561,10 @@ packages:
functions-have-names@1.2.3:
resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
+ gensync@1.0.0-beta.2:
+ resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+ engines: {node: '>=6.9.0'}
+
get-intrinsic@1.2.4:
resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==}
engines: {node: '>= 0.4'}
@@ -1384,6 +1585,10 @@ packages:
resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==}
hasBin: true
+ globals@11.12.0:
+ resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
+ engines: {node: '>=4'}
+
globals@13.24.0:
resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==}
engines: {node: '>=8'}
@@ -1439,6 +1644,25 @@ packages:
hosted-git-info@2.8.9:
resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
+ html-encoding-sniffer@4.0.0:
+ resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==}
+ engines: {node: '>=18'}
+
+ html-escaper@2.0.2:
+ resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
+
+ http-proxy-agent@7.0.2:
+ resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
+ engines: {node: '>= 14'}
+
+ https-proxy-agent@7.0.5:
+ resolution: {integrity: sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==}
+ engines: {node: '>= 14'}
+
+ iconv-lite@0.6.3:
+ resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
+ engines: {node: '>=0.10.0'}
+
ignore@5.3.2:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
engines: {node: '>= 4'}
@@ -1539,6 +1763,9 @@ packages:
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
engines: {node: '>=0.12.0'}
+ is-potential-custom-element-name@1.0.1:
+ resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
+
is-regex@1.1.4:
resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
engines: {node: '>= 0.4'}
@@ -1580,6 +1807,26 @@ packages:
isexe@2.0.0:
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+ istanbul-lib-coverage@3.2.2:
+ resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==}
+ engines: {node: '>=8'}
+
+ istanbul-lib-instrument@6.0.3:
+ resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==}
+ engines: {node: '>=10'}
+
+ istanbul-lib-report@3.0.1:
+ resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==}
+ engines: {node: '>=10'}
+
+ istanbul-lib-source-maps@5.0.6:
+ resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==}
+ engines: {node: '>=10'}
+
+ istanbul-reports@3.1.7:
+ resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==}
+ engines: {node: '>=8'}
+
iterator.prototype@1.1.2:
resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==}
@@ -1601,6 +1848,15 @@ packages:
resolution: {integrity: sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==}
engines: {node: '>=12.0.0'}
+ jsdom@25.0.1:
+ resolution: {integrity: sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ canvas: ^2.11.2
+ peerDependenciesMeta:
+ canvas:
+ optional: true
+
jsesc@0.5.0:
resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==}
hasBin: true
@@ -1626,6 +1882,11 @@ packages:
resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==}
hasBin: true
+ json5@2.2.3:
+ resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+ engines: {node: '>=6'}
+ hasBin: true
+
jsx-ast-utils@3.3.5:
resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==}
engines: {node: '>=4.0'}
@@ -1669,9 +1930,23 @@ packages:
lru-cache@10.4.3:
resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==}
+ lru-cache@5.1.1:
+ resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+
+ lz-string@1.5.0:
+ resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==}
+ hasBin: true
+
magic-string@0.30.12:
resolution: {integrity: sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==}
+ magicast@0.3.5:
+ resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==}
+
+ make-dir@4.0.0:
+ resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==}
+ engines: {node: '>=10'}
+
merge2@1.4.1:
resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
engines: {node: '>= 8'}
@@ -1680,6 +1955,14 @@ packages:
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
engines: {node: '>=8.6'}
+ mime-db@1.52.0:
+ resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
+ engines: {node: '>= 0.6'}
+
+ mime-types@2.1.35:
+ resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
+ engines: {node: '>= 0.6'}
+
min-indent@1.0.1:
resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
engines: {node: '>=4'}
@@ -1753,6 +2036,9 @@ packages:
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
engines: {node: '>=0.10.0'}
+ nwsapi@2.2.13:
+ resolution: {integrity: sha512-cTGB9ptp9dY9A5VbMSe7fQBcl/tt22Vcqdq8+eN93rblOuE0aCFu4aZ2vMwct/2t+lFnosm8RkQW1I0Omb1UtQ==}
+
object-assign@4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
engines: {node: '>=0.10.0'}
@@ -1828,6 +2114,9 @@ packages:
resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
engines: {node: '>=8'}
+ parse5@7.2.1:
+ resolution: {integrity: sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==}
+
path-exists@4.0.0:
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
engines: {node: '>=8'}
@@ -1949,6 +2238,10 @@ packages:
engines: {node: '>=14'}
hasBin: true
+ pretty-format@27.5.1:
+ resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==}
+ engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+
prop-types@15.8.1:
resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
@@ -1967,6 +2260,13 @@ packages:
react-is@16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
+ react-is@17.0.2:
+ resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
+
+ react-refresh@0.14.2:
+ resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==}
+ engines: {node: '>=0.10.0'}
+
react@18.3.1:
resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==}
engines: {node: '>=0.10.0'}
@@ -1990,6 +2290,9 @@ packages:
resolution: {integrity: sha512-fmfw4XgoDke3kdI6h4xcUz1dG8uaiv5q9gcEwLS4Pnth2kxT+GZ7YehS1JTMGBQmtV7Y4GFGbs2re2NqhdozUg==}
engines: {node: '>= 0.4'}
+ regenerator-runtime@0.14.1:
+ resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
+
regexp-tree@0.1.27:
resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==}
hasBin: true
@@ -2027,6 +2330,9 @@ packages:
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
hasBin: true
+ rrweb-cssom@0.7.1:
+ resolution: {integrity: sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==}
+
run-parallel@1.2.0:
resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
@@ -2038,6 +2344,13 @@ packages:
resolution: {integrity: sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw==}
engines: {node: '>= 0.4'}
+ safer-buffer@2.1.2:
+ resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+
+ saxes@6.0.0:
+ resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
+ engines: {node: '>=v12.22.7'}
+
scheduler@0.23.2:
resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==}
@@ -2196,6 +2509,9 @@ packages:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
+ symbol-tree@3.2.4:
+ resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
+
synckit@0.9.2:
resolution: {integrity: sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==}
engines: {node: ^14.18.0 || >=16.0.0}
@@ -2205,6 +2521,10 @@ packages:
engines: {node: '>=14.0.0'}
hasBin: true
+ test-exclude@7.0.1:
+ resolution: {integrity: sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==}
+ engines: {node: '>=18'}
+
text-table@0.2.0:
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
@@ -2233,10 +2553,25 @@ packages:
resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==}
engines: {node: '>=14.0.0'}
+ tldts-core@6.1.57:
+ resolution: {integrity: sha512-lXnRhuQpx3zU9EONF9F7HfcRLvN1uRYUBIiKL+C/gehC/77XTU+Jye6ui86GA3rU6FjlJ0triD1Tkjt2F/2lEg==}
+
+ tldts@6.1.57:
+ resolution: {integrity: sha512-Oy7yDXK8meJl8vPMOldzA+MtueAJ5BrH4l4HXwZuj2AtfoQbLjmTJmjNWPUcAo+E/ibHn7QlqMS0BOcXJFJyHQ==}
+ hasBin: true
+
to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
+ tough-cookie@5.0.0:
+ resolution: {integrity: sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==}
+ engines: {node: '>=16'}
+
+ tr46@5.0.0:
+ resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==}
+ engines: {node: '>=18'}
+
ts-api-utils@1.3.0:
resolution: {integrity: sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==}
engines: {node: '>=16'}
@@ -2371,6 +2706,26 @@ packages:
jsdom:
optional: true
+ w3c-xmlserializer@5.0.0:
+ resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
+ engines: {node: '>=18'}
+
+ webidl-conversions@7.0.0:
+ resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==}
+ engines: {node: '>=12'}
+
+ whatwg-encoding@3.1.1:
+ resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
+ engines: {node: '>=18'}
+
+ whatwg-mimetype@4.0.0:
+ resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
+ engines: {node: '>=18'}
+
+ whatwg-url@14.0.0:
+ resolution: {integrity: sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==}
+ engines: {node: '>=18'}
+
which-boxed-primitive@1.0.2:
resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
@@ -2408,6 +2763,28 @@ packages:
resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
engines: {node: '>=12'}
+ ws@8.18.0:
+ resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: '>=5.0.2'
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+
+ xml-name-validator@5.0.0:
+ resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
+ engines: {node: '>=18'}
+
+ xmlchars@2.2.0:
+ resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
+
+ yallist@3.1.1:
+ resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+
yaml@2.5.0:
resolution: {integrity: sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==}
engines: {node: '>= 14'}
@@ -2421,14 +2798,125 @@ snapshots:
'@alloc/quick-lru@5.2.0': {}
+ '@ampproject/remapping@2.3.0':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.5
+ '@jridgewell/trace-mapping': 0.3.25
+
'@babel/code-frame@7.26.2':
dependencies:
'@babel/helper-validator-identifier': 7.25.9
js-tokens: 4.0.0
picocolors: 1.1.0
+ '@babel/compat-data@7.26.2': {}
+
+ '@babel/core@7.26.0':
+ dependencies:
+ '@ampproject/remapping': 2.3.0
+ '@babel/code-frame': 7.26.2
+ '@babel/generator': 7.26.2
+ '@babel/helper-compilation-targets': 7.25.9
+ '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.0)
+ '@babel/helpers': 7.26.0
+ '@babel/parser': 7.26.2
+ '@babel/template': 7.25.9
+ '@babel/traverse': 7.25.9
+ '@babel/types': 7.26.0
+ convert-source-map: 2.0.0
+ debug: 4.3.7
+ gensync: 1.0.0-beta.2
+ json5: 2.2.3
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/generator@7.26.2':
+ dependencies:
+ '@babel/parser': 7.26.2
+ '@babel/types': 7.26.0
+ '@jridgewell/gen-mapping': 0.3.5
+ '@jridgewell/trace-mapping': 0.3.25
+ jsesc: 3.0.2
+
+ '@babel/helper-compilation-targets@7.25.9':
+ dependencies:
+ '@babel/compat-data': 7.26.2
+ '@babel/helper-validator-option': 7.25.9
+ browserslist: 4.24.2
+ lru-cache: 5.1.1
+ semver: 6.3.1
+
+ '@babel/helper-module-imports@7.25.9':
+ dependencies:
+ '@babel/traverse': 7.25.9
+ '@babel/types': 7.26.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-module-imports': 7.25.9
+ '@babel/helper-validator-identifier': 7.25.9
+ '@babel/traverse': 7.25.9
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-plugin-utils@7.25.9': {}
+
+ '@babel/helper-string-parser@7.25.9': {}
+
'@babel/helper-validator-identifier@7.25.9': {}
+ '@babel/helper-validator-option@7.25.9': {}
+
+ '@babel/helpers@7.26.0':
+ dependencies:
+ '@babel/template': 7.25.9
+ '@babel/types': 7.26.0
+
+ '@babel/parser@7.26.2':
+ dependencies:
+ '@babel/types': 7.26.0
+
+ '@babel/plugin-transform-react-jsx-self@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.25.9
+
+ '@babel/plugin-transform-react-jsx-source@7.25.9(@babel/core@7.26.0)':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/helper-plugin-utils': 7.25.9
+
+ '@babel/runtime@7.26.0':
+ dependencies:
+ regenerator-runtime: 0.14.1
+
+ '@babel/template@7.25.9':
+ dependencies:
+ '@babel/code-frame': 7.26.2
+ '@babel/parser': 7.26.2
+ '@babel/types': 7.26.0
+
+ '@babel/traverse@7.25.9':
+ dependencies:
+ '@babel/code-frame': 7.26.2
+ '@babel/generator': 7.26.2
+ '@babel/parser': 7.26.2
+ '@babel/template': 7.25.9
+ '@babel/types': 7.26.0
+ debug: 4.3.7
+ globals: 11.12.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/types@7.26.0':
+ dependencies:
+ '@babel/helper-string-parser': 7.25.9
+ '@babel/helper-validator-identifier': 7.25.9
+
'@emnapi/runtime@1.3.1':
dependencies:
tslib: 2.7.0
@@ -2667,6 +3155,8 @@ snapshots:
wrap-ansi: 8.1.0
wrap-ansi-cjs: wrap-ansi@7.0.0
+ '@istanbuljs/schema@0.1.3': {}
+
'@jridgewell/gen-mapping@0.3.5':
dependencies:
'@jridgewell/set-array': 1.2.1
@@ -2710,7 +3200,7 @@ snapshots:
'@next/swc-win32-x64-msvc@15.0.2':
optional: true
- '@nhcarrigan/eslint-config@5.0.0-rc2(@typescript-eslint/utils@8.12.2(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.13.0(jiti@1.21.6))(playwright@1.48.2)(prettier@3.3.3)(react@18.3.1)(typescript@5.6.3)(vitest@2.1.4(@types/node@22.8.4))':
+ '@nhcarrigan/eslint-config@5.0.0-rc2(@typescript-eslint/utils@8.12.2(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.13.0(jiti@1.21.6))(playwright@1.48.2)(prettier@3.3.3)(react@18.3.1)(typescript@5.6.3)(vitest@2.1.4(@types/node@22.8.4)(jsdom@25.0.1))':
dependencies:
'@eslint/compat': 1.1.1
'@eslint/eslintrc': 3.1.0
@@ -2718,7 +3208,7 @@ snapshots:
'@stylistic/eslint-plugin': 2.8.0(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3)
'@typescript-eslint/eslint-plugin': 8.7.0(@typescript-eslint/parser@8.7.0(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3)
'@typescript-eslint/parser': 8.7.0(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3)
- '@vitest/eslint-plugin': 1.1.4(@typescript-eslint/utils@8.12.2(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3)(vitest@2.1.4(@types/node@22.8.4))
+ '@vitest/eslint-plugin': 1.1.4(@typescript-eslint/utils@8.12.2(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3)(vitest@2.1.4(@types/node@22.8.4)(jsdom@25.0.1))
eslint: 9.13.0(jiti@1.21.6)
eslint-config-prettier: 9.1.0(eslint@9.13.0(jiti@1.21.6))
eslint-plugin-deprecation: 3.0.0(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3)
@@ -2733,7 +3223,7 @@ snapshots:
playwright: 1.48.2
react: 18.3.1
typescript: 5.6.3
- vitest: 2.1.4(@types/node@22.8.4)
+ vitest: 2.1.4(@types/node@22.8.4)(jsdom@25.0.1)
transitivePeerDependencies:
- '@types/eslint'
- '@typescript-eslint/utils'
@@ -2838,6 +3328,50 @@ snapshots:
dependencies:
tslib: 2.7.0
+ '@testing-library/dom@10.4.0':
+ dependencies:
+ '@babel/code-frame': 7.26.2
+ '@babel/runtime': 7.26.0
+ '@types/aria-query': 5.0.4
+ aria-query: 5.3.0
+ chalk: 4.1.2
+ dom-accessibility-api: 0.5.16
+ lz-string: 1.5.0
+ pretty-format: 27.5.1
+
+ '@testing-library/react@16.0.1(@testing-library/dom@10.4.0)(@types/react-dom@18.3.1)(@types/react@18.3.12)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
+ dependencies:
+ '@babel/runtime': 7.26.0
+ '@testing-library/dom': 10.4.0
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ optionalDependencies:
+ '@types/react': 18.3.12
+ '@types/react-dom': 18.3.1
+
+ '@types/aria-query@5.0.4': {}
+
+ '@types/babel__core@7.20.5':
+ dependencies:
+ '@babel/parser': 7.26.2
+ '@babel/types': 7.26.0
+ '@types/babel__generator': 7.6.8
+ '@types/babel__template': 7.4.4
+ '@types/babel__traverse': 7.20.6
+
+ '@types/babel__generator@7.6.8':
+ dependencies:
+ '@babel/types': 7.26.0
+
+ '@types/babel__template@7.4.4':
+ dependencies:
+ '@babel/parser': 7.26.2
+ '@babel/types': 7.26.0
+
+ '@types/babel__traverse@7.20.6':
+ dependencies:
+ '@babel/types': 7.26.0
+
'@types/estree@1.0.6': {}
'@types/json-schema@7.0.15': {}
@@ -3018,13 +3552,40 @@ snapshots:
'@typescript-eslint/types': 8.7.0
eslint-visitor-keys: 3.4.3
- '@vitest/eslint-plugin@1.1.4(@typescript-eslint/utils@8.12.2(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3)(vitest@2.1.4(@types/node@22.8.4))':
+ '@vitejs/plugin-react@4.3.3(vite@5.4.10(@types/node@22.8.4))':
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.0)
+ '@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.0)
+ '@types/babel__core': 7.20.5
+ react-refresh: 0.14.2
+ vite: 5.4.10(@types/node@22.8.4)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@vitest/coverage-istanbul@2.1.4(vitest@2.1.4(@types/node@22.8.4)(jsdom@25.0.1))':
+ dependencies:
+ '@istanbuljs/schema': 0.1.3
+ debug: 4.3.7
+ istanbul-lib-coverage: 3.2.2
+ istanbul-lib-instrument: 6.0.3
+ istanbul-lib-report: 3.0.1
+ istanbul-lib-source-maps: 5.0.6
+ istanbul-reports: 3.1.7
+ magicast: 0.3.5
+ test-exclude: 7.0.1
+ tinyrainbow: 1.2.0
+ vitest: 2.1.4(@types/node@22.8.4)(jsdom@25.0.1)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@vitest/eslint-plugin@1.1.4(@typescript-eslint/utils@8.12.2(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3))(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3)(vitest@2.1.4(@types/node@22.8.4)(jsdom@25.0.1))':
dependencies:
eslint: 9.13.0(jiti@1.21.6)
optionalDependencies:
'@typescript-eslint/utils': 8.12.2(eslint@9.13.0(jiti@1.21.6))(typescript@5.6.3)
typescript: 5.6.3
- vitest: 2.1.4(@types/node@22.8.4)
+ vitest: 2.1.4(@types/node@22.8.4)(jsdom@25.0.1)
'@vitest/expect@2.1.4':
dependencies:
@@ -3078,6 +3639,12 @@ snapshots:
acorn@8.14.0: {}
+ agent-base@7.1.1:
+ dependencies:
+ debug: 4.3.7
+ transitivePeerDependencies:
+ - supports-color
+
ajv@6.12.6:
dependencies:
fast-deep-equal: 3.1.3
@@ -3093,6 +3660,8 @@ snapshots:
dependencies:
color-convert: 2.0.1
+ ansi-styles@5.2.0: {}
+
ansi-styles@6.2.1: {}
any-promise@1.3.0: {}
@@ -3108,6 +3677,10 @@ snapshots:
argparse@2.0.1: {}
+ aria-query@5.3.0:
+ dependencies:
+ dequal: 2.0.3
+
array-buffer-byte-length@1.0.1:
dependencies:
call-bind: 1.0.7
@@ -3177,6 +3750,8 @@ snapshots:
assertion-error@2.0.1: {}
+ asynckit@0.4.0: {}
+
available-typed-arrays@1.0.7:
dependencies:
possible-typed-array-names: 1.0.0
@@ -3280,12 +3855,18 @@ snapshots:
color-string: 1.9.1
optional: true
+ combined-stream@1.0.8:
+ dependencies:
+ delayed-stream: 1.0.0
+
commander@4.1.1: {}
comment-parser@1.4.1: {}
concat-map@0.0.1: {}
+ convert-source-map@2.0.0: {}
+
core-js-compat@3.38.1:
dependencies:
browserslist: 4.24.2
@@ -3298,8 +3879,17 @@ snapshots:
cssesc@3.0.0: {}
+ cssstyle@4.1.0:
+ dependencies:
+ rrweb-cssom: 0.7.1
+
csstype@3.1.3: {}
+ data-urls@5.0.0:
+ dependencies:
+ whatwg-mimetype: 4.0.0
+ whatwg-url: 14.0.0
+
data-view-buffer@1.0.1:
dependencies:
call-bind: 1.0.7
@@ -3330,6 +3920,8 @@ snapshots:
dependencies:
ms: 2.1.3
+ decimal.js@10.4.3: {}
+
deep-eql@5.0.2: {}
deep-is@0.1.4: {}
@@ -3346,6 +3938,10 @@ snapshots:
has-property-descriptors: 1.0.2
object-keys: 1.1.1
+ delayed-stream@1.0.0: {}
+
+ dequal@2.0.3: {}
+
detect-libc@2.0.3:
optional: true
@@ -3361,6 +3957,8 @@ snapshots:
dependencies:
esutils: 2.0.3
+ dom-accessibility-api@0.5.16: {}
+
eastasianwidth@0.2.0: {}
electron-to-chromium@1.5.49: {}
@@ -3369,6 +3967,8 @@ snapshots:
emoji-regex@9.2.2: {}
+ entities@4.5.0: {}
+
error-ex@1.3.2:
dependencies:
is-arrayish: 0.2.1
@@ -3776,6 +4376,12 @@ snapshots:
cross-spawn: 7.0.3
signal-exit: 4.1.0
+ form-data@4.0.1:
+ dependencies:
+ asynckit: 0.4.0
+ combined-stream: 1.0.8
+ mime-types: 2.1.35
+
fsevents@2.3.2:
optional: true
@@ -3793,6 +4399,8 @@ snapshots:
functions-have-names@1.2.3: {}
+ gensync@1.0.0-beta.2: {}
+
get-intrinsic@1.2.4:
dependencies:
es-errors: 1.3.0
@@ -3824,6 +4432,8 @@ snapshots:
package-json-from-dist: 1.0.0
path-scurry: 1.11.1
+ globals@11.12.0: {}
+
globals@13.24.0:
dependencies:
type-fest: 0.20.2
@@ -3874,6 +4484,30 @@ snapshots:
hosted-git-info@2.8.9: {}
+ html-encoding-sniffer@4.0.0:
+ dependencies:
+ whatwg-encoding: 3.1.1
+
+ html-escaper@2.0.2: {}
+
+ http-proxy-agent@7.0.2:
+ dependencies:
+ agent-base: 7.1.1
+ debug: 4.3.7
+ transitivePeerDependencies:
+ - supports-color
+
+ https-proxy-agent@7.0.5:
+ dependencies:
+ agent-base: 7.1.1
+ debug: 4.3.7
+ transitivePeerDependencies:
+ - supports-color
+
+ iconv-lite@0.6.3:
+ dependencies:
+ safer-buffer: 2.1.2
+
ignore@5.3.2: {}
import-fresh@3.3.0:
@@ -3962,6 +4596,8 @@ snapshots:
is-number@7.0.0: {}
+ is-potential-custom-element-name@1.0.1: {}
+
is-regex@1.1.4:
dependencies:
call-bind: 1.0.7
@@ -4000,6 +4636,37 @@ snapshots:
isexe@2.0.0: {}
+ istanbul-lib-coverage@3.2.2: {}
+
+ istanbul-lib-instrument@6.0.3:
+ dependencies:
+ '@babel/core': 7.26.0
+ '@babel/parser': 7.26.2
+ '@istanbuljs/schema': 0.1.3
+ istanbul-lib-coverage: 3.2.2
+ semver: 7.6.3
+ transitivePeerDependencies:
+ - supports-color
+
+ istanbul-lib-report@3.0.1:
+ dependencies:
+ istanbul-lib-coverage: 3.2.2
+ make-dir: 4.0.0
+ supports-color: 7.2.0
+
+ istanbul-lib-source-maps@5.0.6:
+ dependencies:
+ '@jridgewell/trace-mapping': 0.3.25
+ debug: 4.3.7
+ istanbul-lib-coverage: 3.2.2
+ transitivePeerDependencies:
+ - supports-color
+
+ istanbul-reports@3.1.7:
+ dependencies:
+ html-escaper: 2.0.2
+ istanbul-lib-report: 3.0.1
+
iterator.prototype@1.1.2:
dependencies:
define-properties: 1.2.1
@@ -4024,6 +4691,34 @@ snapshots:
jsdoc-type-pratt-parser@4.1.0: {}
+ jsdom@25.0.1:
+ dependencies:
+ cssstyle: 4.1.0
+ data-urls: 5.0.0
+ decimal.js: 10.4.3
+ form-data: 4.0.1
+ html-encoding-sniffer: 4.0.0
+ http-proxy-agent: 7.0.2
+ https-proxy-agent: 7.0.5
+ is-potential-custom-element-name: 1.0.1
+ nwsapi: 2.2.13
+ parse5: 7.2.1
+ rrweb-cssom: 0.7.1
+ saxes: 6.0.0
+ symbol-tree: 3.2.4
+ tough-cookie: 5.0.0
+ w3c-xmlserializer: 5.0.0
+ webidl-conversions: 7.0.0
+ whatwg-encoding: 3.1.1
+ whatwg-mimetype: 4.0.0
+ whatwg-url: 14.0.0
+ ws: 8.18.0
+ xml-name-validator: 5.0.0
+ transitivePeerDependencies:
+ - bufferutil
+ - supports-color
+ - utf-8-validate
+
jsesc@0.5.0: {}
jsesc@3.0.2: {}
@@ -4040,6 +4735,8 @@ snapshots:
dependencies:
minimist: 1.2.8
+ json5@2.2.3: {}
+
jsx-ast-utils@3.3.5:
dependencies:
array-includes: 3.1.8
@@ -4080,10 +4777,26 @@ snapshots:
lru-cache@10.4.3: {}
+ lru-cache@5.1.1:
+ dependencies:
+ yallist: 3.1.1
+
+ lz-string@1.5.0: {}
+
magic-string@0.30.12:
dependencies:
'@jridgewell/sourcemap-codec': 1.5.0
+ magicast@0.3.5:
+ dependencies:
+ '@babel/parser': 7.26.2
+ '@babel/types': 7.26.0
+ source-map-js: 1.2.1
+
+ make-dir@4.0.0:
+ dependencies:
+ semver: 7.6.3
+
merge2@1.4.1: {}
micromatch@4.0.8:
@@ -4091,6 +4804,12 @@ snapshots:
braces: 3.0.3
picomatch: 2.3.1
+ mime-db@1.52.0: {}
+
+ mime-types@2.1.35:
+ dependencies:
+ mime-db: 1.52.0
+
min-indent@1.0.1: {}
minimatch@3.1.2:
@@ -4119,13 +4838,13 @@ snapshots:
natural-compare@1.4.0: {}
- next-plausible@3.12.2(next@15.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+ next-plausible@3.12.2(next@15.0.2(@babel/core@7.26.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
- next: 15.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ next: 15.0.2(@babel/core@7.26.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
- next@15.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+ next@15.0.2(@babel/core@7.26.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
dependencies:
'@next/env': 15.0.2
'@swc/counter': 0.1.3
@@ -4135,7 +4854,7 @@ snapshots:
postcss: 8.4.31
react: 18.3.1
react-dom: 18.3.1(react@18.3.1)
- styled-jsx: 5.1.6(react@18.3.1)
+ styled-jsx: 5.1.6(@babel/core@7.26.0)(react@18.3.1)
optionalDependencies:
'@next/swc-darwin-arm64': 15.0.2
'@next/swc-darwin-x64': 15.0.2
@@ -4161,6 +4880,8 @@ snapshots:
normalize-path@3.0.0: {}
+ nwsapi@2.2.13: {}
+
object-assign@4.1.1: {}
object-hash@3.0.0: {}
@@ -4246,6 +4967,10 @@ snapshots:
json-parse-even-better-errors: 2.3.1
lines-and-columns: 1.2.4
+ parse5@7.2.1:
+ dependencies:
+ entities: 4.5.0
+
path-exists@4.0.0: {}
path-key@3.1.1: {}
@@ -4336,6 +5061,12 @@ snapshots:
prettier@3.3.3: {}
+ pretty-format@27.5.1:
+ dependencies:
+ ansi-regex: 5.0.1
+ ansi-styles: 5.2.0
+ react-is: 17.0.2
+
prop-types@15.8.1:
dependencies:
loose-envify: 1.4.0
@@ -4354,6 +5085,10 @@ snapshots:
react-is@16.13.1: {}
+ react-is@17.0.2: {}
+
+ react-refresh@0.14.2: {}
+
react@18.3.1:
dependencies:
loose-envify: 1.4.0
@@ -4389,6 +5124,8 @@ snapshots:
globalthis: 1.0.4
which-builtin-type: 1.1.4
+ regenerator-runtime@0.14.1: {}
+
regexp-tree@0.1.27: {}
regexp.prototype.flags@1.5.2:
@@ -4444,6 +5181,8 @@ snapshots:
'@rollup/rollup-win32-x64-msvc': 4.24.3
fsevents: 2.3.3
+ rrweb-cssom@0.7.1: {}
+
run-parallel@1.2.0:
dependencies:
queue-microtask: 1.2.3
@@ -4461,6 +5200,12 @@ snapshots:
es-errors: 1.3.0
is-regex: 1.1.4
+ safer-buffer@2.1.2: {}
+
+ saxes@6.0.0:
+ dependencies:
+ xmlchars: 2.2.0
+
scheduler@0.23.2:
dependencies:
loose-envify: 1.4.0
@@ -4634,10 +5379,12 @@ snapshots:
strip-json-comments@3.1.1: {}
- styled-jsx@5.1.6(react@18.3.1):
+ styled-jsx@5.1.6(@babel/core@7.26.0)(react@18.3.1):
dependencies:
client-only: 0.0.1
react: 18.3.1
+ optionalDependencies:
+ '@babel/core': 7.26.0
sucrase@3.35.0:
dependencies:
@@ -4655,6 +5402,8 @@ snapshots:
supports-preserve-symlinks-flag@1.0.0: {}
+ symbol-tree@3.2.4: {}
+
synckit@0.9.2:
dependencies:
'@pkgr/core': 0.1.1
@@ -4687,6 +5436,12 @@ snapshots:
transitivePeerDependencies:
- ts-node
+ test-exclude@7.0.1:
+ dependencies:
+ '@istanbuljs/schema': 0.1.3
+ glob: 10.4.5
+ minimatch: 9.0.5
+
text-table@0.2.0: {}
thenify-all@1.6.0:
@@ -4707,10 +5462,24 @@ snapshots:
tinyspy@3.0.2: {}
+ tldts-core@6.1.57: {}
+
+ tldts@6.1.57:
+ dependencies:
+ tldts-core: 6.1.57
+
to-regex-range@5.0.1:
dependencies:
is-number: 7.0.0
+ tough-cookie@5.0.0:
+ dependencies:
+ tldts: 6.1.57
+
+ tr46@5.0.0:
+ dependencies:
+ punycode: 2.3.1
+
ts-api-utils@1.3.0(typescript@5.6.3):
dependencies:
typescript: 5.6.3
@@ -4822,7 +5591,7 @@ snapshots:
'@types/node': 22.8.4
fsevents: 2.3.3
- vitest@2.1.4(@types/node@22.8.4):
+ vitest@2.1.4(@types/node@22.8.4)(jsdom@25.0.1):
dependencies:
'@vitest/expect': 2.1.4
'@vitest/mocker': 2.1.4(vite@5.4.10(@types/node@22.8.4))
@@ -4846,6 +5615,7 @@ snapshots:
why-is-node-running: 2.3.0
optionalDependencies:
'@types/node': 22.8.4
+ jsdom: 25.0.1
transitivePeerDependencies:
- less
- lightningcss
@@ -4857,6 +5627,23 @@ snapshots:
- supports-color
- terser
+ w3c-xmlserializer@5.0.0:
+ dependencies:
+ xml-name-validator: 5.0.0
+
+ webidl-conversions@7.0.0: {}
+
+ whatwg-encoding@3.1.1:
+ dependencies:
+ iconv-lite: 0.6.3
+
+ whatwg-mimetype@4.0.0: {}
+
+ whatwg-url@14.0.0:
+ dependencies:
+ tr46: 5.0.0
+ webidl-conversions: 7.0.0
+
which-boxed-primitive@1.0.2:
dependencies:
is-bigint: 1.0.4
@@ -4918,6 +5705,14 @@ snapshots:
string-width: 5.1.2
strip-ansi: 7.1.0
+ ws@8.18.0: {}
+
+ xml-name-validator@5.0.0: {}
+
+ xmlchars@2.2.0: {}
+
+ yallist@3.1.1: {}
+
yaml@2.5.0: {}
yocto-queue@0.1.0: {}
diff --git a/src/app/about/page.tsx b/src/app/about/page.tsx
index 92e2635..bed2512 100644
--- a/src/app/about/page.tsx
+++ b/src/app/about/page.tsx
@@ -57,7 +57,8 @@ const About = (): JSX.Element => {
satisfaction, with an emphasis on making resources accessible to
newcomers in the field.`}
-
+
{`Mentorship and Advocacy:`}
{` Implemented programs to
support aspiring developers, particularly those from
diff --git a/src/config/Jobs.ts b/src/config/Jobs.ts
index c6c340e..b107807 100644
--- a/src/config/Jobs.ts
+++ b/src/config/Jobs.ts
@@ -118,7 +118,7 @@ Additionally, I guided and mentored other developers in the design and coding of
`,
- end: new Date("June 4 2024"),
+ end: new Date("June 5 2024"),
link: "https://deepgram.com",
logo: "deepgram.jpeg",
start: new Date("July 5 2023"),
diff --git a/src/config/NavItems.ts b/src/config/NavItems.ts
index 5f25551..16eb52e 100644
--- a/src/config/NavItems.ts
+++ b/src/config/NavItems.ts
@@ -20,7 +20,6 @@ export const NavItems = [
{ href: "/polycule", text: "Polycule" },
{ href: "/activity", text: "Activity" },
{ href: "/art", text: "Art" },
- { href: "https://nhcarrigan.creator-spring.com/", text: "Merch" },
{ href: "/manifesto", text: "Transfemme Manifesto" },
].sort((a, b) => {
return a.text.localeCompare(b.text);
diff --git a/src/config/Socials.ts b/src/config/Socials.ts
index d9627c6..fa98480 100644
--- a/src/config/Socials.ts
+++ b/src/config/Socials.ts
@@ -42,12 +42,11 @@ import {
faComputer,
faEnvelope,
faGamepad,
- faGift,
faHashtag,
faMoneyBill,
- faUniversity,
} from "@fortawesome/free-solid-svg-icons";
import { Codeberg } from "../icons/Codeberg";
+import { Coursera } from "../icons/Coursera";
import { Fiverr } from "../icons/Fiverr";
import { Gather } from "../icons/Gather";
import { Gog } from "../icons/Gog";
@@ -58,7 +57,9 @@ import { Pixiv } from "../icons/Pixiv";
import { Polywork } from "../icons/Polywork";
import { Saylor } from "../icons/Saylor";
import { TeeSpring } from "../icons/TeeSpring";
+import { Throne } from "../icons/Throne";
import { TreeNation } from "../icons/TreeNation";
+import { Udemy } from "../icons/Udemy";
import { VRoid } from "../icons/VRoid";
import type { IconDefinition } from "@fortawesome/fontawesome-svg-core";
@@ -85,7 +86,7 @@ const HireMe: {
color: "#003600",
icon: faBriefcase,
label: "Hire Us!",
- link: "https://docs.nhcarrigan.com/#/hire",
+ link: "https://docs.nhcarrigan.com/about/hire/",
};
/**
@@ -117,7 +118,7 @@ const Donate: {
color: "#003600",
icon: faMoneyBill,
label: "Donate 💜",
- link: "https://docs.nhcarrigan.com/#/donate",
+ link: "https://docs.nhcarrigan.com/about/donate/",
};
/**
@@ -164,7 +165,7 @@ const Socials: Array<{
link: "https://matrix.to/#/#naomi:matrix.org",
},
{
- alt: "Hash symbol",
+ alt: "Octothorpe",
background: "#000",
color: "#FFF",
icon: faHashtag,
@@ -380,18 +381,18 @@ const Socials: Array<{
link: "https://docs.nhcarrigan.com/about/contact/#7-email-communication",
},
{
- alt: "University Icon",
+ alt: "Coursera Logo",
background: "#0056D2",
color: "#FFF",
- icon: faUniversity,
+ icon: Coursera,
label: "Coursera",
link: "https://www.coursera.org/learner/naomi-lgbt",
},
{
- alt: "University Icon",
+ alt: "Udemy Logo",
background: "#EC5252",
color: "#FFF",
- icon: faUniversity,
+ icon: Udemy,
label: "Udemy",
link: "https://www.udemy.com/user/naomi-carrigan/",
},
@@ -412,10 +413,10 @@ const Socials: Array<{
link: "https://pcpartpicker.com/user/nhcarrigan/",
},
{
- alt: "Gift Icon",
+ alt: "Throne.me Logo",
background: "#000",
color: "#FFF",
- icon: faGift,
+ icon: Throne,
label: "Throne",
link: "https://throne.com/naomilgbt",
},
diff --git a/src/icons/Coursera.ts b/src/icons/Coursera.ts
new file mode 100644
index 0000000..1108de1
--- /dev/null
+++ b/src/icons/Coursera.ts
@@ -0,0 +1,126 @@
+/**
+ * @copyright nhcarrigan
+ * @license Naomi's Public License
+ * @author Naomi Carrigan
+ */
+import type { IconDefinition } from "@fortawesome/fontawesome-svg-core";
+
+/**
+ * Custom FontAwesome icon definition for the
+ * Coursera logo.
+ */
+export const Coursera: IconDefinition = {
+ icon: [
+ 825,
+ 825,
+ [],
+ "U+E002",
+ `M 397.00,0.23
+ C 397.00,0.23 421.00,0.23 421.00,0.23
+ 421.00,0.23 436.00,1.92 436.00,1.92
+ 436.00,1.92 445.00,1.92 445.00,1.92
+ 464.83,2.24 500.36,9.70 520.00,14.91
+ 564.71,26.78 604.32,45.18 642.72,70.91
+ 642.72,70.91 666.54,87.59 666.54,87.59
+ 679.69,97.87 691.91,109.49 703.71,121.29
+ 750.05,167.63 785.48,224.18 804.76,287.00
+ 804.76,287.00 816.55,331.00 816.55,331.00
+ 816.55,331.00 818.37,344.00 818.37,344.00
+ 820.23,353.40 822.89,368.59 823.09,378.00
+ 823.09,378.00 823.09,390.00 823.09,390.00
+ 823.81,395.27 824.91,394.72 825.00,402.00
+ 825.00,402.00 825.00,419.00 825.00,419.00
+ 824.99,427.76 823.88,428.69 823.08,436.00
+ 823.08,436.00 823.08,446.00 823.08,446.00
+ 822.88,455.97 820.17,471.99 818.31,482.00
+ 818.31,482.00 816.76,493.00 816.76,493.00
+ 813.29,511.46 806.77,533.13 800.72,551.00
+ 800.72,551.00 789.17,579.17 789.17,579.17
+ 743.04,685.52 649.01,770.68 538.00,804.76
+ 514.40,812.01 493.53,817.23 469.00,820.29
+ 469.00,820.29 445.00,823.09 445.00,823.09
+ 445.00,823.09 435.00,823.09 435.00,823.09
+ 427.99,823.92 429.93,824.98 420.00,825.00
+ 420.00,825.00 405.00,825.00 405.00,825.00
+ 405.00,825.00 389.00,823.08 389.00,823.08
+ 389.00,823.08 379.00,823.08 379.00,823.08
+ 368.84,822.88 353.18,820.20 343.00,818.31
+ 343.00,818.31 332.00,816.76 332.00,816.76
+ 308.66,812.37 277.98,802.81 256.00,793.71
+ 211.64,775.33 173.47,751.31 137.46,719.58
+ 137.46,719.58 121.29,703.71 121.29,703.71
+ 74.93,657.35 39.53,600.84 20.24,538.00
+ 20.24,538.00 8.24,493.00 8.24,493.00
+ 8.24,493.00 6.81,482.00 6.81,482.00
+ 6.81,482.00 5.21,475.00 5.21,475.00
+ 3.59,463.77 2.02,452.36 2.00,441.00
+ 2.00,441.00 0.00,421.00 0.00,421.00
+ 0.00,421.00 0.00,405.00 0.00,405.00
+ 0.00,405.00 1.92,389.00 1.92,389.00
+ 1.92,389.00 1.92,380.00 1.92,380.00
+ 2.15,367.31 4.82,354.46 6.86,342.00
+ 12.62,306.62 23.37,271.55 38.55,239.00
+ 85.92,137.42 175.10,56.39 282.00,21.68
+ 301.73,15.28 321.55,10.19 342.00,6.86
+ 342.00,6.86 350.00,5.18 350.00,5.18
+ 350.00,5.18 378.00,2.00 378.00,2.00
+ 387.62,1.89 387.16,2.56 397.00,0.23 Z
+ M 654.00,274.00
+ C 652.59,267.31 647.58,262.01 643.51,256.72
+ 635.54,246.34 626.80,236.89 617.43,227.75
+ 582.03,193.25 534.25,169.22 486.00,159.22
+ 486.00,159.22 475.00,157.75 475.00,157.75
+ 475.00,157.75 469.00,156.26 469.00,156.26
+ 469.00,156.26 443.00,154.00 443.00,154.00
+ 443.00,154.00 419.00,154.00 419.00,154.00
+ 419.00,154.00 393.00,156.26 393.00,156.26
+ 393.00,156.26 387.00,157.75 387.00,157.75
+ 387.00,157.75 376.00,159.22 376.00,159.22
+ 359.46,162.64 337.52,169.42 322.09,176.22
+ 310.29,181.41 299.65,187.64 288.72,194.38
+ 230.27,230.42 189.21,287.07 173.67,354.00
+ 173.67,354.00 169.00,376.00 169.00,376.00
+ 169.00,376.00 169.00,384.00 169.00,384.00
+ 168.19,389.27 167.09,388.72 167.00,396.00
+ 167.00,396.00 167.00,427.00 167.00,427.00
+ 167.02,437.40 168.03,434.44 169.06,441.00
+ 169.06,441.00 169.06,450.00 169.06,450.00
+ 171.22,463.47 175.55,480.94 179.94,493.83
+ 208.58,578.07 278.08,641.10 364.00,663.24
+ 375.88,666.30 405.06,670.98 417.00,671.00
+ 417.00,671.00 445.00,671.00 445.00,671.00
+ 445.00,671.00 469.00,668.63 469.00,668.63
+ 469.00,668.63 475.00,667.19 475.00,667.19
+ 475.00,667.19 486.00,665.76 486.00,665.76
+ 486.00,665.76 503.00,661.27 503.00,661.27
+ 547.18,648.72 587.85,626.54 620.12,593.45
+ 631.39,581.89 638.18,574.40 647.68,561.15
+ 651.38,556.00 655.09,552.51 656.00,546.00
+ 656.00,546.00 622.00,526.15 622.00,526.15
+ 622.00,526.15 574.08,498.06 574.08,498.06
+ 574.08,498.06 546.00,482.00 546.00,482.00
+ 534.75,499.99 523.33,511.09 506.00,523.14
+ 494.21,531.34 480.79,536.77 467.09,540.79
+ 458.67,543.26 443.67,545.99 435.00,546.00
+ 424.27,546.02 411.46,544.96 401.00,542.55
+ 357.16,532.46 320.34,499.15 303.73,457.72
+ 300.20,448.91 295.05,428.27 295.00,419.00
+ 295.00,419.00 295.00,405.00 295.00,405.00
+ 295.08,398.63 295.76,399.32 296.68,394.00
+ 298.79,381.82 299.55,375.89 304.46,364.00
+ 311.29,347.50 322.76,330.34 335.46,317.80
+ 352.35,301.13 374.31,288.72 397.17,282.45
+ 397.17,282.45 406.00,280.91 406.00,280.91
+ 413.70,279.46 411.09,279.01 420.00,279.00
+ 420.00,279.00 442.00,279.00 442.00,279.00
+ 461.77,279.03 484.13,288.32 501.09,297.97
+ 508.30,302.07 525.10,315.12 530.25,321.17
+ 532.57,323.88 541.23,335.82 544.15,336.27
+ 546.02,336.55 550.51,333.55 552.25,332.55
+ 552.25,332.55 574.00,319.99 574.00,319.99
+ 574.00,319.99 631.99,286.46 631.99,286.46
+ 631.99,286.46 654.00,274.00 654.00,274.00 Z`,
+ ],
+ iconName: "yyy",
+ prefix: "xxx",
+} as never;
diff --git a/src/icons/Throne.ts b/src/icons/Throne.ts
new file mode 100644
index 0000000..85f9b65
--- /dev/null
+++ b/src/icons/Throne.ts
@@ -0,0 +1,93 @@
+/**
+ * @copyright nhcarrigan
+ * @license Naomi's Public License
+ * @author Naomi Carrigan
+ */
+import type { IconDefinition } from "@fortawesome/fontawesome-svg-core";
+
+/**
+ * Custom FontAwesome icon definition for the
+ * Throne.me logo.
+ */
+export const Throne: IconDefinition = {
+ icon: [
+ 738,
+ 621,
+ [],
+ "U+E002",
+ `M 364.00,0.65
+ C 380.58,-2.77 384.82,13.12 390.75,25.00
+ 390.75,25.00 436.25,114.00 436.25,114.00
+ 436.25,114.00 501.25,242.00 501.25,242.00
+ 509.55,258.59 526.58,284.93 526.99,303.00
+ 527.58,329.12 511.89,341.11 495.00,358.00
+ 495.00,358.00 471.96,382.00 471.96,382.00
+ 471.96,382.00 440.00,414.00 440.00,414.00
+ 440.00,414.00 418.96,436.00 418.96,436.00
+ 418.96,436.00 390.00,465.00 390.00,465.00
+ 383.82,471.18 376.58,481.87 367.00,480.80
+ 359.95,480.01 351.03,469.03 346.00,464.00
+ 346.00,464.00 300.00,417.00 300.00,417.00
+ 300.00,417.00 239.00,355.00 239.00,355.00
+ 224.23,340.23 209.04,325.70 210.51,303.00
+ 210.78,298.85 211.58,292.96 212.77,289.00
+ 212.77,289.00 235.75,242.00 235.75,242.00
+ 235.75,242.00 298.75,119.00 298.75,119.00
+ 298.75,119.00 320.74,76.00 320.74,76.00
+ 320.74,76.00 343.69,31.00 343.69,31.00
+ 346.88,25.11 354.43,7.97 358.21,4.21
+ 360.24,2.20 361.48,1.71 364.00,0.65 Z
+ M 11.00,204.59
+ C 13.62,204.13 15.36,203.84 18.00,204.59
+ 23.76,206.43 34.33,218.33 39.00,223.00
+ 39.00,223.00 102.00,287.00 102.00,287.00
+ 102.00,287.00 131.96,317.00 131.96,317.00
+ 131.96,317.00 141.01,327.00 141.01,327.00
+ 141.01,327.00 176.96,363.00 176.96,363.00
+ 176.96,363.00 186.01,373.00 186.01,373.00
+ 186.01,373.00 235.96,423.00 235.96,423.00
+ 235.96,423.00 316.00,505.00 316.00,505.00
+ 316.00,505.00 361.00,551.00 361.00,551.00
+ 366.28,556.28 376.33,564.19 376.52,572.00
+ 376.65,577.57 372.41,583.22 367.00,584.58
+ 367.00,584.58 360.00,585.17 360.00,585.17
+ 360.00,585.17 345.00,586.00 345.00,586.00
+ 345.00,586.00 334.00,586.96 334.00,586.96
+ 299.43,588.47 264.60,593.98 231.00,602.37
+ 212.72,606.95 183.56,617.64 167.00,618.82
+ 163.80,619.04 161.15,618.37 158.00,618.07
+ 135.75,616.01 118.33,604.57 107.44,585.00
+ 102.99,577.01 98.68,561.24 96.15,552.00
+ 96.15,552.00 64.42,443.00 64.42,443.00
+ 64.42,443.00 26.02,311.00 26.02,311.00
+ 26.02,311.00 3.42,233.00 3.42,233.00
+ 0.08,221.29 -4.79,209.26 11.00,204.59 Z
+ M 720.00,204.61
+ C 730.62,202.68 739.38,209.98 737.53,221.00
+ 737.53,221.00 729.71,249.00 729.71,249.00
+ 729.71,249.00 714.58,301.00 714.58,301.00
+ 714.58,301.00 678.42,425.00 678.42,425.00
+ 678.42,425.00 646.72,534.00 646.72,534.00
+ 638.26,562.21 633.90,594.97 606.00,611.13
+ 601.21,613.90 595.29,616.69 590.00,618.28
+ 569.52,624.42 556.97,616.60 538.00,611.14
+ 538.00,611.14 500.00,600.87 500.00,600.87
+ 491.51,598.75 482.71,597.52 475.00,593.12
+ 465.90,587.93 450.35,570.35 442.00,562.00
+ 442.00,562.00 416.72,536.00 416.72,536.00
+ 412.08,530.66 408.35,526.63 410.43,519.00
+ 411.45,515.25 414.38,512.76 416.92,510.00
+ 416.92,510.00 432.00,494.00 432.00,494.00
+ 432.00,494.00 465.96,460.01 465.96,460.01
+ 465.96,460.01 483.00,442.00 483.00,442.00
+ 483.00,442.00 509.96,415.00 509.96,415.00
+ 509.96,415.00 519.01,405.00 519.01,405.00
+ 519.01,405.00 552.96,371.01 552.96,371.01
+ 552.96,371.01 651.00,271.00 651.00,271.00
+ 651.00,271.00 665.04,256.00 665.04,256.00
+ 665.04,256.00 696.00,225.00 696.00,225.00
+ 702.65,218.35 711.22,207.67 720.00,204.61 Z`,
+ ],
+ iconName: "yyy",
+ prefix: "xxx",
+} as never;
diff --git a/src/icons/Udemy.ts b/src/icons/Udemy.ts
new file mode 100644
index 0000000..340548a
--- /dev/null
+++ b/src/icons/Udemy.ts
@@ -0,0 +1,155 @@
+/**
+ * @copyright nhcarrigan
+ * @license Naomi's Public License
+ * @author Naomi Carrigan
+ */
+import type { IconDefinition } from "@fortawesome/fontawesome-svg-core";
+
+/**
+ * Custom FontAwesome icon definition for the
+ * Udemy logo.
+ */
+export const Udemy: IconDefinition = {
+ icon: [
+ 2118,
+ 2118,
+ [],
+ "U+E002",
+ `M 1027.00,0.21
+ C 1027.00,0.21 1083.00,0.21 1083.00,0.21
+ 1083.00,0.21 1098.00,1.00 1098.00,1.00
+ 1098.00,1.00 1148.00,3.83 1148.00,3.83
+ 1148.00,3.83 1185.00,7.28 1185.00,7.28
+ 1242.11,14.33 1297.67,25.39 1353.00,41.29
+ 1516.40,88.26 1668.12,175.50 1791.00,293.04
+ 1933.98,429.80 2037.87,606.97 2085.87,799.00
+ 2098.98,851.43 2107.96,904.25 2113.17,958.00
+ 2113.17,958.00 2116.00,989.00 2116.00,989.00
+ 2116.00,989.00 2116.00,998.00 2116.00,998.00
+ 2116.00,998.00 2117.04,1008.00 2117.04,1008.00
+ 2117.04,1008.00 2117.04,1023.00 2117.04,1023.00
+ 2117.04,1023.00 2118.00,1035.00 2118.00,1035.00
+ 2118.00,1035.00 2118.00,1083.00 2118.00,1083.00
+ 2118.00,1083.00 2117.00,1100.00 2117.00,1100.00
+ 2117.00,1100.00 2114.17,1148.00 2114.17,1148.00
+ 2114.17,1148.00 2110.72,1185.00 2110.72,1185.00
+ 2102.72,1249.75 2089.35,1313.67 2069.98,1376.00
+ 2017.27,1545.58 1919.24,1702.31 1791.00,1824.96
+ 1682.55,1928.69 1552.28,2008.03 1411.00,2058.31
+ 1330.08,2087.10 1245.37,2104.90 1160.00,2113.17
+ 1160.00,2113.17 1100.00,2117.00 1100.00,2117.00
+ 1100.00,2117.00 1083.00,2118.00 1083.00,2118.00
+ 1083.00,2118.00 1035.00,2118.00 1035.00,2118.00
+ 1035.00,2118.00 1023.00,2117.04 1023.00,2117.04
+ 1023.00,2117.04 1008.00,2117.04 1008.00,2117.04
+ 1008.00,2117.04 982.00,2115.09 982.00,2115.09
+ 982.00,2115.09 915.00,2108.28 915.00,2108.28
+ 844.59,2099.01 767.69,2080.44 701.00,2056.05
+ 586.65,2014.23 483.34,1956.90 389.00,1879.58
+ 306.50,1811.96 237.26,1735.42 177.67,1647.00
+ 84.91,1509.36 27.58,1349.41 7.28,1185.00
+ 4.48,1162.30 1.03,1122.44 1.00,1100.00
+ 1.00,1100.00 0.00,1083.00 0.00,1083.00
+ 0.00,1083.00 0.00,1035.00 0.00,1035.00
+ 0.00,1035.00 0.96,1023.00 0.96,1023.00
+ 0.96,1023.00 0.96,1008.00 0.96,1008.00
+ 0.96,1008.00 2.91,982.00 2.91,982.00
+ 4.78,954.82 7.75,927.99 11.73,901.00
+ 26.84,798.40 56.07,704.16 99.31,610.00
+ 99.31,610.00 112.75,583.00 112.75,583.00
+ 133.71,541.08 157.37,500.60 183.98,462.00
+ 216.84,414.31 252.99,368.87 293.04,327.00
+ 415.66,198.79 572.46,100.72 742.00,48.02
+ 801.74,29.46 871.80,13.82 934.00,7.16
+ 934.00,7.16 989.00,2.00 989.00,2.00
+ 989.00,2.00 998.00,2.00 998.00,2.00
+ 998.00,2.00 1009.00,1.00 1009.00,1.00
+ 1015.02,0.97 1021.03,1.27 1027.00,0.21 Z
+ M 1518.00,690.00
+ C 1518.00,690.00 1518.00,426.00 1518.00,426.00
+ 1518.00,423.77 1518.24,420.43 1517.01,418.53
+ 1515.72,416.54 1512.07,414.78 1510.00,413.58
+ 1510.00,413.58 1494.00,404.15 1494.00,404.15
+ 1494.00,404.15 1419.00,359.80 1419.00,359.80
+ 1419.00,359.80 1152.00,202.20 1152.00,202.20
+ 1152.00,202.20 1083.00,161.58 1083.00,161.58
+ 1083.00,161.58 1066.00,151.58 1066.00,151.58
+ 1063.11,149.94 1060.49,148.08 1057.00,148.74
+ 1054.06,149.29 1047.80,153.32 1045.00,155.00
+ 1045.00,155.00 1024.00,167.40 1024.00,167.40
+ 1024.00,167.40 952.00,209.99 952.00,209.99
+ 952.00,209.99 789.00,306.01 789.00,306.01
+ 789.00,306.01 704.00,356.28 704.00,356.28
+ 704.00,356.28 624.00,403.42 624.00,403.42
+ 624.00,403.42 608.00,412.99 608.00,412.99
+ 605.78,414.29 601.33,416.46 599.99,418.53
+ 598.76,420.43 599.00,423.77 599.00,426.00
+ 599.00,426.00 599.00,689.00 599.00,689.00
+ 599.00,689.00 638.00,667.20 638.00,667.20
+ 638.00,667.20 710.00,624.60 710.00,624.60
+ 710.00,624.60 940.00,488.99 940.00,488.99
+ 940.00,488.99 1024.00,439.40 1024.00,439.40
+ 1024.00,439.40 1048.00,425.15 1048.00,425.15
+ 1048.00,425.15 1058.00,420.19 1058.00,420.19
+ 1058.00,420.19 1068.00,424.72 1068.00,424.72
+ 1068.00,424.72 1092.00,438.99 1092.00,438.99
+ 1092.00,438.99 1183.00,492.60 1183.00,492.60
+ 1183.00,492.60 1399.00,620.01 1399.00,620.01
+ 1399.00,620.01 1481.00,668.42 1481.00,668.42
+ 1481.00,668.42 1518.00,690.00 1518.00,690.00 Z
+ M 839.73,918.00
+ C 839.73,918.00 599.00,918.00 599.00,918.00
+ 599.00,918.00 599.00,1443.00 599.00,1443.00
+ 599.00,1443.00 599.00,1516.00 599.00,1516.00
+ 599.00,1516.00 599.00,1560.00 599.00,1560.00
+ 599.00,1560.00 603.45,1613.28 603.45,1613.28
+ 614.91,1704.32 653.23,1790.62 722.00,1852.83
+ 783.61,1908.57 850.45,1939.88 931.00,1957.58
+ 951.57,1962.09 982.42,1966.56 1003.32,1967.81
+ 1003.32,1967.81 1027.00,1969.00 1027.00,1969.00
+ 1030.87,1969.05 1037.63,1969.15 1041.00,1971.00
+ 1041.00,1971.00 1042.00,1969.00 1042.00,1969.00
+ 1042.00,1969.00 1043.00,1971.00 1043.00,1971.00
+ 1043.00,1971.00 1045.00,1969.00 1045.00,1969.00
+ 1048.05,1971.81 1060.56,1971.10 1064.00,1969.00
+ 1066.02,1970.69 1067.61,1969.70 1070.00,1969.00
+ 1070.00,1969.00 1070.00,1971.00 1070.00,1971.00
+ 1070.00,1971.00 1071.00,1971.00 1071.00,1971.00
+ 1071.00,1971.00 1072.00,1969.00 1072.00,1969.00
+ 1072.00,1969.00 1072.00,1971.00 1072.00,1971.00
+ 1072.00,1971.00 1073.00,1971.00 1073.00,1971.00
+ 1075.36,1968.47 1082.62,1969.01 1086.00,1969.00
+ 1086.00,1969.00 1108.49,1967.78 1108.49,1967.78
+ 1140.13,1965.79 1171.15,1959.99 1202.00,1952.99
+ 1202.00,1952.99 1237.00,1942.67 1237.00,1942.67
+ 1300.26,1921.58 1359.29,1886.83 1406.87,1839.86
+ 1468.86,1778.64 1503.51,1693.23 1513.68,1607.58
+ 1520.04,1554.02 1518.00,1499.86 1518.00,1446.00
+ 1518.00,1446.00 1518.00,918.00 1518.00,918.00
+ 1518.00,918.00 1277.21,918.00 1277.21,918.00
+ 1277.21,918.00 1277.21,965.89 1277.21,965.89
+ 1277.21,965.89 1277.21,1030.98 1277.21,1030.98
+ 1277.21,1030.98 1277.21,1173.03 1277.21,1173.03
+ 1277.21,1173.03 1277.21,1493.03 1277.21,1493.03
+ 1277.21,1493.03 1277.21,1523.00 1277.21,1523.00
+ 1276.78,1553.58 1270.07,1585.69 1258.55,1614.00
+ 1225.82,1694.41 1146.00,1745.09 1060.00,1746.00
+ 994.18,1746.69 928.60,1716.51 887.45,1665.00
+ 876.49,1651.28 867.61,1635.94 860.31,1620.00
+ 845.13,1586.86 839.80,1549.82 839.73,1513.67
+ 839.73,1513.67 839.73,1116.33 839.73,1116.33
+ 839.73,1116.33 839.73,984.33 839.73,984.33
+ 839.73,984.33 839.73,941.67 839.73,941.67
+ 839.73,941.67 839.73,918.00 839.73,918.00 Z
+ M 1048.00,1970.00
+ C 1048.00,1970.00 1047.00,1970.00 1047.00,1970.00
+ 1047.00,1970.00 1048.00,1971.00 1048.00,1971.00
+ 1048.00,1971.00 1048.00,1970.00 1048.00,1970.00 Z
+ M 1068.00,1970.00
+ C 1068.00,1970.00 1067.00,1970.00 1067.00,1970.00
+ 1067.00,1970.00 1068.00,1971.00 1068.00,1971.00
+ 1068.00,1971.00 1068.00,1970.00 1068.00,1970.00 Z`,
+ ],
+ iconName: "yyy",
+ prefix: "xxx",
+} as never;
diff --git a/test/app/api/activity/route.spec.ts b/test/app/api/activity/route.spec.ts
new file mode 100644
index 0000000..471ec64
--- /dev/null
+++ b/test/app/api/activity/route.spec.ts
@@ -0,0 +1,100 @@
+/**
+ * @copyright nhcarrigan
+ * @license Naomi's Public License
+ * @author Naomi Carrigan
+ */
+/* eslint-disable new-cap */
+import { NextResponse } from "next/server";
+import { describe, it, expect, vi } from "vitest";
+import { GET } from "../../../../src/app/api/activity/route";
+import { getCodebergData } from "../../../../src/lib/codeberg";
+import { getGithubData } from "../../../../src/lib/github";
+
+vi.mock("../../../../src/lib/codeberg");
+vi.mock("../../../../src/lib/github");
+
+describe("gET /api/activity", () => {
+ it("should return a sorted and limited list of activities", async() => {
+ expect.assertions(2);
+ const mockCodebergData = [
+ {
+ created: "2023-01-01T00:00:00Z",
+ op_type: "push",
+ repo: { full_name: "repo1", html_url: "https://codeberg.org/repo1" },
+ },
+ ];
+ const mockGithubData = [
+ {
+ created_at: "2023-01-02T00:00:00Z",
+ repo: {
+ name: "repo2",
+ url: "https://api.github.com/repos/repo2",
+ },
+ type: "pull_request",
+ },
+ ];
+
+ vi.mocked(getCodebergData).mockResolvedValue(mockCodebergData);
+ vi.mocked(getGithubData).mockResolvedValue(mockGithubData);
+
+ const response = await GET();
+ const json = await response.json();
+
+ expect(response, "did not respond with Next").toBeInstanceOf(NextResponse);
+ expect(json, "incorrect payload").toStrictEqual([
+ {
+ date: "2023-01-02T00:00:00.000Z",
+ repo: "https://github.com/repo2",
+ repoName: "repo2",
+ type: "pull_request",
+ },
+ {
+ date: "2023-01-01T00:00:00.000Z",
+ repo: "https://codeberg.org/repo1",
+ repoName: "repo1",
+ type: "push",
+ },
+ ]);
+ });
+
+ it("should handle empty data from both sources", async() => {
+ expect.assertions(2);
+ vi.mocked(getCodebergData).mockResolvedValue([]);
+ vi.mocked(getGithubData).mockResolvedValue([]);
+
+ const response = await GET();
+ const json = await response.json();
+
+ expect(response, "did not use Next to respond").
+ toBeInstanceOf(NextResponse);
+ expect(json, "was not empty array").toStrictEqual([]);
+ });
+
+ it("should limit the results to 100 entries", async() => {
+ expect.assertions(2);
+ const mockCodebergData = Array.from({ length: 60 }, (_, index) => {
+ return {
+ created: `2023-01-${index + 1}T00:00:00Z`,
+ op_type: "push",
+ repo: { full_name: `repo${index + 1}`, html_url: `https://codeberg.org/repo${index + 1}` },
+ };
+ });
+ const mockGithubData = Array.from({ length: 60 }, (_, index) => {
+ return {
+ created_at: `2023-02-${index + 1}T00:00:00Z`,
+ repo: { name: `repo${index + 61}`, url: `https://api.github.com/repos/repo${index + 61}` },
+ type: "pull_request",
+ };
+ });
+
+ vi.mocked(getCodebergData).mockResolvedValue(mockCodebergData);
+ vi.mocked(getGithubData).mockResolvedValue(mockGithubData);
+
+ const response = await GET();
+ const json = await response.json();
+
+ expect(response, "did not use Next to respond").
+ toBeInstanceOf(NextResponse);
+ expect(json, "did not limit to 100 entries").toHaveLength(100);
+ });
+});
diff --git a/test/config/Art.spec.ts b/test/config/Art.spec.ts
new file mode 100644
index 0000000..98ff80d
--- /dev/null
+++ b/test/config/Art.spec.ts
@@ -0,0 +1,37 @@
+/**
+ * @copyright nhcarrigan
+ * @license Naomi's Public License
+ * @author Naomi Carrigan
+ */
+import { describe, it, expect } from "vitest";
+import { Art } from "../../src/config/Art";
+
+describe("art objects", () => {
+ it("should have unique names", () => {
+ expect.assertions(1);
+ const set = new Set(
+ Art.map((a) => {
+ return a.name;
+ }),
+ );
+ expect(set, "are not unique").toHaveLength(Art.length);
+ });
+
+ it("should have unique img properties", () => {
+ expect.assertions(1);
+ const set = new Set(
+ Art.map((a) => {
+ return a.img;
+ }),
+ );
+ expect(set, "are not unique").toHaveLength(Art.length);
+ });
+
+ it("should have alt text", () => {
+ expect.assertions(1);
+ const noText = Art.filter((a) => {
+ return a.alt.length === 0;
+ });
+ expect(noText, "found missing alt").toHaveLength(0);
+ });
+});
diff --git a/test/config/Certifications.spec.ts b/test/config/Certifications.spec.ts
new file mode 100644
index 0000000..726722b
--- /dev/null
+++ b/test/config/Certifications.spec.ts
@@ -0,0 +1,29 @@
+/**
+ * @copyright nhcarrigan
+ * @license Naomi's Public License
+ * @author Naomi Carrigan
+ */
+import { describe, it, expect } from "vitest";
+import { Certifications } from "../../src/config/Certifications";
+
+describe("certification objects", () => {
+ it("should have unique names", () => {
+ expect.assertions(1);
+ const set = new Set(
+ Certifications.map((c) => {
+ return c.name;
+ }),
+ );
+ expect(set, "are not unique").toHaveLength(Certifications.length);
+ });
+
+ it("should have unique file names", () => {
+ expect.assertions(1);
+ const set = new Set(
+ Certifications.map((c) => {
+ return c.fileName;
+ }),
+ );
+ expect(set, "are not unique").toHaveLength(Certifications.length);
+ });
+});
diff --git a/test/config/Games.spec.ts b/test/config/Games.spec.ts
new file mode 100644
index 0000000..e0089b4
--- /dev/null
+++ b/test/config/Games.spec.ts
@@ -0,0 +1,47 @@
+/**
+ * @copyright nhcarrigan
+ * @license Naomi's Public License
+ * @author Naomi Carrigan
+ */
+import { describe, it, expect } from "vitest";
+import { Games } from "../../src/config/Games";
+
+describe("games objects", () => {
+ it("should have unique names", () => {
+ expect.assertions(1);
+ const set = new Set(
+ Games.map((g) => {
+ return g.name;
+ }),
+ );
+ expect(set, "are not unique").toHaveLength(Games.length);
+ });
+
+ it("should have unique img properties", () => {
+ expect.assertions(1);
+ const set = new Set(
+ Games.map((g) => {
+ return g.img;
+ }),
+ );
+ expect(set, "are not unique").toHaveLength(Games.length);
+ });
+
+ it("should have unique urls", () => {
+ expect.assertions(1);
+ const set = new Set(
+ Games.map((g) => {
+ return g.url;
+ }),
+ );
+ expect(set, "are not unique").toHaveLength(Games.length);
+ });
+
+ it("should have alt text", () => {
+ expect.assertions(1);
+ const noText = Games.filter((g) => {
+ return g.alt.length === 0;
+ });
+ expect(noText, "found missing alt").toHaveLength(0);
+ });
+});
diff --git a/test/config/Jobs.spec.ts b/test/config/Jobs.spec.ts
new file mode 100644
index 0000000..6d1afa0
--- /dev/null
+++ b/test/config/Jobs.spec.ts
@@ -0,0 +1,33 @@
+/**
+ * @copyright nhcarrigan
+ * @license Naomi's Public License
+ * @author Naomi Carrigan
+ */
+import { describe, it, expect } from "vitest";
+import { Jobs } from "../../src/config/Jobs";
+
+describe("jobs objects", () => {
+ it("should have correct start dates", () => {
+ expect.hasAssertions();
+ for (const job of Jobs.slice(1)) {
+ expect(job.start.getDate(), `${job.title} has bad start`).toBe(5);
+ }
+ });
+
+ it("should have correct end dates", () => {
+ expect.hasAssertions();
+ for (const job of Jobs.slice(1)) {
+ if (!job.end) {
+ continue;
+ }
+ expect(job.end.getDate(), `${job.title} has bad end`).toBe(5);
+ }
+ });
+
+ it("should have no future start dates", () => {
+ expect.hasAssertions();
+ expect(Jobs.filter((job) => {
+ return job.start > new Date();
+ }), "have future start dates").toHaveLength(0);
+ });
+});
diff --git a/test/config/NavItems.spec.ts b/test/config/NavItems.spec.ts
new file mode 100644
index 0000000..2aaa6fd
--- /dev/null
+++ b/test/config/NavItems.spec.ts
@@ -0,0 +1,30 @@
+/**
+ * @copyright nhcarrigan
+ * @license Naomi's Public License
+ * @author Naomi Carrigan
+ */
+import { describe, it, expect } from "vitest";
+import { NavItems } from "../../src/config/NavItems";
+
+describe("nav items", () => {
+ it("should be unique", () => {
+ expect.assertions(2);
+ const href = new Set(
+ NavItems.map((n) => {
+ return n.href;
+ }),
+ );
+ const text = new Set(NavItems.map((n) => {
+ return n.text;
+ }));
+ expect(href, "links are not unique").toHaveLength(NavItems.length);
+ expect(text, "names are not unique").toHaveLength(NavItems.length);
+ });
+
+ it("should be internal links", () => {
+ expect.hasAssertions();
+ for (const nav of NavItems) {
+ expect(nav.href, `${nav.href} is not internal`).toMatch(/^\/[\da-z-]+$/);
+ }
+ });
+});
diff --git a/test/config/Partners.spec.ts b/test/config/Partners.spec.ts
new file mode 100644
index 0000000..12622cd
--- /dev/null
+++ b/test/config/Partners.spec.ts
@@ -0,0 +1,29 @@
+/**
+ * @copyright nhcarrigan
+ * @license Naomi's Public License
+ * @author Naomi Carrigan
+ */
+import { describe, it, expect } from "vitest";
+import { Partners } from "../../src/config/Partners";
+
+describe("partner objects", () => {
+ it("should have unique names", () => {
+ expect.assertions(1);
+ const set = new Set(
+ Partners.map((p) => {
+ return p.name;
+ }),
+ );
+ expect(set, "are not unique").toHaveLength(Partners.length);
+ });
+
+ it("should have unique avatars", () => {
+ expect.assertions(1);
+ const set = new Set(
+ Partners.map((p) => {
+ return p.avatar;
+ }),
+ );
+ expect(set, "are not unique").toHaveLength(Partners.length);
+ });
+});
diff --git a/test/config/Socials.spec.ts b/test/config/Socials.spec.ts
new file mode 100644
index 0000000..67b0396
--- /dev/null
+++ b/test/config/Socials.spec.ts
@@ -0,0 +1,109 @@
+/**
+ * @copyright nhcarrigan
+ * @license Naomi's Public License
+ * @author Naomi Carrigan
+ */
+import { faBriefcase, faMoneyBill } from "@fortawesome/free-solid-svg-icons";
+import { describe, it, expect } from "vitest";
+import { HireMe, Donate, Socials } from "../../src/config/Socials";
+
+describe("socials objects", () => {
+ it("should have unique labels", () => {
+ expect.assertions(1);
+ const set = new Set(
+ Socials.map((s) => {
+ return s.label;
+ }),
+ );
+ expect(set, "are not unique").toHaveLength(Socials.length);
+ });
+
+ it("should have unique links", () => {
+ expect.assertions(1);
+ const set = new Set(
+ Socials.map((s) => {
+ return s.link;
+ }),
+ );
+ expect(set, "are not unique").toHaveLength(Socials.length);
+ });
+
+ it("should have unique icons", () => {
+ expect.assertions(1);
+ const set = new Set(
+ Socials.map((s) => {
+ return s.icon;
+ }),
+ );
+ expect(set, "are not unique").toHaveLength(Socials.length);
+ });
+});
+
+describe("hire me object", () => {
+ it("should have correct label", () => {
+ expect.assertions(1);
+ expect(HireMe.label, "does not").toBe("Hire Us!");
+ });
+
+ it("should have correct link", () => {
+ expect.assertions(1);
+ expect(HireMe.link, "does not").
+ toBe("https://docs.nhcarrigan.com/about/hire/");
+ });
+
+ it("should have correct colours", () => {
+ expect.assertions(2);
+ expect(HireMe.color, "colour is wrong").toBe("#003600");
+ expect(HireMe.background, "background is wrong").toBe(`linear-gradient(
+ 90deg,
+ #5bcefa,
+ #f5a9b8,
+ #ffffff,
+ #f5a9b8,
+ #5bcefa
+ )`);
+ });
+
+ it("should have correct icon", () => {
+ expect.assertions(1);
+ expect(HireMe.icon, "does not").toBe(faBriefcase);
+ });
+});
+
+describe("donate object", () => {
+ it("should have correct label", () => {
+ expect.assertions(1);
+ expect(Donate.label, "does not").toBe("Donate 💜");
+ });
+
+ it("should have correct link", () => {
+ expect.assertions(1);
+ expect(Donate.link, "does not").
+ toBe("https://docs.nhcarrigan.com/about/donate/");
+ });
+
+ it("should have correct colours", () => {
+ expect.assertions(2);
+ expect(Donate.color, "has wrong colour").toBe("#003600");
+ expect(Donate.background, "has wrong background").toBe(`linear-gradient(
+ 90deg,
+ rgba(255, 0, 0, 1) 0%,
+ rgba(251, 7, 217, 1) 10%,
+ rgba(186, 12, 248, 1) 20%,
+ rgba(95, 21, 242, 1) 30%,
+ rgba(28, 127, 238, 1) 40%,
+ rgba(47, 201, 226, 1) 50%,
+ rgba(63, 218, 216, 1) 60%,
+ rgba(79, 220, 74, 1) 70%,
+ rgba(208, 222, 33, 1) 80%,
+ rgba(255, 154, 0, 1) 90%,
+ rgba(255, 0, 0, 1) 100%
+ )`);
+ });
+
+ it("should have correct icon", () => {
+ expect.assertions(1);
+ expect(Donate.icon, "does not").toBe(faMoneyBill);
+ });
+});
+
diff --git a/test/config/TeamMembers.spec.ts b/test/config/TeamMembers.spec.ts
new file mode 100644
index 0000000..db7f70d
--- /dev/null
+++ b/test/config/TeamMembers.spec.ts
@@ -0,0 +1,36 @@
+/**
+ * @copyright nhcarrigan
+ * @license Naomi's Public License
+ * @author Naomi Carrigan
+ */
+import { describe, it, expect } from "vitest";
+import { TeamMembers } from "../../src/config/TeamMembers";
+
+describe("partner objects", () => {
+ it("should have unique names", () => {
+ expect.assertions(1);
+ const set = new Set(
+ TeamMembers.map((t) => {
+ return t.name;
+ }),
+ );
+ expect(set, "are not unique").toHaveLength(TeamMembers.length);
+ });
+
+ it("should have unique avatars", () => {
+ expect.assertions(1);
+ const set = new Set(
+ TeamMembers.map((t) => {
+ return t.avatar;
+ }),
+ );
+ expect(set, "are not unique").toHaveLength(TeamMembers.length);
+ });
+
+ it("should have no future dates", () => {
+ expect.assertions(1);
+ expect(TeamMembers.filter((t) => {
+ return new Date(t.joinDate) > new Date();
+ }), "have future dates").toHaveLength(0);
+ });
+});
diff --git a/test/config/Testimonials.spec.ts b/test/config/Testimonials.spec.ts
new file mode 100644
index 0000000..adc7381
--- /dev/null
+++ b/test/config/Testimonials.spec.ts
@@ -0,0 +1,26 @@
+/**
+ * @copyright nhcarrigan
+ * @license Naomi's Public License
+ * @author Naomi Carrigan
+ */
+import { describe, it, expect } from "vitest";
+import { Testimonials } from "../../src/config/Testimonials";
+
+describe("testimonial objects", () => {
+ it("should have unique names", () => {
+ expect.assertions(1);
+ const set = new Set(
+ Testimonials.map((t) => {
+ return t.name;
+ }),
+ );
+ expect(set, "are not unique").toHaveLength(Testimonials.length);
+ });
+
+ it("should have no future dates", () => {
+ expect.assertions(1);
+ expect(Testimonials.filter((t) => {
+ return new Date(t.date) > new Date();
+ }), "have future dates").toHaveLength(0);
+ });
+});
diff --git a/test/icons/Codeberg.spec.ts b/test/icons/Codeberg.spec.ts
new file mode 100644
index 0000000..d078ec5
--- /dev/null
+++ b/test/icons/Codeberg.spec.ts
@@ -0,0 +1,38 @@
+/**
+ * @copyright nhcarrigan
+ * @license Naomi's Public License
+ * @author Naomi Carrigan
+ */
+
+import { describe, it, expect } from "vitest";
+import { Codeberg } from "../../src/icons/Codeberg";
+
+describe("codeberg icon", () => {
+ it("should have a valid width", () => {
+ expect.assertions(1);
+ expect(Codeberg.icon[0], "width is negative").toBeGreaterThan(0);
+ });
+
+ it("should have a valid height", () => {
+ expect.assertions(1);
+ expect(Codeberg.icon[1], "height is negative").toBeGreaterThan(0);
+ });
+
+ it("should not have any ligatures", () => {
+ expect.assertions(1);
+ expect(Codeberg.icon[2], "ligatures are present").toStrictEqual([]);
+ });
+
+ it("should have a valid unicode set", () => {
+ expect.assertions(1);
+ expect(Codeberg.icon[3], "unicode set is wrong").toBe("U+E002");
+ });
+
+ it("should have valid SVG path data", () => {
+ expect.assertions(1);
+ expect(Codeberg.icon[4], "path data is bad").toMatch(
+ // eslint-disable-next-line stylistic/max-len
+ /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi,
+ );
+ });
+});
diff --git a/test/icons/Coursera.spec.ts b/test/icons/Coursera.spec.ts
new file mode 100644
index 0000000..8156800
--- /dev/null
+++ b/test/icons/Coursera.spec.ts
@@ -0,0 +1,38 @@
+/**
+ * @copyright nhcarrigan
+ * @license Naomi's Public License
+ * @author Naomi Carrigan
+ */
+
+import { describe, it, expect } from "vitest";
+import { Coursera } from "../../src/icons/Coursera";
+
+describe("coursera icon", () => {
+ it("should have a valid width", () => {
+ expect.assertions(1);
+ expect(Coursera.icon[0], "width is negative").toBeGreaterThan(0);
+ });
+
+ it("should have a valid height", () => {
+ expect.assertions(1);
+ expect(Coursera.icon[1], "height is negative").toBeGreaterThan(0);
+ });
+
+ it("should not have any ligatures", () => {
+ expect.assertions(1);
+ expect(Coursera.icon[2], "ligatures are present").toStrictEqual([]);
+ });
+
+ it("should have a valid unicode set", () => {
+ expect.assertions(1);
+ expect(Coursera.icon[3], "unicode set is wrong").toBe("U+E002");
+ });
+
+ it("should have valid SVG path data", () => {
+ expect.assertions(1);
+ expect(Coursera.icon[4], "path data is bad").toMatch(
+ // eslint-disable-next-line stylistic/max-len
+ /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi,
+ );
+ });
+});
diff --git a/test/icons/Fiverr.spec.ts b/test/icons/Fiverr.spec.ts
new file mode 100644
index 0000000..433f13c
--- /dev/null
+++ b/test/icons/Fiverr.spec.ts
@@ -0,0 +1,38 @@
+/**
+ * @copyright nhcarrigan
+ * @license Naomi's Public License
+ * @author Naomi Carrigan
+ */
+
+import { describe, it, expect } from "vitest";
+import { Fiverr } from "../../src/icons/Fiverr";
+
+describe("fiverr icon", () => {
+ it("should have a valid width", () => {
+ expect.assertions(1);
+ expect(Fiverr.icon[0], "width is negative").toBeGreaterThan(0);
+ });
+
+ it("should have a valid height", () => {
+ expect.assertions(1);
+ expect(Fiverr.icon[1], "height is negative").toBeGreaterThan(0);
+ });
+
+ it("should not have any ligatures", () => {
+ expect.assertions(1);
+ expect(Fiverr.icon[2], "ligatures are present").toStrictEqual([]);
+ });
+
+ it("should have a valid unicode set", () => {
+ expect.assertions(1);
+ expect(Fiverr.icon[3], "unicode set is wrong").toBe("U+E002");
+ });
+
+ it("should have valid SVG path data", () => {
+ expect.assertions(1);
+ expect(Fiverr.icon[4], "path data is bad").toMatch(
+ // eslint-disable-next-line stylistic/max-len
+ /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi,
+ );
+ });
+});
diff --git a/test/icons/Gather.spec.ts b/test/icons/Gather.spec.ts
new file mode 100644
index 0000000..c50e9fa
--- /dev/null
+++ b/test/icons/Gather.spec.ts
@@ -0,0 +1,38 @@
+/**
+ * @copyright nhcarrigan
+ * @license Naomi's Public License
+ * @author Naomi Carrigan
+ */
+
+import { describe, it, expect } from "vitest";
+import { Gather } from "../../src/icons/Gather";
+
+describe("gather icon", () => {
+ it("should have a valid width", () => {
+ expect.assertions(1);
+ expect(Gather.icon[0], "width is negative").toBeGreaterThan(0);
+ });
+
+ it("should have a valid height", () => {
+ expect.assertions(1);
+ expect(Gather.icon[1], "height is negative").toBeGreaterThan(0);
+ });
+
+ it("should not have any ligatures", () => {
+ expect.assertions(1);
+ expect(Gather.icon[2], "ligatures are present").toStrictEqual([]);
+ });
+
+ it("should have a valid unicode set", () => {
+ expect.assertions(1);
+ expect(Gather.icon[3], "unicode set is wrong").toBe("U+E002");
+ });
+
+ it("should have valid SVG path data", () => {
+ expect.assertions(1);
+ expect(Gather.icon[4], "path data is bad").toMatch(
+ // eslint-disable-next-line stylistic/max-len
+ /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi,
+ );
+ });
+});
diff --git a/test/icons/Gog.spec.ts b/test/icons/Gog.spec.ts
new file mode 100644
index 0000000..3c505bb
--- /dev/null
+++ b/test/icons/Gog.spec.ts
@@ -0,0 +1,38 @@
+/**
+ * @copyright nhcarrigan
+ * @license Naomi's Public License
+ * @author Naomi Carrigan
+ */
+
+import { describe, it, expect } from "vitest";
+import { Gog } from "../../src/icons/Gog";
+
+describe("gog icon", () => {
+ it("should have a valid width", () => {
+ expect.assertions(1);
+ expect(Gog.icon[0], "width is negative").toBeGreaterThan(0);
+ });
+
+ it("should have a valid height", () => {
+ expect.assertions(1);
+ expect(Gog.icon[1], "height is negative").toBeGreaterThan(0);
+ });
+
+ it("should not have any ligatures", () => {
+ expect.assertions(1);
+ expect(Gog.icon[2], "ligatures are present").toStrictEqual([]);
+ });
+
+ it("should have a valid unicode set", () => {
+ expect.assertions(1);
+ expect(Gog.icon[3], "unicode set is wrong").toBe("U+E002");
+ });
+
+ it("should have valid SVG path data", () => {
+ expect.assertions(1);
+ expect(Gog.icon[4], "path data is bad").toMatch(
+ // eslint-disable-next-line stylistic/max-len
+ /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi,
+ );
+ });
+});
diff --git a/test/icons/Kofi.spec.ts b/test/icons/Kofi.spec.ts
new file mode 100644
index 0000000..396e310
--- /dev/null
+++ b/test/icons/Kofi.spec.ts
@@ -0,0 +1,38 @@
+/**
+ * @copyright nhcarrigan
+ * @license Naomi's Public License
+ * @author Naomi Carrigan
+ */
+
+import { describe, it, expect } from "vitest";
+import { Kofi } from "../../src/icons/KoFi";
+
+describe("kofi icon", () => {
+ it("should have a valid width", () => {
+ expect.assertions(1);
+ expect(Kofi.icon[0], "width is negative").toBeGreaterThan(0);
+ });
+
+ it("should have a valid height", () => {
+ expect.assertions(1);
+ expect(Kofi.icon[1], "height is negative").toBeGreaterThan(0);
+ });
+
+ it("should not have any ligatures", () => {
+ expect.assertions(1);
+ expect(Kofi.icon[2], "ligatures are present").toStrictEqual([]);
+ });
+
+ it("should have a valid unicode set", () => {
+ expect.assertions(1);
+ expect(Kofi.icon[3], "unicode set is wrong").toBe("U+E002");
+ });
+
+ it("should have valid SVG path data", () => {
+ expect.assertions(1);
+ expect(Kofi.icon[4], "path data is bad").toMatch(
+ // eslint-disable-next-line stylistic/max-len
+ /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi,
+ );
+ });
+});
diff --git a/test/icons/Matrix.spec.ts b/test/icons/Matrix.spec.ts
new file mode 100644
index 0000000..506bbc9
--- /dev/null
+++ b/test/icons/Matrix.spec.ts
@@ -0,0 +1,38 @@
+/**
+ * @copyright nhcarrigan
+ * @license Naomi's Public License
+ * @author Naomi Carrigan
+ */
+
+import { describe, it, expect } from "vitest";
+import { Matrix } from "../../src/icons/Matrix";
+
+describe("matrix icon", () => {
+ it("should have a valid width", () => {
+ expect.assertions(1);
+ expect(Matrix.icon[0], "width is negative").toBeGreaterThan(0);
+ });
+
+ it("should have a valid height", () => {
+ expect.assertions(1);
+ expect(Matrix.icon[1], "height is negative").toBeGreaterThan(0);
+ });
+
+ it("should not have any ligatures", () => {
+ expect.assertions(1);
+ expect(Matrix.icon[2], "ligatures are present").toStrictEqual([]);
+ });
+
+ it("should have a valid unicode set", () => {
+ expect.assertions(1);
+ expect(Matrix.icon[3], "unicode set is wrong").toBe("U+E002");
+ });
+
+ it("should have valid SVG path data", () => {
+ expect.assertions(1);
+ expect(Matrix.icon[4], "path data is bad").toMatch(
+ // eslint-disable-next-line stylistic/max-len
+ /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi,
+ );
+ });
+});
diff --git a/test/icons/Peerlist.spec.ts b/test/icons/Peerlist.spec.ts
new file mode 100644
index 0000000..f427344
--- /dev/null
+++ b/test/icons/Peerlist.spec.ts
@@ -0,0 +1,38 @@
+/**
+ * @copyright nhcarrigan
+ * @license Naomi's Public License
+ * @author Naomi Carrigan
+ */
+
+import { describe, it, expect } from "vitest";
+import { Peerlist } from "../../src/icons/Peerlist";
+
+describe("peerlist icon", () => {
+ it("should have a valid width", () => {
+ expect.assertions(1);
+ expect(Peerlist.icon[0], "width is negative").toBeGreaterThan(0);
+ });
+
+ it("should have a valid height", () => {
+ expect.assertions(1);
+ expect(Peerlist.icon[1], "height is negative").toBeGreaterThan(0);
+ });
+
+ it("should not have any ligatures", () => {
+ expect.assertions(1);
+ expect(Peerlist.icon[2], "ligatures are present").toStrictEqual([]);
+ });
+
+ it("should have a valid unicode set", () => {
+ expect.assertions(1);
+ expect(Peerlist.icon[3], "unicode set is wrong").toBe("U+E002");
+ });
+
+ it("should have valid SVG path data", () => {
+ expect.assertions(1);
+ expect(Peerlist.icon[4], "path data is bad").toMatch(
+ // eslint-disable-next-line stylistic/max-len
+ /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi,
+ );
+ });
+});
diff --git a/test/icons/Pixiv.spec.ts b/test/icons/Pixiv.spec.ts
new file mode 100644
index 0000000..30685e3
--- /dev/null
+++ b/test/icons/Pixiv.spec.ts
@@ -0,0 +1,38 @@
+/**
+ * @copyright nhcarrigan
+ * @license Naomi's Public License
+ * @author Naomi Carrigan
+ */
+
+import { describe, it, expect } from "vitest";
+import { Pixiv } from "../../src/icons/Pixiv";
+
+describe("pixiv icon", () => {
+ it("should have a valid width", () => {
+ expect.assertions(1);
+ expect(Pixiv.icon[0], "width is negative").toBeGreaterThan(0);
+ });
+
+ it("should have a valid height", () => {
+ expect.assertions(1);
+ expect(Pixiv.icon[1], "height is negative").toBeGreaterThan(0);
+ });
+
+ it("should not have any ligatures", () => {
+ expect.assertions(1);
+ expect(Pixiv.icon[2], "ligatures are present").toStrictEqual([]);
+ });
+
+ it("should have a valid unicode set", () => {
+ expect.assertions(1);
+ expect(Pixiv.icon[3], "unicode set is wrong").toBe("U+E002");
+ });
+
+ it("should have valid SVG path data", () => {
+ expect.assertions(1);
+ expect(Pixiv.icon[4], "path data is bad").toMatch(
+ // eslint-disable-next-line stylistic/max-len
+ /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi,
+ );
+ });
+});
diff --git a/test/icons/Polywork.spec.ts b/test/icons/Polywork.spec.ts
new file mode 100644
index 0000000..927bc04
--- /dev/null
+++ b/test/icons/Polywork.spec.ts
@@ -0,0 +1,38 @@
+/**
+ * @copyright nhcarrigan
+ * @license Naomi's Public License
+ * @author Naomi Carrigan
+ */
+
+import { describe, it, expect } from "vitest";
+import { Polywork } from "../../src/icons/Polywork";
+
+describe("polywork icon", () => {
+ it("should have a valid width", () => {
+ expect.assertions(1);
+ expect(Polywork.icon[0], "width is negative").toBeGreaterThan(0);
+ });
+
+ it("should have a valid height", () => {
+ expect.assertions(1);
+ expect(Polywork.icon[1], "height is negative").toBeGreaterThan(0);
+ });
+
+ it("should not have any ligatures", () => {
+ expect.assertions(1);
+ expect(Polywork.icon[2], "ligatures are present").toStrictEqual([]);
+ });
+
+ it("should have a valid unicode set", () => {
+ expect.assertions(1);
+ expect(Polywork.icon[3], "unicode set is wrong").toBe("U+E002");
+ });
+
+ it("should have valid SVG path data", () => {
+ expect.assertions(1);
+ expect(Polywork.icon[4], "path data is bad").toMatch(
+ // eslint-disable-next-line stylistic/max-len
+ /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi,
+ );
+ });
+});
diff --git a/test/icons/Saylor.spec.ts b/test/icons/Saylor.spec.ts
new file mode 100644
index 0000000..e80b899
--- /dev/null
+++ b/test/icons/Saylor.spec.ts
@@ -0,0 +1,38 @@
+/**
+ * @copyright nhcarrigan
+ * @license Naomi's Public License
+ * @author Naomi Carrigan
+ */
+
+import { describe, it, expect } from "vitest";
+import { Saylor } from "../../src/icons/Saylor";
+
+describe("saylor icon", () => {
+ it("should have a valid width", () => {
+ expect.assertions(1);
+ expect(Saylor.icon[0], "width is negative").toBeGreaterThan(0);
+ });
+
+ it("should have a valid height", () => {
+ expect.assertions(1);
+ expect(Saylor.icon[1], "height is negative").toBeGreaterThan(0);
+ });
+
+ it("should not have any ligatures", () => {
+ expect.assertions(1);
+ expect(Saylor.icon[2], "ligatures are present").toStrictEqual([]);
+ });
+
+ it("should have a valid unicode set", () => {
+ expect.assertions(1);
+ expect(Saylor.icon[3], "unicode set is wrong").toBe("U+E002");
+ });
+
+ it("should have valid SVG path data", () => {
+ expect.assertions(1);
+ expect(Saylor.icon[4], "path data is bad").toMatch(
+ // eslint-disable-next-line stylistic/max-len
+ /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi,
+ );
+ });
+});
diff --git a/test/icons/TeeSpring.spec.ts b/test/icons/TeeSpring.spec.ts
new file mode 100644
index 0000000..01aa234
--- /dev/null
+++ b/test/icons/TeeSpring.spec.ts
@@ -0,0 +1,38 @@
+/**
+ * @copyright nhcarrigan
+ * @license Naomi's Public License
+ * @author Naomi Carrigan
+ */
+
+import { describe, it, expect } from "vitest";
+import { TeeSpring } from "../../src/icons/TeeSpring";
+
+describe("teespring icon", () => {
+ it("should have a valid width", () => {
+ expect.assertions(1);
+ expect(TeeSpring.icon[0], "width is negative").toBeGreaterThan(0);
+ });
+
+ it("should have a valid height", () => {
+ expect.assertions(1);
+ expect(TeeSpring.icon[1], "height is negative").toBeGreaterThan(0);
+ });
+
+ it("should not have any ligatures", () => {
+ expect.assertions(1);
+ expect(TeeSpring.icon[2], "ligatures are present").toStrictEqual([]);
+ });
+
+ it("should have a valid unicode set", () => {
+ expect.assertions(1);
+ expect(TeeSpring.icon[3], "unicode set is wrong").toBe("U+E002");
+ });
+
+ it("should have valid SVG path data", () => {
+ expect.assertions(1);
+ expect(TeeSpring.icon[4], "path data is bad").toMatch(
+ // eslint-disable-next-line stylistic/max-len
+ /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi,
+ );
+ });
+});
diff --git a/test/icons/Throne.spec.ts b/test/icons/Throne.spec.ts
new file mode 100644
index 0000000..a90a3c4
--- /dev/null
+++ b/test/icons/Throne.spec.ts
@@ -0,0 +1,38 @@
+/**
+ * @copyright nhcarrigan
+ * @license Naomi's Public License
+ * @author Naomi Carrigan
+ */
+
+import { describe, it, expect } from "vitest";
+import { Throne } from "../../src/icons/Throne";
+
+describe("throne icon", () => {
+ it("should have a valid width", () => {
+ expect.assertions(1);
+ expect(Throne.icon[0], "width is negative").toBeGreaterThan(0);
+ });
+
+ it("should have a valid height", () => {
+ expect.assertions(1);
+ expect(Throne.icon[1], "height is negative").toBeGreaterThan(0);
+ });
+
+ it("should not have any ligatures", () => {
+ expect.assertions(1);
+ expect(Throne.icon[2], "ligatures are present").toStrictEqual([]);
+ });
+
+ it("should have a valid unicode set", () => {
+ expect.assertions(1);
+ expect(Throne.icon[3], "unicode set is wrong").toBe("U+E002");
+ });
+
+ it("should have valid SVG path data", () => {
+ expect.assertions(1);
+ expect(Throne.icon[4], "path data is bad").toMatch(
+ // eslint-disable-next-line stylistic/max-len
+ /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi,
+ );
+ });
+});
diff --git a/test/icons/TreeNation.spec.ts b/test/icons/TreeNation.spec.ts
new file mode 100644
index 0000000..fe36f2d
--- /dev/null
+++ b/test/icons/TreeNation.spec.ts
@@ -0,0 +1,38 @@
+/**
+ * @copyright nhcarrigan
+ * @license Naomi's Public License
+ * @author Naomi Carrigan
+ */
+
+import { describe, it, expect } from "vitest";
+import { TreeNation } from "../../src/icons/TreeNation";
+
+describe("treenation icon", () => {
+ it("should have a valid width", () => {
+ expect.assertions(1);
+ expect(TreeNation.icon[0], "width is negative").toBeGreaterThan(0);
+ });
+
+ it("should have a valid height", () => {
+ expect.assertions(1);
+ expect(TreeNation.icon[1], "height is negative").toBeGreaterThan(0);
+ });
+
+ it("should not have any ligatures", () => {
+ expect.assertions(1);
+ expect(TreeNation.icon[2], "ligatures are present").toStrictEqual([]);
+ });
+
+ it("should have a valid unicode set", () => {
+ expect.assertions(1);
+ expect(TreeNation.icon[3], "unicode set is wrong").toBe("U+E002");
+ });
+
+ it("should have valid SVG path data", () => {
+ expect.assertions(1);
+ expect(TreeNation.icon[4], "path data is bad").toMatch(
+ // eslint-disable-next-line stylistic/max-len
+ /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi,
+ );
+ });
+});
diff --git a/test/icons/Udemy.spec.ts b/test/icons/Udemy.spec.ts
new file mode 100644
index 0000000..9c8c7cf
--- /dev/null
+++ b/test/icons/Udemy.spec.ts
@@ -0,0 +1,38 @@
+/**
+ * @copyright nhcarrigan
+ * @license Naomi's Public License
+ * @author Naomi Carrigan
+ */
+
+import { describe, it, expect } from "vitest";
+import { Udemy } from "../../src/icons/Udemy";
+
+describe("udemy icon", () => {
+ it("should have a valid width", () => {
+ expect.assertions(1);
+ expect(Udemy.icon[0], "width is negative").toBeGreaterThan(0);
+ });
+
+ it("should have a valid height", () => {
+ expect.assertions(1);
+ expect(Udemy.icon[1], "height is negative").toBeGreaterThan(0);
+ });
+
+ it("should not have any ligatures", () => {
+ expect.assertions(1);
+ expect(Udemy.icon[2], "ligatures are present").toStrictEqual([]);
+ });
+
+ it("should have a valid unicode set", () => {
+ expect.assertions(1);
+ expect(Udemy.icon[3], "unicode set is wrong").toBe("U+E002");
+ });
+
+ it("should have valid SVG path data", () => {
+ expect.assertions(1);
+ expect(Udemy.icon[4], "path data is bad").toMatch(
+ // eslint-disable-next-line stylistic/max-len
+ /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi,
+ );
+ });
+});
diff --git a/test/icons/VRoid.spec.ts b/test/icons/VRoid.spec.ts
new file mode 100644
index 0000000..7b05791
--- /dev/null
+++ b/test/icons/VRoid.spec.ts
@@ -0,0 +1,38 @@
+/**
+ * @copyright nhcarrigan
+ * @license Naomi's Public License
+ * @author Naomi Carrigan
+ */
+
+import { describe, it, expect } from "vitest";
+import { VRoid } from "../../src/icons/VRoid";
+
+describe("vroid icon", () => {
+ it("should have a valid width", () => {
+ expect.assertions(1);
+ expect(VRoid.icon[0], "width is negative").toBeGreaterThan(0);
+ });
+
+ it("should have a valid height", () => {
+ expect.assertions(1);
+ expect(VRoid.icon[1], "height is negative").toBeGreaterThan(0);
+ });
+
+ it("should not have any ligatures", () => {
+ expect.assertions(1);
+ expect(VRoid.icon[2], "ligatures are present").toStrictEqual([]);
+ });
+
+ it("should have a valid unicode set", () => {
+ expect.assertions(1);
+ expect(VRoid.icon[3], "unicode set is wrong").toBe("U+E002");
+ });
+
+ it("should have valid SVG path data", () => {
+ expect.assertions(1);
+ expect(VRoid.icon[4], "path data is bad").toMatch(
+ // eslint-disable-next-line stylistic/max-len
+ /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi,
+ );
+ });
+});
diff --git a/test/icons/Volunteer.spec.ts b/test/icons/Volunteer.spec.ts
new file mode 100644
index 0000000..5ab0c01
--- /dev/null
+++ b/test/icons/Volunteer.spec.ts
@@ -0,0 +1,38 @@
+/**
+ * @copyright nhcarrigan
+ * @license Naomi's Public License
+ * @author Naomi Carrigan
+ */
+
+import { describe, it, expect } from "vitest";
+import { Volunteer } from "../../src/icons/Volunteer";
+
+describe("volunteer icon", () => {
+ it("should have a valid width", () => {
+ expect.assertions(1);
+ expect(Volunteer.icon[0], "width is negative").toBeGreaterThan(0);
+ });
+
+ it("should have a valid height", () => {
+ expect.assertions(1);
+ expect(Volunteer.icon[1], "height is negative").toBeGreaterThan(0);
+ });
+
+ it("should not have any ligatures", () => {
+ expect.assertions(1);
+ expect(Volunteer.icon[2], "ligatures are present").toStrictEqual([]);
+ });
+
+ it("should have a valid unicode set", () => {
+ expect.assertions(1);
+ expect(Volunteer.icon[3], "unicode set is wrong").toBe("U+E002");
+ });
+
+ it("should have valid SVG path data", () => {
+ expect.assertions(1);
+ expect(Volunteer.icon[4], "path data is bad").toMatch(
+ // eslint-disable-next-line stylistic/max-len
+ /(?:[lm]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:[hv]\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))|(?:c\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){5})|(?:q\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3}(?:\s?t?\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+)))*)|(?:a\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2}[\s,]?(?:[01][\s,]+){2}(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){2})|(?:s\s?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))(?:[\s,]?-?(?:(?:\d+(?:\.\d+)?)|(?:\.\d+))){3})|z/gi,
+ );
+ });
+});
diff --git a/test/lib/codeberg.spec.ts b/test/lib/codeberg.spec.ts
new file mode 100644
index 0000000..a304d27
--- /dev/null
+++ b/test/lib/codeberg.spec.ts
@@ -0,0 +1,345 @@
+/**
+ * @copyright nhcarrigan
+ * @license Naomi's Public License
+ * @author Naomi Carrigan
+ */
+import { describe, it, expect, vi, beforeEach } from "vitest";
+import { getCodebergData } from "../../src/lib/codeberg";
+
+describe("codeberg", () => {
+ beforeEach(() => {
+ vi.resetAllMocks();
+ });
+
+ it("should fetch and cache activities", async() => {
+ expect.assertions(1);
+ const mockResponse = [
+ {
+ act_user: {
+ active: true,
+ avatar_url: "https://example.com/avatar.png",
+ created: "2023-01-01T00:00:00Z",
+ description: "Developer",
+ email: "naomi@example.com",
+ followers_count: 100,
+ following_count: 50,
+ full_name: "Naomi Carrigan",
+ html_url: "https://example.com",
+ id: 1,
+ is_admin: false,
+ language: "en",
+ last_login: "2023-01-01T00:00:00Z",
+ location: "Earth",
+ login: "naomi",
+ login_name: "naomi",
+ prohibit_login: false,
+ pronouns: "she/her",
+ restricted: false,
+ source_id: 1,
+ starred_repos_count: 10,
+ username: "naomi",
+ visibility: "public",
+ website: "https://example.com",
+ },
+ act_user_id: 1,
+ comment: {
+ assets: [],
+ body: "A comment",
+ created_at: "2023-01-01T00:00:00Z",
+ html_url: "https://example.com/comment",
+ id: 1,
+ issue_url: "https://example.com/issue",
+ original_author: "naomi",
+ original_author_id: 1,
+ pull_request_url: "https://example.com/pr",
+ updated_at: "2023-01-01T00:00:00Z",
+ user: null,
+ },
+ comment_id: 1,
+ content: "Activity content",
+ created: "2023-01-01T00:00:00Z",
+ id: 1,
+ is_private: false,
+ op_type: "create",
+ ref_name: "main",
+ repo: {
+ allow_fast_forward_only_merge: false,
+ allow_merge_commits: true,
+ allow_rebase: true,
+ allow_rebase_explicit: true,
+ allow_rebase_update: true,
+ allow_squash_merge: true,
+ archived: false,
+ archived_at: "2023-01-01T00:00:00Z",
+ avatar_url: "https://example.com/avatar.png",
+ clone_url: "https://example.com/repo.git",
+ created_at: "2023-01-01T00:00:00Z",
+ default_allow_maintainer_edit: true,
+ default_branch: "main",
+ default_delete_branch_after_merge: true,
+ default_merge_style: "merge",
+ description: "A repository",
+ empty: false,
+ external_tracker: {
+ external_tracker_format: "format",
+ external_tracker_regexp_pattern: "pattern",
+ external_tracker_style: "style",
+ external_tracker_url: "https://example.com/tracker",
+ },
+ fork: false,
+ forks_count: 5,
+ full_name: "naomi/repo",
+ globally_editable_wiki: false,
+ has_actions: true,
+ has_issues: true,
+ has_packages: true,
+ has_projects: true,
+ has_pull_requests: true,
+ has_releases: true,
+ has_wiki: true,
+ html_url: "https://example.com/repo",
+ id: 1,
+ ignore_whitespace_conflicts: false,
+ internal: false,
+ language: "TypeScript",
+ languages_url: "https://example.com/languages",
+ link: "https://example.com/repo",
+ mirror: false,
+ mirror_interval: "24h",
+ mirror_updated: "2023-01-01T00:00:00Z",
+ name: "repo",
+ object_format_name: "format",
+ open_issues_count: 1,
+ open_pr_counter: 0,
+ original_url: "https://example.com/repo",
+ owner: {
+ active: true,
+ avatar_url: "https://example.com/avatar.png",
+ created: "2023-01-01T00:00:00Z",
+ description: "Developer",
+ email: "naomi@example.com",
+ followers_count: 100,
+ following_count: 50,
+ full_name: "Naomi Carrigan",
+ html_url: "https://example.com",
+ id: 1,
+ is_admin: false,
+ language: "en",
+ last_login: "2023-01-01T00:00:00Z",
+ location: "Earth",
+ login: "naomi",
+ login_name: "naomi",
+ prohibit_login: false,
+ pronouns: "she/her",
+ restricted: false,
+ source_id: 1,
+ starred_repos_count: 10,
+ username: "naomi",
+ visibility: "public",
+ website: "https://example.com",
+ },
+ parent: null,
+ permissions: {
+ admin: true,
+ pull: true,
+ push: true,
+ },
+ private: false,
+ release_counter: 0,
+ repo_transfer: null,
+ size: 100,
+ ssh_url: "git@example.com:repo.git",
+ stars_count: 10,
+ template: false,
+ topics: [],
+ updated_at: "2023-01-01T00:00:00Z",
+ url: "https://example.com/repo",
+ watchers_count: 3,
+ website: "https://example.com",
+ wiki_branch: "main",
+ },
+ repo_id: 1,
+ user_id: 1,
+ },
+ ];
+
+ vi.spyOn(global, "fetch").mockResolvedValue({
+ json: () => {
+ return Promise.resolve(mockResponse);
+ },
+ });
+
+ const data = await getCodebergData();
+ expect(data, "did not have correct payload").
+ toStrictEqual(mockResponse);
+ });
+
+ it("should return cached data if not stale", async() => {
+ expect.assertions(3);
+ const mockResponse = [
+ {
+ act_user: {
+ active: true,
+ avatar_url: "https://example.com/avatar.png",
+ created: "2023-01-01T00:00:00Z",
+ description: "Developer",
+ email: "naomi@example.com",
+ followers_count: 100,
+ following_count: 50,
+ full_name: "Naomi Carrigan",
+ html_url: "https://example.com",
+ id: 1,
+ is_admin: false,
+ language: "en",
+ last_login: "2023-01-01T00:00:00Z",
+ location: "Earth",
+ login: "naomi",
+ login_name: "naomi",
+ prohibit_login: false,
+ pronouns: "she/her",
+ restricted: false,
+ source_id: 1,
+ starred_repos_count: 10,
+ username: "naomi",
+ visibility: "public",
+ website: "https://example.com",
+ },
+ act_user_id: 1,
+ comment: {
+ assets: [],
+ body: "A comment",
+ created_at: "2023-01-01T00:00:00Z",
+ html_url: "https://example.com/comment",
+ id: 1,
+ issue_url: "https://example.com/issue",
+ original_author: "naomi",
+ original_author_id: 1,
+ pull_request_url: "https://example.com/pr",
+ updated_at: "2023-01-01T00:00:00Z",
+ user: null,
+ },
+ comment_id: 1,
+ content: "Activity content",
+ created: "2023-01-01T00:00:00Z",
+ id: 1,
+ is_private: false,
+ op_type: "create",
+ ref_name: "main",
+ repo: {
+ allow_fast_forward_only_merge: false,
+ allow_merge_commits: true,
+ allow_rebase: true,
+ allow_rebase_explicit: true,
+ allow_rebase_update: true,
+ allow_squash_merge: true,
+ archived: false,
+ archived_at: "2023-01-01T00:00:00Z",
+ avatar_url: "https://example.com/avatar.png",
+ clone_url: "https://example.com/repo.git",
+ created_at: "2023-01-01T00:00:00Z",
+ default_allow_maintainer_edit: true,
+ default_branch: "main",
+ default_delete_branch_after_merge: true,
+ default_merge_style: "merge",
+ description: "A repository",
+ empty: false,
+ external_tracker: {
+ external_tracker_format: "format",
+ external_tracker_regexp_pattern: "pattern",
+ external_tracker_style: "style",
+ external_tracker_url: "https://example.com/tracker",
+ },
+ fork: false,
+ forks_count: 5,
+ full_name: "naomi/repo",
+ globally_editable_wiki: false,
+ has_actions: true,
+ has_issues: true,
+ has_packages: true,
+ has_projects: true,
+ has_pull_requests: true,
+ has_releases: true,
+ has_wiki: true,
+ html_url: "https://example.com/repo",
+ id: 1,
+ ignore_whitespace_conflicts: false,
+ internal: false,
+ language: "TypeScript",
+ languages_url: "https://example.com/languages",
+ link: "https://example.com/repo",
+ mirror: false,
+ mirror_interval: "24h",
+ mirror_updated: "2023-01-01T00:00:00Z",
+ name: "repo",
+ object_format_name: "format",
+ open_issues_count: 1,
+ open_pr_counter: 0,
+ original_url: "https://example.com/repo",
+ owner: {
+ active: true,
+ avatar_url: "https://example.com/avatar.png",
+ created: "2023-01-01T00:00:00Z",
+ description: "Developer",
+ email: "naomi@example.com",
+ followers_count: 100,
+ following_count: 50,
+ full_name: "Naomi Carrigan",
+ html_url: "https://example.com",
+ id: 1,
+ is_admin: false,
+ language: "en",
+ last_login: "2023-01-01T00:00:00Z",
+ location: "Earth",
+ login: "naomi",
+ login_name: "naomi",
+ prohibit_login: false,
+ pronouns: "she/her",
+ restricted: false,
+ source_id: 1,
+ starred_repos_count: 10,
+ username: "naomi",
+ visibility: "public",
+ website: "https://example.com",
+ },
+ parent: null,
+ permissions: {
+ admin: true,
+ pull: true,
+ push: true,
+ },
+ private: false,
+ release_counter: 0,
+ repo_transfer: null,
+ size: 100,
+ ssh_url: "git@example.com:repo.git",
+ stars_count: 10,
+ template: false,
+ topics: [],
+ updated_at: "2023-01-01T00:00:00Z",
+ url: "https://example.com/repo",
+ watchers_count: 3,
+ website: "https://example.com",
+ wiki_branch: "main",
+ },
+ repo_id: 1,
+ user_id: 1,
+ },
+ ];
+
+ vi.spyOn(global, "fetch").mockResolvedValue({
+ json: () => {
+ return Promise.resolve(mockResponse);
+ },
+ });
+
+ const data = await getCodebergData();
+ expect(data, "did not have correct payload").
+ toStrictEqual(mockResponse);
+
+ // Call again to check if cached data is returned
+ const cachedData = await getCodebergData();
+ expect(cachedData, "did not cache correct payload").
+ toStrictEqual(mockResponse);
+ expect(global.fetch, "did not hit cache").not.toHaveBeenCalled();
+ });
+});
diff --git a/test/lib/github.spec.ts b/test/lib/github.spec.ts
new file mode 100644
index 0000000..e56bc4c
--- /dev/null
+++ b/test/lib/github.spec.ts
@@ -0,0 +1,343 @@
+/**
+ * @copyright nhcarrigan
+ * @license Naomi's Public License
+ * @author Naomi Carrigan
+ */
+import { describe, it, expect, vi, beforeEach } from "vitest";
+import { getGithubData } from "../../src/lib/github";
+
+describe("github", () => {
+ beforeEach(() => {
+ vi.resetAllMocks();
+ });
+
+ it("should fetch and cache activities", async() => {
+ expect.assertions(1);
+ const mockResponse = [
+ {
+ act_user: {
+ active: true,
+ avatar_url: "https://example.com/avatar.png",
+ created: "2023-01-01T00:00:00Z",
+ description: "Developer",
+ email: "naomi@example.com",
+ followers_count: 100,
+ following_count: 50,
+ full_name: "Naomi Carrigan",
+ html_url: "https://example.com",
+ id: 1,
+ is_admin: false,
+ language: "en",
+ last_login: "2023-01-01T00:00:00Z",
+ location: "Earth",
+ login: "naomi",
+ login_name: "naomi",
+ prohibit_login: false,
+ pronouns: "she/her",
+ restricted: false,
+ source_id: 1,
+ starred_repos_count: 10,
+ username: "naomi",
+ visibility: "public",
+ website: "https://example.com",
+ },
+ act_user_id: 1,
+ comment: {
+ assets: [],
+ body: "A comment",
+ created_at: "2023-01-01T00:00:00Z",
+ html_url: "https://example.com/comment",
+ id: 1,
+ issue_url: "https://example.com/issue",
+ original_author: "naomi",
+ original_author_id: 1,
+ pull_request_url: "https://example.com/pr",
+ updated_at: "2023-01-01T00:00:00Z",
+ user: null,
+ },
+ comment_id: 1,
+ content: "Activity content",
+ created: "2023-01-01T00:00:00Z",
+ id: 1,
+ is_private: false,
+ op_type: "create",
+ ref_name: "main",
+ repo: {
+ allow_fast_forward_only_merge: false,
+ allow_merge_commits: true,
+ allow_rebase: true,
+ allow_rebase_explicit: true,
+ allow_rebase_update: true,
+ allow_squash_merge: true,
+ archived: false,
+ archived_at: "2023-01-01T00:00:00Z",
+ avatar_url: "https://example.com/avatar.png",
+ clone_url: "https://example.com/repo.git",
+ created_at: "2023-01-01T00:00:00Z",
+ default_allow_maintainer_edit: true,
+ default_branch: "main",
+ default_delete_branch_after_merge: true,
+ default_merge_style: "merge",
+ description: "A repository",
+ empty: false,
+ external_tracker: {
+ external_tracker_format: "format",
+ external_tracker_regexp_pattern: "pattern",
+ external_tracker_style: "style",
+ external_tracker_url: "https://example.com/tracker",
+ },
+ fork: false,
+ forks_count: 5,
+ full_name: "naomi/repo",
+ globally_editable_wiki: false,
+ has_actions: true,
+ has_issues: true,
+ has_packages: true,
+ has_projects: true,
+ has_pull_requests: true,
+ has_releases: true,
+ has_wiki: true,
+ html_url: "https://example.com/repo",
+ id: 1,
+ ignore_whitespace_conflicts: false,
+ internal: false,
+ language: "TypeScript",
+ languages_url: "https://example.com/languages",
+ link: "https://example.com/repo",
+ mirror: false,
+ mirror_interval: "24h",
+ mirror_updated: "2023-01-01T00:00:00Z",
+ name: "repo",
+ object_format_name: "format",
+ open_issues_count: 1,
+ open_pr_counter: 0,
+ original_url: "https://example.com/repo",
+ owner: {
+ active: true,
+ avatar_url: "https://example.com/avatar.png",
+ created: "2023-01-01T00:00:00Z",
+ description: "Developer",
+ email: "naomi@example.com",
+ followers_count: 100,
+ following_count: 50,
+ full_name: "Naomi Carrigan",
+ html_url: "https://example.com",
+ id: 1,
+ is_admin: false,
+ language: "en",
+ last_login: "2023-01-01T00:00:00Z",
+ location: "Earth",
+ login: "naomi",
+ login_name: "naomi",
+ prohibit_login: false,
+ pronouns: "she/her",
+ restricted: false,
+ source_id: 1,
+ starred_repos_count: 10,
+ username: "naomi",
+ visibility: "public",
+ website: "https://example.com",
+ },
+ parent: null,
+ permissions: {
+ admin: true,
+ pull: true,
+ push: true,
+ },
+ private: false,
+ release_counter: 0,
+ repo_transfer: null,
+ size: 100,
+ ssh_url: "git@example.com:repo.git",
+ stars_count: 10,
+ template: false,
+ topics: [],
+ updated_at: "2023-01-01T00:00:00Z",
+ url: "https://example.com/repo",
+ watchers_count: 3,
+ website: "https://example.com",
+ wiki_branch: "main",
+ },
+ repo_id: 1,
+ user_id: 1,
+ },
+ ];
+
+ vi.spyOn(global, "fetch").mockResolvedValue({
+ json: () => {
+ return Promise.resolve(mockResponse);
+ },
+ });
+
+ const data = await getGithubData();
+ expect(data, "did not have correct payload").toStrictEqual(mockResponse);
+ });
+
+ it("should return cached data if not stale", async() => {
+ expect.assertions(3);
+ const mockResponse = [
+ {
+ act_user: {
+ active: true,
+ avatar_url: "https://example.com/avatar.png",
+ created: "2023-01-01T00:00:00Z",
+ description: "Developer",
+ email: "naomi@example.com",
+ followers_count: 100,
+ following_count: 50,
+ full_name: "Naomi Carrigan",
+ html_url: "https://example.com",
+ id: 1,
+ is_admin: false,
+ language: "en",
+ last_login: "2023-01-01T00:00:00Z",
+ location: "Earth",
+ login: "naomi",
+ login_name: "naomi",
+ prohibit_login: false,
+ pronouns: "she/her",
+ restricted: false,
+ source_id: 1,
+ starred_repos_count: 10,
+ username: "naomi",
+ visibility: "public",
+ website: "https://example.com",
+ },
+ act_user_id: 1,
+ comment: {
+ assets: [],
+ body: "A comment",
+ created_at: "2023-01-01T00:00:00Z",
+ html_url: "https://example.com/comment",
+ id: 1,
+ issue_url: "https://example.com/issue",
+ original_author: "naomi",
+ original_author_id: 1,
+ pull_request_url: "https://example.com/pr",
+ updated_at: "2023-01-01T00:00:00Z",
+ user: null,
+ },
+ comment_id: 1,
+ content: "Activity content",
+ created: "2023-01-01T00:00:00Z",
+ id: 1,
+ is_private: false,
+ op_type: "create",
+ ref_name: "main",
+ repo: {
+ allow_fast_forward_only_merge: false,
+ allow_merge_commits: true,
+ allow_rebase: true,
+ allow_rebase_explicit: true,
+ allow_rebase_update: true,
+ allow_squash_merge: true,
+ archived: false,
+ archived_at: "2023-01-01T00:00:00Z",
+ avatar_url: "https://example.com/avatar.png",
+ clone_url: "https://example.com/repo.git",
+ created_at: "2023-01-01T00:00:00Z",
+ default_allow_maintainer_edit: true,
+ default_branch: "main",
+ default_delete_branch_after_merge: true,
+ default_merge_style: "merge",
+ description: "A repository",
+ empty: false,
+ external_tracker: {
+ external_tracker_format: "format",
+ external_tracker_regexp_pattern: "pattern",
+ external_tracker_style: "style",
+ external_tracker_url: "https://example.com/tracker",
+ },
+ fork: false,
+ forks_count: 5,
+ full_name: "naomi/repo",
+ globally_editable_wiki: false,
+ has_actions: true,
+ has_issues: true,
+ has_packages: true,
+ has_projects: true,
+ has_pull_requests: true,
+ has_releases: true,
+ has_wiki: true,
+ html_url: "https://example.com/repo",
+ id: 1,
+ ignore_whitespace_conflicts: false,
+ internal: false,
+ language: "TypeScript",
+ languages_url: "https://example.com/languages",
+ link: "https://example.com/repo",
+ mirror: false,
+ mirror_interval: "24h",
+ mirror_updated: "2023-01-01T00:00:00Z",
+ name: "repo",
+ object_format_name: "format",
+ open_issues_count: 1,
+ open_pr_counter: 0,
+ original_url: "https://example.com/repo",
+ owner: {
+ active: true,
+ avatar_url: "https://example.com/avatar.png",
+ created: "2023-01-01T00:00:00Z",
+ description: "Developer",
+ email: "naomi@example.com",
+ followers_count: 100,
+ following_count: 50,
+ full_name: "Naomi Carrigan",
+ html_url: "https://example.com",
+ id: 1,
+ is_admin: false,
+ language: "en",
+ last_login: "2023-01-01T00:00:00Z",
+ location: "Earth",
+ login: "naomi",
+ login_name: "naomi",
+ prohibit_login: false,
+ pronouns: "she/her",
+ restricted: false,
+ source_id: 1,
+ starred_repos_count: 10,
+ username: "naomi",
+ visibility: "public",
+ website: "https://example.com",
+ },
+ parent: null,
+ permissions: {
+ admin: true,
+ pull: true,
+ push: true,
+ },
+ private: false,
+ release_counter: 0,
+ repo_transfer: null,
+ size: 100,
+ ssh_url: "git@example.com:repo.git",
+ stars_count: 10,
+ template: false,
+ topics: [],
+ updated_at: "2023-01-01T00:00:00Z",
+ url: "https://example.com/repo",
+ watchers_count: 3,
+ website: "https://example.com",
+ wiki_branch: "main",
+ },
+ repo_id: 1,
+ user_id: 1,
+ },
+ ];
+
+ vi.spyOn(global, "fetch").mockResolvedValue({
+ json: () => {
+ return Promise.resolve(mockResponse);
+ },
+ });
+
+ const data = await getGithubData();
+ expect(data, "did not have correct payload").toStrictEqual(mockResponse);
+
+ // Call again to check if cached data is returned
+ const cachedData = await getGithubData();
+ expect(cachedData, "did not cache correct payload").
+ toStrictEqual(mockResponse);
+ expect(global.fetch, "did not hit cache").not.toHaveBeenCalled();
+ });
+});
diff --git a/tsconfig.json b/tsconfig.json
index 8ecb82f..85ef36e 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -18,11 +18,6 @@
"name": "next"
}
],
- "paths": {
- "@/*": [
- "./src/*"
- ]
- },
"allowJs": true
},
"include": [
@@ -32,6 +27,7 @@
".next/types/**/*.ts"
],
"exclude": [
- "node_modules"
+ "node_modules",
+ "vitest.config.ts"
]
}
diff --git a/vitest.config.ts b/vitest.config.ts
new file mode 100644
index 0000000..32fce47
--- /dev/null
+++ b/vitest.config.ts
@@ -0,0 +1,20 @@
+import { coverageConfigDefaults, defineConfig } from "vitest/config";
+
+export default defineConfig({
+ test: {
+ coverage: {
+ provider: "istanbul",
+ reporter: ["text", "html"],
+ all: true,
+ allowExternal: true,
+ thresholds: {
+ lines: 100,
+ statements: 100,
+ functions: 100,
+ branches: 100
+ },
+ extension: [".ts"],
+ exclude: [...coverageConfigDefaults.exclude, "tailwind.config.ts"]
+ }
+ }
+});
\ No newline at end of file