From 5e0561df7efc3b96fced8c5dec8eef80d950e16c Mon Sep 17 00:00:00 2001 From: Preston Maness Date: Sun, 11 Jun 2023 22:40:37 -0500 Subject: [PATCH 001/144] Quickstart guidance updates --- .vscode/launch.json | 23 +++++++++++++++++++++++ README.md | 25 ++++++++++++++++--------- 2 files changed, 39 insertions(+), 9 deletions(-) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..d0b1191 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,23 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch via yarn", + "runtimeVersion": "18", + "request": "launch", + "runtimeArgs": [ + "dev" + ], + "runtimeExecutable": "yarn", + "skipFiles": [ + "/**" + ], + "type": "node", + "env": { "DOMAIN": "localhost:3000"}, + "outputCapture": "std" + } + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 138ea5e..81a1633 100644 --- a/README.md +++ b/README.md @@ -41,14 +41,21 @@ Please note that this project has a [Code of Conduct](https://codeberg.org/keyox To run Keyoxide locally on your machine for development: -- install either - - NodeJS - - directly from their [website](https://nodejs.org/en/), or - - using [nvm](https://github.com/nvm-sh/nvm): `nvm install --lts; nvm use --lts` - - [yarn](https://yarnpkg.com/) - - [nix](https://nixos.org/guides/install-nix.html) with - [direnv](https://direnv.net/) will install yarn and other dependencies. -- install dependencies with `npm install` or `yarn` -- run the server with `npm dev` or `yarn dev` +1. Install Node using [nvm](https://github.com/nvm-sh/nvm): `nvm install --lts` +2. Swap over to LTS: `nvm use --lts` +3. Ensure that yarn is installed: `npm install -g yarn` +4. Install node dependencies: `yarn` +5. Run the server a la `DOMAIN='localhost:3000' yarn dev` Keyoxide will now be available at [https://localhost:3000](https://localhost:3000) + +For **vscode/vscodium** development, a `launcher.json` is provided. Ensure that +the first four steps have been performed at least once to ensure the proper +node and yarn binaries are available to vscode, then load the project. The +launcher provides a "Launch via yarn" debug option that will start the project +with the appropriate version and environment variables set. The project will +then be available in a browser. + +There is also a `shell.nix` file to install Node and yarn (steps one through +three above). + From ecc789a4a92b49a220fb25057be154bd07e55ec7 Mon Sep 17 00:00:00 2001 From: Preston Maness Date: Sun, 11 Jun 2023 23:41:33 -0500 Subject: [PATCH 002/144] first stab at it from keyoxide-web end --- src/api/v0/index.js | 3 ++- src/api/v2/keyoxide_profile.js | 3 ++- src/index.js | 4 +++- src/server/index.js | 8 ++++---- static-src/kx-claim.js | 7 ++++--- static-src/utils.js | 11 ++++++----- 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/api/v0/index.js b/src/api/v0/index.js index 32c7764..c99e526 100644 --- a/src/api/v0/index.js +++ b/src/api/v0/index.js @@ -192,7 +192,8 @@ const doVerification = async (data) => { const verificationOptions = { proxy: { hostname: process.env.PROXY_HOSTNAME, - policy: (process.env.PROXY_HOSTNAME !== '') ? 'adaptive' : 'never' + policy: (process.env.PROXY_HOSTNAME !== '') ? 'adaptive' : 'never', + scheme: (process.env.PROXY_SCHEME !== '') ? process.env.PROXY_SCHEME : (process.env.SCHEME !== '') ? process.env.SCHEME : 'https' } } diff --git a/src/api/v2/keyoxide_profile.js b/src/api/v2/keyoxide_profile.js index f13a7c2..c009808 100644 --- a/src/api/v2/keyoxide_profile.js +++ b/src/api/v2/keyoxide_profile.js @@ -195,7 +195,8 @@ const doVerification = async (data) => { const verificationOptions = { proxy: { hostname: process.env.PROXY_HOSTNAME, - policy: (process.env.PROXY_HOSTNAME !== '') ? 'adaptive' : 'never' + policy: (process.env.PROXY_HOSTNAME !== '') ? 'adaptive' : 'never', + scheme: (process.env.PROXY_SCHEME !== '') ? process.env.PROXY_SCHEME : (process.env.SCHEME !== '') ? process.env.SCHEME : 'https' } } diff --git a/src/index.js b/src/index.js index 4e5008e..691e462 100644 --- a/src/index.js +++ b/src/index.js @@ -48,6 +48,7 @@ app.set('env', process.env.NODE_ENV || 'production') app.engine('pug', pug.__express).set('view engine', 'pug') app.set('port', process.env.PORT || 3000) app.set('domain', process.env.DOMAIN) +app.set('scheme', process.env.SCHEME || 'https') app.set('keyoxide_version', packageData.version) app.set('onion_url', process.env.ONION_URL) @@ -65,7 +66,8 @@ if (app.get('onion_url')) { } app.use(stringReplace({ - PLACEHOLDER__PROXY_HOSTNAME: process.env.PROXY_HOSTNAME || process.env.DOMAIN || 'null' + PLACEHOLDER__PROXY_HOSTNAME: process.env.PROXY_HOSTNAME || process.env.DOMAIN || 'null', + PLACEHOLDER__PROXY_SCHEME: process.env.PROXY_SCHEME || process.env.SCHEME || 'https' }, { contentTypeFilterRegexp: /application\/javascript/ })) diff --git a/src/server/index.js b/src/server/index.js index 06ad467..c348a13 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -46,7 +46,7 @@ const generateWKDProfile = async (id) => { keyData = processKeyData(keyData) const keyoxideData = {} - keyoxideData.url = `https://${process.env.DOMAIN}/wkd/${id}` + keyoxideData.url = `${process.env.SCHEME}://${process.env.DOMAIN}/wkd/${id}` logger.debug('Generating a WKD profile', { component: 'wkd_profile_generator', action: 'done', profile_id: id }) @@ -88,9 +88,9 @@ const generateHKPProfile = async (id, keyserverDomain) => { const keyoxideData = {} if (!keyserverDomain || keyserverDomain === 'keys.openpgp.org') { - keyoxideData.url = `https://${process.env.DOMAIN}/hkp/${id}` + keyoxideData.url = `${process.env.SCHEME}://${process.env.DOMAIN}/hkp/${id}` } else { - keyoxideData.url = `https://${process.env.DOMAIN}/hkp/${keyserverDomain}/${id}` + keyoxideData.url = `${process.env.SCHEME}://${process.env.DOMAIN}/hkp/${keyserverDomain}/${id}` } logger.debug('Generating a HKP profile', @@ -196,7 +196,7 @@ const generateKeybaseProfile = async (username, fingerprint) => { keyData = processKeyData(keyData) const keyoxideData = {} - keyoxideData.url = `https://${process.env.DOMAIN}/keybase/${username}/${fingerprint}` + keyoxideData.url = `${process.env.SCHEME}://${process.env.DOMAIN}/keybase/${username}/${fingerprint}` logger.debug('Generating a Keybase profile', { component: 'keybase_profile_generator', action: 'done', username, fingerprint }) diff --git a/static-src/kx-claim.js b/static-src/kx-claim.js index fa4ab83..5db2adf 100644 --- a/static-src/kx-claim.js +++ b/static-src/kx-claim.js @@ -49,7 +49,8 @@ export class Claim extends HTMLElement { await claim.verify({ proxy: { policy: 'adaptive', - hostname: 'PLACEHOLDER__PROXY_HOSTNAME' + hostname: 'PLACEHOLDER__PROXY_HOSTNAME', + scheme: 'PLACEHOLDER__PROXY_SCHEME' } }); this.setAttribute('data-claim', JSON.stringify(claim)); @@ -192,7 +193,7 @@ export class Claim extends HTMLElement { const subsection_info_text = subsection_info.appendChild(document.createElement('div')); const result_proxyUsed = subsection_info_text.appendChild(document.createElement('p')); - result_proxyUsed.innerHTML = `A proxy was used to fetch the proof: PLACEHOLDER__PROXY_HOSTNAME`; + result_proxyUsed.innerHTML = `A proxy was used to fetch the proof: PLACEHOLDER__PROXY_HOSTNAME`; } // TODO Display errors @@ -217,4 +218,4 @@ export class Claim extends HTMLElement { // }); // } } -} \ No newline at end of file +} diff --git a/static-src/utils.js b/static-src/utils.js index 50e21b5..98135cb 100644 --- a/static-src/utils.js +++ b/static-src/utils.js @@ -46,19 +46,20 @@ export async function computeWKDLocalPart(localPart) { // Generate Keyoxide profile URL export async function generateProfileURL(data) { let hostname = data.hostname || window.location.hostname; + let scheme = data.scheme || window.location.protocol.slice(0,-1); if (data.input == "") { return "Waiting for input…"; } switch (data.source) { case "wkd": - return `https://${hostname}/${data.input}`; + return `${scheme}://${hostname}/${data.input}`; break; case "hkp": if (/.*@.*\..*/.test(data.input)) { - return `https://${hostname}/hkp/${data.input}`; + return `${scheme}://${hostname}/hkp/${data.input}`; } else { - return `https://${hostname}/${data.input}`; + return `${scheme}://${hostname}/${data.input}`; } break; case "keybase": @@ -67,7 +68,7 @@ export async function generateProfileURL(data) { return "Incorrect Keybase public key URL."; } const match = data.input.match(re); - return `https://${hostname}/keybase/${match[1]}/${match[2]}`; + return `${scheme}://${hostname}/keybase/${match[1]}/${match[2]}`; break; } } @@ -240,4 +241,4 @@ export async function verifyBcryptHash(input, hash) { } catch (_) { return false; } -} \ No newline at end of file +} From 9196aeaad636df15432406bc803aa2489f9611c5 Mon Sep 17 00:00:00 2001 From: Preston Maness Date: Sun, 11 Jun 2023 23:52:53 -0500 Subject: [PATCH 003/144] Add SCHEME environment variable to vscodium launcher --- .vscode/launch.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index d0b1191..07e5488 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -16,8 +16,8 @@ "/**" ], "type": "node", - "env": { "DOMAIN": "localhost:3000"}, + "env": { "DOMAIN": "localhost:3000", "SCHEME": "http"}, "outputCapture": "std" } ] -} \ No newline at end of file +} From 21bae8df69ccd60c834897c3d79216159b240faf Mon Sep 17 00:00:00 2001 From: Preston Maness Date: Mon, 12 Jun 2023 00:16:52 -0500 Subject: [PATCH 004/144] fix expected URL with SCHEME env var added --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 81a1633..6c582f1 100644 --- a/README.md +++ b/README.md @@ -45,16 +45,16 @@ To run Keyoxide locally on your machine for development: 2. Swap over to LTS: `nvm use --lts` 3. Ensure that yarn is installed: `npm install -g yarn` 4. Install node dependencies: `yarn` -5. Run the server a la `DOMAIN='localhost:3000' yarn dev` +5. Run the server locally a la `SCHEME='http' DOMAIN='localhost:3000' yarn dev` -Keyoxide will now be available at [https://localhost:3000](https://localhost:3000) +Keyoxide will now be available at [http://localhost:3000](http://localhost:3000) For **vscode/vscodium** development, a `launcher.json` is provided. Ensure that -the first four steps have been performed at least once to ensure the proper -node and yarn binaries are available to vscode, then load the project. The -launcher provides a "Launch via yarn" debug option that will start the project -with the appropriate version and environment variables set. The project will -then be available in a browser. +the first four steps have been performed at least once so that the proper node +and yarn binaries are available to vscode, then load the project. The launcher +provides a "Launch via yarn" debug option that will start the project with the +appropriate version and environment variables set. The project will then be +available in a browser. There is also a `shell.nix` file to install Node and yarn (steps one through three above). From 223e39209c719d42062f55177b744d196c5a51ef Mon Sep 17 00:00:00 2001 From: Preston Maness Date: Mon, 12 Jun 2023 01:00:05 -0500 Subject: [PATCH 005/144] Make sure default scheme is set here too --- src/server/index.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/server/index.js b/src/server/index.js index c348a13..313a15b 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -32,6 +32,10 @@ import * as doipjs from 'doipjs' import { fetchWKD, fetchHKP, fetchSignature, fetchKeybase } from './keys.js' import libravatar from 'libravatar' +const scheme = process.env.PROXY_SCHEME ? process.env.PROXY_SCHEME +: process.env.SCHEME ? process.env.SCHEME +: 'https' + const generateWKDProfile = async (id) => { logger.debug('Generating a WKD profile', { component: 'wkd_profile_generator', action: 'start', profile_id: id }) @@ -46,7 +50,7 @@ const generateWKDProfile = async (id) => { keyData = processKeyData(keyData) const keyoxideData = {} - keyoxideData.url = `${process.env.SCHEME}://${process.env.DOMAIN}/wkd/${id}` + keyoxideData.url = `${scheme}://${process.env.DOMAIN}/wkd/${id}` logger.debug('Generating a WKD profile', { component: 'wkd_profile_generator', action: 'done', profile_id: id }) @@ -88,9 +92,9 @@ const generateHKPProfile = async (id, keyserverDomain) => { const keyoxideData = {} if (!keyserverDomain || keyserverDomain === 'keys.openpgp.org') { - keyoxideData.url = `${process.env.SCHEME}://${process.env.DOMAIN}/hkp/${id}` + keyoxideData.url = `${scheme}://${process.env.DOMAIN}/hkp/${id}` } else { - keyoxideData.url = `${process.env.SCHEME}://${process.env.DOMAIN}/hkp/${keyserverDomain}/${id}` + keyoxideData.url = `${scheme}://${process.env.DOMAIN}/hkp/${keyserverDomain}/${id}` } logger.debug('Generating a HKP profile', @@ -196,7 +200,7 @@ const generateKeybaseProfile = async (username, fingerprint) => { keyData = processKeyData(keyData) const keyoxideData = {} - keyoxideData.url = `${process.env.SCHEME}://${process.env.DOMAIN}/keybase/${username}/${fingerprint}` + keyoxideData.url = `${scheme}://${process.env.DOMAIN}/keybase/${username}/${fingerprint}` logger.debug('Generating a Keybase profile', { component: 'keybase_profile_generator', action: 'done', username, fingerprint }) From cf11a3f343fbb5d68ee62307f051af2d103a7bf2 Mon Sep 17 00:00:00 2001 From: Preston Maness Date: Mon, 12 Jun 2023 10:02:43 -0500 Subject: [PATCH 006/144] Linting change --- src/server/index.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/server/index.js b/src/server/index.js index 313a15b..d35dbc3 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -32,9 +32,11 @@ import * as doipjs from 'doipjs' import { fetchWKD, fetchHKP, fetchSignature, fetchKeybase } from './keys.js' import libravatar from 'libravatar' -const scheme = process.env.PROXY_SCHEME ? process.env.PROXY_SCHEME -: process.env.SCHEME ? process.env.SCHEME -: 'https' +const scheme = process.env.PROXY_SCHEME + ? process.env.PROXY_SCHEME + : process.env.SCHEME + ? process.env.SCHEME + : 'https' const generateWKDProfile = async (id) => { logger.debug('Generating a WKD profile', From 916dfcc6d3e0c7c043d85489bd7caee81bc51175 Mon Sep 17 00:00:00 2001 From: Preston Maness Date: Tue, 13 Jun 2023 19:12:38 -0500 Subject: [PATCH 007/144] Add empty matrix env vars to launch.json --- .vscode/launch.json | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 07e5488..dab9439 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -16,7 +16,12 @@ "/**" ], "type": "node", - "env": { "DOMAIN": "localhost:3000", "SCHEME": "http"}, + "env": { + "DOMAIN": "localhost:3000", + "SCHEME": "http", + "MATRIX_ACCESS_TOKEN": "", + "MATRIX_INSTANCE": "" + }, "outputCapture": "std" } ] From 0dfc0cbe103728dae7d2b86082e9b25cb9d67edc Mon Sep 17 00:00:00 2001 From: Preston Maness Date: Tue, 13 Jun 2023 19:24:47 -0500 Subject: [PATCH 008/144] Update tests to pass in `scheme`, update dev quick start instructions --- README.md | 2 +- test/browser.test.js | 14 +++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 6c582f1..c4ea6b3 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ To run Keyoxide locally on your machine for development: 2. Swap over to LTS: `nvm use --lts` 3. Ensure that yarn is installed: `npm install -g yarn` 4. Install node dependencies: `yarn` -5. Run the server locally a la `SCHEME='http' DOMAIN='localhost:3000' yarn dev` +5. Run the server locally a la `SCHEME='http' DOMAIN='localhost:3000' MATRIX_ACCESS_TOKEN='XXXXXXyourTokenXXXXXX' MATRIX_INSTANCE='abc.yourmatrixdomain.tld' yarn dev` Keyoxide will now be available at [http://localhost:3000](http://localhost:3000) diff --git a/test/browser.test.js b/test/browser.test.js index a6c25ef..2315fa8 100644 --- a/test/browser.test.js +++ b/test/browser.test.js @@ -70,7 +70,8 @@ describe('browser', function () { const local = await utils.generateProfileURL({ source: 'wkd', input: 'test@doip.rocks', - hostname: 'keyoxide.instance' + hostname: 'keyoxide.instance', + scheme: 'https' }) local.should.equal('https://keyoxide.instance/test@doip.rocks') }) @@ -78,7 +79,8 @@ describe('browser', function () { const local = await utils.generateProfileURL({ source: 'hkp', input: 'test@doip.rocks', - hostname: 'keyoxide.instance' + hostname: 'keyoxide.instance', + scheme: 'https' }) local.should.equal('https://keyoxide.instance/hkp/test@doip.rocks') }) @@ -86,7 +88,8 @@ describe('browser', function () { const local = await utils.generateProfileURL({ source: 'hkp', input: '3637202523E7C1309AB79E99EF2DC5827B445F4B', - hostname: 'keyoxide.instance' + hostname: 'keyoxide.instance', + scheme: 'https' }) local.should.equal('https://keyoxide.instance/3637202523E7C1309AB79E99EF2DC5827B445F4B') }) @@ -94,10 +97,11 @@ describe('browser', function () { const local = await utils.generateProfileURL({ source: 'keybase', input: 'https://keybase.io/doip/pgp_keys.asc?fingerprint=3637202523E7C1309AB79E99EF2DC5827B445F4B', - hostname: 'keyoxide.instance' + hostname: 'keyoxide.instance', + scheme: 'https' }) local.should.equal('https://keyoxide.instance/keybase/doip/3637202523E7C1309AB79E99EF2DC5827B445F4B') }) }) }) -}) \ No newline at end of file +}) From c75291c201125d0bdf399b96181953adb507fbbe Mon Sep 17 00:00:00 2001 From: Preston Maness Date: Mon, 3 Jul 2023 14:52:28 -0500 Subject: [PATCH 009/144] Revert "Update tests to pass in `scheme`, update dev quick start instructions" This reverts commit 0dfc0cbe103728dae7d2b86082e9b25cb9d67edc. --- README.md | 2 +- test/browser.test.js | 14 +++++--------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index c4ea6b3..6c582f1 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ To run Keyoxide locally on your machine for development: 2. Swap over to LTS: `nvm use --lts` 3. Ensure that yarn is installed: `npm install -g yarn` 4. Install node dependencies: `yarn` -5. Run the server locally a la `SCHEME='http' DOMAIN='localhost:3000' MATRIX_ACCESS_TOKEN='XXXXXXyourTokenXXXXXX' MATRIX_INSTANCE='abc.yourmatrixdomain.tld' yarn dev` +5. Run the server locally a la `SCHEME='http' DOMAIN='localhost:3000' yarn dev` Keyoxide will now be available at [http://localhost:3000](http://localhost:3000) diff --git a/test/browser.test.js b/test/browser.test.js index 2315fa8..a6c25ef 100644 --- a/test/browser.test.js +++ b/test/browser.test.js @@ -70,8 +70,7 @@ describe('browser', function () { const local = await utils.generateProfileURL({ source: 'wkd', input: 'test@doip.rocks', - hostname: 'keyoxide.instance', - scheme: 'https' + hostname: 'keyoxide.instance' }) local.should.equal('https://keyoxide.instance/test@doip.rocks') }) @@ -79,8 +78,7 @@ describe('browser', function () { const local = await utils.generateProfileURL({ source: 'hkp', input: 'test@doip.rocks', - hostname: 'keyoxide.instance', - scheme: 'https' + hostname: 'keyoxide.instance' }) local.should.equal('https://keyoxide.instance/hkp/test@doip.rocks') }) @@ -88,8 +86,7 @@ describe('browser', function () { const local = await utils.generateProfileURL({ source: 'hkp', input: '3637202523E7C1309AB79E99EF2DC5827B445F4B', - hostname: 'keyoxide.instance', - scheme: 'https' + hostname: 'keyoxide.instance' }) local.should.equal('https://keyoxide.instance/3637202523E7C1309AB79E99EF2DC5827B445F4B') }) @@ -97,11 +94,10 @@ describe('browser', function () { const local = await utils.generateProfileURL({ source: 'keybase', input: 'https://keybase.io/doip/pgp_keys.asc?fingerprint=3637202523E7C1309AB79E99EF2DC5827B445F4B', - hostname: 'keyoxide.instance', - scheme: 'https' + hostname: 'keyoxide.instance' }) local.should.equal('https://keyoxide.instance/keybase/doip/3637202523E7C1309AB79E99EF2DC5827B445F4B') }) }) }) -}) +}) \ No newline at end of file From 55847461dc33d00dd1cd6e8d344a4837d089c8a9 Mon Sep 17 00:00:00 2001 From: Preston Maness Date: Mon, 3 Jul 2023 14:52:42 -0500 Subject: [PATCH 010/144] Revert "Add empty matrix env vars to launch.json" This reverts commit 916dfcc6d3e0c7c043d85489bd7caee81bc51175. --- .vscode/launch.json | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index dab9439..07e5488 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -16,12 +16,7 @@ "/**" ], "type": "node", - "env": { - "DOMAIN": "localhost:3000", - "SCHEME": "http", - "MATRIX_ACCESS_TOKEN": "", - "MATRIX_INSTANCE": "" - }, + "env": { "DOMAIN": "localhost:3000", "SCHEME": "http"}, "outputCapture": "std" } ] From 939f118931f0b1d92c4539db7b184e9559337dbd Mon Sep 17 00:00:00 2001 From: Preston Maness Date: Mon, 3 Jul 2023 14:52:54 -0500 Subject: [PATCH 011/144] Revert "fix expected URL with SCHEME env var added" This reverts commit 21bae8df69ccd60c834897c3d79216159b240faf. --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 6c582f1..81a1633 100644 --- a/README.md +++ b/README.md @@ -45,16 +45,16 @@ To run Keyoxide locally on your machine for development: 2. Swap over to LTS: `nvm use --lts` 3. Ensure that yarn is installed: `npm install -g yarn` 4. Install node dependencies: `yarn` -5. Run the server locally a la `SCHEME='http' DOMAIN='localhost:3000' yarn dev` +5. Run the server a la `DOMAIN='localhost:3000' yarn dev` -Keyoxide will now be available at [http://localhost:3000](http://localhost:3000) +Keyoxide will now be available at [https://localhost:3000](https://localhost:3000) For **vscode/vscodium** development, a `launcher.json` is provided. Ensure that -the first four steps have been performed at least once so that the proper node -and yarn binaries are available to vscode, then load the project. The launcher -provides a "Launch via yarn" debug option that will start the project with the -appropriate version and environment variables set. The project will then be -available in a browser. +the first four steps have been performed at least once to ensure the proper +node and yarn binaries are available to vscode, then load the project. The +launcher provides a "Launch via yarn" debug option that will start the project +with the appropriate version and environment variables set. The project will +then be available in a browser. There is also a `shell.nix` file to install Node and yarn (steps one through three above). From 59e4281b438f6ba69e293ab7e042c13969dd2299 Mon Sep 17 00:00:00 2001 From: Preston Maness Date: Mon, 3 Jul 2023 14:53:06 -0500 Subject: [PATCH 012/144] Revert "Add SCHEME environment variable to vscodium launcher" This reverts commit 9196aeaad636df15432406bc803aa2489f9611c5. --- .vscode/launch.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 07e5488..d0b1191 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -16,8 +16,8 @@ "/**" ], "type": "node", - "env": { "DOMAIN": "localhost:3000", "SCHEME": "http"}, + "env": { "DOMAIN": "localhost:3000"}, "outputCapture": "std" } ] -} +} \ No newline at end of file From f620684f35db8e31cc87a2afa3fb5451f95331e8 Mon Sep 17 00:00:00 2001 From: Preston Maness Date: Mon, 3 Jul 2023 14:53:15 -0500 Subject: [PATCH 013/144] Revert "Quickstart guidance updates" This reverts commit 5e0561df7efc3b96fced8c5dec8eef80d950e16c. --- .vscode/launch.json | 23 ----------------------- README.md | 25 +++++++++---------------- 2 files changed, 9 insertions(+), 39 deletions(-) delete mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index d0b1191..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "name": "Launch via yarn", - "runtimeVersion": "18", - "request": "launch", - "runtimeArgs": [ - "dev" - ], - "runtimeExecutable": "yarn", - "skipFiles": [ - "/**" - ], - "type": "node", - "env": { "DOMAIN": "localhost:3000"}, - "outputCapture": "std" - } - ] -} \ No newline at end of file diff --git a/README.md b/README.md index 81a1633..138ea5e 100644 --- a/README.md +++ b/README.md @@ -41,21 +41,14 @@ Please note that this project has a [Code of Conduct](https://codeberg.org/keyox To run Keyoxide locally on your machine for development: -1. Install Node using [nvm](https://github.com/nvm-sh/nvm): `nvm install --lts` -2. Swap over to LTS: `nvm use --lts` -3. Ensure that yarn is installed: `npm install -g yarn` -4. Install node dependencies: `yarn` -5. Run the server a la `DOMAIN='localhost:3000' yarn dev` +- install either + - NodeJS + - directly from their [website](https://nodejs.org/en/), or + - using [nvm](https://github.com/nvm-sh/nvm): `nvm install --lts; nvm use --lts` + - [yarn](https://yarnpkg.com/) + - [nix](https://nixos.org/guides/install-nix.html) with + [direnv](https://direnv.net/) will install yarn and other dependencies. +- install dependencies with `npm install` or `yarn` +- run the server with `npm dev` or `yarn dev` Keyoxide will now be available at [https://localhost:3000](https://localhost:3000) - -For **vscode/vscodium** development, a `launcher.json` is provided. Ensure that -the first four steps have been performed at least once to ensure the proper -node and yarn binaries are available to vscode, then load the project. The -launcher provides a "Launch via yarn" debug option that will start the project -with the appropriate version and environment variables set. The project will -then be available in a browser. - -There is also a `shell.nix` file to install Node and yarn (steps one through -three above). - From ba9ae78d7d90894a746ff4552e2257b64c501f81 Mon Sep 17 00:00:00 2001 From: Preston Maness Date: Mon, 3 Jul 2023 15:02:15 -0500 Subject: [PATCH 014/144] Update and add tests for configurable scheme --- test/browser.test.js | 58 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 49 insertions(+), 9 deletions(-) diff --git a/test/browser.test.js b/test/browser.test.js index a6c25ef..f8130c2 100644 --- a/test/browser.test.js +++ b/test/browser.test.js @@ -66,38 +66,78 @@ describe('browser', function () { }) }) describe('generateProfileURL()', function () { - it('should handle a WKD URL', async function () { + it('should handle a https WKD URL', async function () { const local = await utils.generateProfileURL({ source: 'wkd', input: 'test@doip.rocks', - hostname: 'keyoxide.instance' + hostname: 'keyoxide.instance', + scheme: 'https' }) local.should.equal('https://keyoxide.instance/test@doip.rocks') }) - it('should handle a HKP+email URL', async function () { + it('should handle a http WKD URL', async function () { + const local = await utils.generateProfileURL({ + source: 'wkd', + input: 'test@doip.rocks', + hostname: 'keyoxide.instance', + scheme: 'http' + }) + local.should.equal('http://keyoxide.instance/test@doip.rocks') + }) + it('should handle a https HKP+email URL', async function () { const local = await utils.generateProfileURL({ source: 'hkp', input: 'test@doip.rocks', - hostname: 'keyoxide.instance' + hostname: 'keyoxide.instance', + scheme: 'https' }) local.should.equal('https://keyoxide.instance/hkp/test@doip.rocks') }) - it('should handle a HKP+fingerprint URL', async function () { + it('should handle a http HKP+email URL', async function () { + const local = await utils.generateProfileURL({ + source: 'hkp', + input: 'test@doip.rocks', + hostname: 'keyoxide.instance', + scheme: 'http' + }) + local.should.equal('http://keyoxide.instance/hkp/test@doip.rocks') + }) + it('should handle a https HKP+fingerprint URL', async function () { const local = await utils.generateProfileURL({ source: 'hkp', input: '3637202523E7C1309AB79E99EF2DC5827B445F4B', - hostname: 'keyoxide.instance' + hostname: 'keyoxide.instance', + scheme: 'https' }) local.should.equal('https://keyoxide.instance/3637202523E7C1309AB79E99EF2DC5827B445F4B') }) - it('should handle a keybase URL', async function () { + it('should handle a http HKP+fingerprint URL', async function () { + const local = await utils.generateProfileURL({ + source: 'hkp', + input: '3637202523E7C1309AB79E99EF2DC5827B445F4B', + hostname: 'keyoxide.instance', + scheme: 'http' + }) + local.should.equal('http://keyoxide.instance/3637202523E7C1309AB79E99EF2DC5827B445F4B') + }) + it('should handle a https keybase URL', async function () { const local = await utils.generateProfileURL({ source: 'keybase', input: 'https://keybase.io/doip/pgp_keys.asc?fingerprint=3637202523E7C1309AB79E99EF2DC5827B445F4B', - hostname: 'keyoxide.instance' + hostname: 'keyoxide.instance', + scheme: 'https' }) local.should.equal('https://keyoxide.instance/keybase/doip/3637202523E7C1309AB79E99EF2DC5827B445F4B') }) + it('should handle a http keybase URL', async function () { + const local = await utils.generateProfileURL({ + source: 'keybase', + input: 'https://keybase.io/doip/pgp_keys.asc?fingerprint=3637202523E7C1309AB79E99EF2DC5827B445F4B', + hostname: 'keyoxide.instance', + scheme: 'http' + }) + local.should.equal('http://keyoxide.instance/keybase/doip/3637202523E7C1309AB79E99EF2DC5827B445F4B') + }) }) }) -}) \ No newline at end of file +}) From f5ef4b6623c992e1c748ff0b4088cd94a35cb23b Mon Sep 17 00:00:00 2001 From: Preston Maness Date: Tue, 4 Jul 2023 00:06:33 -0500 Subject: [PATCH 015/144] Initial testing of configurable scheme code with mocks --- package.json | 1 + test/server.test.js | 63 +++++++++++++++++++++++++++++++++++++++++++++ yarn.lock | 5 ++++ 3 files changed, 69 insertions(+) diff --git a/package.json b/package.json index 35b6eb4..088dee9 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "chai": "^4.3.6", "copy-webpack-plugin": "^10.2.4", "css-loader": "^6.6.0", + "esmock": "^2.3.1", "license-check-and-add": "^4.0.5", "mini-css-extract-plugin": "^2.5.3", "mocha": "^10.1.0", diff --git a/test/server.test.js b/test/server.test.js index 42185f9..c19ea92 100644 --- a/test/server.test.js +++ b/test/server.test.js @@ -1,6 +1,10 @@ import 'chai/register-should.js' +import esmock from 'esmock' + import * as utils from '../src/server/utils.js' +const _env = Object.assign({},process.env) + describe('server', function () { describe('utils', function () { describe('computeWKDLocalPart()', function () { @@ -26,4 +30,63 @@ describe('server', function () { }) }) }) + describe('index', function () { + + // Brittle mocking :( + describe('generateHKPProfile', function() { + + it('should handle implicit scheme with implicit keys.openpgp.org keyserver', async function () { + + // Arrange + const fingerprint = '79895B2E0F87503F1DDE80B649765D7F0DDD9BD5' + // the process.env needs to be here, before the esmock setup. + process.env.DOMAIN = "keyoxide.org" + //process.env.SCHEME = "http" + + const index = await esmock('../src/server/index.js', { + '../src/server/keys.js': { + fetchHKP: () => { + return Promise.resolve({ + publicKey: { + getPrimaryUser: () => { + return { + user: { + userID: { + email: "example@example.org" + } + } + } + } + }, + fetchURL: 'example.com' + }) + } + }, + 'doipjs': { + keys: { + process: () => { + return { + key: {}, + 'fingerprint': fingerprint, + users: [] + } + } + } + }, + 'libravatar': { + get_avatar_url: () => { + return "example.org/avatar.png" + } + } + }) + + // Act + const local = await index.generateHKPProfile(fingerprint) + + // Assert + local.keyoxide.url.should.equal(`https://keyoxide.org/hkp/${fingerprint}`) + + }) + }) + }) }) \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 19a39c0..3614114 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2204,6 +2204,11 @@ eslint@^8.13.0: strip-json-comments "^3.1.0" text-table "^0.2.0" +esmock@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/esmock/-/esmock-2.3.1.tgz#27a3afea73d7fb352f27c7ac04f66cfbd2c34316" + integrity sha512-ZxuxfhwGSlStiJFbw6Z+a70fB6SutTcUr0X8dhehx6aqiC5kgBvEYV4xNW94cKaD8gaqD7P00RjBH/pfao2CQA== + espree@^9.4.0: version "9.4.1" resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.1.tgz#51d6092615567a2c2cff7833445e37c28c0065bd" From f74260f79be277149c6e65cabe13d8e718ec5c5f Mon Sep 17 00:00:00 2001 From: Preston Maness Date: Tue, 4 Jul 2023 10:43:41 -0500 Subject: [PATCH 016/144] Use esmock for mocking dependencies --- package.json | 2 +- src/server/index.js | 22 ++++++++-------- test/server.test.js | 62 ++++++++++++++++++++++++++++++++++++++------- 3 files changed, 66 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index 088dee9..06b33ac 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "scripts": { "start": "node --experimental-fetch ./", "dev": "LOG_LEVEL=debug yarn run watch & yarn run build:static:dev", - "test": "yarn run standard:check && yarn run rome:check && mocha", + "test": "yarn run standard:check && yarn run rome:check && mocha --loader=esmock", "watch": "./node_modules/.bin/nodemon --config nodemon.json ./", "build": "yarn run build:server & yarn run build:static", "build:server": "ncc build ./src/index.js -o dist", diff --git a/src/server/index.js b/src/server/index.js index d35dbc3..eeef49c 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -32,12 +32,6 @@ import * as doipjs from 'doipjs' import { fetchWKD, fetchHKP, fetchSignature, fetchKeybase } from './keys.js' import libravatar from 'libravatar' -const scheme = process.env.PROXY_SCHEME - ? process.env.PROXY_SCHEME - : process.env.SCHEME - ? process.env.SCHEME - : 'https' - const generateWKDProfile = async (id) => { logger.debug('Generating a WKD profile', { component: 'wkd_profile_generator', action: 'start', profile_id: id }) @@ -52,7 +46,7 @@ const generateWKDProfile = async (id) => { keyData = processKeyData(keyData) const keyoxideData = {} - keyoxideData.url = `${scheme}://${process.env.DOMAIN}/wkd/${id}` + keyoxideData.url = `${getScheme()}://${process.env.DOMAIN}/wkd/${id}` logger.debug('Generating a WKD profile', { component: 'wkd_profile_generator', action: 'done', profile_id: id }) @@ -94,9 +88,9 @@ const generateHKPProfile = async (id, keyserverDomain) => { const keyoxideData = {} if (!keyserverDomain || keyserverDomain === 'keys.openpgp.org') { - keyoxideData.url = `${scheme}://${process.env.DOMAIN}/hkp/${id}` + keyoxideData.url = `${getScheme()}://${process.env.DOMAIN}/hkp/${id}` } else { - keyoxideData.url = `${scheme}://${process.env.DOMAIN}/hkp/${keyserverDomain}/${id}` + keyoxideData.url = `${getScheme()}://${process.env.DOMAIN}/hkp/${keyserverDomain}/${id}` } logger.debug('Generating a HKP profile', @@ -202,7 +196,7 @@ const generateKeybaseProfile = async (username, fingerprint) => { keyData = processKeyData(keyData) const keyoxideData = {} - keyoxideData.url = `${scheme}://${process.env.DOMAIN}/keybase/${username}/${fingerprint}` + keyoxideData.url = `${getScheme()}://${process.env.DOMAIN}/keybase/${username}/${fingerprint}` logger.debug('Generating a Keybase profile', { component: 'keybase_profile_generator', action: 'done', username, fingerprint }) @@ -271,6 +265,14 @@ const computeExtraData = async (key, keyData) => { } } +const getScheme = () => { + return process.env.PROXY_SCHEME + ? process.env.PROXY_SCHEME + : process.env.SCHEME + ? process.env.SCHEME + : 'https' +} + export { generateWKDProfile } export { generateHKPProfile } export { generateAutoProfile } diff --git a/test/server.test.js b/test/server.test.js index c19ea92..192d53f 100644 --- a/test/server.test.js +++ b/test/server.test.js @@ -30,20 +30,27 @@ describe('server', function () { }) }) }) + + // NOTE: This is necessarily brittle. If these tests fail + // in the future, start looking here for what new behaviour + // in the implementation is or isn't getting mocked + // appropriately. describe('index', function () { - // Brittle mocking :( - describe('generateHKPProfile', function() { + describe('generateHKPProfile()', function() { - it('should handle implicit scheme with implicit keys.openpgp.org keyserver', async function () { + let index; + let fingerprint; - // Arrange - const fingerprint = '79895B2E0F87503F1DDE80B649765D7F0DDD9BD5' - // the process.env needs to be here, before the esmock setup. + this.beforeEach(async () => { + + // Common arrangement pieces that don't change per test + fingerprint = '79895B2E0F87503F1DDE80B649765D7F0DDD9BD5' process.env.DOMAIN = "keyoxide.org" - //process.env.SCHEME = "http" - const index = await esmock('../src/server/index.js', { + // mock the appropriate pieces of our dependencies so we + // can test just the `keyoxide.url` return value. + index = await esmock('../src/server/index.js', { '../src/server/keys.js': { fetchHKP: () => { return Promise.resolve({ @@ -52,7 +59,7 @@ describe('server', function () { return { user: { userID: { - email: "example@example.org" + email: "example@example.net" } } } @@ -79,6 +86,16 @@ describe('server', function () { } } }) + }) + + this.afterEach(() => { + process.env = _env + }) + + it('should handle implicit scheme for keyoxide URL', async function () { + + // Arrange + // no setting process.env.SCHEME // Act const local = await index.generateHKPProfile(fingerprint) @@ -87,6 +104,33 @@ describe('server', function () { local.keyoxide.url.should.equal(`https://keyoxide.org/hkp/${fingerprint}`) }) + + it('should handle explicit http scheme for keyoxide URL', async function () { + + // Arrange + process.env.SCHEME = "http" + + // Act + const local = await index.generateHKPProfile(fingerprint) + + // Assert + local.keyoxide.url.should.equal(`http://keyoxide.org/hkp/${fingerprint}`) + + }) + + it('should handle explicit https scheme for keyoxide URL', async function () { + + // Arrange + process.env.SCHEME = "https" + + // Act + const local = await index.generateHKPProfile(fingerprint) + + // Assert + local.keyoxide.url.should.equal(`https://keyoxide.org/hkp/${fingerprint}`) + + }) + }) }) }) \ No newline at end of file From 03d7bf3446813cf869d782925a12db0a9ed40e21 Mon Sep 17 00:00:00 2001 From: Preston Maness Date: Fri, 14 Jul 2023 18:38:06 -0500 Subject: [PATCH 017/144] Test adjustments for doipjs 1.0 and v3 API --- package.json | 1 - src/server/index.js | 2 +- test/server.test.js | 43 +++++++++++++------------------------------ 3 files changed, 14 insertions(+), 32 deletions(-) diff --git a/package.json b/package.json index beed55f..0acc5e5 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,6 @@ "scripts": { "start": "node --experimental-fetch ./", "dev": "LOG_LEVEL=debug yarn run watch & yarn run build:static:dev", - "test": "yarn run standard:check && yarn run rome:check && mocha --loader=esmock", "test": "yarn run lint && mocha --loader=esmock", "watch": "./node_modules/.bin/nodemon --config nodemon.json ./", "build": "yarn run build:server & yarn run build:static", diff --git a/src/server/index.js b/src/server/index.js index 19f217f..6990187 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -38,7 +38,7 @@ const generateAspeProfile = async (id) => { return doipjs.asp.fetchASPE(id) .then(profile => { - profile.addVerifier('keyoxide', `https://${process.env.DOMAIN}/${id}`) + profile.addVerifier('keyoxide', `${getScheme()}://${process.env.DOMAIN}/${id}`) profile = processAspProfile(profile) return profile }) diff --git a/test/server.test.js b/test/server.test.js index 192d53f..1674161 100644 --- a/test/server.test.js +++ b/test/server.test.js @@ -1,5 +1,6 @@ import 'chai/register-should.js' import esmock from 'esmock' +import * as doipjs from 'doipjs' import * as utils from '../src/server/utils.js' @@ -41,6 +42,8 @@ describe('server', function () { let index; let fingerprint; + /** @type {import('doipjs').Profile */ + let profile; this.beforeEach(async () => { @@ -48,36 +51,16 @@ describe('server', function () { fingerprint = '79895B2E0F87503F1DDE80B649765D7F0DDD9BD5' process.env.DOMAIN = "keyoxide.org" + const persona = new doipjs.Persona("test", [new doipjs.Claim('dns:domain.tld?type=TXT')]) + + profile = new doipjs.Profile(doipjs.enums.ProfileType.OPENPGP, fingerprint, [persona]) + // mock the appropriate pieces of our dependencies so we // can test just the `keyoxide.url` return value. index = await esmock('../src/server/index.js', { - '../src/server/keys.js': { + '../src/server/openpgpProfiles.js': { fetchHKP: () => { - return Promise.resolve({ - publicKey: { - getPrimaryUser: () => { - return { - user: { - userID: { - email: "example@example.net" - } - } - } - } - }, - fetchURL: 'example.com' - }) - } - }, - 'doipjs': { - keys: { - process: () => { - return { - key: {}, - 'fingerprint': fingerprint, - users: [] - } - } + return Promise.resolve(profile) } }, 'libravatar': { @@ -101,7 +84,7 @@ describe('server', function () { const local = await index.generateHKPProfile(fingerprint) // Assert - local.keyoxide.url.should.equal(`https://keyoxide.org/hkp/${fingerprint}`) + local.verifiers[0].url.should.equal(`https://keyoxide.org/hkp/${fingerprint}`) }) @@ -114,7 +97,7 @@ describe('server', function () { const local = await index.generateHKPProfile(fingerprint) // Assert - local.keyoxide.url.should.equal(`http://keyoxide.org/hkp/${fingerprint}`) + local.verifiers[0].url.should.equal(`http://keyoxide.org/hkp/${fingerprint}`) }) @@ -127,10 +110,10 @@ describe('server', function () { const local = await index.generateHKPProfile(fingerprint) // Assert - local.keyoxide.url.should.equal(`https://keyoxide.org/hkp/${fingerprint}`) + local.verifiers[0].url.should.equal(`https://keyoxide.org/hkp/${fingerprint}`) }) }) }) -}) \ No newline at end of file +}) From ba62125b18e386b99dee5626e7160d82698b2503 Mon Sep 17 00:00:00 2001 From: Preston Maness Date: Sat, 19 Aug 2023 15:49:53 -0500 Subject: [PATCH 018/144] Temporary fork while working on new providers --- nodemon.json | 3 ++- package.json | 8 ++++---- yarn.lock | 48 ++++++++++++++++++++++++------------------------ 3 files changed, 30 insertions(+), 29 deletions(-) diff --git a/nodemon.json b/nodemon.json index ade96d8..54320a5 100644 --- a/nodemon.json +++ b/nodemon.json @@ -1,6 +1,7 @@ { "execArgs": [ - "--experimental-fetch" + "--experimental-fetch", + "--preserve-symlinks" ], "env": { "NODE_ENV": "development" diff --git a/package.json b/package.json index 0acc5e5..aa46e80 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "keyoxide-web", - "version": "3.6.4", + "name": "@aspensmonster/keyoxide-web", + "version": "3.7.0", "description": "Verifying online identity with cryptography", "main": "./src/index.js", "type": "module", @@ -9,7 +9,7 @@ "bent": "^7.3.12", "body-parser": "^1.19.0", "dialog-polyfill": "^0.5.6", - "doipjs": "^1.0.0", + "@aspensmonster/doipjs": "^1.0.1", "dotenv": "^16.0.3", "express": "^4.17.1", "express-validator": "^6.13.0", @@ -43,7 +43,7 @@ "webpack-cli": "^5.0.0" }, "scripts": { - "start": "node --experimental-fetch ./", + "start": "node --experimental-fetch --preserve-symlinks ./", "dev": "LOG_LEVEL=debug yarn run watch & yarn run build:static:dev", "test": "yarn run lint && mocha --loader=esmock", "watch": "./node_modules/.bin/nodemon --config nodemon.json ./", diff --git a/yarn.lock b/yarn.lock index 2844930..0a4aefb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15,6 +15,30 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" +"@aspensmonster/doipjs@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@aspensmonster/doipjs/-/doipjs-1.0.1.tgz#de8e033f67773f6614b40c5101db8f6af0bcc20b" + integrity sha512-HhcFrUGvpbLcFWIyeT3unz+sjhIHs9H3nbp/zO1upiN/J/Ipn2klLVtuXlkqWcOtyG8Sy+lcGn7VwWXOI4TG6g== + dependencies: + "@openpgp/hkp-client" "^0.0.3" + "@openpgp/wkd-client" "^0.0.4" + "@xmpp/client" "^0.13.1" + "@xmpp/debug" "^0.13.0" + axios "^0.25.0" + browser-or-node "^1.3.0" + cors "^2.8.5" + entities "^4.4.0" + express "^4.17.1" + express-validator "^6.10.0" + hash-wasm "^4.9.0" + irc-upd "^0.11.0" + jose "^4.14.4" + merge-options "^3.0.3" + openpgp "^5.5.0" + rfc4648 "^1.5.2" + valid-url "^1.0.9" + validator "^13.9.0" + "@babel/cli@^7.16.0": version "7.22.6" resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.22.6.tgz#63f5be2a0abd587ccfbdc93424fa85f43142cc53" @@ -1830,30 +1854,6 @@ doctypes@^1.1.0: resolved "https://registry.yarnpkg.com/doctypes/-/doctypes-1.1.0.tgz#ea80b106a87538774e8a3a4a5afe293de489e0a9" integrity sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ== -doipjs@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/doipjs/-/doipjs-1.0.0.tgz#cb0fe9f324a8c3cd3ddb0a4bec009c772f06ab1e" - integrity sha512-KUIbHBE5fdIose6mml9uf4rd5m7cz2g3929DPs1EZamHV/V0M8RgUhGC4u6QGYYHREDrG3fOj43V9RrCG4hv5A== - dependencies: - "@openpgp/hkp-client" "^0.0.3" - "@openpgp/wkd-client" "^0.0.4" - "@xmpp/client" "^0.13.1" - "@xmpp/debug" "^0.13.0" - axios "^0.25.0" - browser-or-node "^1.3.0" - cors "^2.8.5" - entities "^4.4.0" - express "^4.17.1" - express-validator "^6.10.0" - hash-wasm "^4.9.0" - irc-upd "^0.11.0" - jose "^4.14.4" - merge-options "^3.0.3" - openpgp "^5.5.0" - rfc4648 "^1.5.2" - valid-url "^1.0.9" - validator "^13.9.0" - dotenv@^16.0.3: version "16.3.1" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" From 3285d43be4fe5cdd13849ad03be43865e6627ce8 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Fri, 25 Aug 2023 15:20:11 +0200 Subject: [PATCH 019/144] chore: update ci syntax --- .woodpecker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index fb57928..e61dbc4 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -1,4 +1,4 @@ -pipeline: +steps: test: image: node commands: From 7cc54d9fa23faaf9c0eb160b20fe807a7a007c8a Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Fri, 25 Aug 2023 15:49:55 +0200 Subject: [PATCH 020/144] fix: fix ci copy error --- Dockerfile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8fcf2aa..3cbf745 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,10 +12,10 @@ FROM node:16-alpine WORKDIR /app COPY --from=builder /app/package.json /app/package.json -COPY --from=builder /app/dist /app/dist -COPY --from=builder /app/content /app/content -COPY --from=builder /app/views /app/views -COPY --from=builder /app/static /app/static +COPY --from=builder /app/dist/* /app/dist/ +COPY --from=builder /app/content/* /app/content/ +COPY --from=builder /app/views/* /app/views/ +COPY --from=builder /app/static/* /app/static/ EXPOSE 3000 From bc2f2b6a8618fc645ded683472205aaa54755f97 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Fri, 25 Aug 2023 15:53:42 +0200 Subject: [PATCH 021/144] fix: fix ci copy error --- Dockerfile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 3cbf745..a273a08 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,11 +11,11 @@ RUN yarn run build FROM node:16-alpine WORKDIR /app -COPY --from=builder /app/package.json /app/package.json -COPY --from=builder /app/dist/* /app/dist/ -COPY --from=builder /app/content/* /app/content/ -COPY --from=builder /app/views/* /app/views/ -COPY --from=builder /app/static/* /app/static/ +COPY --from=builder /app/package.json ./package.json +COPY --from=builder /app/dist ./dist +COPY --from=builder /app/content ./content +COPY --from=builder /app/views ./views +COPY --from=builder /app/static ./static EXPOSE 3000 From fef0e1105267eaeabb8ac7af2943234234c1b151 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Fri, 25 Aug 2023 15:55:54 +0200 Subject: [PATCH 022/144] fix: fix ci copy error --- Dockerfile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index a273a08..e724d2a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,10 +12,10 @@ FROM node:16-alpine WORKDIR /app COPY --from=builder /app/package.json ./package.json -COPY --from=builder /app/dist ./dist -COPY --from=builder /app/content ./content -COPY --from=builder /app/views ./views -COPY --from=builder /app/static ./static +COPY --from=builder /app/dist/ /app/dist/ +COPY --from=builder /app/content/ /app/content/ +COPY --from=builder /app/views/ /app/views/ +COPY --from=builder /app/static/ /app/static/ EXPOSE 3000 From 876959dd7c68f7c4099d0528185745c766ff8c54 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Fri, 25 Aug 2023 16:32:07 +0200 Subject: [PATCH 023/144] fix: fix ci copy error --- Dockerfile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index e724d2a..a6b2adb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,11 +11,11 @@ RUN yarn run build FROM node:16-alpine WORKDIR /app -COPY --from=builder /app/package.json ./package.json -COPY --from=builder /app/dist/ /app/dist/ -COPY --from=builder /app/content/ /app/content/ -COPY --from=builder /app/views/ /app/views/ -COPY --from=builder /app/static/ /app/static/ +COPY --from=builder /app/package.json . +COPY --from=builder /app/dist/ . +COPY --from=builder /app/content/ . +COPY --from=builder /app/views/ . +COPY --from=builder /app/static/ . EXPOSE 3000 From 0ea467ab3bf2bf36d3bfbd65a8f038417546fb13 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Sat, 26 Aug 2023 10:52:46 +0200 Subject: [PATCH 024/144] fix: fix ci copy error --- Dockerfile | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index a6b2adb..4efc4f8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,11 +11,8 @@ RUN yarn run build FROM node:16-alpine WORKDIR /app -COPY --from=builder /app/package.json . -COPY --from=builder /app/dist/ . -COPY --from=builder /app/content/ . -COPY --from=builder /app/views/ . -COPY --from=builder /app/static/ . +COPY --from=builder /app /app +RUN rm -r /app/node_modules EXPOSE 3000 From 4e0562a2db6e22ddd817137816cb3b0066cbe290 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Mon, 28 Aug 2023 13:24:57 +0200 Subject: [PATCH 025/144] chore: release 4.0.0 --- CHANGELOG.md | 12 ++++++++++++ package.json | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a35ae06..8be3cd5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [4.0.0] - 2023-08-28 +### Added +- ASPE support +- Rome linting and formatting +- API v3 +### Changed +- Updated doipjs to 1.0.0 +### Fixed +- Missing primaryUserIndex +### Removed +- API v0, v1, v2 + ## [3.6.4] - 2023-03-27 ### Fixed - Missing /graphql proxy API endpoint diff --git a/package.json b/package.json index 3a5d0bb..4a9c462 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keyoxide-web", - "version": "3.6.4", + "version": "4.0.0", "description": "Verifying online identity with cryptography", "main": "./src/index.js", "type": "module", From 7f432d40ec9cce48320bbfe9dc30f44c54f20121 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Mon, 28 Aug 2023 14:44:06 +0200 Subject: [PATCH 026/144] debug: debug Dockerfile --- Dockerfile | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 4efc4f8..96db0df 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,15 +4,19 @@ WORKDIR /app COPY . . RUN yarn --pure-lockfile -RUN yarn run build +RUN yarn run build:server +RUN yarn run build:static ### FROM node:16-alpine WORKDIR /app -COPY --from=builder /app /app -RUN rm -r /app/node_modules +COPY --from=builder /app/package.json /app/package.json +COPY --from=builder /app/dist /app/dist +COPY --from=builder /app/content /app/content +COPY --from=builder /app/views /app/views +COPY --from=builder /app/static /app/static EXPOSE 3000 From 44f9664300a0568d49b21d97b0a81a2348d5f87e Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Mon, 28 Aug 2023 14:48:42 +0200 Subject: [PATCH 027/144] chore: release 4.0.1 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8be3cd5..70aa8c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [4.0.1] - 2023-08-28 +### Fixed +- CI docker builds + ## [4.0.0] - 2023-08-28 ### Added - ASPE support diff --git a/package.json b/package.json index 4a9c462..e3cf97b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keyoxide-web", - "version": "4.0.0", + "version": "4.0.1", "description": "Verifying online identity with cryptography", "main": "./src/index.js", "type": "module", From 1373f145879261000bb2dccbac41a0ccf842b0ec Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Thu, 31 Aug 2023 14:38:03 +0200 Subject: [PATCH 028/144] fix: handle doip promise rejection --- src/server/openpgpProfiles.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/server/openpgpProfiles.js b/src/server/openpgpProfiles.js index 2192bbe..c0086af 100644 --- a/src/server/openpgpProfiles.js +++ b/src/server/openpgpProfiles.js @@ -102,7 +102,16 @@ const fetchWKD = (id) => { reject(new Error('No public keys could be read from the data fetched using WKD')) } - profile = await doipjs.openpgp.parsePublicKey(publicKey) + try { + profile = await doipjs.openpgp.parsePublicKey(publicKey) + } catch (error) { + profile = null + } + + if (!profile) { + reject(new Error('No public keys could be fetched using WKD')) + return + } profile.publicKey.fetch.method = 'wkd' profile.publicKey.fetch.query = id profile.publicKey.fetch.resolvedUrl = fetchURL From 35c1dbc13d0cf5d76f027e6ba40c6d6cad9a87e7 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Thu, 31 Aug 2023 14:50:50 +0200 Subject: [PATCH 029/144] fix: syntax --- src/server/openpgpProfiles.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/server/openpgpProfiles.js b/src/server/openpgpProfiles.js index c0086af..ef11ace 100644 --- a/src/server/openpgpProfiles.js +++ b/src/server/openpgpProfiles.js @@ -105,13 +105,10 @@ const fetchWKD = (id) => { try { profile = await doipjs.openpgp.parsePublicKey(publicKey) } catch (error) { - profile = null - } - - if (!profile) { reject(new Error('No public keys could be fetched using WKD')) return } + profile.publicKey.fetch.method = 'wkd' profile.publicKey.fetch.query = id profile.publicKey.fetch.resolvedUrl = fetchURL From 563718ed51056d14feb0605effae5ee9d83108df Mon Sep 17 00:00:00 2001 From: Preston Maness Date: Thu, 31 Aug 2023 20:28:05 -0500 Subject: [PATCH 030/144] Revert "Temporary fork while working on new providers" This reverts commit ba62125b18e386b99dee5626e7160d82698b2503. Accidentally committed this on the wrong branch. --- nodemon.json | 3 +-- package.json | 8 ++++---- yarn.lock | 48 ++++++++++++++++++++++++------------------------ 3 files changed, 29 insertions(+), 30 deletions(-) diff --git a/nodemon.json b/nodemon.json index 54320a5..ade96d8 100644 --- a/nodemon.json +++ b/nodemon.json @@ -1,7 +1,6 @@ { "execArgs": [ - "--experimental-fetch", - "--preserve-symlinks" + "--experimental-fetch" ], "env": { "NODE_ENV": "development" diff --git a/package.json b/package.json index aa46e80..0acc5e5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { - "name": "@aspensmonster/keyoxide-web", - "version": "3.7.0", + "name": "keyoxide-web", + "version": "3.6.4", "description": "Verifying online identity with cryptography", "main": "./src/index.js", "type": "module", @@ -9,7 +9,7 @@ "bent": "^7.3.12", "body-parser": "^1.19.0", "dialog-polyfill": "^0.5.6", - "@aspensmonster/doipjs": "^1.0.1", + "doipjs": "^1.0.0", "dotenv": "^16.0.3", "express": "^4.17.1", "express-validator": "^6.13.0", @@ -43,7 +43,7 @@ "webpack-cli": "^5.0.0" }, "scripts": { - "start": "node --experimental-fetch --preserve-symlinks ./", + "start": "node --experimental-fetch ./", "dev": "LOG_LEVEL=debug yarn run watch & yarn run build:static:dev", "test": "yarn run lint && mocha --loader=esmock", "watch": "./node_modules/.bin/nodemon --config nodemon.json ./", diff --git a/yarn.lock b/yarn.lock index 0a4aefb..2844930 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15,30 +15,6 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@aspensmonster/doipjs@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@aspensmonster/doipjs/-/doipjs-1.0.1.tgz#de8e033f67773f6614b40c5101db8f6af0bcc20b" - integrity sha512-HhcFrUGvpbLcFWIyeT3unz+sjhIHs9H3nbp/zO1upiN/J/Ipn2klLVtuXlkqWcOtyG8Sy+lcGn7VwWXOI4TG6g== - dependencies: - "@openpgp/hkp-client" "^0.0.3" - "@openpgp/wkd-client" "^0.0.4" - "@xmpp/client" "^0.13.1" - "@xmpp/debug" "^0.13.0" - axios "^0.25.0" - browser-or-node "^1.3.0" - cors "^2.8.5" - entities "^4.4.0" - express "^4.17.1" - express-validator "^6.10.0" - hash-wasm "^4.9.0" - irc-upd "^0.11.0" - jose "^4.14.4" - merge-options "^3.0.3" - openpgp "^5.5.0" - rfc4648 "^1.5.2" - valid-url "^1.0.9" - validator "^13.9.0" - "@babel/cli@^7.16.0": version "7.22.6" resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.22.6.tgz#63f5be2a0abd587ccfbdc93424fa85f43142cc53" @@ -1854,6 +1830,30 @@ doctypes@^1.1.0: resolved "https://registry.yarnpkg.com/doctypes/-/doctypes-1.1.0.tgz#ea80b106a87538774e8a3a4a5afe293de489e0a9" integrity sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ== +doipjs@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/doipjs/-/doipjs-1.0.0.tgz#cb0fe9f324a8c3cd3ddb0a4bec009c772f06ab1e" + integrity sha512-KUIbHBE5fdIose6mml9uf4rd5m7cz2g3929DPs1EZamHV/V0M8RgUhGC4u6QGYYHREDrG3fOj43V9RrCG4hv5A== + dependencies: + "@openpgp/hkp-client" "^0.0.3" + "@openpgp/wkd-client" "^0.0.4" + "@xmpp/client" "^0.13.1" + "@xmpp/debug" "^0.13.0" + axios "^0.25.0" + browser-or-node "^1.3.0" + cors "^2.8.5" + entities "^4.4.0" + express "^4.17.1" + express-validator "^6.10.0" + hash-wasm "^4.9.0" + irc-upd "^0.11.0" + jose "^4.14.4" + merge-options "^3.0.3" + openpgp "^5.5.0" + rfc4648 "^1.5.2" + valid-url "^1.0.9" + validator "^13.9.0" + dotenv@^16.0.3: version "16.3.1" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" From ae6a3b0fdbdd09d654df4b1c78eb483bb95650be Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Fri, 1 Sep 2023 13:16:53 +0200 Subject: [PATCH 031/144] fix: yarn script calls --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index e3cf97b..dd99dee 100644 --- a/package.json +++ b/package.json @@ -43,10 +43,10 @@ }, "scripts": { "start": "node --experimental-fetch ./", - "dev": "LOG_LEVEL=debug yarn run watch & yarn run build:static:dev", + "dev": "LOG_LEVEL=debug yarn run watch && yarn run build:static:dev", "test": "yarn run lint && mocha", "watch": "./node_modules/.bin/nodemon --config nodemon.json ./", - "build": "yarn run build:server & yarn run build:static", + "build": "yarn run build:server && yarn run build:static", "build:server": "ncc build ./src/index.js -o dist", "build:static": "webpack --config webpack.config.js --env static=true --env mode=production", "build:static:dev": "webpack --config webpack.config.js --env static=true --env mode=development", From cc343198b4a40a4828c9685f9b8a80c190d225a5 Mon Sep 17 00:00:00 2001 From: Preston Maness Date: Wed, 6 Sep 2023 23:29:54 -0500 Subject: [PATCH 032/144] `return` from `reject`s --- src/server/openpgpProfiles.js | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/server/openpgpProfiles.js b/src/server/openpgpProfiles.js index ef11ace..d82eade 100644 --- a/src/server/openpgpProfiles.js +++ b/src/server/openpgpProfiles.js @@ -44,12 +44,12 @@ const fetchWKD = (id) => { let fetchURL = null if (!id.includes('@')) { - reject(new Error(`The WKD identifier "${id}" is invalid`)) + return reject(new Error(`The WKD identifier "${id}" is invalid`)) } const [, localPart, domain] = /([^@]*)@(.*)/.exec(id) if (!(localPart && domain)) { - reject(new Error(`The WKD identifier "${id}" is invalid`)) + return reject(new Error(`The WKD identifier "${id}" is invalid`)) } const localEncoded = await computeWKDLocalPart(localPart) const urlAdvanced = `https://openpgpkey.${domain}/.well-known/openpgpkey/${domain}/hu/${localEncoded}` @@ -82,12 +82,12 @@ const fetchWKD = (id) => { } }) } catch (error) { - reject(new Error('No public keys could be fetched using WKD')) + return reject(new Error('No public keys could be fetched using WKD')) } } if (!plaintext) { - reject(new Error('No public keys could be fetched using WKD')) + return reject(new Error('No public keys could be fetched using WKD')) } try { @@ -95,18 +95,17 @@ const fetchWKD = (id) => { binaryKey: plaintext }) } catch (error) { - reject(new Error('No public keys could be read from the data fetched using WKD')) + return reject(new Error('No public keys could be read from the data fetched using WKD')) } if (!publicKey) { - reject(new Error('No public keys could be read from the data fetched using WKD')) + return reject(new Error('No public keys could be read from the data fetched using WKD')) } try { profile = await doipjs.openpgp.parsePublicKey(publicKey) } catch (error) { - reject(new Error('No public keys could be fetched using WKD')) - return + return reject(new Error('No public keys could be fetched using WKD')) } profile.publicKey.fetch.method = 'wkd' @@ -160,8 +159,7 @@ const fetchHKP = (id, keyserverDomain) => { } if (!profile) { - reject(new Error('No public keys could be fetched using HKP')) - return + return reject(new Error('No public keys could be fetched using HKP')) } profile.publicKey.fetch.method = 'hkp' @@ -187,12 +185,12 @@ const fetchSignature = (signature) => { profile = await doipjs.signatures.parse(signature) // TODO Find the URL to the key } catch (error) { - reject(new Error(`Signature could not be properly read (${error.message})`)) + return reject(new Error(`Signature could not be properly read (${error.message})`)) } // Check if a key was fetched if (!profile) { - reject(new Error('No profile could be fetched')) + return reject(new Error('No profile could be fetched')) } resolve(profile) @@ -210,11 +208,11 @@ const fetchKeybase = (username, fingerprint) => { profile = await doipjs.openpgp.fetchKeybase(username, fingerprint) fetchURL = `https://keybase.io/${username}/pgp_keys.asc?fingerprint=${fingerprint}` } catch (error) { - reject(new Error('No public keys could be fetched from Keybase')) + return reject(new Error('No public keys could be fetched from Keybase')) } if (!profile) { - reject(new Error('No public keys could be fetched from Keybase')) + return reject(new Error('No public keys could be fetched from Keybase')) } profile.publicKey.fetch.method = 'http' From 0dd591c2c3d597467b4570547d5484eee3ed9f79 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Tue, 12 Sep 2023 18:40:08 +0200 Subject: [PATCH 033/144] chore: release 4.0.2 --- CHANGELOG.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 70aa8c4..0aaa99c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [4.0.2] - 2023-09-12 +### Fixed +- Handle doip promise rejection +- yarn script calls + ## [4.0.1] - 2023-08-28 ### Fixed - CI docker builds diff --git a/package.json b/package.json index dd99dee..4e5d59b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keyoxide-web", - "version": "4.0.1", + "version": "4.0.2", "description": "Verifying online identity with cryptography", "main": "./src/index.js", "type": "module", From 4907f094eab526a846d1d6c268d98e07fb093623 Mon Sep 17 00:00:00 2001 From: Preston Maness Date: Tue, 12 Sep 2023 16:40:57 -0500 Subject: [PATCH 034/144] Use latest esmock 2.5.0, remove --loader --loader=esmock is required for Node versions less than v20.6.0. --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index b38dadc..d5cb14d 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,7 @@ "chai": "^4.3.6", "copy-webpack-plugin": "^11.0.0", "css-loader": "^6.6.0", - "esmock": "^2.3.1", + "esmock": "^2.5.0", "license-check-and-add": "^4.0.5", "mini-css-extract-plugin": "^2.5.3", "mocha": "^10.1.0", @@ -45,7 +45,7 @@ "scripts": { "start": "node --experimental-fetch ./", "dev": "LOG_LEVEL=debug yarn run watch && yarn run build:static:dev", - "test": "yarn run lint && mocha --loader=esmock", + "test": "yarn run lint && mocha", "watch": "./node_modules/.bin/nodemon --config nodemon.json ./", "build": "yarn run build:server && yarn run build:static", "build:server": "ncc build ./src/index.js -o dist", From 49568b9422625f3c20393755f62ad2145023ac88 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Thu, 14 Sep 2023 10:31:11 +0200 Subject: [PATCH 035/144] fix: docker container reference --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 138ea5e..397aa34 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,9 @@ Self-hosting Keyoxide is an important aspect of the project. Users need to trust The Docker container allows you to easily self-host the [Keyoxide](https://keyoxide.org) project. To get started, simply run: -`docker run -d -p 3000:3000 keyoxide/keyoxide:stable` +```sh +docker run -d -p 3000:3000 codeberg.org/keyoxide/keyoxide-web:latest +``` Keyoxide will now be available by visiting http://localhost:3000. From 6ec95cb6591d85c4d0e040d5fedf0e8ff104a9cc Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Thu, 14 Sep 2023 10:36:22 +0200 Subject: [PATCH 036/144] fix: replace funding badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 397aa34..ebff545 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![Docker Image Version (latest semver)](https://img.shields.io/docker/v/keyoxide/keyoxide?sort=semver&style=for-the-badge)](https://hub.docker.com/r/keyoxide/keyoxide) [![Docker Pulls](https://img.shields.io/docker/pulls/keyoxide/keyoxide?style=for-the-badge)](https://hub.docker.com/r/keyoxide/keyoxide) [![Mastodon Follow](https://img.shields.io/mastodon/follow/247838?domain=https%3A%2F%2Ffosstodon.org&style=for-the-badge)](https://fosstodon.org/@keyoxide) -[![Liberapay receiving](https://img.shields.io/liberapay/receives/keyoxide?style=for-the-badge)](https://liberapay.com/Keyoxide) +[![Open Collective backers and sponsors](https://img.shields.io/opencollective/all/keyoxide?style=for-the-badge)](https://opencollective.com/keyoxide) [Keyoxide](https://keyoxide.org) is a modern, secure and decentralized platform to prove your online identity. From bad5af6770f2a67370667c7e5ce1a667f15a4574 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Thu, 14 Sep 2023 11:04:31 +0200 Subject: [PATCH 037/144] feat: update to node 20 --- Dockerfile | 6 +++--- package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Dockerfile b/Dockerfile index 96db0df..71c311d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:16-alpine as builder +FROM node:20-alpine as builder WORKDIR /app COPY . . @@ -9,7 +9,7 @@ RUN yarn run build:static ### -FROM node:16-alpine +FROM node:20-alpine WORKDIR /app COPY --from=builder /app/package.json /app/package.json @@ -20,4 +20,4 @@ COPY --from=builder /app/static /app/static EXPOSE 3000 -CMD node --experimental-fetch ./dist/ \ No newline at end of file +CMD node ./dist/ \ No newline at end of file diff --git a/package.json b/package.json index 4e5d59b..071511f 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ "webpack-cli": "^5.0.0" }, "scripts": { - "start": "node --experimental-fetch ./", + "start": "node ./", "dev": "LOG_LEVEL=debug yarn run watch && yarn run build:static:dev", "test": "yarn run lint && mocha", "watch": "./node_modules/.bin/nodemon --config nodemon.json ./", From 75709a1168308b5922f2e6e3a3acb05f0308be1f Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Thu, 14 Sep 2023 11:55:45 +0200 Subject: [PATCH 038/144] fix: avoid adding script if data is missing --- views/profile.pug | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/views/profile.pug b/views/profile.pug index 95db723..c96dc3e 100644 --- a/views/profile.pug +++ b/views/profile.pug @@ -37,10 +37,11 @@ mixin generatePersona(persona, isPrimary) p Proof link: not accessible from browser block content - script. - kx = { - publicKey: !{JSON.stringify(data.publicKey)} - } + if (data && 'publicKey' in data) + script. + kx = { + publicKey: !{JSON.stringify(data.publicKey)} + } if (data && 'errors' in data && data.errors.length > 0) section From aeee377ea327d988e9c50f61703ad7dfbc56737f Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Thu, 14 Sep 2023 12:30:12 +0200 Subject: [PATCH 039/144] fix: remove obsolete argument --- nodemon.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/nodemon.json b/nodemon.json index ade96d8..099d21a 100644 --- a/nodemon.json +++ b/nodemon.json @@ -1,7 +1,4 @@ { - "execArgs": [ - "--experimental-fetch" - ], "env": { "NODE_ENV": "development" }, From 8eb90beda2594d2f8ba5b267133d8503735a3b2a Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Thu, 14 Sep 2023 12:31:44 +0200 Subject: [PATCH 040/144] feat: display version --- src/routes/main.js | 5 +++-- src/routes/profile.js | 21 ++++++++++++++------- src/routes/util.js | 27 ++++++++++++++------------- src/server/utils.js | 9 +++++++++ views/partials/footer.pug | 7 ++++++- 5 files changed, 46 insertions(+), 23 deletions(-) diff --git a/src/routes/main.js b/src/routes/main.js index 34fb518..d4b165b 100644 --- a/src/routes/main.js +++ b/src/routes/main.js @@ -30,6 +30,7 @@ more information on this, and how to apply and follow the GNU AGPL, see { } } - res.render('index', { highlights }) + res.render('index', { highlights, meta: getMetaFromReq(req) }) }) router.get('/privacy', (req, res) => { const rawContent = readFileSync('./content/privacy-policy.md', 'utf8') const content = md.render(rawContent) - res.render('article', { title: 'Privacy policy', content }) + res.render('article', { title: 'Privacy policy', content, meta: getMetaFromReq(req) }) }) router.get('/.well-known/webfinger', (req, res) => { diff --git a/src/routes/profile.js b/src/routes/profile.js index 78149f5..9211bc5 100644 --- a/src/routes/profile.js +++ b/src/routes/profile.js @@ -31,12 +31,13 @@ import express from 'express' import bodyParserImport from 'body-parser' import { generateSignatureProfile, utils, generateWKDProfile, generateHKPProfile, generateAutoProfile, generateKeybaseProfile } from '../server/index.js' import { Profile } from 'doipjs' +import { getMetaFromReq } from '../server/utils.js' const router = express.Router() const bodyParser = bodyParserImport.urlencoded({ extended: false }) router.get('/sig', (req, res) => { - res.render('profile', { isSignature: true, signature: null }) + res.render('profile', { isSignature: true, signature: null, meta: getMetaFromReq(req) }) }) router.post('/sig', bodyParser, async (req, res) => { @@ -49,7 +50,8 @@ router.post('/sig', bodyParser, async (req, res) => { isSignature: true, signature: req.body.signature, enable_message_encryption: false, - enable_signature_verification: false + enable_signature_verification: false, + meta: getMetaFromReq(req) }) }) @@ -61,7 +63,8 @@ router.get('/wkd/:id', async (req, res) => { title, data: data instanceof Profile ? data.toJSON() : data, enable_message_encryption: false, - enable_signature_verification: false + enable_signature_verification: false, + meta: getMetaFromReq(req) }) }) @@ -73,7 +76,8 @@ router.get('/hkp/:id', async (req, res) => { title, data: data instanceof Profile ? data.toJSON() : data, enable_message_encryption: false, - enable_signature_verification: false + enable_signature_verification: false, + meta: getMetaFromReq(req) }) }) @@ -85,7 +89,8 @@ router.get('/hkp/:server/:id', async (req, res) => { title, data: data instanceof Profile ? data.toJSON() : data, enable_message_encryption: false, - enable_signature_verification: false + enable_signature_verification: false, + meta: getMetaFromReq(req) }) }) @@ -97,7 +102,8 @@ router.get('/keybase/:username/:fingerprint', async (req, res) => { title, data: data instanceof Profile ? data.toJSON() : data, enable_message_encryption: false, - enable_signature_verification: false + enable_signature_verification: false, + meta: getMetaFromReq(req) }) }) @@ -109,7 +115,8 @@ router.get('/:id', async (req, res) => { title, data: data instanceof Profile ? data.toJSON() : data, enable_message_encryption: false, - enable_signature_verification: false + enable_signature_verification: false, + meta: getMetaFromReq(req) }) }) diff --git a/src/routes/util.js b/src/routes/util.js index 84412f6..bc5d207 100644 --- a/src/routes/util.js +++ b/src/routes/util.js @@ -28,52 +28,53 @@ if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see . */ import express from 'express' +import { getMetaFromReq } from '../server/utils.js' const router = express.Router() router.get('/', function (req, res) { - res.render('util/index') + res.render('util/index', { meta: getMetaFromReq(req) }) }) router.get('/profile-url', function (req, res) { - res.render('util/profile-url') + res.render('util/profile-url', { meta: getMetaFromReq(req) }) }) router.get('/profile-url/:input', function (req, res) { - res.render('util/profile-url', { input: req.params.input }) + res.render('util/profile-url', { input: req.params.input, meta: getMetaFromReq(req) }) }) router.get('/qr', function (req, res) { - res.render('util/qr') + res.render('util/qr', { meta: getMetaFromReq(req) }) }) router.get('/qr/:input', function (req, res) { - res.render('util/qr', { input: req.params.input }) + res.render('util/qr', { input: req.params.input, meta: getMetaFromReq(req) }) }) router.get('/qrfp', function (req, res) { - res.render('util/qrfp') + res.render('util/qrfp', { meta: getMetaFromReq(req) }) }) router.get('/qrfp/:input', function (req, res) { - res.render('util/qrfp', { input: req.params.input }) + res.render('util/qrfp', { input: req.params.input, meta: getMetaFromReq(req) }) }) router.get('/wkd', function (req, res) { - res.render('util/wkd') + res.render('util/wkd', { meta: getMetaFromReq(req) }) }) router.get('/wkd/:input', function (req, res) { - res.render('util/wkd', { input: req.params.input }) + res.render('util/wkd', { input: req.params.input, meta: getMetaFromReq(req) }) }) router.get('/argon2', function (req, res) { - res.render('util/argon2') + res.render('util/argon2', { meta: getMetaFromReq(req) }) }) router.get('/argon2/:input', function (req, res) { - res.render('util/argon2', { input: req.params.input }) + res.render('util/argon2', { input: req.params.input, meta: getMetaFromReq(req) }) }) router.get('/bcrypt', function (req, res) { - res.render('util/bcrypt') + res.render('util/bcrypt', { meta: getMetaFromReq(req) }) }) router.get('/bcrypt/:input', function (req, res) { - res.render('util/bcrypt', { input: req.params.input }) + res.render('util/bcrypt', { input: req.params.input, meta: getMetaFromReq(req) }) }) export default router diff --git a/src/server/utils.js b/src/server/utils.js index 30884e2..7f0b6e0 100644 --- a/src/server/utils.js +++ b/src/server/utils.js @@ -78,3 +78,12 @@ export function encodeZBase32 (data) { } return result } + +export function getMetaFromReq (req) { + return { + env: req.app.get('env'), + keyoxide: { + version: req.app.get('keyoxide_version') + } + } +} \ No newline at end of file diff --git a/views/partials/footer.pug b/views/partials/footer.pug index 4552fd4..85b7d88 100644 --- a/views/partials/footer.pug +++ b/views/partials/footer.pug @@ -27,4 +27,9 @@ footer br a(href="https://ariadne.id") ariadne.id - p.copyright © 2022 Keyoxide project contributors \ No newline at end of file + p.copyright + | Version #{meta.keyoxide.version} + if (meta.env === "development") + | -dev + br + | © 2023 Keyoxide project contributors \ No newline at end of file From 339e0e9e0eb0bec06b321ff7df2bbeda5e751b64 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Thu, 14 Sep 2023 12:35:39 +0200 Subject: [PATCH 041/144] fix: wrong color --- static-src/styles.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static-src/styles.css b/static-src/styles.css index b7948ac..5e9b3cd 100644 --- a/static-src/styles.css +++ b/static-src/styles.css @@ -223,7 +223,7 @@ footer { margin: 4.8rem 0 0; padding: 0 1.6rem 1.6rem; background-color: var(--footer-background-color); - color: var(--footer-background-color); + color: var(--footer-text-color); } .container { From 31d9fb541eb1c315160c6700d3ece7f1ca8618b1 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Thu, 14 Sep 2023 12:37:29 +0200 Subject: [PATCH 042/144] fix: apply linting fix --- src/server/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/utils.js b/src/server/utils.js index 7f0b6e0..6ca15ec 100644 --- a/src/server/utils.js +++ b/src/server/utils.js @@ -86,4 +86,4 @@ export function getMetaFromReq (req) { version: req.app.get('keyoxide_version') } } -} \ No newline at end of file +} From 211d30b2c1fb26ae94036ee65ec27813bbad1526 Mon Sep 17 00:00:00 2001 From: Preston Maness Date: Thu, 14 Sep 2023 11:33:10 -0500 Subject: [PATCH 043/144] Include yarn.lock with updated esmock --- yarn.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/yarn.lock b/yarn.lock index 2844930..028e4c5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2216,10 +2216,10 @@ eslint@^8.41.0: strip-json-comments "^3.1.0" text-table "^0.2.0" -esmock@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/esmock/-/esmock-2.3.1.tgz#27a3afea73d7fb352f27c7ac04f66cfbd2c34316" - integrity sha512-ZxuxfhwGSlStiJFbw6Z+a70fB6SutTcUr0X8dhehx6aqiC5kgBvEYV4xNW94cKaD8gaqD7P00RjBH/pfao2CQA== +esmock@^2.5.0: + version "2.5.1" + resolved "https://registry.yarnpkg.com/esmock/-/esmock-2.5.1.tgz#cef05c9cd23c46edbfb2e0add34466f6c52e37f6" + integrity sha512-3pu+ri9kNrRjahR8c+FWXphK3xpKrgBwLHu+A+Xj3vw84fGsScWY3SWTH1v5nSiheYQAdlz5Ny+a319tlle1mA== espree@^9.6.0: version "9.6.0" From 2afcb240d30641f5116d32ae154bfdcab8916ebd Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Fri, 15 Sep 2023 14:24:10 +0200 Subject: [PATCH 044/144] feat: support scss --- package.json | 6 ++++-- webpack.config.js | 5 +++-- yarn.lock | 33 +++++++++++++++++++++++++++------ 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index db65e31..8aecc17 100644 --- a/package.json +++ b/package.json @@ -36,15 +36,17 @@ "mocha": "^10.1.0", "nodemon": "^2.0.20", "rome": "^12.1", + "sass": "^1.67.0", + "sass-loader": "^13.3.2", "standard": "^17.0.0", "style-loader": "^3.3.1", - "webpack": "^5.75.0", + "webpack": "^5.88.2", "webpack-bundle-analyzer": "^4.7.0", "webpack-cli": "^5.0.0" }, "scripts": { "start": "node ./", - "dev": "LOG_LEVEL=debug yarn run watch && yarn run build:static:dev", + "dev": "LOG_LEVEL=debug yarn run watch & yarn run build:static:dev", "test": "yarn run lint && mocha", "watch": "./node_modules/.bin/nodemon --config nodemon.json ./", "build": "yarn run build:server && yarn run build:static", diff --git a/webpack.config.js b/webpack.config.js index fcbc590..04f836e 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -25,10 +25,11 @@ export default (env) => { module: { rules: [ { - test: /\.css$/, + test: /\.s[ca]ss$/, use: [ MiniCssExtractPlugin.loader, - 'css-loader' + 'css-loader', + 'sass-loader' ] } ] diff --git a/yarn.lock b/yarn.lock index 028e4c5..746e052 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1451,7 +1451,7 @@ check-error@^1.0.2: resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA== -chokidar@3.5.3, chokidar@^3.4.0, chokidar@^3.5.2: +chokidar@3.5.3, "chokidar@>=3.0.0 <4.0.0", chokidar@^3.4.0, chokidar@^3.5.2: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== @@ -2818,6 +2818,11 @@ ignore@^5.1.1, ignore@^5.1.2, ignore@^5.2.0, ignore@^5.2.4: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== +immutable@^4.0.0: + version "4.3.4" + resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.4.tgz#2e07b33837b4bb7662f288c244d1ced1ef65a78f" + integrity sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA== + import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" @@ -4368,6 +4373,22 @@ saslmechanisms@^0.1.1: resolved "https://registry.yarnpkg.com/saslmechanisms/-/saslmechanisms-0.1.1.tgz#478be1429500fcfaa780be88b3343ced7d2a9182" integrity sha512-pVlvK5ysevz8MzybRnDIa2YMxn0OJ7b9lDiWhMoaKPoJ7YkAg/7YtNjUgaYzElkwHxsw8dBMhaEn7UP6zxEwPg== +sass-loader@^13.3.2: + version "13.3.2" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-13.3.2.tgz#460022de27aec772480f03de17f5ba88fa7e18c6" + integrity sha512-CQbKl57kdEv+KDLquhC+gE3pXt74LEAzm+tzywcA0/aHZuub8wTErbjAoNI57rPUWRYRNC5WUnNl8eGJNbDdwg== + dependencies: + neo-async "^2.6.2" + +sass@^1.67.0: + version "1.67.0" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.67.0.tgz#fed84d74b9cd708db603b1380d6dc1f71bb24f6f" + integrity sha512-SVrO9ZeX/QQyEGtuZYCVxoeAL5vGlYjJ9p4i4HFuekWl8y/LtJ7tJc10Z+ck1c8xOuoBm2MYzcLfTAffD0pl/A== + dependencies: + chokidar ">=3.0.0 <4.0.0" + immutable "^4.0.0" + source-map-js ">=0.6.2 <2.0.0" + schema-utils@^3.1.1, schema-utils@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" @@ -4536,7 +4557,7 @@ slash@^4.0.0: resolved "https://registry.yarnpkg.com/slash/-/slash-4.0.0.tgz#2422372176c4c6c5addb5e2ada885af984b396a7" integrity sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew== -source-map-js@^1.0.2: +"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== @@ -5011,10 +5032,10 @@ webpack-sources@^3.2.3: resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== -webpack@^5.75.0: - version "5.88.1" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.88.1.tgz#21eba01e81bd5edff1968aea726e2fbfd557d3f8" - integrity sha512-FROX3TxQnC/ox4N+3xQoWZzvGXSuscxR32rbzjpXgEzWudJFEJBpdlkkob2ylrv5yzzufD1zph1OoFsLtm6stQ== +webpack@^5.88.2: + version "5.88.2" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.88.2.tgz#f62b4b842f1c6ff580f3fcb2ed4f0b579f4c210e" + integrity sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ== dependencies: "@types/eslint-scope" "^3.7.3" "@types/estree" "^1.0.0" From 4cb94fd40e5f947673e233dc08c4b81cae07126c Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Fri, 15 Sep 2023 14:24:54 +0200 Subject: [PATCH 045/144] feat: rewrite styles --- static-src/index.js | 4 +- static-src/kx-claim.js | 19 +- static-src/kx-styles.css | 264 ------------ static-src/kx-styles.scss | 269 ++++++++++++ static-src/styles.css | 656 ------------------------------ static-src/styles.scss | 100 +++++ static-src/styles/forms.scss | 152 +++++++ static-src/styles/layout.scss | 193 +++++++++ static-src/styles/typography.scss | 146 +++++++ static-src/styles/vars.scss | 158 +++++++ 10 files changed, 1033 insertions(+), 928 deletions(-) delete mode 100644 static-src/kx-styles.css create mode 100644 static-src/kx-styles.scss delete mode 100644 static-src/styles.css create mode 100644 static-src/styles.scss create mode 100644 static-src/styles/forms.scss create mode 100644 static-src/styles/layout.scss create mode 100644 static-src/styles/typography.scss create mode 100644 static-src/styles/vars.scss diff --git a/static-src/index.js b/static-src/index.js index 10d9bbe..9e35a14 100644 --- a/static-src/index.js +++ b/static-src/index.js @@ -35,8 +35,8 @@ import * as ui from './ui.js' import * as utils from './utils.js' // Import CSS files -import './styles.css' -import './kx-styles.css' +import './styles.scss' +import './kx-styles.scss' // Add functions to window window.showQR = utils.showQR diff --git a/static-src/kx-claim.js b/static-src/kx-claim.js index 29165c3..b7b16f3 100644 --- a/static-src/kx-claim.js +++ b/static-src/kx-claim.js @@ -58,19 +58,26 @@ export class Claim extends HTMLElement { updateContent(value) { const root = this; - const claim = doipjs.Claim.fromJson(JSON.parse(value)); + const claimJson = JSON.parse(value); + const claim = doipjs.Claim.fromJson(claimJson); - root.querySelector('.info .subtitle').innerText = claim.matches[0].about.name; - root.querySelector('.info .title').innerText = claim.matches[0].profile.display; + console.log(claimJson); + + root.querySelector('.info .title').innerText = claimJson.display.name; + root.querySelector('.info .subtitle').innerText = claimJson.display.serviceProviderName ?? + (claim.status < 300 ? '???' : '---'); try { if (claim.status >= 200) { - root.querySelector('.icons .verificationStatus').setAttribute('data-value', claim.status < 300 ? 'success' : 'failed'); + root.setAttribute('data-status', claim.status < 300 ? 'success' : 'failed'); + // root.querySelector('.icons .verificationStatus').setAttribute('data-value', claim.status < 300 ? 'success' : 'failed'); } else { - root.querySelector('.icons .verificationStatus').setAttribute('data-value', 'running'); + root.setAttribute('data-status', 'running'); + // root.querySelector('.icons .verificationStatus').setAttribute('data-value', 'running'); } } catch (error) { - root.querySelector('.icons .verificationStatus').setAttribute('data-value', 'failed'); + root.setAttribute('data-status', 'failed'); + // root.querySelector('.icons .verificationStatus').setAttribute('data-value', 'failed'); } const elContent = root.querySelector('.content'); diff --git a/static-src/kx-styles.css b/static-src/kx-styles.css deleted file mode 100644 index c8fb7f3..0000000 --- a/static-src/kx-styles.css +++ /dev/null @@ -1,264 +0,0 @@ -/* -Copyright (C) 2021 Yarmo Mackenbach - -This program is free software: you can redistribute it and/or modify it under -the terms of the GNU Affero General Public License as published by the Free -Software Foundation, either version 3 of the License, or (at your option) -any later version. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more -details. - -You should have received a copy of the GNU Affero General Public License along -with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - -If your software can interact with users remotely through a computer network, -you should also make sure that it provides a way for users to get its source. -For example, if your program is a web application, its interface could display -a "Source" link that leads users to an archive of the code. There are many -ways you could offer source, and different solutions will be better for different -programs; see section 13 for the specific requirements. - -You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. For -more information on this, and how to apply and follow the GNU AGPL, see . -*/ -kx-claim { - display: block; - margin: 12px 0; -} - -/* KX-ITEM */ -.kx-item details { - width: 100%; - border-radius: 8px; -} -.kx-item details p { - margin: 0; - word-break: break-word; - font-size: 1rem; -} -.kx-item details a { - color: var(--link-color); -} -.kx-item details hr { - border: none; - border-top: 2px solid var(--claim-background-color); -} -.kx-item details .content { - padding: 12px; - border: solid 3px var(--claim-background-color); - border-top: 0px; - border-radius: 0px 0px 8px 8px; -} -.kx-item details summary { - display: flex; - align-items: center; - padding: 8px 12px; - background-color: var(--claim-background-color); - border: solid 3px var(--claim-background-color); - border-radius: 8px; - list-style: none; - cursor: pointer; -} -.kx-item details summary::-webkit-details-marker { - display: none; -} -.kx-item details summary:hover, summary:focus { - border-color: var(--claim-border-accent-color); -} -details[open] summary { - border-radius: 8px 8px 0px 0px; -} -.kx-item details summary .info { - flex: 1; -} -.kx-item details summary .info .title { - font-size: 1.1em; -} -.kx-item details summary .claim__description p { - font-size: 1.4rem; - line-height: 2rem; -} -.kx-item details summary .claim__links p, p.subtle-links { - display: flex; - align-items: center; - flex-wrap: wrap; - font-size: 1rem; - color: var(--link-subtle-color); -} -.kx-item details summary .claim__links a, summary .claim__links span, p.subtle-links a { - font-size: 1rem; - margin: 0 10px 0 0; - color: var(--link-subtle-color); -} -.kx-item details summary .subtitle { - color: var(--claim-title-text-color); -} -.kx-item details summary .verificationStatus { - position: relative; - display: flex; - align-items: center; - justify-content: center; - width: 48px; - height: 48px; - border-radius: 100%; - color: #fff; - font-size: 2rem; - user-select: none; -} -.kx-item details summary .verificationStatus::after { - position: absolute; - display: flex; - top: 0; - left: 0; - right: 0; - bottom: 0; - align-items: center; - justify-content: center; -} -.kx-item details summary .verificationStatus .inProgress { - opacity: 0; - transition: opacity 0.4s ease; - pointer-events: none; -} -.kx-item details summary .verificationStatus[data-value="success"] { - content: "v"; - background-color: var(--success-color); -} -.kx-item details summary .verificationStatus[data-value="success"]::after { - content: "✔"; -} -.kx-item details summary .verificationStatus[data-value="failed"] { - background-color: var(--failure-color); -} -.kx-item details summary .verificationStatus[data-value="failed"]::after { - content: "✕"; -} -.kx-item details summary .verificationStatus[data-value="running"] .inProgress { - opacity: 1; -} - -.kx-item details .subsection { - display: flex; - align-items: center; - gap: 16px; -} -.kx-item details .subsection > img { - width: 24px; - height: 24px; - opacity: 0.4; -} -@media (prefers-color-scheme: dark) { - .kx-item details .subsection > img { - filter: invert(1); - } -} - -.kx-item details .inProgress { - font-size: 10px; - margin: 50px auto; - text-indent: -9999em; - width: 48px; - height: 48px; - border-radius: 50%; - background: var(--loader-color); - background: -moz-linear-gradient(left, var(--loader-color) 10%, rgba(255, 255, 255, 0) 42%); - background: -webkit-linear-gradient(left, var(--loader-color) 10%, rgba(255, 255, 255, 0) 42%); - background: -o-linear-gradient(left, var(--loader-color) 10%, rgba(255, 255, 255, 0) 42%); - background: -ms-linear-gradient(left, var(--loader-color) 10%, rgba(255, 255, 255, 0) 42%); - background: linear-gradient(to right, var(--loader-color) 10%, rgba(255, 255, 255, 0) 42%); - position: relative; - -webkit-animation: load3 1.4s infinite linear; - animation: load3 1.4s infinite linear; - -webkit-transform: translateZ(0); - -ms-transform: translateZ(0); - transform: translateZ(0); -} -.kx-item details .inProgress:before { - width: 50%; - height: 50%; - background: var(--loader-color); - border-radius: 100% 0 0 0; - position: absolute; - top: 0; - left: 0; - content: ''; -} -.kx-item details .inProgress:after { - background: var(--claim-background-color); - width: 65%; - height: 65%; - border-radius: 50%; - content: ''; - margin: auto; - position: absolute; - top: 0; - left: 0; - bottom: 0; - right: 0; -} -.kx-item details button { - padding: 0.4rem 0.8rem; - margin-right: 8px; - text-decoration: none; - text-transform: uppercase; - color: var(--button-text-color); - background-color: var(--button-background-color); - border: solid 2px var(--button-border-color); - border-radius: 4px; - cursor: pointer; -} -.kx-item details button:hover { - background-color: var(--button-hover-background-color); - border-color: var(--button-hover-border-color); - color: var(--button-hover-text-color); -} - -@media screen and (max-width: 640px) { - .kx-item details summary .claim__description p { - font-size: 1.2rem; - } - .kx-item details summary .claim__links a, p.subtle-links a { - font-size: 0.9rem; - } -} -@media screen and (max-width: 480px) { - summary .claim__description p { - font-size: 1rem; - } - .kx-item details summary .verificationStatus { - width: 36px; - height: 36px; - font-size: 1.6rem; - } - .kx-item details .inProgress { - width: 36px; - height: 36px; - } -} - -@-webkit-keyframes load3 { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } -} -@keyframes load3 { - 0% { - -webkit-transform: rotate(0deg); - transform: rotate(0deg); - } - 100% { - -webkit-transform: rotate(360deg); - transform: rotate(360deg); - } -} diff --git a/static-src/kx-styles.scss b/static-src/kx-styles.scss new file mode 100644 index 0000000..52db260 --- /dev/null +++ b/static-src/kx-styles.scss @@ -0,0 +1,269 @@ +/* +Copyright (C) 2021 Yarmo Mackenbach + +This program is free software: you can redistribute it and/or modify it under +the terms of the GNU Affero General Public License as published by the Free +Software Foundation, either version 3 of the License, or (at your option) +any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +details. + +You should have received a copy of the GNU Affero General Public License along +with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + +If your software can interact with users remotely through a computer network, +you should also make sure that it provides a way for users to get its source. +For example, if your program is a web application, its interface could display +a "Source" link that leads users to an archive of the code. There are many +ways you could offer source, and different solutions will be better for different +programs; see section 13 for the specific requirements. + +You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. For +more information on this, and how to apply and follow the GNU AGPL, see . +*/ +kx-claim { + padding: 2px 0; +} + +.kx-item { + display: block; + font-size: 0.9rem; + margin-left: -4px; + + details { + position: relative; + width: 100%; + border-radius: 4px; + z-index: 0; + + &[open] { + box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1); + z-index: 100; + } + + p { + margin: 0; + word-break: break-word; + font-size: 1em; + } + + a { + color: var(--link-color); + } + + hr { + border: none; + border-top: 2px solid var(--background-color); + } + + .content { + font-size: 0.9em; + padding: 12px; + background-color: var(--background-color); + border-top: 0px; + border-radius: 0px 0px 4px 4px; + } + + &[open] summary { + border-radius: 4px 4px 0px 0px; + background-color: var(--header-background-color) !important; + } + + summary { + display: flex; + align-items: center; + padding: 2px 4px; + border-radius: 4px; + list-style: none; + cursor: pointer; + + &::-webkit-details-marker { + display: none; + } + + &:hover, + &:focus { + background-color: var(--header-background-color); + } + + .info { + flex: 1; + } + + .info .title { + color: var(--text-color); + } + + .claim__links { + + p { + display: flex; + align-items: center; + flex-wrap: wrap; + font-size: 1em; + color: var(--link-color-subtle); + } + + .a, + span { + font-size: 1em; + margin: 0 10px 0 0; + color: var(--link-color-subtle); + } + } + + .subtitle-wrapper { + color: var(--text-color-subtle); + } + } + + .subsection { + display: flex; + align-items: center; + gap: 8px; + } + + .subsection>img { + width: 20px; + height: 20px; + opacity: 0.4; + } + + @media (prefers-color-scheme: dark) { + .subsection>img { + filter: invert(1); + } + } + } + + .verificationStatus { + position: relative; + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + border-radius: 100%; + color: #fff; + font-size: 1.4em; + user-select: none; + + &::after { + position: absolute; + display: flex; + top: 0; + left: 0; + right: 0; + bottom: 0; + align-items: center; + justify-content: center; + } + + .inProgress, + .success, + .failure { + position: absolute; + top: 0; + left: 0; + opacity: 0; + pointer-events: none; + } + + .inProgress { + color: var(--loader-color); + + svg { + animation: 1s linear 0s infinite rot360; + } + } + + .success { + color: var(--success-color); + } + + .failure { + color: var(--failure-color); + } + } + + button { + padding: 0.4rem 0.8em; + margin-right: 8px; + text-decoration: none; + text-transform: uppercase; + color: var(--button-text-color); + background-color: var(--button-background-color); + border: solid 2px var(--button-border-color); + border-radius: 4px; + cursor: pointer; + } + + button:hover { + background-color: var(--button-hover-background-color); + border-color: var(--button-hover-border-color); + color: var(--button-hover-text-color); + } + + &[data-status="running"] { + .title { + color: var(--text-color-subtle) !important; + } + .inProgress { + opacity: 1 !important; + } + } + + &[data-status="success"] { + .title { + color: var(--primary-color) !important; + font-weight: bold; + } + .success { + opacity: 1 !important; + } + } + + &[data-status="failed"] { + .title { + color: var(--text-color-subtle) !important; + } + .failure { + opacity: 1 !important; + } + } + + &[data-status="success"] .verificationStatus>div, + &[data-status="failed"] .verificationStatus>div { + transition: opacity 0.4s ease !important; + } + + @-webkit-keyframes rot360 { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } + } + + @keyframes rot360 { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + + 100% { + -webkit-transform: rotate(360deg); + transform: rotate(360deg); + } + } +} \ No newline at end of file diff --git a/static-src/styles.css b/static-src/styles.css deleted file mode 100644 index 5e9b3cd..0000000 --- a/static-src/styles.css +++ /dev/null @@ -1,656 +0,0 @@ -/* -Copyright (C) 2021 Yarmo Mackenbach - -This program is free software: you can redistribute it and/or modify it under -the terms of the GNU Affero General Public License as published by the Free -Software Foundation, either version 3 of the License, or (at your option) -any later version. - -This program is distributed in the hope that it will be useful, but WITHOUT -ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more -details. - -You should have received a copy of the GNU Affero General Public License along -with this program. If not, see . - -Also add information on how to contact you by electronic and paper mail. - -If your software can interact with users remotely through a computer network, -you should also make sure that it provides a way for users to get its source. -For example, if your program is a web application, its interface could display -a "Source" link that leads users to an archive of the code. There are many -ways you could offer source, and different solutions will be better for different -programs; see section 13 for the specific requirements. - -You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. For -more information on this, and how to apply and follow the GNU AGPL, see . -*/ -@import '../node_modules/fork-awesome/css/fork-awesome.css'; -@import '../node_modules/dialog-polyfill/dist/dialog-polyfill.css'; - -:root { - --white: hsl(0, 0%, 100%); - --black: hsl(0, 0%, 0%); - --grey-100: hsl(0, 0%, 90%); - --grey-200: hsl(0, 0%, 80%); - --grey-300: hsl(0, 0%, 70%); - --grey-400: hsl(0, 0%, 60%); - --grey-500: hsl(0, 0%, 50%); - --grey-600: hsl(0, 0%, 40%); - --grey-700: hsl(0, 0%, 30%); - --grey-900: hsl(0, 0%, 10%); - --green-300: hsl(110, 45%, 70%); - --green-400: hsl(110, 45%, 60%); - --green-600: hsl(110, 45%, 40%); - --red-400: hsl(10, 60%, 60%); - --blue-500: rgb(67, 176, 234); - --blue-700: hsl(201, 90%, 30%); - --purple-50: rgb(249, 248, 251); - --purple-100: rgb(238, 236, 248); - --purple-200: hsl(250, 48%, 90%); - --purple-300: hsl(250, 48%, 85%); - --purple-400: hsl(250, 48%, 70%); - --purple-500: hsl(250, 48%, 65%); - --purple-600: hsl(250, 48%, 60%); - --purple-700: hsl(250, 48%, 55%); - --purple-900: hsl(250, 38%, 45%); - --yellow-100: hsl(56, 100%, 95%); - --yellow-200: hsl(56, 100%, 90%); - --yellow-500: hsl(56, 100%, 65%); - - --loader-color: var(--purple-400); - --success-color: var(--green-600); - --failure-color: var(--red-400); - - --text-color: var(--grey-900); - --h1-color: var(--purple-700); - --h2-color: var(--purple-700); - --h2-small-color: var(--purple-600); - --h3-color: var(--grey-700); - --h3-small-color: var(--purple-400); - --h4-color: var(--grey-600); - --h4-small-color: var(--purple-400); - --link-color: var(--blue-700); - --link-subtle-color: var(--grey-700); - --nav-link-color: var(--purple-700); - - --button-text-color: var(--text-color); - --button-border-color: var(--purple-500); - --button-background-color: var(--white); - --button-hover-text-color: var(--white); - --button-hover-border-color: var(--purple-500); - --button-hover-background-color: var(--purple-500); - - --body-background-color: var(--white); - --footer-background-color: var(--purple-900); - --footer-text-color: var(--purple-200); - --card-background-color: var(--purple-50); - --card-border-color: var(--purple-200); - --claim-background-color: var(--purple-100); - --claim-border-accent-color: var(--purple-400); - --claim-title-text-color: var(--purple-700); - --input-focus-background-color: azure; - --focus-outline-color: lightskyblue; -} - -@media (prefers-color-scheme: dark) { - :root { - --text-color: var(--grey-100); - --h1-color: var(--purple-700); - --h2-color: var(--purple-300); - --h2-small-color: var(--purple-600); - --h3-color: var(--grey-300); - --h3-small-color: var(--purple-400); - --h4-color: var(--grey-300); - --h4-small-color: var(--purple-6400); - --link-color: var(--blue-500); - --link-subtle-color: var(--grey-700); - --nav-link-color: var(--purple-100); - - --loader-color: var(--purple-600); - --success-color: var(--green-600); - --failure-color: var(--red-400); - - --button-text-color: var(--white); - --button-border-color: var(--purple-700); - --button-background-color: var(--purple-900); - --button-hover-text-color: var(--white); - --button-hover-border-color: var(--purple-700); - --button-hover-background-color: var(--purple-700); - - --body-background-color: #121212; - --footer-background-color: #191720; - --footer-text-color: var(--purple-200); - --card-background-color: #191720; - --card-border-color: #26203a; - --claim-background-color: #26203a; - --claim-border-accent-color: var(--purple-400); - --claim-title-text-color: var(--purple-300); - --input-focus-background-color: azure; - --focus-outline-color: lightskyblue; - } -} - -* { - box-sizing: border-box; -} -:focus { - outline: none; - box-shadow: 0 0 0 3px var(--focus-outline-color); -} -input:focus, textarea:focus { - background: var(--input-focus-background-color); -} -input[type="radio"]:focus + label { - box-shadow: 0 0 0 3px var(--focus-outline-color); - background: var(--input-focus-background-color) !important; - color: var(--text-color) !important; -} -body { - display: flex; - flex-direction: column; - min-height: 100vh; - margin: 0; - padding: 1.6rem 0 0; - line-height: 1.4rem; - font-family: sans-serif; - color: var(--text-color); - background-color: var(--body-background-color); -} - -/* HELPERS */ -.spacer { - flex: 1; -} -.no-margin { - margin: 0 !important; -} -.full-width { - display: block; - width: 100% !important; -} -.half-width { - display: block; - width: 50% !important; -} -.select-all { - user-select: all; -} - -/* LAYOUT */ -header { - margin: 0 1.6rem 1.6rem; -} -header nav { - flex: 1; - display: flex; - flex-wrap: wrap; - align-items: center; - justify-content: center; - gap: 8px; -} -header nav a.logo { - width: 64px; - height: 64px; - font-size: 1.6rem; - text-transform: uppercase; - text-decoration: none; - color: var(--nav-link-color); -} -header nav a.logo img { - width: 100%; -} -nav a.text { - /* font-size: 0.9em; */ - margin: 0; - padding: 0.5em 1em; - text-transform: uppercase; - text-decoration: none; - color: var(--nav-link-color); - border-radius: 4px; -} -nav a.text:hover, nav a.text:active { - color: #fff; - background-color: var(--purple-500); -} -main { - flex: 1; - margin: 0 1.6rem; -} -footer { - margin: 4.8rem 0 0; - padding: 0 1.6rem 1.6rem; - background-color: var(--footer-background-color); - color: var(--footer-text-color); -} - -.container { - width: 100%; - max-width: 720px; - margin: 0 auto; -} -section.profile p, .demo p { - font-size: 1.2rem; -} -.demo { - margin: 4.8rem auto; -} - -.card { - margin: 0 0 1.6rem; - padding: 0 1.2rem; - background-color: #fff; - background-color: var(--card-background-color); - border: 2px solid var(--card-border-color); - border-radius: 4px; -} -.card.card--transparent { - padding-left: 0; - padding-right: 0; - background-color: transparent; - border: 0; -} -.card--profileHeader { - display: flex; - flex-direction: column; - flex-wrap: wrap; - align-items: center; - gap: 24px; -} -.card--profileHeader p, .card--profileHeader small { - margin: 0; -} -.card--small-profile { - display: flex; - flex-direction: column; - text-align: center; -} -.card--small-profile-dummy { - opacity: 0.5; - border: 0; -} -.card--small-profile .name { - font-size: 1.4em; -} -.card--small-profile p { - margin-top: 0; -} -.card--small-profile p span.fingerprint { - display: inline-block; - width: 100%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - font-size: 0.8rem; -} -#profileName { - font-size: 1.6rem; - color: var(--text-color); -} -#profileURLFingerprint { - font-size: 1rem; - margin: 0 0 1.2rem; -} - -.hcards { - display: grid; - grid-gap: 1.2rem; - grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); - margin-bottom: 1.6rem; -} -.hcards .card { - margin: 0; -} -.hcards.hcards--max-2 { - grid-template-columns: repeat(auto-fit, minmax(256px, 1fr)); -} -.hcards--col-1-2, .hcards--col-2-1 { - grid-template-columns: repeat(auto-fit, minmax(256px, 1fr)); -} -.hcards--col-1-2 .card, .hcards--col-2-1 .card { - grid-column: 1 / 2; -} -@media screen and (min-width: 1024px) { - .hcards--max-3 { - grid-template-columns: 1fr 1fr 1fr; - } - .hcards--max-2 { - grid-template-columns: 1fr 1fr; - } - .hcards--col-1-2, .hcards--col-2-1 { - grid-template-columns: repeat(3, 1fr); - } -} -@media screen and (min-width: 720px) { - .hcards--col-2-1 .card:nth-of-type(1) { - grid-column: 1 / -2; - } - .hcards--col-2-1 .card:nth-of-type(2) { - grid-column: -2 / -1; - } - .hcards--col-1-2 .card:nth-of-type(1) { - grid-column: 1 / 2; - } - .hcards--col-1-2 .card:nth-of-type(2) { - grid-column: 2 / -1; - } -} - -.warning { - padding: calc(0.8rem - 2px) 0.8rem; - background-color: var(--yellow-200); - border: solid 2px var(--yellow-500); -} -.warning p:first-of-type { - margin-top: 0; -} -.warning p:last-of-type { - margin-bottom: 0; -} - -#profileAvatar { - display: inline-block; - min-width: 96px; - max-width: 128px; - line-height: 0; - text-align: center; - border-radius: 50%; -} - -/* TYPOGRAPHY */ -h1 { - font-size: 1.6em; - margin: 3.2rem 0 1.6rem; - font-weight: normal; - color: var(--h1-color); - cursor: default; -} -h2 { - font-size: 1.4em; - margin: 3.2rem 0 1.6rem; - font-weight: normal; - color: var(--h2-color); - cursor: default; -} -h2 small { - margin-left: 0.8rem; - padding: 3px 6px; - background-color: var(--h2-small-color); - color: #fff; - border-radius: 4px; -} -h3 { - margin: 1.6rem 0; - font-size: 1.3em; - line-height: 1.6rem; - color: var(--h3-color); - font-weight: normal; - /* text-align: center; */ - cursor: default; -} -h3 small { - margin-left: 0.8rem; - padding: 3px 6px; - background-color: var(--h3-small-color); - color: #fff; - border-radius: 4px; -} -h4 { - margin: 1.6rem 0; - font-size: 1em; - line-height: 1.6rem; - color: var(--h4-color); - /* color: var(--purple-700); */ - font-weight: bold; - cursor: default; -} -h4 small { - margin-left: 0.8rem; - padding: 3px 6px; - background-color: var(--h4-small-color); - color: #fff; - border-radius: 4px; -} -p { - margin: 1.6rem 0; -} -p.warning { - padding: 8px; - background-color: #fffadc; - border: solid 1px #ffeea8; -} -a { - color: var(--link-color); -} -ul { - padding-left: 1em; - list-style: '- '; -} -main h1:first-of-type { - margin-top: 1.6rem; -} -footer h1 { - margin-bottom: 0.8rem; - color: var(--purple-200); - font-size: 1.2rem; - font-weight: bold; -} -footer a { - display: inline-block; - color: var(--purple-100); - height: 32px; -} - -code { - padding: 2px 4px; - background-color: var(--purple-100); - border: 1px solid var(--purple-500); -} -pre { - padding: 8px 12px; - background-color: var(--purple-100); - border: 1px solid var(--purple-500); - overflow-x: auto; - line-height: 1.2rem; - font-size: 1rem; -} -pre code { - padding: 0; - background-color: 0px; - border: 0px; -} - -#search { - margin-top: 96px; - margin-bottom: 128px; -} -#qr { - display: block; - width: 100% !important; - max-width: 256px !important; - height: auto !important; - margin: 0 auto 16px; -} - -/* FORM ELEMENTS */ -.form-wrapper { - align-items: center; - padding-top: 1.4rem; - padding-bottom: 1.6rem; - margin-bottom: 48px; -} -.form-wrapper *:last-child { - margin-bottom: 0; -} -.form-wrapper form { - display: flex; - flex-direction: column; - margin: 0; -} -.form-wrapper h2 { - margin-top: 0; -} -form input[type="text"], form input[type="search"] { - margin: 8px 0; - padding: 4px; - border: 1px solid #999; - border-radius: 3px; - font-size: 0.9rem; -} -form textarea { - width: 100%; - height: 128px; - margin: 8px 0; - resize: vertical; - font-size: 0.9rem; - border: 1px solid #999; -} -.button-wrapper { - display: flex; - flex-wrap: wrap; - gap: 8px; - margin: 8px 0; -} -.radio-wrapper { - display: flex; - flex-wrap: wrap; - margin: 8px 0; -} -.radio-wrapper input[type="radio"] { - position: absolute; - opacity: 0; - z-index: -1; -} -.radio-wrapper input[type="radio"] + label { - margin: 0; - padding: 2px 8px; - background-color: #fff; - border: solid var(--purple-400); - border-width: 2px 1px; - cursor: pointer; -} -.radio-wrapper input[type="radio"]:first-of-type + label { - border-radius: 4px 0 0 4px; - border-left-width: 2px; -} -.radio-wrapper input[type="radio"]:last-of-type + label { - border-radius: 0 4px 4px 0; - border-right-width: 2px; -} -.radio-wrapper input[type="radio"]:focus + label { - z-index: 1; -} -.radio-wrapper input[type="radio"] + label:hover { - background-color: var(--purple-100); - border-color: var(--purple-500); -} -.radio-wrapper input[type="radio"]:checked + label { - color: #fff; - background-color: var(--purple-600); - border-color: var(--purple-600); -} - -input[type="button"], input[type="submit"], button, a.button { - display: inline-block; - min-height: 36px; - margin: 8px 0; - padding: 4px 8px; - font-family: sans-serif; - font-size: 0.9rem; - text-decoration: none; - text-transform: uppercase; - color: var(--button-text-color); - background-color: var(--button-background-color); - border: solid 2px var(--button-border-color); - border-radius: 4px; - cursor: pointer; -} -input[type="button"]:focus, input[type="submit"]:focus, button:focus, a.button:focus { - background-color: var(--input-focus-background-color); -} -input[type="button"]:hover, input[type="submit"]:hover, button:hover, a.button:hover { - background-color: var(--button-hover-background-color); - border-color: var(--button-hover-border-color); - color: var(--button-hover-text-color); -} -a.button i { - font-size: 1.4em; -} -a.button--donate { - display: inline-flex; - align-items: center; - gap: 8px; - margin-right: 12px; - padding: 8px 16px; - font-size: 0.95rem; - border: 0; -} -a.button--donate svg { - width: 24px; - height: 24px; - fill: var(--text-color); -} -a.button--donate.button--opencollective { - color: #fff; - background-color: #0c2d66; -} -a.button--donate.button--opencollective svg { - fill: #fff; -} -a.button--donate.button--opencollective:hover { - color: #fff; - background-color: #144aa9; -} -a.button--donate.button--liberapay { - color: #333; - background-color: #ffee16; -} -a.button--donate.button--liberapay svg { - fill: #333; -} -a.button--donate.button--liberapay:hover { - color: #333; - background-color: #fff463; -} -a.button--donate.button--kofi { - color: #333; - background-color: #1ac0ff; -} -a.button--donate.button--kofi svg { - fill: #333; -} -a.button--donate.button--kofi:hover { - color: #333; - background-color: #66d4ff; -} -button.inline { - min-height: auto; - margin: 0; - padding: 2px 8px; -} - -/* DIALOGS */ -dialog { - width: 100% !important; - max-width: 800px !important; - padding: 0 !important; - word-wrap: anywhere; -} -dialog > div { - padding: 1em; -} -dialog form[method="Dialog"] { - margin: 1em 0 0 !important; -} -dialog form[method="Dialog"] input { - width: auto; -} -dialog p { - font-size: 1rem !important; - margin: 1rem 0; -} -dialog p:first-of-type { - margin-top: 0; -} diff --git a/static-src/styles.scss b/static-src/styles.scss new file mode 100644 index 0000000..c09b638 --- /dev/null +++ b/static-src/styles.scss @@ -0,0 +1,100 @@ +/* +Copyright (C) 2021 Yarmo Mackenbach + +This program is free software: you can redistribute it and/or modify it under +the terms of the GNU Affero General Public License as published by the Free +Software Foundation, either version 3 of the License, or (at your option) +any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +details. + +You should have received a copy of the GNU Affero General Public License along +with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + +If your software can interact with users remotely through a computer network, +you should also make sure that it provides a way for users to get its source. +For example, if your program is a web application, its interface could display +a "Source" link that leads users to an archive of the code. There are many +ways you could offer source, and different solutions will be better for different +programs; see section 13 for the specific requirements. + +You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. For +more information on this, and how to apply and follow the GNU AGPL, see . +*/ +@use './styles/vars.scss'; +@use './styles/layout.scss'; +@use './styles/typography.scss'; +@use './styles/forms.scss'; + +@import '../node_modules/fork-awesome/css/fork-awesome.css'; +@import '../node_modules/dialog-polyfill/dist/dialog-polyfill.css'; + +* { + box-sizing: border-box; +} + +/* HELPERS */ +.spacer { + flex: 1; +} + +.no-margin { + margin: 0 !important; +} + +.full-width { + display: block; + width: 100% !important; +} + +.half-width { + display: block; + width: 50% !important; +} + +.select-all { + user-select: all; +} + +#qr { + display: block; + width: 100% !important; + max-width: 256px !important; + height: auto !important; + margin: 0 auto 16px; +} + +/* DIALOGS */ +dialog { + width: 100% !important; + max-width: 800px !important; + padding: 0 !important; + word-wrap: anywhere; +} + +dialog>div { + padding: 1em; +} + +dialog form[method="Dialog"] { + margin: 1em 0 0 !important; +} + +dialog form[method="Dialog"] input { + width: auto; +} + +dialog p { + font-size: 1rem !important; + margin: 1rem 0; +} + +dialog p:first-of-type { + margin-top: 0; +} \ No newline at end of file diff --git a/static-src/styles/forms.scss b/static-src/styles/forms.scss new file mode 100644 index 0000000..b083e38 --- /dev/null +++ b/static-src/styles/forms.scss @@ -0,0 +1,152 @@ +/* +Copyright (C) 2023 Yarmo Mackenbach + +This program is free software: you can redistribute it and/or modify it under +the terms of the GNU Affero General Public License as published by the Free +Software Foundation, either version 3 of the License, or (at your option) +any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +details. + +You should have received a copy of the GNU Affero General Public License along +with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + +If your software can interact with users remotely through a computer network, +you should also make sure that it provides a way for users to get its source. +For example, if your program is a web application, its interface could display +a "Source" link that leads users to an archive of the code. There are many +ways you could offer source, and different solutions will be better for different +programs; see section 13 for the specific requirements. + +You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. For +more information on this, and how to apply and follow the GNU AGPL, see . +*/ +.form-wrapper { + align-items: center; + padding-top: 1.4rem; + padding-bottom: 1.6rem; + margin-bottom: 48px; +} + +.form-wrapper *:last-child { + margin-bottom: 0; +} + +.form-wrapper form { + display: flex; + flex-direction: column; + margin: 0; +} + +.form-wrapper h2 { + margin-top: 0; +} + +form input[type="text"], +form input[type="search"] { + margin: 8px 0; + padding: 4px; + border: 1px solid var(--input-border-color); + border-radius: 3px; + font-size: 0.9rem; +} + +form textarea { + width: 100%; + height: 128px; + margin: 8px 0; + resize: vertical; + font-size: 0.9rem; + border: 1px solid var(--input-border-color); +} + +.button-wrapper { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin: 8px 0; +} + +input { + color: var(--input-text-color); + background-color: var(--input-background-color); + border: solid 1px var(--input-border-color); + border-radius: 4px; + + &:hover { + background-color: var(--input-background-color-hover); + border-color: var(--input-border-color-hover); + color: var(--input-text-color-hover); + } +} + +input[type="button"], +input[type="submit"], +button, +a.button { + display: inline-block; + margin: 8px 0; + padding: 4px 8px; + font-family: sans-serif; + font-size: 0.9rem; + text-decoration: none; + color: var(--button-text-color); + background-color: var(--button-background-color); + border: solid 1px var(--button-border-color); + border-radius: 4px; + cursor: pointer; + + &:hover { + background-color: var(--button-background-color-hover); + border-color: var(--button-border-color-hover); + color: var(--button-text-color-hover); + } +} + +button.inline { + min-height: auto; + margin: 0; + padding: 2px 8px; +} + + +#search { + display: flex; + align-items: center; + justify-content: center; + min-height: 50vh; + background: transparent; + border: 0; + + form { + display: flex; + flex-direction: row; + width: 100%; + max-width: 30em; + + input { + font-size: 1rem; + } + + input[type="search"] { + flex: 1; + min-width: 0; + margin: 0; + padding: 8px; + border-radius: 8px 0 0 8px; + } + + input[type="submit"] { + margin: 0; + padding: 8px 16px; + border-left: 0; + border-radius: 0 8px 8px 0; + } + } +} \ No newline at end of file diff --git a/static-src/styles/layout.scss b/static-src/styles/layout.scss new file mode 100644 index 0000000..95e3715 --- /dev/null +++ b/static-src/styles/layout.scss @@ -0,0 +1,193 @@ +/* +Copyright (C) 2023 Yarmo Mackenbach + +This program is free software: you can redistribute it and/or modify it under +the terms of the GNU Affero General Public License as published by the Free +Software Foundation, either version 3 of the License, or (at your option) +any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +details. + +You should have received a copy of the GNU Affero General Public License along +with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + +If your software can interact with users remotely through a computer network, +you should also make sure that it provides a way for users to get its source. +For example, if your program is a web application, its interface could display +a "Source" link that leads users to an archive of the code. There are many +ways you could offer source, and different solutions will be better for different +programs; see section 13 for the specific requirements. + +You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. For +more information on this, and how to apply and follow the GNU AGPL, see . +*/ +body { + display: flex; + flex-direction: column; + min-height: 100vh; + margin: 0 8px; + padding: 1.6rem 0 0; + line-height: 1.4rem; + font-family: sans-serif; + color: var(--text-color); + background-color: var(--body-background-color); +} + +header { + margin: 0 0 3rem; + + nav { + flex: 1; + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: center; + gap: 16px; + + @media screen and (max-width: 400px) { + flex-direction: column; + align-items: start; + } + + a.logo { + flex: 1; + display: flex; + align-items: center; + gap: 16px; + font-size: 1.8rem; + text-decoration: none; + font-weight: bold; + color: var(--primary-color); + + @media screen and (max-width: 480px) { + gap: 8px; + font-size: 1.4rem; + } + + img { + width: 40px; + height: 40px; + + @media screen and (max-width: 480px) { + width: 32px; + height: 32px; + } + } + } + + a.text { + margin: 0; + color: var(--text-color); + + &:hover, &:active { + color: var(--link-color-hover); + } + } + + .links { + display: flex; + gap: 12px; + } + } +} +main { + flex: 1; +} +footer { + margin: 4.8rem 0 0; + background-color: var(--footer-background-color); + color: var(--footer-text-color); + + a { + display: inline-block; + color: var(--footer-text-color); + } +} + +.container { + width: 100%; + max-width: 920px; + margin: 0 auto; +} + +section { + margin: 0 0 32px; + padding: 16px; + background-color: var(--section-background-color); + border-radius: 8px; + + h1:first-child, h2:first-child { + margin-top: 0; + } + + p:last-child { + margin-bottom: 0; + } + + &.transparent { + padding-left: 0; + padding-right: 0; + background-color: transparent; + border: 0; + } +} + +.profile { + display: flex; + gap: 16px; + padding-top: 24px; + + @media screen and (max-width: 600px) { + flex-direction: column; + padding-top: 32px; + } + + .profile__name { + font-size: 1.6rem; + color: var(--text-color); + } + + .profile__header { + flex: 3; + display: flex; + flex-direction: column; + flex-wrap: wrap; + align-items: center; + gap: 16px; + + & p, & small { + margin: 0; + } + } + + .profile__claims { + flex: 4; + } + + .profile__avatar { + display: inline-block; + min-width: 96px; + max-width: 128px; + line-height: 0; + text-align: center; + border-radius: 16px; + } + + .persona__description { + display: block; + margin: 8px 0 8px; + font-size: 0.9em; + + svg { + width: 16px; + height: 16px; + vertical-align: sub; + } + } +} \ No newline at end of file diff --git a/static-src/styles/typography.scss b/static-src/styles/typography.scss new file mode 100644 index 0000000..e300cfe --- /dev/null +++ b/static-src/styles/typography.scss @@ -0,0 +1,146 @@ +/* +Copyright (C) 2023 Yarmo Mackenbach + +This program is free software: you can redistribute it and/or modify it under +the terms of the GNU Affero General Public License as published by the Free +Software Foundation, either version 3 of the License, or (at your option) +any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +details. + +You should have received a copy of the GNU Affero General Public License along +with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + +If your software can interact with users remotely through a computer network, +you should also make sure that it provides a way for users to get its source. +For example, if your program is a web application, its interface could display +a "Source" link that leads users to an archive of the code. There are many +ways you could offer source, and different solutions will be better for different +programs; see section 13 for the specific requirements. + +You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. For +more information on this, and how to apply and follow the GNU AGPL, see . +*/ +h1 { + font-size: 1.6em; + margin: 3.2rem 0 1.6rem; + font-weight: normal; + color: var(--h1-color); + cursor: default; +} +h2 { + margin: 2em 0 0.5em; + font-size: 1.2em; + font-weight: bold; + color: var(--h2-color); + cursor: default; + + small { + margin-left: 0.8rem; + padding: 3px 6px; + background-color: var(--h2-small-color); + color: #fff; + border-radius: 4px; + font-size: 0.8em; + } +} +h3 { + margin: 1em 0 0.5em; + font-size: 1.1em; + line-height: 1.6rem; + color: var(--h3-color); + font-weight: normal; + cursor: default; + + small { + margin-left: 0.8rem; + padding: 3px 6px; + background-color: var(--h3-small-color); + color: #fff; + border-radius: 4px; + } +} +h4 { + margin: 1.6rem 0; + font-size: 1em; + line-height: 1.6rem; + color: var(--h4-color); + font-weight: bold; + cursor: default; + + small { + margin-left: 0.8rem; + padding: 3px 6px; + background-color: var(--h4-small-color); + color: #fff; + border-radius: 4px; + } +} +a { + color: var(--link-color); + + &:hover { + color: var(--link-color-hover); + } +} +a.button i { + font-size: 1.4em; +} +a.button.button--donate { + display: inline-flex; + align-items: center; + gap: 8px; + margin-right: 12px; + padding: 8px 16px; + font-size: 0.95rem; + border: 0; +} +a.button.button--donate svg { + width: 24px; + height: 24px; + fill: var(--text-color); +} +a.button.button--donate.button--opencollective { + color: #fff; + background-color: #0c2d66; +} +a.button.button--donate.button--opencollective svg { + fill: #fff; +} +a.button.button--donate.button--opencollective:hover { + color: #fff; + background-color: #144aa9; +} +a.button.button--donate.button--liberapay { + color: #333; + background-color: #ffee16; +} +a.button.button--donate.button--liberapay svg { + fill: #333; +} +a.button.button--donate.button--liberapay:hover { + color: #333; + background-color: #fff463; +} +a.button.button--donate.button--kofi { + color: #333; + background-color: #1ac0ff; +} +a.button.button--donate.button--kofi svg { + fill: #333; +} +a.button.button--donate.button--kofi:hover { + color: #333; + background-color: #66d4ff; +} + +ul { + padding-left: 1em; + list-style: '- '; +} \ No newline at end of file diff --git a/static-src/styles/vars.scss b/static-src/styles/vars.scss new file mode 100644 index 0000000..6d282d2 --- /dev/null +++ b/static-src/styles/vars.scss @@ -0,0 +1,158 @@ +/* +Copyright (C) 2023 Yarmo Mackenbach + +This program is free software: you can redistribute it and/or modify it under +the terms of the GNU Affero General Public License as published by the Free +Software Foundation, either version 3 of the License, or (at your option) +any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +details. + +You should have received a copy of the GNU Affero General Public License along +with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + +If your software can interact with users remotely through a computer network, +you should also make sure that it provides a way for users to get its source. +For example, if your program is a web application, its interface could display +a "Source" link that leads users to an archive of the code. There are many +ways you could offer source, and different solutions will be better for different +programs; see section 13 for the specific requirements. + +You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. For +more information on this, and how to apply and follow the GNU AGPL, see . +*/ +:root { + --white: hsl(0, 0%, 100%); + --black: hsl(0, 0%, 0%); + --grey-10: hsl(0, 0%, 99%); + --grey-50: hsl(0, 0%, 95%); + --grey-100: hsl(0, 0%, 90%); + --grey-200: hsl(0, 0%, 80%); + --grey-300: hsl(0, 0%, 70%); + --grey-400: hsl(0, 0%, 60%); + --grey-500: hsl(0, 0%, 50%); + --grey-600: hsl(0, 0%, 40%); + --grey-700: hsl(0, 0%, 30%); + --grey-800: hsl(0, 0%, 20%); + --grey-900: hsl(0, 0%, 10%); + --grey-950: hsl(0, 0%, 7%); + --green-300: hsl(110, 45%, 70%); + --green-400: hsl(110, 45%, 60%); + --green-600: hsl(110, 45%, 40%); + --red-300: hsl(10, 60%, 70%); + --red-400: hsl(10, 60%, 60%); + --blue-500: rgb(67, 176, 234); + --blue-700: hsl(201, 90%, 30%); + --purple-50: rgb(249, 248, 251); + --purple-100: rgb(238, 236, 248); + --purple-200: hsl(250, 48%, 90%); + --purple-300: hsl(250, 48%, 80%); + --purple-400: hsl(250, 48%, 70%); + --purple-500: hsl(250, 48%, 65%); + --purple-600: hsl(250, 48%, 60%); + --purple-700: hsl(250, 48%, 55%); + --purple-900: hsl(250, 38%, 45%); + --yellow-100: hsl(56, 100%, 95%); + --yellow-200: hsl(56, 100%, 90%); + --yellow-500: hsl(56, 100%, 65%); +} + +:root { + --primary-color: var(--purple-700); + --primary-color-subtle: var(--purple-400); + + --body-background-color: #fafafa; + --section-background-color: var(--white); + + --text-color: var(--grey-800); + --text-color-subtle: var(--grey-500); + --h1-color: var(--text-color); + --h2-color: var(--text-color); + --h2-small-color: var(--primary-color-subtle); + --h3-color: var(--text-color-subtle); + --h3-small-color: var(--primary-color-subtle); + --h4-color: var(--text-color-subtle); + --h4-small-color: var(--primary-color-subtle); + + --link-color: var(--blue-700); + --link-color-subtle: var(--text-color); + --link-color-hover: var(--purple-700); + + --button-text-color: var(--text-color); + --button-text-color-hover: var(--text-color); + --button-border-color: var(--grey-300); + --button-border-color-hover: var(--grey-300); + --button-background-color: var(--grey-100); + --button-background-color-hover: var(--grey-200); + + --input-text-color: var(--text-color); + --input-text-color-hover: var(--text-color); + --input-border-color: var(--grey-300); + --input-border-color-hover: var(--grey-300); + --input-background-color: var(--white); + --input-background-color-hover: var(--white); + + --footer-text-color: var(--text-color-subtle); + + @media (prefers-color-scheme: dark) { + --primary-color: var(--purple-300); + --primary-color-subtle: var(--purple-500); + + --body-background-color: #0a0a0a; + --section-background-color: var(--grey-900); + + --text-color: var(--grey-50); + --text-color-subtle: var(--grey-300); + --h1-color: var(--text-color); + --h2-color: var(--text-color); + --h2-small-color: var(--primary-color-subtle); + --h3-color: var(--text-color-subtle); + --h3-small-color: var(--primary-color-subtle); + --h4-color: var(--text-color-subtle); + --h4-small-color: var(--primary-color-subtle); + + --link-color: var(--blue-500); + --link-color-subtle: var(--text-color); + --link-color-hover: var(--primary-color); + + --button-text-color: var(--text-color); + --button-text-color-hover: var(--text-color); + --button-border-color: var(--grey-700); + --button-border-color-hover: var(--grey-700); + --button-background-color: var(--grey-700); + --button-background-color-hover: var(--grey-600); + + --input-text-color: var(--text-color); + --input-text-color-hover: var(--text-color); + --input-border-color: var(--grey-700); + --input-border-color-hover: var(--grey-700); + --input-background-color: var(--grey-800); + --input-background-color-hover: var(--grey-800); + + --footer-text-color: var(--text-color-subtle); + } +} + +kx-claim, kx-key { + --loader-color: var(--grey-400); + --success-color: var(--green-600); + --failure-color: var(--red-400); + + --background-color: var(--grey-10); + --header-background-color: var(--grey-50); + + @media (prefers-color-scheme: dark) { + --loader-color: var(--grey-400); + --success-color: var(--green-400); + --failure-color: var(--red-300); + + --background-color: var(--grey-800); + --header-background-color: var(--grey-700); + } +} \ No newline at end of file From c5ea6c302459cc8e1baaadf4c966787c45ac1a7b Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Fri, 15 Sep 2023 14:25:15 +0200 Subject: [PATCH 046/144] feat: rewrite templates --- views/article.pug | 4 +- views/index.pug | 89 +++++++++++------------------------- views/partials/footer.pug | 51 +++++++++++---------- views/partials/header.pug | 9 ++-- views/profile.pug | 92 +++++++++++++++++++++++--------------- views/util/argon2.pug | 2 +- views/util/bcrypt.pug | 2 +- views/util/index.pug | 4 +- views/util/profile-url.pug | 2 +- views/util/qr.pug | 2 +- views/util/qrfp.pug | 2 +- views/util/wkd.pug | 2 +- 12 files changed, 124 insertions(+), 137 deletions(-) diff --git a/views/article.pug b/views/article.pug index 5668ef7..c4e6db8 100644 --- a/views/article.pug +++ b/views/article.pug @@ -1,6 +1,6 @@ extends templates/base.pug block content - section.long_form.narrow + section h1= title - .card !{ content } + !{ content } diff --git a/views/index.pug b/views/index.pug index 0d7ae95..dba4e16 100644 --- a/views/index.pug +++ b/views/index.pug @@ -1,76 +1,41 @@ extends templates/base.pug block content - #search.form-wrapper.card - h2#searchTitle View a profile + section#search.form-wrapper form(action="post") - label#searchQuery(for="query") Query for fingerprint or email identifier - input#query(type="search" name="query" required placeholder="3637202523e7c1309ab79e99ef2dc5827b445f4b, test@doip.rocks" aria-labelledby="searchTitle searchQuery") + input#query(type="search" name="query" required placeholder="Search for a profile") + input(type="submit" value="Search") - input(type="submit" value="View profile") - p Or view a - a(href="/sig") plaintext signature - | profile. - - if highlights.length > 0 - h2 Highlights - .hcards.hcards--highlights - each hl in highlights - .card.card--small-profile - h3.name= hl.name - p - span.fingerprint= hl.fingerprint - br - span.details= hl.description - .spacer - p - a(href=`/${hl.fingerprint}`).button.full-width View profile - - var n = 0 - while n < 3-highlights.length - .card.card--small-profile-dummy - - n++ - - h2 About Keyoxide - .hcards.hcards--features.hcards--max-2 - .card - h3 Online identity - p Verifying online identity with cryptography. View - a(href="/project@keyoxide.org") Keyoxide's profile - | . - .card - h3 Mobile app - p Available on Android and iOS. More information on - a(href="https://mobile.keyoxide.org") mobile.keyoxide.org - | . - .card - h3 Decentralization & privacy - p No central server or database. No collected data. Control how your data is stored and accessed. - .card - h3 Cryptography - p Your online identity verifiably signed with widely-used cryptographic standards (OpenPGP, others coming). - .card - h3 Open Source - p All Keyoxide projects are licensed under AGPL-3.0-or-later. - .card - h3 Transparent funding - p Funded by donations. Keyoxide stands against VC and surveillance capitalism. - - h2 Community - .card + section + h2 About Keyoxide + p Keyoxide is a decentralized tool to create and verify decentralized online identities. + p Just like passports for real life identities, Keyoxide can be used to verify the online identity of people to make sure one is interacting with whom they intended to be and not an imposter. + p Unlike real life passports, Keyoxide works with online identities or "personas", meaning these identities can be anonymous and one can have multiple separate personas to protect their privacy, both online and in real life. p - | Discussion of the Keyoxide project happens primarily on the + | Here is what a + a(href="/project@keyoxide.org") Keyoxide profile + | looks like. + p + a(href="https://docs.keyoxide.org/getting-started") Get started + | and create your own! + section + h2 Community + p + | Discussion of the Keyoxide project primarily happens on the + a(href="https://matrix.to/#/#keyoxide:matrix.org") #keyoxide Matrix channel + | and the a(href="https://community.keyoxide.org") Keyoxide Community Forum - | . This is the place to propose new service providers for identity verification, make feature suggestions or report bugs. + | . + | The Matrix channel is great for troubleshooting. + | The forum is the place to propose new service providers for identity verification, make feature suggestions or report bugs. p | There is also the a(href="irc://irc.libera.chat/#keyoxide") #keyoxide:libera.chat IRC room - | , the - a(href="https://matrix.to/#/#keyoxide:matrix.org") #keyoxide Matrix channel | and the a(href="https://lists.sr.ht/~yarmo/keyoxide-devel") keyoxide-devel mailing list | . The IRC room and Matrix channel are bridged together. p - | The project is also present on the fediverse as + | The project is also present on the fediverse: a(href="https://fosstodon.org/@keyoxide") @keyoxide@fosstodon.org | . p @@ -79,10 +44,10 @@ block content | . - h2 Fund the project - .card + section + h2 Fund the project p - | The development of Keyoxide and the Decentralized OpenPGP Identity Proofs ecosystem is entirely funded by donations. + | The development of Keyoxide and the Decentralized Online Identity Proofs ecosystem is entirely funded by donations. p | The Keyoxide project was awarded a NGI Zero grant from the a(href='https://nlnet.nl/') NLnet Foundation diff --git a/views/partials/footer.pug b/views/partials/footer.pug index 85b7d88..d7b4241 100644 --- a/views/partials/footer.pug +++ b/views/partials/footer.pug @@ -1,35 +1,34 @@ footer .container - .hcards - div - h1 Keyoxide - a(href="/") Homepage - br - a(href="/util") Utilities - br - a(href="/privacy") Privacy policy + p + | This site: + a(href="/") Homepage + | • + a(href="/util") Utilities + | • + a(href="/privacy") Privacy policy + br - div - h1 Keyoxide project - a(href="https://keyoxide.org") Keyoxide.org - br - a(href="https://community.keyoxide.org") Community forum - br - a(href="https://docs.keyoxide.org") Documentation - br - a(href="https://blog.keyoxide.org") Blog + | Keyoxide project: + a(href="https://community.keyoxide.org") Forum + | • + a(href="https://docs.keyoxide.org") Documentation + | • + a(href="https://blog.keyoxide.org") Blog + | • + a(href="https://codeberg.org/keyoxide") Source code + br - div - h1 Development - a(href="https://codeberg.org/keyoxide/") Source code - br - a(href="https://doip.rocks") doip.rocks - br - a(href="https://ariadne.id") ariadne.id + | Related: + a(href="https://doip.rocks") doip.rocks + | • + a(href="https://ariadne.id") ariadne.id - p.copyright + p | Version #{meta.keyoxide.version} if (meta.env === "development") | -dev br - | © 2023 Keyoxide project contributors \ No newline at end of file + + | © 2023 Keyoxide project contributors + \ No newline at end of file diff --git a/views/partials/header.pug b/views/partials/header.pug index 45ae21c..542c7c8 100644 --- a/views/partials/header.pug +++ b/views/partials/header.pug @@ -3,7 +3,8 @@ header nav a.logo(href='/' aria-label='Home') img(src='/static/img/logo_circle.png' alt='Keyoxide' aria-hidden='true') - a.text(href='/') Home - a.text(href='https://docs.keyoxide.org') Docs - a.text(href='https://blog.keyoxide.org') Blog - a.text(href='https://community.keyoxide.org') Forum + | Keyoxide + .links + a.text(href='https://docs.keyoxide.org/getting-started') Getting started + a.text(href='https://docs.keyoxide.org') Docs + a.text(href='https://blog.keyoxide.org') Blog diff --git a/views/profile.pug b/views/profile.pug index c96dc3e..23e654d 100644 --- a/views/profile.pug +++ b/views/profile.pug @@ -3,24 +3,38 @@ extends templates/base.pug mixin generatePersona(persona, isPrimary) h2 if persona.email - span.p-email Identity claims (#{persona.email}) + | Identity claims ( + span.p-email #{persona.email} + | ) else - span.p-email Identity claims + | Identity claims if isPrimary small.primary primary if persona.description - span.p-comment ⓘ #{persona.description} + span.persona__description.p-comment + + | #{persona.description} each claim in persona.claims if claim.matches.length > 0 - kx-claim.kx-item(data-claim=claim) + kx-claim.kx-item(data-claim=claim,data-status='running') details(aria-label="Claim") summary .info - p.subtitle= claim.display.serviceproviderName - p.title= claim.display.name + p + span.title= claim.display.name + span.subtitle-wrapper + | [ + span.subtitle= claim.display.serviceproviderName + | ] .icons - .verificationStatus(data-value='running') + .verificationStatus .inProgress + + .success + + .failure + + .content .subsection img(src='/static/img/link.png') @@ -59,7 +73,7 @@ block content a(href="https://docs.keyoxide.org/getting-started/something-went-wrong/") documentation | for help. else - section.profile.narrow.h-card + section.profile noscript p Keyoxide requires JavaScript to function. @@ -103,10 +117,12 @@ block content input(type='submit', name='submit', value='Generate profile') unless (isSignature && !signature) - #profileHeader.card.card--transparent.card--profileHeader - img#profileAvatar.u-logo(src=data.personas[data.primaryPersonaIndex].avatarUrl alt="avatar") + .profile__header + img.profile__avatar.u-logo(src=data.personas[data.primaryPersonaIndex].avatarUrl alt="avatar") - p#profileName.p-name= data.personas[data.primaryPersonaIndex].name + p.profile__name.p-name= data.personas[data.primaryPersonaIndex].name + if (data.personas[data.primaryPersonaIndex].description) + p= data.personas[data.primaryPersonaIndex].description if (enable_message_encryption || enable_signature_verification) .button-wrapper @@ -115,28 +131,34 @@ block content if (enable_signature_verification) button(onClick="document.querySelector('#dialog--verifySignature').showModal();") Verify signature - +generatePersona(data.personas[data.primaryPersonaIndex], true && data.personas.length > 1) - each persona, index in data.personas - unless index == data.primaryPersonaIndex - +generatePersona(persona, false) + .profile__claims + +generatePersona(data.personas[data.primaryPersonaIndex], true && data.personas.length > 1) + each persona, index in data.personas + unless index == data.primaryPersonaIndex + +generatePersona(persona, false) - #profileProofs.card.card--transparent - h2 Key - kx-key.kx-item(data-keydata=data.publicKey) - details(aria-label="Key") - summary - .info - p.subtitle= data.publicKey.fetch.method - p.title= data.identifier - .content - .subsection - img(src='/static/img/link.png') - div - p Key link: - a.u-key(href=data.publicKey.fetch.resolvedUrl rel="pgpkey" aria-label="Link to cryptographic key")= data.publicKey.fetch.resolvedUrl - hr - if (data.profileType === 'openpgp') - .subsection - img(src='/static/img/qrcode.png') - div - button(onClick=`showQR('${data.publicKey.fingerprint}', 'fingerprint')` aria-label='Show QR code for cryptographic fingerprint') Show OpenPGP fingerprint QR \ No newline at end of file + section + h2 Profile information + h3 Public key + kx-key.kx-item(data-keydata=data.publicKey) + details(aria-label="Key") + summary + .info + p + span.title= data.identifier + span.subtitle-wrapper + | [ + span.subtitle= data.publicKey.fetch.method + | ] + .content + .subsection + img(src='/static/img/link.png') + div + p Key link: + a.u-key(href=data.publicKey.fetch.resolvedUrl rel="pgpkey" aria-label="Link to cryptographic key")= data.publicKey.fetch.resolvedUrl + hr + if (data.profileType === 'openpgp') + .subsection + img(src='/static/img/qrcode.png') + div + button(onClick=`showQR('${data.publicKey.fingerprint}', 'fingerprint')` aria-label='Show QR code for cryptographic fingerprint') Show OpenPGP fingerprint QR \ No newline at end of file diff --git a/views/util/argon2.pug b/views/util/argon2.pug index 97b9177..d7189b7 100644 --- a/views/util/argon2.pug +++ b/views/util/argon2.pug @@ -1,7 +1,7 @@ extends ../templates/base.pug block content - section.narrow + section h1 Argon2 utility h2 Generate Argon2 hash diff --git a/views/util/bcrypt.pug b/views/util/bcrypt.pug index 2895c87..90d6f84 100644 --- a/views/util/bcrypt.pug +++ b/views/util/bcrypt.pug @@ -1,7 +1,7 @@ extends ../templates/base.pug block content - section.narrow + section h1 bcrypt utility h2 Generate bcrypt hash diff --git a/views/util/index.pug b/views/util/index.pug index a542959..69b8bfa 100644 --- a/views/util/index.pug +++ b/views/util/index.pug @@ -1,8 +1,8 @@ extends ../templates/base.pug block content - section.narrow - h1 Keyoxide utilities + section + h2 Keyoxide utilities p a(href="/util/profile-url") Get the URL for a Keyoxide profile p diff --git a/views/util/profile-url.pug b/views/util/profile-url.pug index c368ac4..2c82eb9 100644 --- a/views/util/profile-url.pug +++ b/views/util/profile-url.pug @@ -1,7 +1,7 @@ extends ../templates/base.pug block content - section.narrow + section h1 Profile URL form#form-util-profile-url(method='post') p This tool generates an URL for your Keyoxide profile page. diff --git a/views/util/qr.pug b/views/util/qr.pug index 1a28df4..e102c62 100644 --- a/views/util/qr.pug +++ b/views/util/qr.pug @@ -1,7 +1,7 @@ extends ../templates/base.pug block content - section.narrow + section h1 QR Code form#form-util-qr(method='post') pre diff --git a/views/util/qrfp.pug b/views/util/qrfp.pug index 98eee11..7258ef8 100644 --- a/views/util/qrfp.pug +++ b/views/util/qrfp.pug @@ -1,7 +1,7 @@ extends ../templates/base.pug block content - section.narrow + section h1 QR Code form#form-util-qrfp(method='post') p diff --git a/views/util/wkd.pug b/views/util/wkd.pug index 0bb0733..020ad34 100644 --- a/views/util/wkd.pug +++ b/views/util/wkd.pug @@ -1,7 +1,7 @@ extends ../templates/base.pug block content - section.narrow + section h1 Web Key Directory URL generator form#form-util-wkd(method='post') p From 139ab487a5d7707e53c665fd030ebe144f882964 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Fri, 15 Sep 2023 17:59:26 +0200 Subject: [PATCH 047/144] feat: Update privacy policy --- content/privacy-policy.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/content/privacy-policy.md b/content/privacy-policy.md index 20b1667..342b4ef 100644 --- a/content/privacy-policy.md +++ b/content/privacy-policy.md @@ -8,21 +8,21 @@ There are no accounts on Keyoxide. Never does Keyoxide need to know any of your ## Profile pages -To generate a profile page, Keyoxide will look at the URL and fetch the associated OpenPGP key. The OpenPGP key is parsed on the server and the claims are rendered unverified then sent to the browser. The browser will then attempt to verify the claims by fetching the required proofs, usually JSON documents. +To generate a profile page, Keyoxide will look at the URL and fetch the associated OpenPGP key or ASP profile. The fetched document is parsed on the server, the claims are rendered unverified then sent to the browser. The browser will then attempt to verify the claims by fetching the required proofs, usually JSON documents. If a browser cannot fetch the proof (for example, due to CORS restraints), it will ask a proxy server to fetch the proof instead if a proxy is configured by the instance administrator. -OpenPGP keys, profile pages and claim verifications are not cached on the server. +Profile pages and claim verifications are not cached on the server. OpenPGP keys may be cached for a minute to alleviate strain on keyservers. ## Donations -Donations are handled by Stripe. Keyoxide does not store personal or payment-related information. +Donations are handled by OpenCollective. Keyoxide does not store personal or payment-related information. -How Stripe processes the data is covered by the [Stripe privacy policy](https://stripe.com/privacy). +How OpenCollective processes the data is covered by the [OpenCollective Privacy Policy](https://opencollective.com/privacypolicy). ## Keyoxide instances -Each Keyoxide instance is administered by different people using different technologies and different configurations. This document cannot account for the way each particular instance handles/stores/processes the HTTP requests. +Each Keyoxide instance is administered by different people using different infrastructure and different configurations. This document cannot account for the way each particular instance handles/stores/processes the HTTP requests. ## Keyoxide.org instance From 4cdbe1783b1e5e7d21d4c25ad494d370141f89a4 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Fri, 15 Sep 2023 21:07:00 +0200 Subject: [PATCH 048/144] fix: fix styles --- static-src/kx-styles.scss | 8 ++++---- static-src/styles.scss | 12 +++++++++--- static-src/styles/forms.scss | 1 - 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/static-src/kx-styles.scss b/static-src/kx-styles.scss index 52db260..2928103 100644 --- a/static-src/kx-styles.scss +++ b/static-src/kx-styles.scss @@ -199,15 +199,15 @@ kx-claim { text-transform: uppercase; color: var(--button-text-color); background-color: var(--button-background-color); - border: solid 2px var(--button-border-color); + border: solid 1px var(--button-border-color); border-radius: 4px; cursor: pointer; } button:hover { - background-color: var(--button-hover-background-color); - border-color: var(--button-hover-border-color); - color: var(--button-hover-text-color); + background-color: var(--button-background-color-hover); + border-color: var(--button-border-color-hover); + color: var(--button-text-color-hover); } &[data-status="running"] { diff --git a/static-src/styles.scss b/static-src/styles.scss index c09b638..cc19fe9 100644 --- a/static-src/styles.scss +++ b/static-src/styles.scss @@ -72,13 +72,19 @@ more information on this, and how to apply and follow the GNU AGPL, see div { + margin: 16px; padding: 1em; } diff --git a/static-src/styles/forms.scss b/static-src/styles/forms.scss index b083e38..361699c 100644 --- a/static-src/styles/forms.scss +++ b/static-src/styles/forms.scss @@ -115,7 +115,6 @@ button.inline { padding: 2px 8px; } - #search { display: flex; align-items: center; From d74aa0854af0b46612ca403f32ca989f389a4bce Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Fri, 15 Sep 2023 21:14:01 +0200 Subject: [PATCH 049/144] feat: remove obsolete dependencies --- package.json | 2 -- static-src/styles.scss | 3 --- static-src/ui.js | 12 ------------ yarn.lock | 10 ---------- 4 files changed, 27 deletions(-) diff --git a/package.json b/package.json index 8aecc17..c85e145 100644 --- a/package.json +++ b/package.json @@ -8,12 +8,10 @@ "ajv": "^8.6.3", "bent": "^7.3.12", "body-parser": "^1.19.0", - "dialog-polyfill": "^0.5.6", "doipjs": "^1.0.0", "dotenv": "^16.0.3", "express": "^4.17.1", "express-validator": "^6.13.0", - "fork-awesome": "^1.2.0", "got": "^11.8.2", "hash-wasm": "^4.9.0", "jstransformer-markdown-it": "^3.0.0", diff --git a/static-src/styles.scss b/static-src/styles.scss index cc19fe9..0e6b3bd 100644 --- a/static-src/styles.scss +++ b/static-src/styles.scss @@ -32,9 +32,6 @@ more information on this, and how to apply and follow the GNU AGPL, see . */ -import dialogPolyfill from 'dialog-polyfill' import QRCode from 'qrcode' import * as openpgp from 'openpgp' import * as utils from './utils.js' @@ -51,17 +50,6 @@ const elUtilBcryptVerification = document.body.querySelector("#form-util-bcrypt- // Initialize UI elements and event listeners export function init() { - // Register modals - document.querySelectorAll('dialog').forEach(function(d) { - dialogPolyfill.registerDialog(d); - d.addEventListener('click', function(ev) { - if (ev && ev.target != d) { - return; - } - d.close(); - }); - }); - // Run context-dependent scripts if (elFormEncrypt) { runEncryptionForm() diff --git a/yarn.lock b/yarn.lock index 746e052..badf363 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1789,11 +1789,6 @@ destroy@1.2.0: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== -dialog-polyfill@^0.5.6: - version "0.5.6" - resolved "https://registry.yarnpkg.com/dialog-polyfill/-/dialog-polyfill-0.5.6.tgz#7507b4c745a82fcee0fa07ce64d835979719599a" - integrity sha512-ZbVDJI9uvxPAKze6z146rmfUZjBqNEwcnFTVamQzXH+svluiV7swmVIGr7miwADgfgt1G2JQIytypM9fbyhX4w== - diff@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" @@ -2451,11 +2446,6 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" -fork-awesome@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/fork-awesome/-/fork-awesome-1.2.0.tgz#acd43f1e1f54510fa45209c31385b4fde3a95003" - integrity sha512-MNwTBnnudMIweHfDtTY8TeR5fxIAZ2w9o8ITn5XDySqdxa4k5AH8IuAMa89RVxDxgPNlosZxqkFKN5UmHXuYSw== - forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" From e2ed828f9df270cdf967b73ab42140ff00ac6835 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Sun, 17 Sep 2023 14:21:21 +0200 Subject: [PATCH 050/144] fix: make public key section optional --- views/profile.pug | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/views/profile.pug b/views/profile.pug index 23e654d..1c141c3 100644 --- a/views/profile.pug +++ b/views/profile.pug @@ -139,26 +139,27 @@ block content section h2 Profile information - h3 Public key - kx-key.kx-item(data-keydata=data.publicKey) - details(aria-label="Key") - summary - .info - p - span.title= data.identifier - span.subtitle-wrapper - | [ - span.subtitle= data.publicKey.fetch.method - | ] - .content - .subsection - img(src='/static/img/link.png') - div - p Key link: - a.u-key(href=data.publicKey.fetch.resolvedUrl rel="pgpkey" aria-label="Link to cryptographic key")= data.publicKey.fetch.resolvedUrl - hr - if (data.profileType === 'openpgp') + if (data && data.publicKey) + h3 Public key + kx-key.kx-item(data-keydata=data.publicKey) + details(aria-label="Key") + summary + .info + p + span.title= data.identifier + span.subtitle-wrapper + | [ + span.subtitle= data.publicKey.fetch.method + | ] + .content .subsection - img(src='/static/img/qrcode.png') + img(src='/static/img/link.png') div - button(onClick=`showQR('${data.publicKey.fingerprint}', 'fingerprint')` aria-label='Show QR code for cryptographic fingerprint') Show OpenPGP fingerprint QR \ No newline at end of file + p Key link: + a.u-key(href=data.publicKey.fetch.resolvedUrl rel="pgpkey" aria-label="Link to cryptographic key")= data.publicKey.fetch.resolvedUrl + hr + if (data.profileType === 'openpgp') + .subsection + img(src='/static/img/qrcode.png') + div + button(onClick=`showQR('${data.publicKey.fingerprint}', 'fingerprint')` aria-label='Show QR code for cryptographic fingerprint') Show OpenPGP fingerprint QR \ No newline at end of file From 494b93bf5cd34205162c359ddc3d92b897c8a379 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Sun, 17 Sep 2023 14:31:56 +0200 Subject: [PATCH 051/144] fix: fix dark theme issues --- static-src/styles/forms.scss | 3 ++- static-src/styles/vars.scss | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/static-src/styles/forms.scss b/static-src/styles/forms.scss index 361699c..5a44321 100644 --- a/static-src/styles/forms.scss +++ b/static-src/styles/forms.scss @@ -73,7 +73,8 @@ form textarea { margin: 8px 0; } -input { +input, +textarea { color: var(--input-text-color); background-color: var(--input-background-color); border: solid 1px var(--input-border-color); diff --git a/static-src/styles/vars.scss b/static-src/styles/vars.scss index 6d282d2..fe2536a 100644 --- a/static-src/styles/vars.scss +++ b/static-src/styles/vars.scss @@ -101,6 +101,8 @@ more information on this, and how to apply and follow the GNU AGPL, see Date: Mon, 18 Sep 2023 18:04:08 +0200 Subject: [PATCH 052/144] chore: release 4.1.0 --- CHANGELOG.md | 8 ++++++++ package.json | 4 ++-- yarn.lock | 8 ++++---- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0aaa99c..9a53d0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [4.1.0] - 2023-09-18 +### Changed +- Redesign +- Update doipjs to 1.0.1 +- Update node to 20 +- Make https scheme for proxy calls optional +- Display site version in footer + ## [4.0.2] - 2023-09-12 ### Fixed - Handle doip promise rejection diff --git a/package.json b/package.json index c85e145..a8d1b90 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keyoxide-web", - "version": "4.0.2", + "version": "4.1.0", "description": "Verifying online identity with cryptography", "main": "./src/index.js", "type": "module", @@ -8,7 +8,7 @@ "ajv": "^8.6.3", "bent": "^7.3.12", "body-parser": "^1.19.0", - "doipjs": "^1.0.0", + "doipjs": "^1.0.1", "dotenv": "^16.0.3", "express": "^4.17.1", "express-validator": "^6.13.0", diff --git a/yarn.lock b/yarn.lock index badf363..34f6703 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1825,10 +1825,10 @@ doctypes@^1.1.0: resolved "https://registry.yarnpkg.com/doctypes/-/doctypes-1.1.0.tgz#ea80b106a87538774e8a3a4a5afe293de489e0a9" integrity sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ== -doipjs@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/doipjs/-/doipjs-1.0.0.tgz#cb0fe9f324a8c3cd3ddb0a4bec009c772f06ab1e" - integrity sha512-KUIbHBE5fdIose6mml9uf4rd5m7cz2g3929DPs1EZamHV/V0M8RgUhGC4u6QGYYHREDrG3fOj43V9RrCG4hv5A== +doipjs@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/doipjs/-/doipjs-1.0.1.tgz#72b3e8eaf042a8542763d82eddd171165ad62d5e" + integrity sha512-mWbZ4hlP6KVDYljLhpJMNREC2Rx/3r2EjN7TrblC4OQ80934eD0cGty4INY89mRcz3jEj1lOsULWsPr63uyYzw== dependencies: "@openpgp/hkp-client" "^0.0.3" "@openpgp/wkd-client" "^0.0.4" From 78ec2a6bc696bff6aa03dd5aa2f187ea4cd978ff Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Tue, 19 Sep 2023 13:00:28 +0200 Subject: [PATCH 053/144] fix: clean up code --- static-src/kx-claim.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/static-src/kx-claim.js b/static-src/kx-claim.js index b7b16f3..75e7e40 100644 --- a/static-src/kx-claim.js +++ b/static-src/kx-claim.js @@ -61,8 +61,6 @@ export class Claim extends HTMLElement { const claimJson = JSON.parse(value); const claim = doipjs.Claim.fromJson(claimJson); - console.log(claimJson); - root.querySelector('.info .title').innerText = claimJson.display.name; root.querySelector('.info .subtitle').innerText = claimJson.display.serviceProviderName ?? (claim.status < 300 ? '???' : '---'); @@ -70,14 +68,11 @@ export class Claim extends HTMLElement { try { if (claim.status >= 200) { root.setAttribute('data-status', claim.status < 300 ? 'success' : 'failed'); - // root.querySelector('.icons .verificationStatus').setAttribute('data-value', claim.status < 300 ? 'success' : 'failed'); } else { root.setAttribute('data-status', 'running'); - // root.querySelector('.icons .verificationStatus').setAttribute('data-value', 'running'); } } catch (error) { root.setAttribute('data-status', 'failed'); - // root.querySelector('.icons .verificationStatus').setAttribute('data-value', 'failed'); } const elContent = root.querySelector('.content'); From bed7a7ee77528917860d63414225b9c07de51762 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Tue, 19 Sep 2023 13:03:37 +0200 Subject: [PATCH 054/144] feat: update doipjs to 1.0.2 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index a8d1b90..0a3ad2e 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "ajv": "^8.6.3", "bent": "^7.3.12", "body-parser": "^1.19.0", - "doipjs": "^1.0.1", + "doipjs": "^1.0.2", "dotenv": "^16.0.3", "express": "^4.17.1", "express-validator": "^6.13.0", diff --git a/yarn.lock b/yarn.lock index 34f6703..9329263 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1825,10 +1825,10 @@ doctypes@^1.1.0: resolved "https://registry.yarnpkg.com/doctypes/-/doctypes-1.1.0.tgz#ea80b106a87538774e8a3a4a5afe293de489e0a9" integrity sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ== -doipjs@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/doipjs/-/doipjs-1.0.1.tgz#72b3e8eaf042a8542763d82eddd171165ad62d5e" - integrity sha512-mWbZ4hlP6KVDYljLhpJMNREC2Rx/3r2EjN7TrblC4OQ80934eD0cGty4INY89mRcz3jEj1lOsULWsPr63uyYzw== +doipjs@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/doipjs/-/doipjs-1.0.2.tgz#6e2aba8b4894a3520ed55a5fccc98f9e3fa5769c" + integrity sha512-5TWS+Vq6KsmlwRWTLIec5frB8LAJ2uHuW92KJMUPL3dnOIXnlkC9Kg7QTH1V9DSZnpjTrrMTv1JMup9ok+GvDQ== dependencies: "@openpgp/hkp-client" "^0.0.3" "@openpgp/wkd-client" "^0.0.4" From b103be7897d6c2a1cdaa90d7f4d23c082416b89b Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Tue, 19 Sep 2023 13:42:34 +0200 Subject: [PATCH 055/144] feat: update doipjs to 1.0.3 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 0a3ad2e..df96170 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "ajv": "^8.6.3", "bent": "^7.3.12", "body-parser": "^1.19.0", - "doipjs": "^1.0.2", + "doipjs": "^1.0.3", "dotenv": "^16.0.3", "express": "^4.17.1", "express-validator": "^6.13.0", diff --git a/yarn.lock b/yarn.lock index 9329263..376ecfc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1825,10 +1825,10 @@ doctypes@^1.1.0: resolved "https://registry.yarnpkg.com/doctypes/-/doctypes-1.1.0.tgz#ea80b106a87538774e8a3a4a5afe293de489e0a9" integrity sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ== -doipjs@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/doipjs/-/doipjs-1.0.2.tgz#6e2aba8b4894a3520ed55a5fccc98f9e3fa5769c" - integrity sha512-5TWS+Vq6KsmlwRWTLIec5frB8LAJ2uHuW92KJMUPL3dnOIXnlkC9Kg7QTH1V9DSZnpjTrrMTv1JMup9ok+GvDQ== +doipjs@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/doipjs/-/doipjs-1.0.3.tgz#75ed45e4d4c3803df69dc56428b6e9ac333861bc" + integrity sha512-BUma7vC5b1GR2BD7d4OUJVj5Vj8BVgG+RlMUdyfEyPFczQymbDHiG2vvT5rJ7CI89cgI28Nu6zC8aKlECv0nWw== dependencies: "@openpgp/hkp-client" "^0.0.3" "@openpgp/wkd-client" "^0.0.4" From 57da895ae5cbf0837e10b764e62b8f3aacacf983 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Tue, 19 Sep 2023 15:42:54 +0200 Subject: [PATCH 056/144] feat: update doipjs to 1.0.4 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index df96170..7547f7b 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "ajv": "^8.6.3", "bent": "^7.3.12", "body-parser": "^1.19.0", - "doipjs": "^1.0.3", + "doipjs": "^1.0.4", "dotenv": "^16.0.3", "express": "^4.17.1", "express-validator": "^6.13.0", diff --git a/yarn.lock b/yarn.lock index 376ecfc..0e4c7f6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1825,10 +1825,10 @@ doctypes@^1.1.0: resolved "https://registry.yarnpkg.com/doctypes/-/doctypes-1.1.0.tgz#ea80b106a87538774e8a3a4a5afe293de489e0a9" integrity sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ== -doipjs@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/doipjs/-/doipjs-1.0.3.tgz#75ed45e4d4c3803df69dc56428b6e9ac333861bc" - integrity sha512-BUma7vC5b1GR2BD7d4OUJVj5Vj8BVgG+RlMUdyfEyPFczQymbDHiG2vvT5rJ7CI89cgI28Nu6zC8aKlECv0nWw== +doipjs@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/doipjs/-/doipjs-1.0.4.tgz#87e7d703ef5f8c452c8464cdbbba2012ff15e052" + integrity sha512-p4njl3tJZdpg2OMVgd9m6pdf7/o999ZsujZ58mmYlnO22p/YeDdCQmZlzqxwGszAuRlkF1r5J0RdUDUY0y3KTg== dependencies: "@openpgp/hkp-client" "^0.0.3" "@openpgp/wkd-client" "^0.0.4" From 1756a37ab2d8e42af376826e670c7a89e038c7e9 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Wed, 20 Sep 2023 08:32:50 +0200 Subject: [PATCH 057/144] fix: missing rel=me link for ambiguous claims --- views/profile.pug | 87 ++++++++++++++++++++++------------------------- 1 file changed, 40 insertions(+), 47 deletions(-) diff --git a/views/profile.pug b/views/profile.pug index 1c141c3..335d9c7 100644 --- a/views/profile.pug +++ b/views/profile.pug @@ -1,54 +1,47 @@ extends templates/base.pug mixin generatePersona(persona, isPrimary) - h2 - if persona.email - | Identity claims ( - span.p-email #{persona.email} - | ) - else - | Identity claims - if isPrimary - small.primary primary - if persona.description - span.persona__description.p-comment - - | #{persona.description} - each claim in persona.claims - if claim.matches.length > 0 - kx-claim.kx-item(data-claim=claim,data-status='running') - details(aria-label="Claim") - summary - .info - p - span.title= claim.display.name - span.subtitle-wrapper - | [ - span.subtitle= claim.display.serviceproviderName - | ] - .icons - .verificationStatus - .inProgress - - .success - - .failure - + if persona.claims.length > 0 + h2 + if persona.email + | Identity claims ( + span.p-email #{persona.email} + | ) + else + | Identity claims + if isPrimary + small.primary primary + if persona.description + span.persona__description.p-comment + + | #{persona.description} + each claim in persona.claims + if claim.matches.length > 0 + kx-claim.kx-item(data-claim=claim,data-status='running') + details(aria-label="Claim") + summary + .info + p + span.title= claim.display.name + span.subtitle-wrapper + | [ + span.subtitle= claim.display.serviceproviderName + | ] + .icons + .verificationStatus + .inProgress + + .success + + .failure + - .content - .subsection - img(src='/static/img/link.png') - div - if (claim.display.url) - p Profile link: - a(rel='me' href=claim.display.url aria-label="Link to profile")= claim.display.url - else - p Profile link: not accessible from browser - if (claim.matches.length === 1 && claim.matches[0].proof.uri) - p Proof link: - a(href=claim.matches[0].proof.uri aria-label="Link to proof")= claim.matches[0].proof.uri - else - p Proof link: not accessible from browser + .content + .subsection + img(src='/static/img/link.png') + div + p Claim link: + a(rel="me" href=claim.uri aria-label="Link to claim")= claim.uri block content if (data && 'publicKey' in data) From 7ce2287894bcc75764123d68c00ed5fc79923b66 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Thu, 21 Sep 2023 15:27:44 +0200 Subject: [PATCH 058/144] feat: update doipjs to 1.1.0 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 7547f7b..eaf6fe6 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "ajv": "^8.6.3", "bent": "^7.3.12", "body-parser": "^1.19.0", - "doipjs": "^1.0.4", + "doipjs": "^1.1.0", "dotenv": "^16.0.3", "express": "^4.17.1", "express-validator": "^6.13.0", diff --git a/yarn.lock b/yarn.lock index 0e4c7f6..546903d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1825,10 +1825,10 @@ doctypes@^1.1.0: resolved "https://registry.yarnpkg.com/doctypes/-/doctypes-1.1.0.tgz#ea80b106a87538774e8a3a4a5afe293de489e0a9" integrity sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ== -doipjs@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/doipjs/-/doipjs-1.0.4.tgz#87e7d703ef5f8c452c8464cdbbba2012ff15e052" - integrity sha512-p4njl3tJZdpg2OMVgd9m6pdf7/o999ZsujZ58mmYlnO22p/YeDdCQmZlzqxwGszAuRlkF1r5J0RdUDUY0y3KTg== +doipjs@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/doipjs/-/doipjs-1.1.0.tgz#1130550c8cd23c72504e27c6fa61dfe3123d36e0" + integrity sha512-L3W+Yqc+CiJjxb2N9wZf2oWqrcnKS7MQYN8E0sJsVqpTdSSNF7NgC5Bq5LYMOwv0nmuLOiFw+qyHpKWIZXcGuw== dependencies: "@openpgp/hkp-client" "^0.0.3" "@openpgp/wkd-client" "^0.0.4" From fc10aeba1c08bd89a35e99a791c0d861b50b1b3a Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Thu, 21 Sep 2023 15:30:35 +0200 Subject: [PATCH 059/144] fix: use correct fromJSON() for Profiles and Claims --- src/server/openpgpProfiles.js | 6 ++++-- static-src/kx-claim.js | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/server/openpgpProfiles.js b/src/server/openpgpProfiles.js index d82eade..6eaaefa 100644 --- a/src/server/openpgpProfiles.js +++ b/src/server/openpgpProfiles.js @@ -58,7 +58,8 @@ const fetchWKD = (id) => { const hash = createHash('md5').update(id).digest('hex') if (c && await c.get(hash)) { - profile = doipjs.Claim.fromJson(JSON.parse(await c.get(hash))) + profile = doipjs.Profile.fromJSON(JSON.parse(await c.get(hash))) + return resolve(profile) } if (!profile) { @@ -147,7 +148,8 @@ const fetchHKP = (id, keyserverDomain) => { const hash = createHash('md5').update(`${query}__${keyserverDomainNormalized}`).digest('hex') if (c && await c.get(hash)) { - profile = doipjs.Claim.fromJson(JSON.parse(await c.get(hash))) + profile = doipjs.Profile.fromJSON(JSON.parse(await c.get(hash))) + return resolve(profile) } if (!profile) { diff --git a/static-src/kx-claim.js b/static-src/kx-claim.js index 75e7e40..87c9f42 100644 --- a/static-src/kx-claim.js +++ b/static-src/kx-claim.js @@ -45,7 +45,7 @@ export class Claim extends HTMLElement { } async verify() { - const claim = doipjs.Claim.fromJson(JSON.parse(this.getAttribute('data-claim'))); + const claim = doipjs.Claim.fromJSON(JSON.parse(this.getAttribute('data-claim'))); await claim.verify({ proxy: { policy: 'adaptive', @@ -59,7 +59,7 @@ export class Claim extends HTMLElement { updateContent(value) { const root = this; const claimJson = JSON.parse(value); - const claim = doipjs.Claim.fromJson(claimJson); + const claim = doipjs.Claim.fromJSON(claimJson); root.querySelector('.info .title').innerText = claimJson.display.name; root.querySelector('.info .subtitle').innerText = claimJson.display.serviceProviderName ?? From 36ff58576b62c1aedb1c5c9a5476726f79482daf Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Thu, 21 Sep 2023 15:33:27 +0200 Subject: [PATCH 060/144] feat: add logging to OpenPGP cache component --- src/server/openpgpProfiles.js | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/server/openpgpProfiles.js b/src/server/openpgpProfiles.js index 6eaaefa..17a9990 100644 --- a/src/server/openpgpProfiles.js +++ b/src/server/openpgpProfiles.js @@ -27,6 +27,7 @@ You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see . */ +import logger from '../log.js' import got from 'got' import * as doipjs from 'doipjs' import { readKey } from 'openpgp' @@ -34,7 +35,13 @@ import { computeWKDLocalPart } from './utils.js' import { createHash } from 'crypto' import Keyv from 'keyv' -const c = process.env.ENABLE_EXPERIMENTAL_CACHE ? new Keyv() : null +let c = null +if (process.env.ENABLE_EXPERIMENTAL_CACHE) { + c = new Keyv() + + logger.debug('OpenPGP cache started', + { component: 'openpgp_cache', action: 'start' }) +} const fetchWKD = (id) => { return new Promise((resolve, reject) => { @@ -59,6 +66,10 @@ const fetchWKD = (id) => { const hash = createHash('md5').update(id).digest('hex') if (c && await c.get(hash)) { profile = doipjs.Profile.fromJSON(JSON.parse(await c.get(hash))) + + logger.debug('WKD profile retrieved from OpenPGP cache', + { component: 'openpgp_cache', action: 'retrieve_wkd' }) + return resolve(profile) } @@ -116,6 +127,9 @@ const fetchWKD = (id) => { if (c && plaintext instanceof Uint8Array) { await c.set(hash, JSON.stringify(profile), 60 * 1000) + + logger.debug('WKD profile stored in OpenPGP cache', + { component: 'openpgp_cache', action: 'store_wkd' }) } resolve(profile) @@ -149,6 +163,10 @@ const fetchHKP = (id, keyserverDomain) => { if (c && await c.get(hash)) { profile = doipjs.Profile.fromJSON(JSON.parse(await c.get(hash))) + + logger.debug('HKP profile retrieved from OpenPGP cache', + { component: 'openpgp_cache', action: 'store_hkp' }) + return resolve(profile) } @@ -170,6 +188,9 @@ const fetchHKP = (id, keyserverDomain) => { if (c && profile instanceof doipjs.Profile) { await c.set(hash, JSON.stringify(profile), 60 * 1000) + + logger.debug('HKP profile stored in OpenPGP cache', + { component: 'openpgp_cache', action: 'store_hkp' }) } resolve(profile) From c272fa7d446c5d2bfcf97c48a2d9d8b589e369aa Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Thu, 21 Sep 2023 15:50:32 +0200 Subject: [PATCH 061/144] chore: release 4.1.1 --- CHANGELOG.md | 7 +++++++ package.json | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a53d0c..72525b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [4.1.1] - 2023-09-18 +### Changed +- Update doipjs to 1.1.0 +### Fixed +- Missing rel=me for ambiguous claims +- OpenPGP cache logic + ## [4.1.0] - 2023-09-18 ### Changed - Redesign diff --git a/package.json b/package.json index eaf6fe6..6b61824 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keyoxide-web", - "version": "4.1.0", + "version": "4.1.1", "description": "Verifying online identity with cryptography", "main": "./src/index.js", "type": "module", From 5f5e039a2c316165ecd119cf3c81443299ffc7f0 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Thu, 21 Sep 2023 15:56:26 +0200 Subject: [PATCH 062/144] fix: fix CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72525b8..35befff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [4.1.1] - 2023-09-18 +## [4.1.1] - 2023-09-21 ### Changed - Update doipjs to 1.1.0 ### Fixed From a812bb0866337784c68323beb963221df188ec64 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Thu, 21 Sep 2023 22:54:37 +0200 Subject: [PATCH 063/144] feat: add request data to logs --- nodemon.json | 3 ++- package.json | 2 ++ src/index.js | 10 ++++++++++ src/log.js | 11 +++++++++++ yarn.lock | 10 ++++++++++ 5 files changed, 35 insertions(+), 1 deletion(-) diff --git a/nodemon.json b/nodemon.json index 099d21a..fa42808 100644 --- a/nodemon.json +++ b/nodemon.json @@ -1,6 +1,7 @@ { "env": { - "NODE_ENV": "development" + "NODE_ENV": "development", + "LOG_LEVEL": "debug" }, "ext": "js,json,css,pug,md" } \ No newline at end of file diff --git a/package.json b/package.json index 6b61824..6d1ca37 100644 --- a/package.json +++ b/package.json @@ -11,12 +11,14 @@ "doipjs": "^1.1.0", "dotenv": "^16.0.3", "express": "^4.17.1", + "express-http-context2": "^1.0.0", "express-validator": "^6.13.0", "got": "^11.8.2", "hash-wasm": "^4.9.0", "jstransformer-markdown-it": "^3.0.0", "keyv": "^4.5.0", "libravatar": "^3.0.0", + "nanoid": "^5.0.1", "openpgp": "^5.5.0", "pug": "^3.0.0", "qrcode": "^1.4.4", diff --git a/src/index.js b/src/index.js index 691e462..f99b1d4 100644 --- a/src/index.js +++ b/src/index.js @@ -28,6 +28,8 @@ if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see . */ import express from 'express' +import * as httpContext from 'express-http-context2' +import { nanoid } from 'nanoid' import { readFileSync } from 'fs' import { stringReplace } from 'string-replace-middleware' import * as pug from 'pug' @@ -53,8 +55,16 @@ app.set('keyoxide_version', packageData.version) app.set('onion_url', process.env.ONION_URL) // Middlewares +app.use(httpContext.middleware) app.use((req, res, next) => { res.setHeader('Permissions-Policy', 'interest-cohort=()') + httpContext.set('requestId', nanoid()) + httpContext.set('requestPath', req.path) + httpContext.set('requestIp', req.ip) + + logger.info(`Handle a request`, + { component: 'http_server', action: 'request' }) + next() }) diff --git a/src/log.js b/src/log.js index 3a4b0b5..afff79e 100644 --- a/src/log.js +++ b/src/log.js @@ -28,6 +28,7 @@ if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see . */ import { createLogger, format, transports } from 'winston' +import * as httpContext from 'express-http-context2' import * as dotenv from 'dotenv' dotenv.config() @@ -37,13 +38,23 @@ const anonymize = format((info, opts) => { info.keyserver_domain = undefined info.username = undefined info.fingerprint = undefined + info.request_path = undefined + info.request_ip = undefined } return info }) +const addRequestData = format((info, opts) => { + if (httpContext.get('requestId')) info.request_id = httpContext.get('requestId') + if (httpContext.get('requestPath')) info.request_path = httpContext.get('requestPath') + if (httpContext.get('requestIp')) info.request_ip = httpContext.get('requestIp') + return info +}) + const logger = createLogger({ level: process.env.LOG_LEVEL || 'info', format: format.combine( + addRequestData(), anonymize(), format.timestamp(), format.json() diff --git a/yarn.lock b/yarn.lock index 546903d..cc01d7a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2264,6 +2264,11 @@ events@^3.2.0, events@^3.3.0: resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== +express-http-context2@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/express-http-context2/-/express-http-context2-1.0.0.tgz#58cd9fb0d233739e0dcd7aabb766d1dc74522d77" + integrity sha512-xdukoNNpWcuMn5ZJcjDe/tA+2A96rQ1MyAB/oWUU7qP15Tkz3txQyFsw/QG8YgRzTJ1sNAA8Bdq0o5b/1Y4zLA== + express-validator@^6.10.0, express-validator@^6.13.0: version "6.15.0" resolved "https://registry.yarnpkg.com/express-validator/-/express-validator-6.15.0.tgz#5e4601428960b0d66f5f4ae09cb32ed2077374a4" @@ -3539,6 +3544,11 @@ nanoid@^3.3.6: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== +nanoid@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-5.0.1.tgz#3e95d775a8bc8a98afbf0a237e2bbc6a71b0662e" + integrity sha512-vWeVtV5Cw68aML/QaZvqN/3QQXc6fBfIieAlu05m7FZW2Dgb+3f0xc0TTxuJW+7u30t7iSDTV/j3kVI0oJqIfQ== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" From 09d22925578c943ac7835206ea9d3be3398a8e7d Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Fri, 22 Sep 2023 09:48:55 +0200 Subject: [PATCH 064/144] feat: add logging to openpgp profiles --- src/index.js | 2 +- src/server/openpgpProfiles.js | 31 +++++++++++++++++++++++++++++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/index.js b/src/index.js index f99b1d4..f583006 100644 --- a/src/index.js +++ b/src/index.js @@ -62,7 +62,7 @@ app.use((req, res, next) => { httpContext.set('requestPath', req.path) httpContext.set('requestIp', req.ip) - logger.info(`Handle a request`, + logger.info('Handle a request', { component: 'http_server', action: 'request' }) next() diff --git a/src/server/openpgpProfiles.js b/src/server/openpgpProfiles.js index 17a9990..b6605e7 100644 --- a/src/server/openpgpProfiles.js +++ b/src/server/openpgpProfiles.js @@ -46,6 +46,9 @@ if (process.env.ENABLE_EXPERIMENTAL_CACHE) { const fetchWKD = (id) => { return new Promise((resolve, reject) => { (async () => { + logger.debug('Fetching an OpenPGP profile via WKD', + { component: 'wkd_profile_fetcher', action: 'start', profile_id: id }) + let publicKey = null let profile = null let fetchURL = null @@ -83,7 +86,10 @@ const fetchWKD = (id) => { return null } }) - } catch (e) { + } catch (errorAdvanced) { + logger.debug('Failed to fetch an OpenPGP profile via WKD (advanced URL)', + { component: 'hkp_profile_fetcher', action: 'failure', profile_id: id, error: errorAdvanced.message }) + try { plaintext = await got(urlDirect).then((response) => { if (response.statusCode === 200) { @@ -93,7 +99,10 @@ const fetchWKD = (id) => { return null } }) - } catch (error) { + } catch (errorDirect) { + logger.debug('Failed to fetch an OpenPGP profile via WKD (direct URL)', + { component: 'hkp_profile_fetcher', action: 'failure', profile_id: id, error: errorDirect.message }) + return reject(new Error('No public keys could be fetched using WKD')) } } @@ -107,6 +116,9 @@ const fetchWKD = (id) => { binaryKey: plaintext }) } catch (error) { + logger.debug('Failed to fetch an OpenPGP profile via WKD (reading key)', + { component: 'hkp_profile_fetcher', action: 'failure', profile_id: id, error: error.message }) + return reject(new Error('No public keys could be read from the data fetched using WKD')) } @@ -117,6 +129,9 @@ const fetchWKD = (id) => { try { profile = await doipjs.openpgp.parsePublicKey(publicKey) } catch (error) { + logger.debug('Failed to fetch an OpenPGP profile via WKD (parsing key)', + { component: 'hkp_profile_fetcher', action: 'failure', profile_id: id, error: error.message }) + return reject(new Error('No public keys could be fetched using WKD')) } @@ -132,6 +147,9 @@ const fetchWKD = (id) => { { component: 'openpgp_cache', action: 'store_wkd' }) } + logger.debug('Fetched an OpenPGP profile via WKD', + { component: 'wkd_profile_fetcher', action: 'done', profile_id: id }) + resolve(profile) })() }) @@ -140,6 +158,9 @@ const fetchWKD = (id) => { const fetchHKP = (id, keyserverDomain) => { return new Promise((resolve, reject) => { (async () => { + logger.debug('Fetching an OpenPGP profile via HKP', + { component: 'hkp_profile_fetcher', action: 'start', profile_id: id, keyserver_domain: keyserverDomain || '' }) + let profile = null let fetchURL = null @@ -174,6 +195,9 @@ const fetchHKP = (id, keyserverDomain) => { try { profile = await doipjs.openpgp.fetchHKP(query, keyserverDomainNormalized) } catch (error) { + logger.debug('Failed to fetch an OpenPGP profile via HKP', + { component: 'hkp_profile_fetcher', action: 'failure', profile_id: id, keyserver_domain: keyserverDomain || '', error: error.message }) + profile = null } } @@ -193,6 +217,9 @@ const fetchHKP = (id, keyserverDomain) => { { component: 'openpgp_cache', action: 'store_hkp' }) } + logger.debug('Fetched an OpenPGP profile via HKP', + { component: 'hkp_profile_fetcher', action: 'done', profile_id: id, keyserver_domain: keyserverDomain || '' }) + resolve(profile) })() }) From 3d7f1ce11a4bf5da8358b3049f738e0382ce8c21 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Fri, 22 Sep 2023 10:22:35 +0200 Subject: [PATCH 065/144] fix: make hash utils aware of ASPE --- static-src/ui.js | 12 ++++++------ views/util/argon2.pug | 2 +- views/util/bcrypt.pug | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/static-src/ui.js b/static-src/ui.js index 61d35f6..dda2fee 100644 --- a/static-src/ui.js +++ b/static-src/ui.js @@ -337,8 +337,8 @@ const runArgon2GenerationUtility = () => { elFeedback.innerHTML = ""; } else { let feedbackContent = ""; - if (!(/openpgp4fpr:[0-9a-zA-Z]+/.test(elInput.value))) { - feedbackContent += "❗ Valid proofs must begin with openpgp4fpr:.
"; + if (!(/[openpgp4fpr|aspe]:[0-9a-zA-Z]+/.test(elInput.value))) { + feedbackContent += "❗ Valid OpenPGP proofs must begin with openpgp4fpr:.
"; } if (!(elInput.value === elInput.value.toLowerCase())) { feedbackContent += "❗ Valid proofs must be lowercase.
"; @@ -379,7 +379,7 @@ window.kx__fixArgon2Input = () => { const elInput = document.querySelector('#form-util-argon2-generate .input'); elInput.value = elInput.value.toLowerCase(); - if (!(/openpgp4fpr:[0-9a-zA-Z]+/.test(elInput.value))) { + if (!(/[openpgp4fpr|aspe]:[0-9a-zA-Z]+/.test(elInput.value))) { elInput.value = `openpgp4fpr:${elInput.value}`; } @@ -402,8 +402,8 @@ const runBcryptGenerationUtility = () => { elFeedback.innerHTML = ""; } else { let feedbackContent = ""; - if (!(/openpgp4fpr:[0-9a-zA-Z]+/.test(elInput.value))) { - feedbackContent += "❗ Valid proofs must begin with openpgp4fpr:.
"; + if (!(/[openpgp4fpr|aspe]:[0-9a-zA-Z]+/.test(elInput.value))) { + feedbackContent += "❗ Valid OpenPGP proofs must begin with openpgp4fpr:.
"; } if (!(elInput.value === elInput.value.toLowerCase())) { feedbackContent += "❗ Valid proofs must be lowercase.
"; @@ -444,7 +444,7 @@ window.kx__fixBcryptInput = () => { const elInput = document.querySelector('#form-util-bcrypt-generate .input'); elInput.value = elInput.value.toLowerCase(); - if (!(/openpgp4fpr:[0-9a-zA-Z]+/.test(elInput.value))) { + if (!(/[openpgp4fpr|aspe]:[0-9a-zA-Z]+/.test(elInput.value))) { elInput.value = `openpgp4fpr:${elInput.value}`; } diff --git a/views/util/argon2.pug b/views/util/argon2.pug index d7189b7..f4023f7 100644 --- a/views/util/argon2.pug +++ b/views/util/argon2.pug @@ -11,7 +11,7 @@ block content a(href='https://en.wikipedia.org/wiki/Argon2') Argon2 | hashes useful to a(href='https://docs.keyoxide.org/understanding-keyoxide/identity-proof-formats/#Hashed_URI') conceal identity proofs - | . Be sure to include "openpgp4fpr:" for a valid proof! + | . Be sure to include "openpgp4fpr:" for a valid OpenPGP proof! For ASP, enter the entire URI beginning with "aspe:". h3 Input input.input.half-width(type='text' name='input' placeholder='openpgp4fpr:…' value=input) h3 Hash diff --git a/views/util/bcrypt.pug b/views/util/bcrypt.pug index 90d6f84..e3fafa9 100644 --- a/views/util/bcrypt.pug +++ b/views/util/bcrypt.pug @@ -11,7 +11,7 @@ block content a(href='https://en.wikipedia.org/wiki/Bcrypt') bcrypt | hashes useful to a(href='https://docs.keyoxide.org/understanding-keyoxide/identity-proof-formats/#Hashed_URI') conceal identity proofs - | . Be sure to include "openpgp4fpr:" for a valid proof! + | . Be sure to include "openpgp4fpr:" for a valid OpenPGP proof! For ASP, enter the entire URI beginning with "aspe:". h3 Input input.input.half-width(type='text' name='input' placeholder='openpgp4fpr:…' value=input) h3 Hash From bccd5d298fa506405d5e2100c2312efc80b86eff Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Fri, 22 Sep 2023 11:07:14 +0200 Subject: [PATCH 066/144] feat: update doipjs to 1.1.1 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 6d1ca37..ed4f587 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "ajv": "^8.6.3", "bent": "^7.3.12", "body-parser": "^1.19.0", - "doipjs": "^1.1.0", + "doipjs": "^1.1.1", "dotenv": "^16.0.3", "express": "^4.17.1", "express-http-context2": "^1.0.0", diff --git a/yarn.lock b/yarn.lock index cc01d7a..a4dfb29 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1825,10 +1825,10 @@ doctypes@^1.1.0: resolved "https://registry.yarnpkg.com/doctypes/-/doctypes-1.1.0.tgz#ea80b106a87538774e8a3a4a5afe293de489e0a9" integrity sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ== -doipjs@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/doipjs/-/doipjs-1.1.0.tgz#1130550c8cd23c72504e27c6fa61dfe3123d36e0" - integrity sha512-L3W+Yqc+CiJjxb2N9wZf2oWqrcnKS7MQYN8E0sJsVqpTdSSNF7NgC5Bq5LYMOwv0nmuLOiFw+qyHpKWIZXcGuw== +doipjs@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/doipjs/-/doipjs-1.1.1.tgz#3a79b360e33749ebff1e978c8ebb2418a5769a10" + integrity sha512-1fGjno11mIPNDJMlFG5VJU+4TvUEBCxBJ0C52sLf5qdnZU08zd9eQOkbwncMCqti4r+M61xVCjlhd2T23oTYFQ== dependencies: "@openpgp/hkp-client" "^0.0.3" "@openpgp/wkd-client" "^0.0.4" From b333365730a913c6c79e5d4f36cb413951c3d710 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Fri, 22 Sep 2023 12:15:10 +0200 Subject: [PATCH 067/144] feat: add profile request rate limiter --- package.json | 1 + src/routes/profile.js | 38 +++++++++++++++++++++++++++++++------- template.env | 6 +++++- views/429.pug | 8 ++++++++ yarn.lock | 5 +++++ 5 files changed, 50 insertions(+), 8 deletions(-) create mode 100644 views/429.pug diff --git a/package.json b/package.json index ed4f587..9a388c4 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "dotenv": "^16.0.3", "express": "^4.17.1", "express-http-context2": "^1.0.0", + "express-rate-limit": "^7.0.1", "express-validator": "^6.13.0", "got": "^11.8.2", "hash-wasm": "^4.9.0", diff --git a/src/routes/profile.js b/src/routes/profile.js index 9211bc5..9e15a1a 100644 --- a/src/routes/profile.js +++ b/src/routes/profile.js @@ -29,18 +29,42 @@ more information on this, and how to apply and follow the GNU AGPL, see { +let profileRateLimiter = (req, res, next) => { + next() +} + +if (process.env.ENABLE_EXPERIMENTAL_RATE_LIMITER) { + profileRateLimiter = rateLimit({ + windowMs: 1000, + limit: 3, + standardHeaders: 'draft-7', + legacyHeaders: false, + handler: (req, res, next, options) => { + logger.debug('Rate-limiting a profile request', + { component: 'profile_rate_limiter', action: 'block' }) + + res.status(options.statusCode).render('429', { meta: getMetaFromReq(req) }) + } + }) + + logger.debug('Starting the profile request rate limiter', + { component: 'profile_rate_limiter', action: 'start' }) +} + +router.get('/sig', profileRateLimiter, (req, res) => { res.render('profile', { isSignature: true, signature: null, meta: getMetaFromReq(req) }) }) -router.post('/sig', bodyParser, async (req, res) => { +router.post('/sig', profileRateLimiter, bodyParser, async (req, res) => { const data = await generateSignatureProfile(req.body.signature) const title = utils.generatePageTitle('profile', data) res.set('ariadne-identity-proof', data.identifier) @@ -55,7 +79,7 @@ router.post('/sig', bodyParser, async (req, res) => { }) }) -router.get('/wkd/:id', async (req, res) => { +router.get('/wkd/:id', profileRateLimiter, async (req, res) => { const data = await generateWKDProfile(req.params.id) const title = utils.generatePageTitle('profile', data) res.set('ariadne-identity-proof', data.identifier) @@ -68,7 +92,7 @@ router.get('/wkd/:id', async (req, res) => { }) }) -router.get('/hkp/:id', async (req, res) => { +router.get('/hkp/:id', profileRateLimiter, async (req, res) => { const data = await generateHKPProfile(req.params.id) const title = utils.generatePageTitle('profile', data) res.set('ariadne-identity-proof', data.identifier) @@ -81,7 +105,7 @@ router.get('/hkp/:id', async (req, res) => { }) }) -router.get('/hkp/:server/:id', async (req, res) => { +router.get('/hkp/:server/:id', profileRateLimiter, async (req, res) => { const data = await generateHKPProfile(req.params.id, req.params.server) const title = utils.generatePageTitle('profile', data) res.set('ariadne-identity-proof', data.identifier) @@ -94,7 +118,7 @@ router.get('/hkp/:server/:id', async (req, res) => { }) }) -router.get('/keybase/:username/:fingerprint', async (req, res) => { +router.get('/keybase/:username/:fingerprint', profileRateLimiter, async (req, res) => { const data = await generateKeybaseProfile(req.params.username, req.params.fingerprint) const title = utils.generatePageTitle('profile', data) res.set('ariadne-identity-proof', data.identifier) @@ -107,7 +131,7 @@ router.get('/keybase/:username/:fingerprint', async (req, res) => { }) }) -router.get('/:id', async (req, res) => { +router.get('/:id', profileRateLimiter, async (req, res) => { const data = await generateAutoProfile(req.params.id) const title = utils.generatePageTitle('profile', data) res.set('ariadne-identity-proof', data.identifier) diff --git a/template.env b/template.env index 5f05cf4..d4a974f 100644 --- a/template.env +++ b/template.env @@ -36,4 +36,8 @@ # Enable caching of keys (experimental) # Opt-in; to disable, omit the environment variable -#ENABLE_EXPERIMENTAL_CACHE= +#ENABLE_EXPERIMENTAL_CACHE=true + +# Enable profile request rate limiting (experimental) +# Opt-in; to disable, omit the environment variable +#ENABLE_EXPERIMENTAL_RATE_LIMITER=true diff --git a/views/429.pug b/views/429.pug new file mode 100644 index 0000000..48bb4cd --- /dev/null +++ b/views/429.pug @@ -0,0 +1,8 @@ +extends templates/base.pug + +block content + h1 429 TOO MANY REQUESTS + p + | Too many requests from this IP, please try again later. + br + | Limit: 3 profile requests per second. diff --git a/yarn.lock b/yarn.lock index a4dfb29..806c582 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2269,6 +2269,11 @@ express-http-context2@^1.0.0: resolved "https://registry.yarnpkg.com/express-http-context2/-/express-http-context2-1.0.0.tgz#58cd9fb0d233739e0dcd7aabb766d1dc74522d77" integrity sha512-xdukoNNpWcuMn5ZJcjDe/tA+2A96rQ1MyAB/oWUU7qP15Tkz3txQyFsw/QG8YgRzTJ1sNAA8Bdq0o5b/1Y4zLA== +express-rate-limit@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-7.0.1.tgz#933af24166990ea4fc8004335e6cd6c86fd31562" + integrity sha512-oTIPm094gh8c7nbShl4TNLqnayzOcbDGY7dCRnFqUAvptyb0pp5231LaH34JtvVEbZlOJMiixikU5AVK8VN3FA== + express-validator@^6.10.0, express-validator@^6.13.0: version "6.15.0" resolved "https://registry.yarnpkg.com/express-validator/-/express-validator-6.15.0.tgz#5e4601428960b0d66f5f4ae09cb32ed2077374a4" From af1bdd872cdf1ccf71e953245d1682fef94e5c47 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Fri, 22 Sep 2023 12:20:16 +0200 Subject: [PATCH 068/144] feat: update template.env --- template.env | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/template.env b/template.env index d4a974f..488bf95 100644 --- a/template.env +++ b/template.env @@ -12,28 +12,14 @@ # $ while read -r line; do echo -nE "$line\n" ; done < public.pem > public-oneline.pem #ACTIVITYPUB_PUBLIC_KEY= -# Domain for DOIP Proxy server -# Source code for the server can be found here https://codeberg.org/keyoxide/doip-proxy +# Domain for Keyoxide Proxy server +# To host a Keyoxide Proxy server, refer to https://docs.keyoxide.org/self-hosting/ #PROXY_HOSTNAME= # Tor Onion URL # The full http:// onion url to add as an 'Onion-Location' header #ONION_URL= -# Highlights -# Highlight up to three profiles on the homepage -#KX_HIGHLIGHTS_1_NAME= -#KX_HIGHLIGHTS_1_DESCRIPTION= -#KX_HIGHLIGHTS_1_FINGERPRINT= - -#KX_HIGHLIGHTS_2_NAME= -#KX_HIGHLIGHTS_2_DESCRIPTION= -#KX_HIGHLIGHTS_2_FINGERPRINT= - -#KX_HIGHLIGHTS_3_NAME= -#KX_HIGHLIGHTS_3_DESCRIPTION= -#KX_HIGHLIGHTS_3_FINGERPRINT= - # Enable caching of keys (experimental) # Opt-in; to disable, omit the environment variable #ENABLE_EXPERIMENTAL_CACHE=true From 4fb8302cc99a50e37d0b036d1cdf7ac51cc5b8dc Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Sat, 23 Sep 2023 10:43:40 +0200 Subject: [PATCH 069/144] feat: update doipjs to 1.2.1 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 9a388c4..65d7260 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "ajv": "^8.6.3", "bent": "^7.3.12", "body-parser": "^1.19.0", - "doipjs": "^1.1.1", + "doipjs": "^1.2.1", "dotenv": "^16.0.3", "express": "^4.17.1", "express-http-context2": "^1.0.0", diff --git a/yarn.lock b/yarn.lock index 806c582..9ea2981 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1825,10 +1825,10 @@ doctypes@^1.1.0: resolved "https://registry.yarnpkg.com/doctypes/-/doctypes-1.1.0.tgz#ea80b106a87538774e8a3a4a5afe293de489e0a9" integrity sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ== -doipjs@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/doipjs/-/doipjs-1.1.1.tgz#3a79b360e33749ebff1e978c8ebb2418a5769a10" - integrity sha512-1fGjno11mIPNDJMlFG5VJU+4TvUEBCxBJ0C52sLf5qdnZU08zd9eQOkbwncMCqti4r+M61xVCjlhd2T23oTYFQ== +doipjs@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/doipjs/-/doipjs-1.2.1.tgz#d80f687a560dacc54ed008983e1b62c40ff81b9c" + integrity sha512-PDhmYEoI0eNXM4IQmXmUoDqXctEsKA24u6ZslLBFOvHReqfH2fTQADnIH5V5tqNpPwWynHIqmrwczY/fpIqnMQ== dependencies: "@openpgp/hkp-client" "^0.0.3" "@openpgp/wkd-client" "^0.0.4" From cd629fabb9fba157b080a6ad39a8a70cb681dc25 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Sat, 23 Sep 2023 10:44:39 +0200 Subject: [PATCH 070/144] chore: release 4.2.0 --- CHANGELOG.md | 10 ++++++++++ package.json | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35befff..645d360 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [4.2.0] - 2023-09-23 +### Added +- Profile request rate limiter (experimental; opt-in) +### Changed +- Update doipjs to 1.2.1 +- Add logging to OpenPGP profile creation +- Add debug data to logs +### Fixed +- Make hash utils aware of ASPE + ## [4.1.1] - 2023-09-21 ### Changed - Update doipjs to 1.1.0 diff --git a/package.json b/package.json index 65d7260..93a3f77 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keyoxide-web", - "version": "4.1.1", + "version": "4.2.0", "description": "Verifying online identity with cryptography", "main": "./src/index.js", "type": "module", From 72e18ac7dd45731415e282e7379a6dc398584e22 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Sat, 23 Sep 2023 22:10:17 +0200 Subject: [PATCH 071/144] fix: tweak rate limiter parameters --- src/routes/profile.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routes/profile.js b/src/routes/profile.js index 9e15a1a..f03e2c4 100644 --- a/src/routes/profile.js +++ b/src/routes/profile.js @@ -44,8 +44,8 @@ let profileRateLimiter = (req, res, next) => { if (process.env.ENABLE_EXPERIMENTAL_RATE_LIMITER) { profileRateLimiter = rateLimit({ - windowMs: 1000, - limit: 3, + windowMs: 5000, + limit: 20, standardHeaders: 'draft-7', legacyHeaders: false, handler: (req, res, next, options) => { From 526e69809c0dee8d54bbf12a13495bb7202cb0b7 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Sat, 23 Sep 2023 22:16:32 +0200 Subject: [PATCH 072/144] chore: release 4.2.1 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 645d360..387d0c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [4.2.1] - 2023-09-23 +### Fixed +- Tweak the rate limiter parameters + ## [4.2.0] - 2023-09-23 ### Added - Profile request rate limiter (experimental; opt-in) diff --git a/package.json b/package.json index 93a3f77..a677fb8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keyoxide-web", - "version": "4.2.0", + "version": "4.2.1", "description": "Verifying online identity with cryptography", "main": "./src/index.js", "type": "module", From 9163b455253281e3cd5eab48639cf88c34beb3dc Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Mon, 25 Sep 2023 16:12:20 +0200 Subject: [PATCH 073/144] feat: add updating icons to claims --- static-src/kx-claim.js | 1 + static-src/kx-styles.scss | 8 ++++++++ views/profile.pug | 1 + 3 files changed, 10 insertions(+) diff --git a/static-src/kx-claim.js b/static-src/kx-claim.js index 87c9f42..4c7557a 100644 --- a/static-src/kx-claim.js +++ b/static-src/kx-claim.js @@ -64,6 +64,7 @@ export class Claim extends HTMLElement { root.querySelector('.info .title').innerText = claimJson.display.name; root.querySelector('.info .subtitle').innerText = claimJson.display.serviceProviderName ?? (claim.status < 300 ? '???' : '---'); + root.querySelector('.info img').setAttribute('src', `https://design.keyoxide.org/brands/service-providers/${claimJson.display.serviceProviderName.toLowerCase() || '_'}/icon.svg`); try { if (claim.status >= 200) { diff --git a/static-src/kx-styles.scss b/static-src/kx-styles.scss index 2928103..5416e54 100644 --- a/static-src/kx-styles.scss +++ b/static-src/kx-styles.scss @@ -100,6 +100,14 @@ kx-claim { color: var(--text-color); } + .info img { + width: 16px; + height: 16px; + margin-right: 8px; + vertical-align: sub; + opacity: 0.5; + } + .claim__links { p { diff --git a/views/profile.pug b/views/profile.pug index 335d9c7..c9a9e11 100644 --- a/views/profile.pug +++ b/views/profile.pug @@ -22,6 +22,7 @@ mixin generatePersona(persona, isPrimary) summary .info p + img(src=`https://design.keyoxide.org/brands/service-providers/_/icon.svg` onerror="this.src='https://design.keyoxide.org/brands/service-providers/_/icon.svg'") span.title= claim.display.name span.subtitle-wrapper | [ From 8e39b11cec851ae13907426cb6b9739e3c28918d Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Mon, 25 Sep 2023 17:05:45 +0200 Subject: [PATCH 074/144] fix: make icons light in dark mode --- static-src/kx-styles.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/static-src/kx-styles.scss b/static-src/kx-styles.scss index 5416e54..904208c 100644 --- a/static-src/kx-styles.scss +++ b/static-src/kx-styles.scss @@ -106,6 +106,10 @@ kx-claim { margin-right: 8px; vertical-align: sub; opacity: 0.5; + + @media (prefers-color-scheme: dark) { + filter: invert(1); + } } .claim__links { From 73dd948a33ca2207eeebe5ff716c9eee1b359b75 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Mon, 25 Sep 2023 17:13:06 +0200 Subject: [PATCH 075/144] fix: improve claim image layout --- static-src/kx-styles.scss | 6 ++++-- views/profile.pug | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/static-src/kx-styles.scss b/static-src/kx-styles.scss index 904208c..41291a6 100644 --- a/static-src/kx-styles.scss +++ b/static-src/kx-styles.scss @@ -93,6 +93,9 @@ kx-claim { } .info { + display: flex; + align-items: baseline; + gap: 8px; flex: 1; } @@ -103,9 +106,8 @@ kx-claim { .info img { width: 16px; height: 16px; - margin-right: 8px; - vertical-align: sub; opacity: 0.5; + transform: translateY(3px); @media (prefers-color-scheme: dark) { filter: invert(1); diff --git a/views/profile.pug b/views/profile.pug index c9a9e11..b858a31 100644 --- a/views/profile.pug +++ b/views/profile.pug @@ -21,8 +21,8 @@ mixin generatePersona(persona, isPrimary) details(aria-label="Claim") summary .info + img(src=`https://design.keyoxide.org/brands/service-providers/_/icon.svg` onerror="this.src='https://design.keyoxide.org/brands/service-providers/_/icon.svg'") p - img(src=`https://design.keyoxide.org/brands/service-providers/_/icon.svg` onerror="this.src='https://design.keyoxide.org/brands/service-providers/_/icon.svg'") span.title= claim.display.name span.subtitle-wrapper | [ From 652bfe227e9c94ef47d82c326c601e22b3bd5ad7 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Mon, 25 Sep 2023 17:24:26 +0200 Subject: [PATCH 076/144] fix: avoid calling function on null --- static-src/kx-claim.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/static-src/kx-claim.js b/static-src/kx-claim.js index 4c7557a..e2800df 100644 --- a/static-src/kx-claim.js +++ b/static-src/kx-claim.js @@ -64,7 +64,9 @@ export class Claim extends HTMLElement { root.querySelector('.info .title').innerText = claimJson.display.name; root.querySelector('.info .subtitle').innerText = claimJson.display.serviceProviderName ?? (claim.status < 300 ? '???' : '---'); - root.querySelector('.info img').setAttribute('src', `https://design.keyoxide.org/brands/service-providers/${claimJson.display.serviceProviderName.toLowerCase() || '_'}/icon.svg`); + root.querySelector('.info img').setAttribute('src', + `https://design.keyoxide.org/brands/service-providers/${claimJson.display.serviceProviderName + ? claimJson.display.serviceProviderName.toLowerCase() : '_'}/icon.svg`); try { if (claim.status >= 200) { From 88ecf73d11717b6469bf0a2944ba3d3339e6d40a Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Tue, 3 Oct 2023 12:30:29 +0200 Subject: [PATCH 077/144] feat: update doipjs to 1.2.2 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index a677fb8..4bf27b2 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "ajv": "^8.6.3", "bent": "^7.3.12", "body-parser": "^1.19.0", - "doipjs": "^1.2.1", + "doipjs": "^1.2.2", "dotenv": "^16.0.3", "express": "^4.17.1", "express-http-context2": "^1.0.0", diff --git a/yarn.lock b/yarn.lock index 9ea2981..3211d70 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1825,10 +1825,10 @@ doctypes@^1.1.0: resolved "https://registry.yarnpkg.com/doctypes/-/doctypes-1.1.0.tgz#ea80b106a87538774e8a3a4a5afe293de489e0a9" integrity sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ== -doipjs@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/doipjs/-/doipjs-1.2.1.tgz#d80f687a560dacc54ed008983e1b62c40ff81b9c" - integrity sha512-PDhmYEoI0eNXM4IQmXmUoDqXctEsKA24u6ZslLBFOvHReqfH2fTQADnIH5V5tqNpPwWynHIqmrwczY/fpIqnMQ== +doipjs@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/doipjs/-/doipjs-1.2.2.tgz#8de338f47799a3f4e4e79e8b71f1748bc55b6a14" + integrity sha512-A3DQwWqyBwhPuiIOz6p3yimLIEOafMdrHFC9HCbrxqvZIQWWgVJ6kcAGfqMG9e/ahpWYZoQgBRrRCCzIV13hpA== dependencies: "@openpgp/hkp-client" "^0.0.3" "@openpgp/wkd-client" "^0.0.4" From b1bc1328eb20030b3abd39b6d4831fb404e2b2b1 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Tue, 3 Oct 2023 12:34:16 +0200 Subject: [PATCH 078/144] chore: release 4.2.2 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 387d0c7..3e6fd81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [4.2.2] - 2023-10-03 +### Changed +- Update doipjs to 1.2.2 + ## [4.2.1] - 2023-09-23 ### Fixed - Tweak the rate limiter parameters diff --git a/package.json b/package.json index 4bf27b2..e50cf43 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keyoxide-web", - "version": "4.2.1", + "version": "4.2.2", "description": "Verifying online identity with cryptography", "main": "./src/index.js", "type": "module", From 86b2b354628564e8c86970c069f7cca42e1d3fff Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Tue, 3 Oct 2023 12:57:39 +0200 Subject: [PATCH 079/144] fix: use correct property to generate icon URL --- static-src/kx-claim.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static-src/kx-claim.js b/static-src/kx-claim.js index e2800df..187a0bb 100644 --- a/static-src/kx-claim.js +++ b/static-src/kx-claim.js @@ -65,8 +65,8 @@ export class Claim extends HTMLElement { root.querySelector('.info .subtitle').innerText = claimJson.display.serviceProviderName ?? (claim.status < 300 ? '???' : '---'); root.querySelector('.info img').setAttribute('src', - `https://design.keyoxide.org/brands/service-providers/${claimJson.display.serviceProviderName - ? claimJson.display.serviceProviderName.toLowerCase() : '_'}/icon.svg`); + `https://design.keyoxide.org/brands/service-providers/${claimJson.display.serviceProviderId + ? claimJson.display.serviceProviderId : '_'}/icon.svg`); try { if (claim.status >= 200) { From cf78f9025160c8645618b1de46cec24f67c6857a Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Tue, 3 Oct 2023 13:10:17 +0200 Subject: [PATCH 080/144] fix: update schemas --- src/schemas.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/schemas.js b/src/schemas.js index 9c4a77f..e70eedf 100644 --- a/src/schemas.js +++ b/src/schemas.js @@ -226,6 +226,10 @@ export const claimSchema = { serviceProviderName: { type: ['string', 'null'], description: 'Name of the service provider to display in the user interface' + }, + serviceProviderId: { + type: ['string', 'null'], + description: 'Id of the service provider' } } } From 5928e4d28dcaccd2b4a18bc2fd52d66f2334f02f Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Tue, 3 Oct 2023 13:11:03 +0200 Subject: [PATCH 081/144] fix: allow toJSON to fail --- src/api/v3/keyoxide_profile.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/api/v3/keyoxide_profile.js b/src/api/v3/keyoxide_profile.js index ed8ebd5..ef637a0 100644 --- a/src/api/v3/keyoxide_profile.js +++ b/src/api/v3/keyoxide_profile.js @@ -128,7 +128,13 @@ router.get('/fetch', data = await doVerification(data) } - data = data.toJSON() + try { + data = data.toJSON() + } catch (error) { + data = { + errors: [error.message] + } + } try { // Validate JSON @@ -162,7 +168,13 @@ router.get('/verify', // Do verification let data = await doVerification(profile) - data = data.toJSON() + try { + data = data.toJSON() + } catch (error) { + data = { + errors: [error.message] + } + } try { // Validate JSON From c0ee28d77860959011275da121e7a3cc901e23fe Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Tue, 3 Oct 2023 13:53:54 +0200 Subject: [PATCH 082/144] feat: update doipjs to 1.2.3 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index e50cf43..99c7127 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "ajv": "^8.6.3", "bent": "^7.3.12", "body-parser": "^1.19.0", - "doipjs": "^1.2.2", + "doipjs": "^1.2.3", "dotenv": "^16.0.3", "express": "^4.17.1", "express-http-context2": "^1.0.0", diff --git a/yarn.lock b/yarn.lock index 3211d70..aba5ac5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1825,10 +1825,10 @@ doctypes@^1.1.0: resolved "https://registry.yarnpkg.com/doctypes/-/doctypes-1.1.0.tgz#ea80b106a87538774e8a3a4a5afe293de489e0a9" integrity sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ== -doipjs@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/doipjs/-/doipjs-1.2.2.tgz#8de338f47799a3f4e4e79e8b71f1748bc55b6a14" - integrity sha512-A3DQwWqyBwhPuiIOz6p3yimLIEOafMdrHFC9HCbrxqvZIQWWgVJ6kcAGfqMG9e/ahpWYZoQgBRrRCCzIV13hpA== +doipjs@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/doipjs/-/doipjs-1.2.3.tgz#31751b5e9ec039d33ef9c2cb169862d011adbc70" + integrity sha512-Y+Nxauo9CW72igJ1xwzL9OnsoZCx8Z59KfaB4DyP4G9x53sK33y0IL/ezEXW9Ywn5jVneeclm483tIpT4EJ/VA== dependencies: "@openpgp/hkp-client" "^0.0.3" "@openpgp/wkd-client" "^0.0.4" From f137c6aa4c2d7e85607eedf13c5f1ee403026881 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Wed, 4 Oct 2023 09:22:30 +0200 Subject: [PATCH 083/144] feat: update doipjs to 1.2.4 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 99c7127..578dc37 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,7 @@ "ajv": "^8.6.3", "bent": "^7.3.12", "body-parser": "^1.19.0", - "doipjs": "^1.2.3", + "doipjs": "^1.2.4", "dotenv": "^16.0.3", "express": "^4.17.1", "express-http-context2": "^1.0.0", diff --git a/yarn.lock b/yarn.lock index aba5ac5..42835ad 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1825,10 +1825,10 @@ doctypes@^1.1.0: resolved "https://registry.yarnpkg.com/doctypes/-/doctypes-1.1.0.tgz#ea80b106a87538774e8a3a4a5afe293de489e0a9" integrity sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ== -doipjs@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/doipjs/-/doipjs-1.2.3.tgz#31751b5e9ec039d33ef9c2cb169862d011adbc70" - integrity sha512-Y+Nxauo9CW72igJ1xwzL9OnsoZCx8Z59KfaB4DyP4G9x53sK33y0IL/ezEXW9Ywn5jVneeclm483tIpT4EJ/VA== +doipjs@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/doipjs/-/doipjs-1.2.4.tgz#f980ac4a3ca71b4496530a1e877230b7b6a4cec0" + integrity sha512-zs/kL/Yc3H4X8SrWW0zf6w6RLWDw2LQ6w/tgx8Kirqf7eMtOO0rur9/0ASCeHvoqRr7O6W7dXnW7nD/I5ZgxDg== dependencies: "@openpgp/hkp-client" "^0.0.3" "@openpgp/wkd-client" "^0.0.4" From 91244b992b5d9f835e7ce003d3b54ed88a40ec2f Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Wed, 4 Oct 2023 10:01:09 +0200 Subject: [PATCH 084/144] fix: references to display object --- static-src/kx-claim.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/static-src/kx-claim.js b/static-src/kx-claim.js index 187a0bb..41feb40 100644 --- a/static-src/kx-claim.js +++ b/static-src/kx-claim.js @@ -61,7 +61,7 @@ export class Claim extends HTMLElement { const claimJson = JSON.parse(value); const claim = doipjs.Claim.fromJSON(claimJson); - root.querySelector('.info .title').innerText = claimJson.display.name; + root.querySelector('.info .title').innerText = claimJson.display.profileName; root.querySelector('.info .subtitle').innerText = claimJson.display.serviceProviderName ?? (claim.status < 300 ? '???' : '---'); root.querySelector('.info img').setAttribute('src', @@ -108,15 +108,15 @@ export class Claim extends HTMLElement { const subsection_links_text = subsection_links.appendChild(document.createElement('div')); const profile_link = subsection_links_text.appendChild(document.createElement('p')); - if (claim.matches[0].profile.uri) { - profile_link.innerHTML = `Profile link: ${claim.matches[0].profile.uri}`; + if (claimJson.display.profileUri) { + profile_link.innerHTML = `Profile link: ${claimJson.display.profileUri}`; } else { profile_link.innerHTML = `Profile link: not accessible from browser`; } const proof_link = subsection_links_text.appendChild(document.createElement('p')); - if (claim.matches[0].proof.request.uri) { - proof_link.innerHTML = `Proof link: ${claim.matches[0].proof.request.uri}`; + if (claimJson.display.proofUrl) { + proof_link.innerHTML = `Proof link: ${claimJson.display.proofUrl}`; } else { proof_link.innerHTML = `Proof link: not accessible from browser`; } From 995e4c73f71035c8b20df37f75f5fc1ac4879816 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Wed, 4 Oct 2023 10:06:21 +0200 Subject: [PATCH 085/144] feat: add QR button for profiles --- static-src/styles/typography.scss | 2 +- views/profile.pug | 10 +++------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/static-src/styles/typography.scss b/static-src/styles/typography.scss index e300cfe..adc6765 100644 --- a/static-src/styles/typography.scss +++ b/static-src/styles/typography.scss @@ -35,7 +35,7 @@ h1 { cursor: default; } h2 { - margin: 2em 0 0.5em; + margin: 1em 0 0.5em; font-size: 1.2em; font-weight: bold; color: var(--h2-color); diff --git a/views/profile.pug b/views/profile.pug index b858a31..c36d0d6 100644 --- a/views/profile.pug +++ b/views/profile.pug @@ -23,7 +23,7 @@ mixin generatePersona(persona, isPrimary) .info img(src=`https://design.keyoxide.org/brands/service-providers/_/icon.svg` onerror="this.src='https://design.keyoxide.org/brands/service-providers/_/icon.svg'") p - span.title= claim.display.name + span.title= claim.display.profileName span.subtitle-wrapper | [ span.subtitle= claim.display.serviceproviderName @@ -130,6 +130,8 @@ block content each persona, index in data.personas unless index == data.primaryPersonaIndex +generatePersona(persona, false) + h2 Profile + button(onClick=`showQR('${data.identifier}', 'profile_identifier')` aria-label='Show profile ID QR') Profile ID QR section h2 Profile information @@ -151,9 +153,3 @@ block content div p Key link: a.u-key(href=data.publicKey.fetch.resolvedUrl rel="pgpkey" aria-label="Link to cryptographic key")= data.publicKey.fetch.resolvedUrl - hr - if (data.profileType === 'openpgp') - .subsection - img(src='/static/img/qrcode.png') - div - button(onClick=`showQR('${data.publicKey.fingerprint}', 'fingerprint')` aria-label='Show QR code for cryptographic fingerprint') Show OpenPGP fingerprint QR \ No newline at end of file From b250392326aace0d0b53845130a5407a8096c6df Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Wed, 4 Oct 2023 10:21:54 +0200 Subject: [PATCH 086/144] fix: typo --- static-src/kx-claim.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static-src/kx-claim.js b/static-src/kx-claim.js index 41feb40..8137aa0 100644 --- a/static-src/kx-claim.js +++ b/static-src/kx-claim.js @@ -108,8 +108,8 @@ export class Claim extends HTMLElement { const subsection_links_text = subsection_links.appendChild(document.createElement('div')); const profile_link = subsection_links_text.appendChild(document.createElement('p')); - if (claimJson.display.profileUri) { - profile_link.innerHTML = `Profile link: ${claimJson.display.profileUri}`; + if (claimJson.display.profileUrl) { + profile_link.innerHTML = `Profile link: ${claimJson.display.profileUrl}`; } else { profile_link.innerHTML = `Profile link: not accessible from browser`; } From be97ff42465414b0f1f6c905fcacc87aea33769a Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Wed, 4 Oct 2023 11:19:54 +0200 Subject: [PATCH 087/144] fix: only add profile verifier when appropriate --- src/server/index.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/server/index.js b/src/server/index.js index 6990187..b1c52cf 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -38,7 +38,9 @@ const generateAspeProfile = async (id) => { return doipjs.asp.fetchASPE(id) .then(profile => { - profile.addVerifier('keyoxide', `${getScheme()}://${process.env.DOMAIN}/${id}`) + if (process.env.DOMAIN) { + profile.addVerifier('keyoxide', `${getScheme()}://${process.env.DOMAIN}/${id}`) + } profile = processAspProfile(profile) return profile }) @@ -58,7 +60,9 @@ const generateWKDProfile = async (id) => { return fetchWKD(id) .then(async profile => { - profile.addVerifier('keyoxide', `${getScheme()}://${process.env.DOMAIN}/wkd/${id}`) + if (process.env.DOMAIN) { + profile.addVerifier('keyoxide', `${getScheme()}://${process.env.DOMAIN}/wkd/${id}`) + } profile = processOpenPgpProfile(profile) logger.debug('Generating a WKD profile', @@ -89,7 +93,9 @@ const generateHKPProfile = async (id, keyserverDomain) => { keyoxideUrl = `${getScheme()}://${process.env.DOMAIN}/hkp/${keyserverDomain}/${id}` } - profile.addVerifier('keyoxide', keyoxideUrl) + if (process.env.DOMAIN) { + profile.addVerifier('keyoxide', keyoxideUrl) + } profile = processOpenPgpProfile(profile) logger.debug('Generating a HKP profile', @@ -168,7 +174,9 @@ const generateKeybaseProfile = async (username, fingerprint) => { return fetchKeybase(username, fingerprint) .then(async profile => { - profile.addVerifier('keyoxide', `${getScheme()}://${process.env.DOMAIN}/keybase/${username}/${fingerprint}`) + if (process.env.DOMAIN) { + profile.addVerifier('keyoxide', `${getScheme()}://${process.env.DOMAIN}/keybase/${username}/${fingerprint}`) + } profile = processOpenPgpProfile(profile) logger.debug('Generating a Keybase profile', From b0a93dcf91b139a5a4e8f03fcbdaea2feabbc7e1 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Wed, 4 Oct 2023 11:20:44 +0200 Subject: [PATCH 088/144] feat: add Keyoxide profile QR button --- static-src/styles.scss | 6 +----- static-src/styles/forms.scss | 4 ++++ views/profile.pug | 2 ++ 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/static-src/styles.scss b/static-src/styles.scss index 0e6b3bd..281def2 100644 --- a/static-src/styles.scss +++ b/static-src/styles.scss @@ -69,6 +69,7 @@ more information on this, and how to apply and follow the GNU AGPL, see div { - margin: 16px; - padding: 1em; -} - dialog form[method="Dialog"] { margin: 1em 0 0 !important; } diff --git a/static-src/styles/forms.scss b/static-src/styles/forms.scss index 5a44321..f916270 100644 --- a/static-src/styles/forms.scss +++ b/static-src/styles/forms.scss @@ -110,6 +110,10 @@ a.button { } } +button { + margin-right: 8px; +} + button.inline { min-height: auto; margin: 0; diff --git a/views/profile.pug b/views/profile.pug index c36d0d6..35777cb 100644 --- a/views/profile.pug +++ b/views/profile.pug @@ -131,6 +131,8 @@ block content unless index == data.primaryPersonaIndex +generatePersona(persona, false) h2 Profile + if data.verifiers.length > 0 + button(onClick=`showQR('${data.verifiers[0].url}', 'profile_verifier_url')` aria-label='Show profile ID QR') Keyoxide profile QR button(onClick=`showQR('${data.identifier}', 'profile_identifier')` aria-label='Show profile ID QR') Profile ID QR section From d91fdfc1bd380c0a9792a13bc10108c234bc8424 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Wed, 4 Oct 2023 12:57:41 +0200 Subject: [PATCH 089/144] feat: remove logo from header, add to index --- static-src/files/img/keyoxide.svg | 7 +++++++ static-src/files/img/keyoxide_outline.svg | 3 +++ static-src/styles/forms.scss | 7 +++++++ static-src/styles/layout.scss | 3 +-- views/index.pug | 3 +++ views/partials/header.pug | 4 +--- 6 files changed, 22 insertions(+), 5 deletions(-) create mode 100644 static-src/files/img/keyoxide.svg create mode 100644 static-src/files/img/keyoxide_outline.svg diff --git a/static-src/files/img/keyoxide.svg b/static-src/files/img/keyoxide.svg new file mode 100644 index 0000000..2214662 --- /dev/null +++ b/static-src/files/img/keyoxide.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/static-src/files/img/keyoxide_outline.svg b/static-src/files/img/keyoxide_outline.svg new file mode 100644 index 0000000..10720a1 --- /dev/null +++ b/static-src/files/img/keyoxide_outline.svg @@ -0,0 +1,3 @@ + + + diff --git a/static-src/styles/forms.scss b/static-src/styles/forms.scss index f916270..7169ad9 100644 --- a/static-src/styles/forms.scss +++ b/static-src/styles/forms.scss @@ -122,12 +122,19 @@ button.inline { #search { display: flex; + flex-direction: column; align-items: center; justify-content: center; min-height: 50vh; + gap: 48px; background: transparent; border: 0; + & > svg { + width: 96px; + fill: var(--primary-color); + } + form { display: flex; flex-direction: row; diff --git a/static-src/styles/layout.scss b/static-src/styles/layout.scss index 95e3715..5577d99 100644 --- a/static-src/styles/layout.scss +++ b/static-src/styles/layout.scss @@ -63,7 +63,6 @@ header { font-size: 1.8rem; text-decoration: none; font-weight: bold; - color: var(--primary-color); @media screen and (max-width: 480px) { gap: 8px; @@ -81,7 +80,7 @@ header { } } - a.text { + a { margin: 0; color: var(--text-color); diff --git a/views/index.pug b/views/index.pug index dba4e16..5474154 100644 --- a/views/index.pug +++ b/views/index.pug @@ -2,6 +2,9 @@ extends templates/base.pug block content section#search.form-wrapper + + + form(action="post") input#query(type="search" name="query" required placeholder="Search for a profile") input(type="submit" value="Search") diff --git a/views/partials/header.pug b/views/partials/header.pug index 542c7c8..ea14f4b 100644 --- a/views/partials/header.pug +++ b/views/partials/header.pug @@ -1,9 +1,7 @@ header .container nav - a.logo(href='/' aria-label='Home') - img(src='/static/img/logo_circle.png' alt='Keyoxide' aria-hidden='true') - | Keyoxide + a.logo(href='/' aria-label='Home') Keyoxide .links a.text(href='https://docs.keyoxide.org/getting-started') Getting started a.text(href='https://docs.keyoxide.org') Docs From f83eb78f80ea585f918073fccc9d2ea91e840c1a Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Wed, 4 Oct 2023 13:04:07 +0200 Subject: [PATCH 090/144] fix: fix styling --- views/index.pug | 2 +- views/templates/base.pug | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/views/index.pug b/views/index.pug index 5474154..c85a9a3 100644 --- a/views/index.pug +++ b/views/index.pug @@ -2,7 +2,7 @@ extends templates/base.pug block content section#search.form-wrapper - + form(action="post") diff --git a/views/templates/base.pug b/views/templates/base.pug index 9dbf6c0..19e16a5 100644 --- a/views/templates/base.pug +++ b/views/templates/base.pug @@ -6,6 +6,7 @@ html(lang='en') meta(name='theme-color' content='#fff') meta(name='description' content='Modern and secure platform to manage a decentralized identity based on cryptographic keys') link(rel='shortcut icon' href='/favicon.svg') + link(rel='stylesheet' href='/static/main.css') title= (title ? title : 'Keyoxide') include ../partials/header.pug @@ -16,7 +17,6 @@ html(lang='en') include ../partials/footer.pug - link(rel='stylesheet' href='/static/main.css') script(type='application/javascript' defer src='/static/openpgp.js' charset='utf-8') script(type='application/javascript' defer src='/static/doipFetchers.js' charset='utf-8') script(type='application/javascript' defer src='/static/doip.js' charset='utf-8') From d8c7ed8e8a67a4a86bbf80a6584ae6d3d3229056 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Wed, 4 Oct 2023 13:53:39 +0200 Subject: [PATCH 091/144] feat: replace rome with biome --- biome.json | 15 ++++++++++ package.json | 8 ++--- yarn.lock | 84 ++++++++++++++++++++++++++-------------------------- 3 files changed, 61 insertions(+), 46 deletions(-) create mode 100644 biome.json diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..167e1b6 --- /dev/null +++ b/biome.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.2.2/schema.json", + "organizeImports": { + "enabled": false + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + }, + "formatter": { + "enabled": false + } +} \ No newline at end of file diff --git a/package.json b/package.json index 578dc37..fe286f2 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "winston": "^3.8.2" }, "devDependencies": { + "@biomejs/biome": "1.2.2", "@vercel/ncc": "^0.34.0", "chai": "^4.3.6", "copy-webpack-plugin": "^11.0.0", @@ -36,7 +37,6 @@ "mini-css-extract-plugin": "^2.5.3", "mocha": "^10.1.0", "nodemon": "^2.0.20", - "rome": "^12.1", "sass": "^1.67.0", "sass-loader": "^13.3.2", "standard": "^17.0.0", @@ -54,11 +54,11 @@ "build:server": "ncc build ./src/index.js -o dist", "build:static": "webpack --config webpack.config.js --env static=true --env mode=production", "build:static:dev": "webpack --config webpack.config.js --env static=true --env mode=development", - "lint": "yarn run standard:check && yarn run rome:check", + "lint": "yarn run standard:check && yarn run biome:check", "standard:check": "./node_modules/.bin/standard ./src", "standard:fix": "./node_modules/.bin/standard --fix ./src", - "rome:check": "./node_modules/.bin/rome check ./src", - "rome:fix": "./node_modules/.bin/rome check --apply ./src", + "biome:check": "biome check ./src && biome lint ./src", + "biome:fix": "biome check --apply ./src && biome lint --apply ./src", "license:check": "./node_modules/.bin/license-check-and-add check", "license:add": "./node_modules/.bin/license-check-and-add add", "license:remove": "./node_modules/.bin/license-check-and-add remove" diff --git a/yarn.lock b/yarn.lock index 42835ad..94a2a4f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -265,6 +265,48 @@ "@babel/helper-validator-identifier" "^7.22.5" to-fast-properties "^2.0.0" +"@biomejs/biome@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@biomejs/biome/-/biome-1.2.2.tgz#cfcceae7f4e55ef057a9b9576ef89563ca0c6d07" + integrity sha512-fXwXi56ZdaKO/N3rTmhWw41UxstoviODk+wia4WWNSlm23r8xJ/NxjaZ88scV2IsmsFHqc8rmwb2dkrStAdIEw== + optionalDependencies: + "@biomejs/cli-darwin-arm64" "1.2.2" + "@biomejs/cli-darwin-x64" "1.2.2" + "@biomejs/cli-linux-arm64" "1.2.2" + "@biomejs/cli-linux-x64" "1.2.2" + "@biomejs/cli-win32-arm64" "1.2.2" + "@biomejs/cli-win32-x64" "1.2.2" + +"@biomejs/cli-darwin-arm64@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-1.2.2.tgz#459a337f574d1a78d9d6a443fc00724d82804ffe" + integrity sha512-Fx1IURKhoqH6wPawtKLT6wcfMSjRRcNK8+VWau0iDOjXvNtjJpSmICbU89B7Vt/gZRwPqkfDMBkFwm6V5vFTSQ== + +"@biomejs/cli-darwin-x64@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@biomejs/cli-darwin-x64/-/cli-darwin-x64-1.2.2.tgz#892ea688b5d5ddbca9d0bc33c24aa2840a62b284" + integrity sha512-JNaAFOI/ZisnmzvcFNd73geJxaFaN2L4YsWM6cgBeKyLY/ycl9C/PBTFfEmeB1c7f5XIIal8P2cj47kLJpN5Ig== + +"@biomejs/cli-linux-arm64@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-arm64/-/cli-linux-arm64-1.2.2.tgz#b7a00f9d9e999daa36ee4581a37a0b13326ec899" + integrity sha512-JHXRnfhOLx8UO/Fcyn2c5pFRri0XKqRZm2wf5oH5GSfLVpckDw2X15dYGbu3nmfM/3pcAaTV46pUpjrCnaAieg== + +"@biomejs/cli-linux-x64@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@biomejs/cli-linux-x64/-/cli-linux-x64-1.2.2.tgz#4c55181f8231e7aa05b2841944a48dd81af443b8" + integrity sha512-5Zr+iM7lUKsw81p9PkXRESuH2/AhRZ6RCWkgE+FSLcxMhXy/4RDR+o2YQDsJM6cWKIzOJM05vDHTGrDq7vXE4A== + +"@biomejs/cli-win32-arm64@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-arm64/-/cli-win32-arm64-1.2.2.tgz#6fa05d52903c3fff55bd693c5b1ca778504cd4cc" + integrity sha512-HvUcG2p++RvYP0zfOqh+DgiUUH+JI/uETr0kzWlOJ9F3lsG525pkywg4RSd4OvJd7Wpd3wt3UpN/A4IEJaVmbA== + +"@biomejs/cli-win32-x64@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-x64/-/cli-win32-x64-1.2.2.tgz#583e50d8a1a1cc81381200d3cb2f34669c6afd35" + integrity sha512-bfaFJwqJ9ApFga2o88OaROSd3pasYRzRGXHJWAE9VUUKdSNSTYxHOqVrNvV54yYPtL6Kt9xkuZa4HNu9it3TaA== + "@colors/colors@1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" @@ -458,36 +500,6 @@ resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1" integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== -"@rometools/cli-darwin-arm64@12.1.3": - version "12.1.3" - resolved "https://registry.yarnpkg.com/@rometools/cli-darwin-arm64/-/cli-darwin-arm64-12.1.3.tgz#b00fe225e34047c4dac63588e237b11ebec47694" - integrity sha512-AmFTUDYjBuEGQp/Wwps+2cqUr+qhR7gyXAUnkL5psCuNCz3807TrUq/ecOoct5MIavGJTH6R4aaSL6+f+VlBEg== - -"@rometools/cli-darwin-x64@12.1.3": - version "12.1.3" - resolved "https://registry.yarnpkg.com/@rometools/cli-darwin-x64/-/cli-darwin-x64-12.1.3.tgz#e5bbf02afb1aab7447e743092245dea992b4b29f" - integrity sha512-k8MbWna8q4LRlb005N2X+JS1UQ+s3ZLBBvwk4fP8TBxlAJXUz17jLLu/Fi+7DTTEmMhM84TWj4FDKW+rNar28g== - -"@rometools/cli-linux-arm64@12.1.3": - version "12.1.3" - resolved "https://registry.yarnpkg.com/@rometools/cli-linux-arm64/-/cli-linux-arm64-12.1.3.tgz#e75b01b74c134edc811e21fa7e1e440602930d59" - integrity sha512-X/uLhJ2/FNA3nu5TiyeNPqiD3OZoFfNfRvw6a3ut0jEREPvEn72NI7WPijH/gxSz55znfQ7UQ6iM4DZumUknJg== - -"@rometools/cli-linux-x64@12.1.3": - version "12.1.3" - resolved "https://registry.yarnpkg.com/@rometools/cli-linux-x64/-/cli-linux-x64-12.1.3.tgz#2b9f4a68079783f275d4d27df83e4fa2220ec6fc" - integrity sha512-csP17q1eWiUXx9z6Jr/JJPibkplyKIwiWPYNzvPCGE8pHlKhwZj3YHRuu7Dm/4EOqx0XFIuqqWZUYm9bkIC8xg== - -"@rometools/cli-win32-arm64@12.1.3": - version "12.1.3" - resolved "https://registry.yarnpkg.com/@rometools/cli-win32-arm64/-/cli-win32-arm64-12.1.3.tgz#714acb67ac4ea4c15e2bc6aea4dd290c76c8efc6" - integrity sha512-RymHWeod57EBOJY4P636CgUwYA6BQdkQjh56XKk4pLEHO6X1bFyMet2XL7KlHw5qOTalzuzf5jJqUs+vf3jdXQ== - -"@rometools/cli-win32-x64@12.1.3": - version "12.1.3" - resolved "https://registry.yarnpkg.com/@rometools/cli-win32-x64/-/cli-win32-x64-12.1.3.tgz#b4f53491d2ca8f1234b3613b7cc73418ad8d76bb" - integrity sha512-yHSKYidqJMV9nADqg78GYA+cZ0hS1twANAjiFibQdXj9aGzD+s/IzIFEIi/U/OBLvWYg/SCw0QVozi2vTlKFDQ== - "@sindresorhus/is@^4.0.0": version "4.6.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" @@ -4300,18 +4312,6 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" -rome@^12.1: - version "12.1.3" - resolved "https://registry.yarnpkg.com/rome/-/rome-12.1.3.tgz#4d4d62cad16216843680bd3ca11a4c248134902a" - integrity sha512-e+ff72hxDpe/t5/Us7YRBVw3PBET7SeczTQNn6tvrWdrCaAw3qOukQQ+tDCkyFtS4yGsnhjrJbm43ctNbz27Yg== - optionalDependencies: - "@rometools/cli-darwin-arm64" "12.1.3" - "@rometools/cli-darwin-x64" "12.1.3" - "@rometools/cli-linux-arm64" "12.1.3" - "@rometools/cli-linux-x64" "12.1.3" - "@rometools/cli-win32-arm64" "12.1.3" - "@rometools/cli-win32-x64" "12.1.3" - run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" From 96983a67dfffa455c8c4c189ddd5fc5d2738ddc2 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Wed, 4 Oct 2023 14:46:40 +0200 Subject: [PATCH 092/144] fix: update schemas --- src/schemas.js | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/schemas.js b/src/schemas.js index e70eedf..be826ac 100644 --- a/src/schemas.js +++ b/src/schemas.js @@ -215,21 +215,25 @@ export const claimSchema = { display: { type: 'object', properties: { - name: { - type: 'string', - description: 'Account name to display in the user interface' + profileName: { + type: "string", + description: "Account name to display in the user interface" }, - url: { - type: ['string', 'null'], - description: 'URL to link to in the user interface' + profileUrl: { + type: ["string", "null"], + description: "Profile URL to link to in the user interface" + }, + proofUrl: { + type: ["string", "null"], + description: "Proof URL to link to in the user interface" }, serviceProviderName: { - type: ['string', 'null'], - description: 'Name of the service provider to display in the user interface' + type: ["string", "null"], + description: "Name of the service provider to display in the user interface" }, serviceProviderId: { - type: ['string', 'null'], - description: 'Id of the service provider' + type: ["string", "null"], + description: "Id of the service provider" } } } From 5200ad3611f7ed10da723f34d4facdbfe624625a Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Wed, 4 Oct 2023 14:49:53 +0200 Subject: [PATCH 093/144] feat: make Dicebear API domain configurable --- src/server/index.js | 3 ++- template.env | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/server/index.js b/src/server/index.js index b1c52cf..176196c 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -223,7 +223,8 @@ const processAspProfile = async (/** @type {import('doipjs').Profile */ profile) // Overwrite avatarUrl // TODO: don't overwrite avatarUrl once it's fully supported - profile.personas[profile.primaryPersonaIndex].avatarUrl = `https://api.dicebear.com/6.x/shapes/svg?seed=${profile.publicKey.fingerprint}&size=128` + profile.personas[profile.primaryPersonaIndex].avatarUrl = + `https://${process.env.DICEBEAR_API_HOSTNAME || "api.dicebear.com" }/7.x/shapes/svg?seed=${profile.publicKey.fingerprint}&size=128` return profile } diff --git a/template.env b/template.env index 488bf95..d9dadd3 100644 --- a/template.env +++ b/template.env @@ -16,6 +16,10 @@ # To host a Keyoxide Proxy server, refer to https://docs.keyoxide.org/self-hosting/ #PROXY_HOSTNAME= +# Domain for Dicebear API server +# Defaults to: api.dicebear.com +#DICEBEAR_API_HOSTNAME= + # Tor Onion URL # The full http:// onion url to add as an 'Onion-Location' header #ONION_URL= From 1b36959d17e74e52d57f20b90f6753905426d24a Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Wed, 4 Oct 2023 14:53:11 +0200 Subject: [PATCH 094/144] fix: fix formatting --- src/schemas.js | 20 ++++++++++---------- src/server/index.js | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/schemas.js b/src/schemas.js index be826ac..8b5a1b2 100644 --- a/src/schemas.js +++ b/src/schemas.js @@ -216,24 +216,24 @@ export const claimSchema = { type: 'object', properties: { profileName: { - type: "string", - description: "Account name to display in the user interface" + type: 'string', + description: 'Account name to display in the user interface' }, profileUrl: { - type: ["string", "null"], - description: "Profile URL to link to in the user interface" + type: ['string', 'null'], + description: 'Profile URL to link to in the user interface' }, proofUrl: { - type: ["string", "null"], - description: "Proof URL to link to in the user interface" + type: ['string', 'null'], + description: 'Proof URL to link to in the user interface' }, serviceProviderName: { - type: ["string", "null"], - description: "Name of the service provider to display in the user interface" + type: ['string', 'null'], + description: 'Name of the service provider to display in the user interface' }, serviceProviderId: { - type: ["string", "null"], - description: "Id of the service provider" + type: ['string', 'null'], + description: 'Id of the service provider' } } } diff --git a/src/server/index.js b/src/server/index.js index 176196c..3edd263 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -224,7 +224,7 @@ const processAspProfile = async (/** @type {import('doipjs').Profile */ profile) // Overwrite avatarUrl // TODO: don't overwrite avatarUrl once it's fully supported profile.personas[profile.primaryPersonaIndex].avatarUrl = - `https://${process.env.DICEBEAR_API_HOSTNAME || "api.dicebear.com" }/7.x/shapes/svg?seed=${profile.publicKey.fingerprint}&size=128` + `https://${process.env.DICEBEAR_API_HOSTNAME || 'api.dicebear.com'}/7.x/shapes/svg?seed=${profile.publicKey.fingerprint}&size=128` return profile } From fafa3ad58d8b313055658e11eef31e5c60b2f1b8 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Wed, 4 Oct 2023 15:37:37 +0200 Subject: [PATCH 095/144] fix: fix styling --- static-src/styles.scss | 3 +++ 1 file changed, 3 insertions(+) diff --git a/static-src/styles.scss b/static-src/styles.scss index 281def2..7121683 100644 --- a/static-src/styles.scss +++ b/static-src/styles.scss @@ -35,6 +35,9 @@ more information on this, and how to apply and follow the GNU AGPL, see Date: Wed, 4 Oct 2023 18:42:14 +0200 Subject: [PATCH 096/144] feat: change logo weight --- views/index.pug | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/views/index.pug b/views/index.pug index c85a9a3..ac9c991 100644 --- a/views/index.pug +++ b/views/index.pug @@ -2,8 +2,8 @@ extends templates/base.pug block content section#search.form-wrapper - - + + form(action="post") input#query(type="search" name="query" required placeholder="Search for a profile") From c6932f8b98b2beab1898910a32bb2e25c3017855 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Wed, 4 Oct 2023 20:39:14 +0200 Subject: [PATCH 097/144] feat: add apps page --- src/routes/main.js | 4 ++ .../files/img/keyoxide_asp_web_home.jpg | Bin 0 -> 50357 bytes .../files/img/keyoxide_mobile_dark_home.jpg | Bin 0 -> 37483 bytes .../img/keyoxide_mobile_dark_profile.jpg | Bin 0 -> 54979 bytes .../files/img/keyoxide_mobile_light_home.jpg | Bin 0 -> 34460 bytes static-src/styles/layout.scss | 26 +++++++++- views/apps.pug | 46 ++++++++++++++++++ views/article.pug | 2 +- views/partials/header.pug | 2 + 9 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 static-src/files/img/keyoxide_asp_web_home.jpg create mode 100644 static-src/files/img/keyoxide_mobile_dark_home.jpg create mode 100644 static-src/files/img/keyoxide_mobile_dark_profile.jpg create mode 100644 static-src/files/img/keyoxide_mobile_light_home.jpg create mode 100644 views/apps.pug diff --git a/src/routes/main.js b/src/routes/main.js index d4b165b..9c1f3df 100644 --- a/src/routes/main.js +++ b/src/routes/main.js @@ -51,6 +51,10 @@ router.get('/', (req, res) => { res.render('index', { highlights, meta: getMetaFromReq(req) }) }) +router.get('/apps', (req, res) => { + res.render('apps', { title: 'Apps', meta: getMetaFromReq(req) }) +}) + router.get('/privacy', (req, res) => { const rawContent = readFileSync('./content/privacy-policy.md', 'utf8') const content = md.render(rawContent) diff --git a/static-src/files/img/keyoxide_asp_web_home.jpg b/static-src/files/img/keyoxide_asp_web_home.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1b186b233590d588ca3d4eee9381daec69a994f6 GIT binary patch literal 50357 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νw#~8EFI5hp`~<*RCxKHu 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 literal 0 HcmV?d00001 diff --git a/static-src/files/img/keyoxide_mobile_dark_home.jpg b/static-src/files/img/keyoxide_mobile_dark_home.jpg new file mode 100644 index 0000000000000000000000000000000000000000..155e9b802c4460822b9ad4491b53eae263bdbe2b GIT binary patch literal 37483 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 literal 0 HcmV?d00001 diff --git a/static-src/files/img/keyoxide_mobile_dark_profile.jpg b/static-src/files/img/keyoxide_mobile_dark_profile.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3648d3eb60ba20e37c2c20c749be8ec61cb95a19 GIT binary patch literal 54979 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 NY;3G-sRjSM{XfS@S|b1e literal 0 HcmV?d00001 diff --git a/static-src/files/img/keyoxide_mobile_light_home.jpg b/static-src/files/img/keyoxide_mobile_light_home.jpg new file mode 100644 index 0000000000000000000000000000000000000000..efe7251857d734d636fc1aa8e279292c180aae15 GIT binary patch literal 34460 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 Date: Thu, 5 Oct 2023 10:51:54 +0200 Subject: [PATCH 098/144] feat: improve apps page --- static-src/styles/layout.scss | 4 ++++ static-src/styles/vars.scss | 4 ++++ views/apps.pug | 2 ++ 3 files changed, 10 insertions(+) diff --git a/static-src/styles/layout.scss b/static-src/styles/layout.scss index 409db7a..ddd5382 100644 --- a/static-src/styles/layout.scss +++ b/static-src/styles/layout.scss @@ -27,6 +27,10 @@ You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see . */ +hr { + margin: 3em 0; + color: var(--line-color-subtle); +} body { display: flex; flex-direction: column; diff --git a/static-src/styles/vars.scss b/static-src/styles/vars.scss index fe2536a..60065e3 100644 --- a/static-src/styles/vars.scss +++ b/static-src/styles/vars.scss @@ -84,6 +84,8 @@ more information on this, and how to apply and follow the GNU AGPL, see Date: Thu, 5 Oct 2023 13:06:35 +0200 Subject: [PATCH 099/144] feat: support profile theming --- package.json | 3 ++- src/routes/profile.js | 4 ++- src/server/utils.js | 50 ++++++++++++++++++++++++++++++++++++ static-src/kx-styles.scss | 3 ++- static-src/styles/forms.scss | 12 +++++++++ static-src/styles/vars.scss | 26 ++++++++++++++----- views/profile.pug | 14 ++++++++-- yarn.lock | 13 +++++++--- 8 files changed, 109 insertions(+), 16 deletions(-) diff --git a/package.json b/package.json index fe286f2..2b41eb2 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ "ajv": "^8.6.3", "bent": "^7.3.12", "body-parser": "^1.19.0", - "doipjs": "^1.2.4", + "colorjs.io": "^0.4.5", + "doipjs": "^1.2.5", "dotenv": "^16.0.3", "express": "^4.17.1", "express-http-context2": "^1.0.0", diff --git a/src/routes/profile.js b/src/routes/profile.js index f03e2c4..431c125 100644 --- a/src/routes/profile.js +++ b/src/routes/profile.js @@ -32,7 +32,7 @@ import bodyParserImport from 'body-parser' import { rateLimit } from 'express-rate-limit' import { generateSignatureProfile, utils, generateWKDProfile, generateHKPProfile, generateAutoProfile, generateKeybaseProfile } from '../server/index.js' import { Profile } from 'doipjs' -import { getMetaFromReq } from '../server/utils.js' +import { generateProfileTheme, getMetaFromReq } from '../server/utils.js' import logger from '../log.js' const router = express.Router() @@ -133,6 +133,7 @@ router.get('/keybase/:username/:fingerprint', profileRateLimiter, async (req, re router.get('/:id', profileRateLimiter, async (req, res) => { const data = await generateAutoProfile(req.params.id) + const theme = generateProfileTheme(data) const title = utils.generatePageTitle('profile', data) res.set('ariadne-identity-proof', data.identifier) res.render('profile', { @@ -140,6 +141,7 @@ router.get('/:id', profileRateLimiter, async (req, res) => { data: data instanceof Profile ? data.toJSON() : data, enable_message_encryption: false, enable_signature_verification: false, + theme, meta: getMetaFromReq(req) }) }) diff --git a/src/server/utils.js b/src/server/utils.js index 6ca15ec..a4583ac 100644 --- a/src/server/utils.js +++ b/src/server/utils.js @@ -28,6 +28,8 @@ if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see . */ import { webcrypto as crypto } from 'crypto' +import { Profile } from 'doipjs' +import Color from 'colorjs.io' export async function computeWKDLocalPart (localPart) { const localPartEncoded = new TextEncoder().encode(localPart.toLowerCase()) @@ -87,3 +89,51 @@ export function getMetaFromReq (req) { } } } + +export function generateProfileTheme (/** @type {Profile} */ profile) { + if (!(profile && profile instanceof Profile)) return null + + if (!profile.personas[profile.primaryPersonaIndex].themeColor) return null + + let base + try { + base = new Color(profile.personas[profile.primaryPersonaIndex].themeColor) + } catch (_) { + return null + } + + if (base.to('hsl').hsl[0].isNaN) return null + if (base.to('hsl').hsl[2] === 0) return null + + const primaryLight = base.to('hsl') + primaryLight.hsl[2] = 40 + const primaryDark = base.to('hsl') + primaryDark.hsl[2] = 80 + + const primarySubtleLight = base.to('hsl') + primarySubtleLight.hsl[2] = 50 + const primarySubtleDark = base.to('hsl') + primarySubtleDark.hsl[2] = 70 + + const backgroundLight = base.to('hsl') + backgroundLight.hsl[2] = 98 + const backgroundDark = base.to('hsl') + backgroundDark.hsl[1] = 20 + backgroundDark.hsl[2] = 5 + + return { + base: base.toString({ format: 'hex' }), + primary: { + light: primaryLight.toString(), + dark: primaryDark.toString() + }, + primarySubtle: { + light: primarySubtleLight.toString(), + dark: primarySubtleDark.toString() + }, + background: { + light: backgroundLight.toString(), + dark: backgroundDark.toString() + } + } +} diff --git a/static-src/kx-styles.scss b/static-src/kx-styles.scss index 41291a6..d9e08b3 100644 --- a/static-src/kx-styles.scss +++ b/static-src/kx-styles.scss @@ -58,8 +58,9 @@ kx-claim { } hr { + margin: 8px 0; border: none; - border-top: 2px solid var(--background-color); + border-top: 2px solid var(--header-background-color); } .content { diff --git a/static-src/styles/forms.scss b/static-src/styles/forms.scss index 7169ad9..835abf3 100644 --- a/static-src/styles/forms.scss +++ b/static-src/styles/forms.scss @@ -108,6 +108,18 @@ a.button { border-color: var(--button-border-color-hover); color: var(--button-text-color-hover); } + + &.themed { + padding: 8px 12px; + color: var(--text-color-inverse); + background-color: var(--primary-color); + border: 0; + + &:hover { + color: var(--text-color-inverse); + background-color: var(--primary-color-subtle); + } + } } button { diff --git a/static-src/styles/vars.scss b/static-src/styles/vars.scss index 60065e3..07300ad 100644 --- a/static-src/styles/vars.scss +++ b/static-src/styles/vars.scss @@ -64,14 +64,25 @@ more information on this, and how to apply and follow the GNU AGPL, see 0 - button(onClick=`showQR('${data.verifiers[0].url}', 'profile_verifier_url')` aria-label='Show profile ID QR') Keyoxide profile QR - button(onClick=`showQR('${data.identifier}', 'profile_identifier')` aria-label='Show profile ID QR') Profile ID QR + button.themed(onClick=`showQR('${data.verifiers[0].url}', 'profile_verifier_url')` aria-label='Show profile ID QR') Keyoxide profile QR + button.themed(onClick=`showQR('${data.identifier}', 'profile_identifier')` aria-label='Show profile ID QR') Profile ID QR section h2 Profile information diff --git a/yarn.lock b/yarn.lock index 94a2a4f..66c193f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1579,6 +1579,11 @@ colorette@^2.0.14: resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== +colorjs.io@^0.4.5: + version "0.4.5" + resolved "https://registry.yarnpkg.com/colorjs.io/-/colorjs.io-0.4.5.tgz#7775f787ff90aca7a38f6edb7b7c0f8cce1e6418" + integrity sha512-yCtUNCmge7llyfd/Wou19PMAcf5yC3XXhgFoAh6zsO2pGswhUPBaaUh8jzgHnXtXuZyFKzXZNAnyF5i+apICow== + colorspace@1.1.x: version "1.1.4" resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.4.tgz#8d442d1186152f60453bf8070cd66eb364e59243" @@ -1837,10 +1842,10 @@ doctypes@^1.1.0: resolved "https://registry.yarnpkg.com/doctypes/-/doctypes-1.1.0.tgz#ea80b106a87538774e8a3a4a5afe293de489e0a9" integrity sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ== -doipjs@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/doipjs/-/doipjs-1.2.4.tgz#f980ac4a3ca71b4496530a1e877230b7b6a4cec0" - integrity sha512-zs/kL/Yc3H4X8SrWW0zf6w6RLWDw2LQ6w/tgx8Kirqf7eMtOO0rur9/0ASCeHvoqRr7O6W7dXnW7nD/I5ZgxDg== +doipjs@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/doipjs/-/doipjs-1.2.5.tgz#98d67dad67ec0d0afafcda4e4740c93a964eecd3" + integrity sha512-366rS4LpzrGUgf1wfxjLpS46T4whB15m4dW8DfVYLLFYqNhwDVirRhysaexCQ8B4Ns+eCSa7qfpVszSxwGmgkA== dependencies: "@openpgp/hkp-client" "^0.0.3" "@openpgp/wkd-client" "^0.0.4" From 94471b4b8e947b4fd269163454957c5abe6d1363 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Thu, 5 Oct 2023 13:15:23 +0200 Subject: [PATCH 100/144] fix: fix CSS vars --- static-src/styles/vars.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static-src/styles/vars.scss b/static-src/styles/vars.scss index 07300ad..903e610 100644 --- a/static-src/styles/vars.scss +++ b/static-src/styles/vars.scss @@ -70,8 +70,8 @@ more information on this, and how to apply and follow the GNU AGPL, see Date: Thu, 5 Oct 2023 17:10:03 +0200 Subject: [PATCH 101/144] fix: update schemas --- src/schemas.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/schemas.js b/src/schemas.js index 8b5a1b2..19210f5 100644 --- a/src/schemas.js +++ b/src/schemas.js @@ -157,6 +157,10 @@ export const personaSchema = { description: 'URL to an avatar image', type: ['string', 'null'] }, + themeColor: { + description: 'Profile page theme color', + type: ['string', 'null'] + }, isRevoked: { type: 'boolean' }, From 28852c74fb46fd18c2e65622826d4ab579288cb2 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Thu, 5 Oct 2023 17:35:33 +0200 Subject: [PATCH 102/144] chore: release 4.2.3 --- CHANGELOG.md | 16 ++++++++++++++++ package.json | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e6fd81..7c3e0f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [4.2.3] - 2023-10-05 +### Added +- Themeable profile pages +- Apps page +### Changed +- Update doipjs to 1.2.5 +- Make Dicebear API domain configurable +### Fixed +- Catch errors potentially thrown by function +- Update JSON schemas +- Icon URL generation in profile view +### Notes +- ASP profiles use the Dicebear API to generate avatars. By default, Keyoxide +uses the official api.dicebear.com instance. To use a custom Dicebear instance, +set the DICEBEAR_API_HOSTNAME environment variable to its hostname. + ## [4.2.2] - 2023-10-03 ### Changed - Update doipjs to 1.2.2 diff --git a/package.json b/package.json index 2b41eb2..a49bbeb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keyoxide-web", - "version": "4.2.2", + "version": "4.2.3", "description": "Verifying online identity with cryptography", "main": "./src/index.js", "type": "module", From 836e6d2077313275bc7b94dd35c150e198aeff88 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Mon, 9 Oct 2023 19:15:05 +0200 Subject: [PATCH 103/144] feat: update doipjs to 1.2.6 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index a49bbeb..a1832af 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "bent": "^7.3.12", "body-parser": "^1.19.0", "colorjs.io": "^0.4.5", - "doipjs": "^1.2.5", + "doipjs": "^1.2.6", "dotenv": "^16.0.3", "express": "^4.17.1", "express-http-context2": "^1.0.0", diff --git a/yarn.lock b/yarn.lock index 66c193f..0ec0414 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1842,10 +1842,10 @@ doctypes@^1.1.0: resolved "https://registry.yarnpkg.com/doctypes/-/doctypes-1.1.0.tgz#ea80b106a87538774e8a3a4a5afe293de489e0a9" integrity sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ== -doipjs@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/doipjs/-/doipjs-1.2.5.tgz#98d67dad67ec0d0afafcda4e4740c93a964eecd3" - integrity sha512-366rS4LpzrGUgf1wfxjLpS46T4whB15m4dW8DfVYLLFYqNhwDVirRhysaexCQ8B4Ns+eCSa7qfpVszSxwGmgkA== +doipjs@^1.2.6: + version "1.2.6" + resolved "https://registry.yarnpkg.com/doipjs/-/doipjs-1.2.6.tgz#397f32f04900552f74e3034d8d9ada6c38fe2895" + integrity sha512-1Y6C6ghKQif3ft7L0rdPS+FkQpoPek7sgiG5QBw5jCxZQl5oUtQjMlAiSIOfkqflh4hteEeg2SCh6mTkr9ENzA== dependencies: "@openpgp/hkp-client" "^0.0.3" "@openpgp/wkd-client" "^0.0.4" From 1a789ab90df3f44582e15719cdd606cab2532317 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Mon, 9 Oct 2023 19:19:52 +0200 Subject: [PATCH 104/144] chore: release 4.2.4 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c3e0f2..925a6ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [4.2.4] - 2023-10-09 +### Changed +- Update doipjs to 1.2.6 + ## [4.2.3] - 2023-10-05 ### Added - Themeable profile pages diff --git a/package.json b/package.json index a1832af..e17b3e7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keyoxide-web", - "version": "4.2.3", + "version": "4.2.4", "description": "Verifying online identity with cryptography", "main": "./src/index.js", "type": "module", From 520165115a195e44f949bd79aa141079f4fb51bf Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Mon, 9 Oct 2023 19:42:16 +0200 Subject: [PATCH 105/144] feat: update doipjs to 1.2.7 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index e17b3e7..67d609c 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "bent": "^7.3.12", "body-parser": "^1.19.0", "colorjs.io": "^0.4.5", - "doipjs": "^1.2.6", + "doipjs": "^1.2.7", "dotenv": "^16.0.3", "express": "^4.17.1", "express-http-context2": "^1.0.0", diff --git a/yarn.lock b/yarn.lock index 0ec0414..32648d0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1842,10 +1842,10 @@ doctypes@^1.1.0: resolved "https://registry.yarnpkg.com/doctypes/-/doctypes-1.1.0.tgz#ea80b106a87538774e8a3a4a5afe293de489e0a9" integrity sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ== -doipjs@^1.2.6: - version "1.2.6" - resolved "https://registry.yarnpkg.com/doipjs/-/doipjs-1.2.6.tgz#397f32f04900552f74e3034d8d9ada6c38fe2895" - integrity sha512-1Y6C6ghKQif3ft7L0rdPS+FkQpoPek7sgiG5QBw5jCxZQl5oUtQjMlAiSIOfkqflh4hteEeg2SCh6mTkr9ENzA== +doipjs@^1.2.7: + version "1.2.7" + resolved "https://registry.yarnpkg.com/doipjs/-/doipjs-1.2.7.tgz#f0853d7ede1fcdeaf01e217acdc9869779ee8349" + integrity sha512-xqFHVoiBaBm8KYQbaEzsULfH0gHl9Rya+5DTc4XC21mU1QxPSeqJ9Mw2lEZgrxtq7eVsi9Kvbd9d32BOc6cCBQ== dependencies: "@openpgp/hkp-client" "^0.0.3" "@openpgp/wkd-client" "^0.0.4" From d661e3d61f3fee3cde885f28cb98e07bc7fbcb2a Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Mon, 9 Oct 2023 19:46:26 +0200 Subject: [PATCH 106/144] chore: release 4.2.5 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 925a6ce..54d49b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [4.2.5] - 2023-10-09 +### Changed +- Update doipjs to 1.2.7 + ## [4.2.4] - 2023-10-09 ### Changed - Update doipjs to 1.2.6 diff --git a/package.json b/package.json index 67d609c..ae3b6b9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keyoxide-web", - "version": "4.2.4", + "version": "4.2.5", "description": "Verifying online identity with cryptography", "main": "./src/index.js", "type": "module", From ce9ffce84d9fa20079e692d9df4ed06aeaf50e03 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Tue, 23 Jan 2024 19:24:24 +0100 Subject: [PATCH 107/144] chore: bump deps --- package.json | 4 +- yarn.lock | 1894 ++++++++++++++++++++++++++++---------------------- 2 files changed, 1084 insertions(+), 814 deletions(-) diff --git a/package.json b/package.json index ae3b6b9..79c2e80 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "bent": "^7.3.12", "body-parser": "^1.19.0", "colorjs.io": "^0.4.5", - "doipjs": "^1.2.7", + "doipjs": "^1.2.8", "dotenv": "^16.0.3", "express": "^4.17.1", "express-http-context2": "^1.0.0", @@ -37,7 +37,7 @@ "license-check-and-add": "^4.0.5", "mini-css-extract-plugin": "^2.5.3", "mocha": "^10.1.0", - "nodemon": "^2.0.20", + "nodemon": "^3.0.3", "sass": "^1.67.0", "sass-loader": "^13.3.2", "standard": "^17.0.0", diff --git a/yarn.lock b/yarn.lock index 32648d0..82895de 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16,13 +16,13 @@ "@jridgewell/trace-mapping" "^0.3.9" "@babel/cli@^7.16.0": - version "7.22.6" - resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.22.6.tgz#63f5be2a0abd587ccfbdc93424fa85f43142cc53" - integrity sha512-Be3/RfEDmkMRGT1+ru5nTkfcvWz5jDOYg1V9rXqTz2u9Qt96O1ryboGvxVBp7wOnYWDB8DNHIWb6DThrpudfOw== + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.23.4.tgz#f5cc90487278065fa0c3b1267cf0c1d44ddf85a7" + integrity sha512-j3luA9xGKCXVyCa5R7lJvOMM+Kc2JEnAEIgz2ggtjQ/j5YUVgfsg/WsG95bbsgq7YLHuiCOzMnoSasuY16qiCw== dependencies: "@jridgewell/trace-mapping" "^0.3.17" commander "^4.0.1" - convert-source-map "^1.1.0" + convert-source-map "^2.0.0" fs-readdir-recursive "^1.1.0" glob "^7.2.0" make-dir "^2.1.0" @@ -31,45 +31,46 @@ "@nicolo-ribaudo/chokidar-2" "2.1.8-no-fsevents.3" chokidar "^3.4.0" -"@babel/code-frame@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658" - integrity sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ== +"@babel/code-frame@^7.22.13", "@babel/code-frame@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.23.5.tgz#9009b69a8c602293476ad598ff53e4562e15c244" + integrity sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA== dependencies: - "@babel/highlight" "^7.22.5" + "@babel/highlight" "^7.23.4" + chalk "^2.4.2" -"@babel/compat-data@^7.22.6": - version "7.22.6" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.22.6.tgz#15606a20341de59ba02cd2fcc5086fcbe73bf544" - integrity sha512-29tfsWTq2Ftu7MXmimyC0C5FDZv5DYxOZkh3XD3+QW4V/BYuv/LyEsjj3c0hqedEaDt6DBfDvexMKU8YevdqFg== +"@babel/compat-data@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.5.tgz#ffb878728bb6bdcb6f4510aa51b1be9afb8cfd98" + integrity sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw== "@babel/core@^7.16.5": - version "7.22.6" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.6.tgz#aafafbe86e9a1679d876b99dc46382964ef72494" - integrity sha512-HPIyDa6n+HKw5dEuway3vVAhBboYCtREBMp+IWeseZy6TFtzn6MHkCH2KKYUOC/vKKwgSMHQW4htBOrmuRPXfw== + version "7.23.7" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.7.tgz#4d8016e06a14b5f92530a13ed0561730b5c6483f" + integrity sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw== dependencies: "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.22.5" - "@babel/generator" "^7.22.5" - "@babel/helper-compilation-targets" "^7.22.6" - "@babel/helper-module-transforms" "^7.22.5" - "@babel/helpers" "^7.22.6" - "@babel/parser" "^7.22.6" - "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.6" - "@babel/types" "^7.22.5" - "@nicolo-ribaudo/semver-v6" "^6.3.3" - convert-source-map "^1.7.0" + "@babel/code-frame" "^7.23.5" + "@babel/generator" "^7.23.6" + "@babel/helper-compilation-targets" "^7.23.6" + "@babel/helper-module-transforms" "^7.23.3" + "@babel/helpers" "^7.23.7" + "@babel/parser" "^7.23.6" + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.23.7" + "@babel/types" "^7.23.6" + convert-source-map "^2.0.0" debug "^4.1.0" gensync "^1.0.0-beta.2" - json5 "^2.2.2" + json5 "^2.2.3" + semver "^6.3.1" -"@babel/generator@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.5.tgz#1e7bf768688acfb05cf30b2369ef855e82d984f7" - integrity sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA== +"@babel/generator@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.23.6.tgz#9e1fca4811c77a10580d17d26b57b036133f3c2e" + integrity sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw== dependencies: - "@babel/types" "^7.22.5" + "@babel/types" "^7.23.6" "@jridgewell/gen-mapping" "^0.3.2" "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" @@ -81,29 +82,29 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-compilation-targets@^7.22.6": - version "7.22.6" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.6.tgz#e30d61abe9480aa5a83232eb31c111be922d2e52" - integrity sha512-534sYEqWD9VfUm3IPn2SLcH4Q3P86XL+QvqdC7ZsFrzyyPF3T4XGiVghF6PTYNdWg6pXuoqXxNQAhbYeEInTzA== +"@babel/helper-compilation-targets@^7.23.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz#4d79069b16cbcf1461289eccfbbd81501ae39991" + integrity sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ== dependencies: - "@babel/compat-data" "^7.22.6" - "@babel/helper-validator-option" "^7.22.5" - "@nicolo-ribaudo/semver-v6" "^6.3.3" - browserslist "^4.21.9" + "@babel/compat-data" "^7.23.5" + "@babel/helper-validator-option" "^7.23.5" + browserslist "^4.22.2" lru-cache "^5.1.1" + semver "^6.3.1" -"@babel/helper-environment-visitor@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz#f06dd41b7c1f44e1f8da6c4055b41ab3a09a7e98" - integrity sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q== +"@babel/helper-environment-visitor@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" + integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== -"@babel/helper-function-name@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz#ede300828905bb15e582c037162f99d5183af1be" - integrity sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ== +"@babel/helper-function-name@^7.23.0": + version "7.23.0" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" + integrity sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw== dependencies: - "@babel/template" "^7.22.5" - "@babel/types" "^7.22.5" + "@babel/template" "^7.22.15" + "@babel/types" "^7.23.0" "@babel/helper-hoist-variables@^7.22.5": version "7.22.5" @@ -112,26 +113,23 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-module-imports@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz#1a8f4c9f4027d23f520bd76b364d44434a72660c" - integrity sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg== +"@babel/helper-module-imports@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0" + integrity sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w== dependencies: - "@babel/types" "^7.22.5" + "@babel/types" "^7.22.15" -"@babel/helper-module-transforms@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz#0f65daa0716961b6e96b164034e737f60a80d2ef" - integrity sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw== +"@babel/helper-module-transforms@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1" + integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ== dependencies: - "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-module-imports" "^7.22.15" "@babel/helper-simple-access" "^7.22.5" - "@babel/helper-split-export-declaration" "^7.22.5" - "@babel/helper-validator-identifier" "^7.22.5" - "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.5" - "@babel/types" "^7.22.5" + "@babel/helper-split-export-declaration" "^7.22.6" + "@babel/helper-validator-identifier" "^7.22.20" "@babel/helper-plugin-utils@^7.22.5": version "7.22.5" @@ -145,124 +143,124 @@ dependencies: "@babel/types" "^7.22.5" -"@babel/helper-split-export-declaration@^7.22.5", "@babel/helper-split-export-declaration@^7.22.6": +"@babel/helper-split-export-declaration@^7.22.6": version "7.22.6" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" integrity sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g== dependencies: "@babel/types" "^7.22.5" -"@babel/helper-string-parser@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" - integrity sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw== +"@babel/helper-string-parser@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" + integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== -"@babel/helper-validator-identifier@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz#9544ef6a33999343c8740fa51350f30eeaaaf193" - integrity sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ== +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== -"@babel/helper-validator-option@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz#de52000a15a177413c8234fa3a8af4ee8102d0ac" - integrity sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw== +"@babel/helper-validator-option@^7.23.5": + version "7.23.5" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307" + integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw== -"@babel/helpers@^7.22.6": - version "7.22.6" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.6.tgz#8e61d3395a4f0c5a8060f309fb008200969b5ecd" - integrity sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA== +"@babel/helpers@^7.23.7": + version "7.23.8" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.8.tgz#fc6b2d65b16847fd50adddbd4232c76378959e34" + integrity sha512-KDqYz4PiOWvDFrdHLPhKtCThtIcKVy6avWD2oG4GEvyQ+XDZwHD4YQd+H2vNMnq2rkdxsDkU82T+Vk8U/WXHRQ== dependencies: - "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.6" - "@babel/types" "^7.22.5" + "@babel/template" "^7.22.15" + "@babel/traverse" "^7.23.7" + "@babel/types" "^7.23.6" -"@babel/highlight@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031" - integrity sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw== +"@babel/highlight@^7.23.4": + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" + integrity sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A== dependencies: - "@babel/helper-validator-identifier" "^7.22.5" - chalk "^2.0.0" + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" js-tokens "^4.0.0" "@babel/node@^7.16.5": - version "7.22.6" - resolved "https://registry.yarnpkg.com/@babel/node/-/node-7.22.6.tgz#a47b4f150f06bad1808823c4519690ded6c93911" - integrity sha512-Lt6v+RUQOTsEOXLv+KfjogLFkFfsLPPSoXZqmbngfVatkWjQPnFGHO0xjFRcN6XEvm3vsnZn+AWQiRpgZFsdIA== + version "7.22.19" + resolved "https://registry.yarnpkg.com/@babel/node/-/node-7.22.19.tgz#d0bd1e84e3d45eb2eeb68046d6dc22161786222b" + integrity sha512-VsKSO9aEHdO16NdtqkJfrXZ9Sxlna1BVnBbToWr1KGdI3cyIk6KqOoa8mWvpK280lJDOwJqxvnl994KmLhq1Yw== dependencies: - "@babel/register" "^7.22.5" + "@babel/register" "^7.22.15" commander "^4.0.1" core-js "^3.30.2" node-environment-flags "^1.0.5" - regenerator-runtime "^0.13.11" + regenerator-runtime "^0.14.0" v8flags "^3.1.1" -"@babel/parser@^7.22.5", "@babel/parser@^7.22.6", "@babel/parser@^7.6.0", "@babel/parser@^7.9.6": - version "7.22.6" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.6.tgz#201f8b47be20c76c7c5743b9c16129760bf9a975" - integrity sha512-EIQu22vNkceq3LbjAq7knDf/UmtI2qbcNI8GRBlijez6TpQLvSodJPYfydQmNA5buwkxxxa/PVI44jjYZ+/cLw== +"@babel/parser@^7.22.15", "@babel/parser@^7.23.6", "@babel/parser@^7.6.0", "@babel/parser@^7.9.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.6.tgz#ba1c9e512bda72a47e285ae42aff9d2a635a9e3b" + integrity sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ== -"@babel/plugin-syntax-jsx@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz#a6b68e84fb76e759fc3b93e901876ffabbe1d918" - integrity sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg== +"@babel/plugin-syntax-jsx@^7.23.3": + version "7.23.3" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.23.3.tgz#8f2e4f8a9b5f9aa16067e142c1ac9cd9f810f473" + integrity sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-react-jsx@^7.16.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.22.5.tgz#932c291eb6dd1153359e2a90cb5e557dcf068416" - integrity sha512-rog5gZaVbUip5iWDMTYbVM15XQq+RkUKhET/IHR6oizR+JEoN6CAfTTuHcK4vwUyzca30qqHqEpzBOnaRMWYMA== + version "7.23.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.23.4.tgz#393f99185110cea87184ea47bcb4a7b0c2e39312" + integrity sha512-5xOpoPguCZCRbo/JeHlloSkTA8Bld1J/E1/kLfD1nsuiW1m8tduTA1ERCgIZokDflX/IBzKcqR3l7VlRgiIfHA== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-module-imports" "^7.22.5" + "@babel/helper-module-imports" "^7.22.15" "@babel/helper-plugin-utils" "^7.22.5" - "@babel/plugin-syntax-jsx" "^7.22.5" - "@babel/types" "^7.22.5" + "@babel/plugin-syntax-jsx" "^7.23.3" + "@babel/types" "^7.23.4" -"@babel/register@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.22.5.tgz#e4d8d0f615ea3233a27b5c6ada6750ee59559939" - integrity sha512-vV6pm/4CijSQ8Y47RH5SopXzursN35RQINfGJkmOlcpAtGuf94miFvIPhCKGQN7WGIcsgG1BHEX2KVdTYwTwUQ== +"@babel/register@^7.22.15": + version "7.23.7" + resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.23.7.tgz#485a5e7951939d21304cae4af1719fdb887bc038" + integrity sha512-EjJeB6+kvpk+Y5DAkEAmbOBEFkh9OASx0huoEkqYTFxAZHzOAX2Oh5uwAUuL2rUddqfM0SA+KPXV2TbzoZ2kvQ== dependencies: clone-deep "^4.0.1" find-cache-dir "^2.0.0" make-dir "^2.1.0" - pirates "^4.0.5" + pirates "^4.0.6" source-map-support "^0.5.16" -"@babel/template@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.5.tgz#0c8c4d944509875849bd0344ff0050756eefc6ec" - integrity sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw== +"@babel/template@^7.22.15": + version "7.22.15" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" + integrity sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w== dependencies: - "@babel/code-frame" "^7.22.5" - "@babel/parser" "^7.22.5" - "@babel/types" "^7.22.5" + "@babel/code-frame" "^7.22.13" + "@babel/parser" "^7.22.15" + "@babel/types" "^7.22.15" -"@babel/traverse@^7.22.5", "@babel/traverse@^7.22.6": - version "7.22.6" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.6.tgz#8f2f83a5c588251584914debeee38f35f661a300" - integrity sha512-53CijMvKlLIDlOTrdWiHileRddlIiwUIyCKqYa7lYnnPldXCG5dUSN38uT0cA6i7rHWNKJLH0VU/Kxdr1GzB3w== +"@babel/traverse@^7.23.7": + version "7.23.7" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.7.tgz#9a7bf285c928cb99b5ead19c3b1ce5b310c9c305" + integrity sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg== dependencies: - "@babel/code-frame" "^7.22.5" - "@babel/generator" "^7.22.5" - "@babel/helper-environment-visitor" "^7.22.5" - "@babel/helper-function-name" "^7.22.5" + "@babel/code-frame" "^7.23.5" + "@babel/generator" "^7.23.6" + "@babel/helper-environment-visitor" "^7.22.20" + "@babel/helper-function-name" "^7.23.0" "@babel/helper-hoist-variables" "^7.22.5" "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.22.6" - "@babel/types" "^7.22.5" - debug "^4.1.0" + "@babel/parser" "^7.23.6" + "@babel/types" "^7.23.6" + debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.22.5", "@babel/types@^7.6.1", "@babel/types@^7.9.6": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.5.tgz#cd93eeaab025880a3a47ec881f4b096a5b786fbe" - integrity sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA== +"@babel/types@^7.22.15", "@babel/types@^7.22.5", "@babel/types@^7.23.0", "@babel/types@^7.23.4", "@babel/types@^7.23.6", "@babel/types@^7.6.1", "@babel/types@^7.9.6": + version "7.23.6" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.23.6.tgz#be33fdb151e1f5a56877d704492c240fc71c7ccd" + integrity sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg== dependencies: - "@babel/helper-string-parser" "^7.22.5" - "@babel/helper-validator-identifier" "^7.22.5" + "@babel/helper-string-parser" "^7.23.4" + "@babel/helper-validator-identifier" "^7.22.20" to-fast-properties "^2.0.0" "@biomejs/biome@1.2.2": @@ -307,10 +305,10 @@ resolved "https://registry.yarnpkg.com/@biomejs/cli-win32-x64/-/cli-win32-x64-1.2.2.tgz#583e50d8a1a1cc81381200d3cb2f34669c6afd35" integrity sha512-bfaFJwqJ9ApFga2o88OaROSd3pasYRzRGXHJWAE9VUUKdSNSTYxHOqVrNvV54yYPtL6Kt9xkuZa4HNu9it3TaA== -"@colors/colors@1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" - integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== +"@colors/colors@1.6.0", "@colors/colors@^1.6.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.6.0.tgz#ec6cd237440700bc23ca23087f513c75508958b0" + integrity sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA== "@dabh/diagnostics@^2.0.2": version "2.0.3" @@ -333,15 +331,15 @@ dependencies: eslint-visitor-keys "^3.3.0" -"@eslint-community/regexpp@^4.4.0": - version "4.5.1" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.1.tgz#cdd35dce4fa1a89a4fd42b1599eb35b3af408884" - integrity sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ== +"@eslint-community/regexpp@^4.6.1": + version "4.10.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63" + integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA== -"@eslint/eslintrc@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.0.tgz#82256f164cc9e0b59669efc19d57f8092706841d" - integrity sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A== +"@eslint/eslintrc@^2.1.4": + version "2.1.4" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.4.tgz#388a269f0f25c1b6adc317b5a2c55714894c70ad" + integrity sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ== dependencies: ajv "^6.12.4" debug "^4.3.2" @@ -353,18 +351,18 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@8.44.0": - version "8.44.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.44.0.tgz#961a5903c74139390478bdc808bcde3fc45ab7af" - integrity sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw== +"@eslint/js@8.56.0": + version "8.56.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.56.0.tgz#ef20350fec605a7f7035a01764731b2de0f3782b" + integrity sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A== -"@humanwhocodes/config-array@^0.11.10": - version "0.11.10" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.10.tgz#5a3ffe32cc9306365fb3fd572596cd602d5e12d2" - integrity sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ== +"@humanwhocodes/config-array@^0.11.13": + version "0.11.14" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b" + integrity sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg== dependencies: - "@humanwhocodes/object-schema" "^1.2.1" - debug "^4.1.1" + "@humanwhocodes/object-schema" "^2.0.2" + debug "^4.3.1" minimatch "^3.0.5" "@humanwhocodes/module-importer@^1.0.1": @@ -372,10 +370,10 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== -"@humanwhocodes/object-schema@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" - integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== +"@humanwhocodes/object-schema@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz#d9fae00a2d5cb40f92cfe64b47ad749fbc38f917" + integrity sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw== "@jridgewell/gen-mapping@^0.3.0", "@jridgewell/gen-mapping@^0.3.2": version "0.3.3" @@ -386,10 +384,10 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/resolve-uri@3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" - integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== "@jridgewell/set-array@^1.0.1": version "1.1.2" @@ -404,34 +402,24 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/sourcemap-codec@1.4.14": - version "1.4.14" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" - integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== - -"@jridgewell/sourcemap-codec@^1.4.10": +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": version "1.4.15" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== -"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.18" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" - integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== +"@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.22" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz#72a621e5de59f5f1ef792d0793a82ee20f645e4c" + integrity sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw== dependencies: - "@jridgewell/resolve-uri" "3.1.0" - "@jridgewell/sourcemap-codec" "1.4.14" + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" "@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3": version "2.1.8-no-fsevents.3" resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz#323d72dd25103d0c4fbdce89dadf574a787b1f9b" integrity sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ== -"@nicolo-ribaudo/semver-v6@^6.3.3": - version "6.3.3" - resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/semver-v6/-/semver-v6-6.3.3.tgz#ea6d23ade78a325f7a52750aab1526b02b628c29" - integrity sha512-3Yc1fUTs69MG/uZbJlLSI3JISMn2UV2rg+1D/vROUqZyh3l6iYHCs7GMp+M40ZD7yOdDbYjJcU1oTJhrc+dGKg== - "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -468,14 +456,14 @@ "@peculiar/webcrypto" "^1.1.6" node-fetch "^2.6.1" -"@peculiar/asn1-schema@^2.3.6": - version "2.3.6" - resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.6.tgz#3dd3c2ade7f702a9a94dfb395c192f5fa5d6b922" - integrity sha512-izNRxPoaeJeg/AyH8hER6s+H7p4itk+03QCa4sbxI3lNdseQYCuxzgsuNK8bTXChtLTjpJz6NmXKA73qLa3rCA== +"@peculiar/asn1-schema@^2.3.8": + version "2.3.8" + resolved "https://registry.yarnpkg.com/@peculiar/asn1-schema/-/asn1-schema-2.3.8.tgz#04b38832a814e25731232dd5be883460a156da3b" + integrity sha512-ULB1XqHKx1WBU/tTFIA+uARuRoBVZ4pNdOA878RDrRbBfBGcSzi5HBkdScC6ZbHn8z7L8gmKCgPC1LHRrP46tA== dependencies: asn1js "^3.0.5" - pvtsutils "^1.3.2" - tslib "^2.4.0" + pvtsutils "^1.3.5" + tslib "^2.6.2" "@peculiar/json-schema@^1.1.12": version "1.1.12" @@ -485,20 +473,20 @@ tslib "^2.0.0" "@peculiar/webcrypto@^1.1.6": - version "1.4.3" - resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.4.3.tgz#078b3e8f598e847b78683dc3ba65feb5029b93a7" - integrity sha512-VtaY4spKTdN5LjJ04im/d/joXuvLbQdgy5Z4DXF4MFZhQ+MTrejbNMkfZBp1Bs3O5+bFqnJgyGdPuZQflvIa5A== + version "1.4.5" + resolved "https://registry.yarnpkg.com/@peculiar/webcrypto/-/webcrypto-1.4.5.tgz#424bed6b0d133b772f5cbffd143d0468a90f40a0" + integrity sha512-oDk93QCDGdxFRM8382Zdminzs44dg3M2+E5Np+JWkpqLDyJC9DviMh8F8mEJkYuUcUOGA5jHO5AJJ10MFWdbZw== dependencies: - "@peculiar/asn1-schema" "^2.3.6" + "@peculiar/asn1-schema" "^2.3.8" "@peculiar/json-schema" "^1.1.12" - pvtsutils "^1.3.2" - tslib "^2.5.0" - webcrypto-core "^1.7.7" + pvtsutils "^1.3.5" + tslib "^2.6.2" + webcrypto-core "^1.7.8" -"@polka/url@^1.0.0-next.20": - version "1.0.0-next.21" - resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1" - integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== +"@polka/url@^1.0.0-next.24": + version "1.0.0-next.24" + resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.24.tgz#58601079e11784d20f82d0585865bb42305c4df3" + integrity sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ== "@sindresorhus/is@^4.0.0": version "4.6.0" @@ -523,25 +511,25 @@ "@types/responselike" "^1.0.0" "@types/eslint-scope@^3.7.3": - version "3.7.4" - resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16" - integrity sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA== + version "3.7.7" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" + integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== dependencies: "@types/eslint" "*" "@types/estree" "*" "@types/eslint@*": - version "8.40.2" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.40.2.tgz#2833bc112d809677864a4b0e7d1de4f04d7dac2d" - integrity sha512-PRVjQ4Eh9z9pmmtaq8nTjZjQwKFk7YIHIud3lRoKRBgUQjgjRmoGxxGEPXQkF+lH7QkHJRNr5F4aBgYCW0lqpQ== + version "8.56.2" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.2.tgz#1c72a9b794aa26a8b94ad26d5b9aa51c8a6384bb" + integrity sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw== dependencies: "@types/estree" "*" "@types/json-schema" "*" "@types/estree@*", "@types/estree@^1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194" - integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA== + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" + integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== "@types/glob@^7.1.1": version "7.2.0" @@ -552,14 +540,14 @@ "@types/node" "*" "@types/http-cache-semantics@*": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812" - integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ== + version "4.0.4" + resolved "https://registry.yarnpkg.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz#b979ebad3919799c979b17c72621c0bc0a31c6c4" + integrity sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA== "@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": - version "7.0.12" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb" - integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA== + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== "@types/json5@^0.0.29": version "0.0.29" @@ -579,21 +567,28 @@ integrity sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA== "@types/node@*": - version "20.3.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.3.3.tgz#329842940042d2b280897150e023e604d11657d6" - integrity sha512-wheIYdr4NYML61AjC8MKj/2jrR/kDQri/CIpVoZwldwhnIrD/j9jIU5bJ8yBKuB2VhpFV7Ab6G2XkBjv9r9Zzw== + version "20.11.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.5.tgz#be10c622ca7fcaa3cf226cf80166abc31389d86e" + integrity sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w== + dependencies: + undici-types "~5.26.4" "@types/responselike@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" - integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA== + version "1.0.3" + resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.3.tgz#cc29706f0a397cfe6df89debfe4bf5cea159db50" + integrity sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw== dependencies: "@types/node" "*" "@types/triple-beam@^1.3.2": - version "1.3.2" - resolved "https://registry.yarnpkg.com/@types/triple-beam/-/triple-beam-1.3.2.tgz#38ecb64f01aa0d02b7c8f4222d7c38af6316fef8" - integrity sha512-txGIh+0eDFzKGC25zORnswy+br1Ha7hj5cMVwKIU7+s0U2AxxJru/jZSMU6OC9MJWP6+pc/hc6ZjyZShpsyY2g== + version "1.3.5" + resolved "https://registry.yarnpkg.com/@types/triple-beam/-/triple-beam-1.3.5.tgz#74fef9ffbaa198eb8b588be029f38b00299caa2c" + integrity sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw== + +"@ungap/structured-clone@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406" + integrity sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ== "@vercel/ncc@^0.34.0": version "0.34.0" @@ -995,9 +990,9 @@ acorn-jsx@^5.3.2: integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== acorn-walk@^8.0.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" - integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + version "8.3.2" + resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.2.tgz#7703af9415f1b6db9315d6895503862e231d34aa" + integrity sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A== acorn@^7.1.1: version "7.4.1" @@ -1005,9 +1000,9 @@ acorn@^7.1.1: integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== acorn@^8.0.4, acorn@^8.7.1, acorn@^8.8.2, acorn@^8.9.0: - version "8.9.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.9.0.tgz#78a16e3b2bcc198c10822786fa6679e245db5b59" - integrity sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ== + version "8.11.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" + integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== ajv-formats@^2.1.1: version "2.1.1" @@ -1028,7 +1023,7 @@ ajv-keywords@^5.1.0: dependencies: fast-deep-equal "^3.1.3" -ajv@^6.10.0, ajv@^6.12.4, ajv@^6.12.5: +ajv@^6.12.4, ajv@^6.12.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -1103,15 +1098,15 @@ array-flatten@1.1.1: resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== -array-includes@^3.1.6: - version "3.1.6" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.6.tgz#9e9e720e194f198266ba9e18c29e6a9b0e4b225f" - integrity sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw== +array-includes@^3.1.6, array-includes@^3.1.7: + version "3.1.7" + resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.7.tgz#8cd2e01b26f7a3086cbc87271593fe921c62abda" + integrity sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - get-intrinsic "^1.1.3" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" is-string "^1.0.7" array-union@^2.1.0: @@ -1119,47 +1114,71 @@ array-union@^2.1.0: resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -array.prototype.flat@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz#ffc6576a7ca3efc2f46a143b9d1dda9b4b3cf5e2" - integrity sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA== +array.prototype.findlastindex@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz#b37598438f97b579166940814e2c0493a4f50207" + integrity sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + get-intrinsic "^1.2.1" + +array.prototype.flat@^1.3.1, array.prototype.flat@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz#1476217df8cff17d72ee8f3ba06738db5b387d18" + integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" es-shim-unscopables "^1.0.0" -array.prototype.flatmap@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz#1aae7903c2100433cb8261cd4ed310aab5c4a183" - integrity sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ== +array.prototype.flatmap@^1.3.1, array.prototype.flatmap@^1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz#c9a7c6831db8e719d6ce639190146c24bbd3e527" + integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" es-shim-unscopables "^1.0.0" -array.prototype.reduce@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.5.tgz#6b20b0daa9d9734dd6bc7ea66b5bbce395471eac" - integrity sha512-kDdugMl7id9COE8R7MHF5jWk7Dqt/fs4Pv+JXoICnYwqpjjjbUurz6w5fT5IG6brLdJhv6/VoHB0H7oyIBXd+Q== +array.prototype.reduce@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/array.prototype.reduce/-/array.prototype.reduce-1.0.6.tgz#63149931808c5fc1e1354814923d92d45f7d96d5" + integrity sha512-UW+Mz8LG/sPSU8jRDCjVr6J/ZKAGpHfwrZ6kWTG5qCxIEiXdVshqGnu5vEZA8S1y6X4aCSbQZ0/EEsfvEvBiSg== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" es-array-method-boxes-properly "^1.0.0" is-string "^1.0.7" array.prototype.tosorted@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.1.tgz#ccf44738aa2b5ac56578ffda97c03fd3e23dd532" - integrity sha512-pZYPXPRl2PqWcsUs6LOMn+1f1532nEoPTYowBtqLwAW+W8vSVhkIGnmOX1t/UQjD6YGI0vcD2B1U7ZFGQH9jnQ== + version "1.1.2" + resolved "https://registry.yarnpkg.com/array.prototype.tosorted/-/array.prototype.tosorted-1.1.2.tgz#620eff7442503d66c799d95503f82b475745cefd" + integrity sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" es-shim-unscopables "^1.0.0" - get-intrinsic "^1.1.3" + get-intrinsic "^1.2.1" + +arraybuffer.prototype.slice@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz#98bd561953e3e74bb34938e77647179dfe6e9f12" + integrity sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + is-array-buffer "^3.0.2" + is-shared-array-buffer "^1.0.2" asap@~2.0.3: version "2.0.6" @@ -1196,21 +1215,35 @@ assertion-error@^1.1.0: integrity sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw== async@^3.2.3: - version "3.2.4" - resolved "https://registry.yarnpkg.com/async/-/async-3.2.4.tgz#2d22e00f8cddeb5fde5dd33522b56d1cf569a81c" - integrity sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ== + version "3.2.5" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.5.tgz#ebd52a8fdaf7a2289a24df399f8d8485c8a46b66" + integrity sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg== + +asynciterator.prototype@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz#8c5df0514936cdd133604dfcc9d3fb93f09b2b62" + integrity sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg== + dependencies: + has-symbols "^1.0.3" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== available-typed-arrays@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== -axios@^0.25.0: - version "0.25.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.25.0.tgz#349cfbb31331a9b4453190791760a8d35b093e0a" - integrity sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g== +axios@^1.6.5: + version "1.6.5" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.5.tgz#2c090da14aeeab3770ad30c3a1461bc970fb0cd8" + integrity sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg== dependencies: - follow-redirects "^1.14.7" + follow-redirects "^1.15.4" + form-data "^4.0.0" + proxy-from-env "^1.1.0" babel-plugin-jsx-pragmatic@^1.0.2: version "1.0.2" @@ -1333,15 +1366,15 @@ browser-stdout@1.3.1: resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== -browserslist@^4.14.5, browserslist@^4.21.9: - version "4.21.9" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.9.tgz#e11bdd3c313d7e2a9e87e8b4b0c7872b13897635" - integrity sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg== +browserslist@^4.14.5, browserslist@^4.22.2: + version "4.22.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.2.tgz#704c4943072bd81ea18997f3bd2180e89c77874b" + integrity sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A== dependencies: - caniuse-lite "^1.0.30001503" - electron-to-chromium "^1.4.431" - node-releases "^2.0.12" - update-browserslist-db "^1.0.11" + caniuse-lite "^1.0.30001565" + electron-to-chromium "^1.4.601" + node-releases "^2.0.14" + update-browserslist-db "^1.0.13" buffer-from@^1.0.0: version "1.1.2" @@ -1383,13 +1416,14 @@ cacheable-request@^7.0.2: normalize-url "^6.0.1" responselike "^2.0.0" -call-bind@^1.0.0, call-bind@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== +call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.4, call-bind@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" + integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" + function-bind "^1.1.2" + get-intrinsic "^1.2.1" + set-function-length "^1.1.1" callsites@^3.0.0: version "3.1.0" @@ -1406,10 +1440,10 @@ camelcase@^6.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001503: - version "1.0.30001512" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001512.tgz#7450843fb581c39f290305a83523c7a9ef0d4cb4" - integrity sha512-2S9nK0G/mE+jasCUsMPlARhRCts1ebcp2Ji8Y8PWi4NDE1iRdLCnEPHkEfeBrGC45L4isBx5ur3IQ6yTE2mRZw== +caniuse-lite@^1.0.30001565: + version "1.0.30001579" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001579.tgz#45c065216110f46d6274311a4b3fcf6278e0852a" + integrity sha512-u5AUVkixruKHJjw/pj9wISlcMpgFWzSrczLZbrqBSxukQixmg0SJ5sZTpvaFvxU0HoQKd4yoyAogyrAz9pzJnA== caseless@~0.12.0: version "0.12.0" @@ -1417,19 +1451,19 @@ caseless@~0.12.0: integrity sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw== chai@^4.3.6: - version "4.3.7" - resolved "https://registry.yarnpkg.com/chai/-/chai-4.3.7.tgz#ec63f6df01829088e8bf55fca839bcd464a8ec51" - integrity sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A== + version "4.4.1" + resolved "https://registry.yarnpkg.com/chai/-/chai-4.4.1.tgz#3603fa6eba35425b0f2ac91a009fe924106e50d1" + integrity sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g== dependencies: assertion-error "^1.1.0" - check-error "^1.0.2" - deep-eql "^4.1.2" - get-func-name "^2.0.0" - loupe "^2.3.1" + check-error "^1.0.3" + deep-eql "^4.1.3" + get-func-name "^2.0.2" + loupe "^2.3.6" pathval "^1.1.1" - type-detect "^4.0.5" + type-detect "^4.0.8" -chalk@^2.0.0: +chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -1454,14 +1488,16 @@ character-parser@^2.2.0: is-regex "^1.0.3" chardet@^1.2.1: - version "1.6.0" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-1.6.0.tgz#d04ea6e254916770c0e15ab678cbc7864c967fd1" - integrity sha512-+QOTw3otC4+FxdjK9RopGpNOglADbr4WPFi0SonkO99JbpkTPbMxmdm4NenhF5Zs+4gPXLI1+y2uazws5TMe8w== + version "1.6.1" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-1.6.1.tgz#4db1d0722ff9b172c08e75d0f3e118ea0b36d637" + integrity sha512-RHP0lMTBsIhM/RxxoOzuTsY7IMNE6/XQ7FKGch0D/aluQzgah0BS4i9ND8wPJdIu7WvMMjk88EXcNi6j8Tdung== -check-error@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" - integrity sha512-BrgHpW9NURQgzoNyjfq0Wu6VFO6D7IZEmJNdtgNqpzGG8RuNFHt2jQxWlAs4HMe119chBnv+34syEZtc6IhLtA== +check-error@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.3.tgz#a6502e4312a7ee969f646e83bb3ddd56281bd694" + integrity sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg== + dependencies: + get-func-name "^2.0.2" chokidar@3.5.3, "chokidar@>=3.0.0 <4.0.0", chokidar@^3.4.0, chokidar@^3.5.2: version "3.5.3" @@ -1592,6 +1628,13 @@ colorspace@1.1.x: color "^3.1.3" text-hex "1.0.x" +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + commander@^10.0.1: version "10.0.1" resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" @@ -1642,10 +1685,10 @@ content-type@~1.0.4, content-type@~1.0.5: resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== -convert-source-map@^1.1.0, convert-source-map@^1.7.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.9.0.tgz#7faae62353fb4213366d0ca98358d22e8368b05f" - integrity sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A== +convert-source-map@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" + integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== cookie-signature@1.0.6: version "1.0.6" @@ -1670,9 +1713,9 @@ copy-webpack-plugin@^11.0.0: serialize-javascript "^6.0.0" core-js@^3.30.2: - version "3.31.0" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.31.0.tgz#4471dd33e366c79d8c0977ed2d940821719db344" - integrity sha512-NIp2TQSGfR6ba5aalZD+ZQ1fSxGhDo/s1w0nx3RYzf2pnJxt7YynxFlFScP6eV7+GZsKO95NSjGxyJsU3DZgeQ== + version "3.35.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.35.1.tgz#9c28f8b7ccee482796f8590cc8d15739eaaf980c" + integrity sha512-IgdsbxNyMskrTFxa9lWHyMwAJU5gXOPP+1yO+K59d50VLVAIDAbs7gIv705KzALModfK3ZrSZTPNpC0PQgIZuw== cors@^2.8.5: version "2.8.5" @@ -1715,24 +1758,29 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3: which "^2.0.1" css-loader@^6.6.0: - version "6.8.1" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.8.1.tgz#0f8f52699f60f5e679eab4ec0fcd68b8e8a50a88" - integrity sha512-xDAXtEVGlD0gJ07iclwWVkLoZOpEvAWaSyf6W18S2pOC//K8+qUDIx8IIT3D+HjnmkJPQeesOPv5aiUaJsCM2g== + version "6.9.1" + resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-6.9.1.tgz#9ec9a434368f2bdfeffbf8f6901a1ce773586c6b" + integrity sha512-OzABOh0+26JKFdMzlK6PY1u5Zx8+Ck7CVRlcGNZoY9qwJjdfu2VWFuprTIpPW+Av5TZTVViYWcFQaEEQURLknQ== dependencies: icss-utils "^5.1.0" - postcss "^8.4.21" + postcss "^8.4.33" postcss-modules-extract-imports "^3.0.0" - postcss-modules-local-by-default "^4.0.3" - postcss-modules-scope "^3.0.0" + postcss-modules-local-by-default "^4.0.4" + postcss-modules-scope "^3.1.1" postcss-modules-values "^4.0.0" postcss-value-parser "^4.2.0" - semver "^7.3.8" + semver "^7.5.4" cssesc@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== +debounce@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/debounce/-/debounce-1.2.1.tgz#38881d8f4166a5c5848020c11827b834bcb3e0a5" + integrity sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug== + debug@2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -1740,7 +1788,7 @@ debug@2.6.9: dependencies: ms "2.0.0" -debug@4.3.4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2: +debug@4.3.4, debug@^4, debug@^4.1.0, debug@^4.3.1, debug@^4.3.2: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -1771,7 +1819,7 @@ decompress-response@^6.0.0: dependencies: mimic-response "^3.1.0" -deep-eql@^4.1.2: +deep-eql@^4.1.3: version "4.1.3" resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-4.1.3.tgz#7c7775513092f7df98d8df9996dd085eb668cc6d" integrity sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw== @@ -1788,14 +1836,29 @@ defer-to-connect@^2.0.0: resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== -define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" - integrity sha512-xvqAVKGfT1+UAvPwKTVw/njhdQ8ZhXK4lI0bCIuCMrp2up9nPnaDftrLtmpTazqd1o+UY4zgzU+avtMbDP+ldA== +define-data-property@^1.0.1, define-data-property@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" + integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== dependencies: + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + +define-properties@^1.1.3, define-properties@^1.2.0, define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" has-property-descriptors "^1.0.0" object-keys "^1.1.1" +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + depd@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" @@ -1842,16 +1905,16 @@ doctypes@^1.1.0: resolved "https://registry.yarnpkg.com/doctypes/-/doctypes-1.1.0.tgz#ea80b106a87538774e8a3a4a5afe293de489e0a9" integrity sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ== -doipjs@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/doipjs/-/doipjs-1.2.7.tgz#f0853d7ede1fcdeaf01e217acdc9869779ee8349" - integrity sha512-xqFHVoiBaBm8KYQbaEzsULfH0gHl9Rya+5DTc4XC21mU1QxPSeqJ9Mw2lEZgrxtq7eVsi9Kvbd9d32BOc6cCBQ== +doipjs@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/doipjs/-/doipjs-1.2.8.tgz#7048da97fca4a7d2cf784a5c84ac31bf6020180d" + integrity sha512-VIyJ091pXZzA7E4IQuMF6qkPRX5f7hZ40ue65vXspGDCxyiCkeZY0sFjAAONQcZFhqPsdUZ/ypfqfIRhvV4Trw== dependencies: "@openpgp/hkp-client" "^0.0.3" "@openpgp/wkd-client" "^0.0.4" "@xmpp/client" "^0.13.1" "@xmpp/debug" "^0.13.0" - axios "^0.25.0" + axios "^1.6.5" browser-or-node "^1.3.0" cors "^2.8.5" entities "^4.4.0" @@ -1867,9 +1930,9 @@ doipjs@^1.2.7: validator "^13.9.0" dotenv@^16.0.3: - version "16.3.1" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e" - integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ== + version "16.3.2" + resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.2.tgz#3cb611ce5a63002dbabf7c281bc331f69d28f03f" + integrity sha512-HTlk5nmhkm8F6JcdXvHIzaorzCoziNQT9mGxLPVXW8wJF1TiGSL60ZGB4gHWabHOaMmWmhvk2/lPHfnBiT78AQ== duplexer@^0.1.2: version "0.1.2" @@ -1881,10 +1944,10 @@ ee-first@1.1.1: resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== -electron-to-chromium@^1.4.431: - version "1.4.449" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.449.tgz#79ffe4514c81c35d4eb13030a63ff3383a8cc655" - integrity sha512-TxLRpRUj/107ATefeP8VIUWNOv90xJxZZbCW/eIbSZQiuiFANCx2b7u+GbVc9X4gU+xnbvypNMYVM/WArE1DNQ== +electron-to-chromium@^1.4.601: + version "1.4.642" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.642.tgz#eb380fa8b58e515c641d642ba452fa2c453c2e4f" + integrity sha512-M4+u22ZJGpk4RY7tne6W+APkZhnnhmAH48FNl8iEFK2lEgob+U5rUQsIqQhvAwCXYpfd3H20pHK/ENsCvwTbsA== emoji-regex@^7.0.1: version "7.0.3" @@ -1937,9 +2000,9 @@ entities@~3.0.1: integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q== envinfo@^7.7.3: - version "7.10.0" - resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.10.0.tgz#55146e3909cc5fe63c22da63fb15b05aeac35b13" - integrity sha512-ZtUjZO6l5mwTHvc1L9+1q5p/R3wTopcfqMW8r5t8SJSKqeVI/LtajORwRFEKpEFuekjD0VBjwu1HMxL4UalIRw== + version "7.11.0" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.11.0.tgz#c3793f44284a55ff8c82faf1ffd91bc6478ea01f" + integrity sha512-G9/6xF1FPbIw0TtalAMaVPpiq2aDEuKLXM314jPVAO9r2fo2a4BLqMNkmRS7O/xPPZ+COAhGIz3ETvHEV3eUcg== error-ex@^1.3.1: version "1.3.2" @@ -1948,25 +2011,26 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.19.0, es-abstract@^1.20.4, es-abstract@^1.21.2: - version "1.21.2" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.21.2.tgz#a56b9695322c8a185dc25975aa3b8ec31d0e7eff" - integrity sha512-y/B5POM2iBnIxCiernH1G7rC9qQoM77lLIMQLuob0zhp8C56Po81+2Nj0WFKnd0pNReDTnkYryc+zhOzpEIROg== +es-abstract@^1.22.1: + version "1.22.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.3.tgz#48e79f5573198de6dee3589195727f4f74bc4f32" + integrity sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA== dependencies: array-buffer-byte-length "^1.0.0" + arraybuffer.prototype.slice "^1.0.2" available-typed-arrays "^1.0.5" - call-bind "^1.0.2" + call-bind "^1.0.5" es-set-tostringtag "^2.0.1" es-to-primitive "^1.2.1" - function.prototype.name "^1.1.5" - get-intrinsic "^1.2.0" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.2" get-symbol-description "^1.0.0" globalthis "^1.0.3" gopd "^1.0.1" - has "^1.0.3" has-property-descriptors "^1.0.0" has-proto "^1.0.1" has-symbols "^1.0.3" + hasown "^2.0.0" internal-slot "^1.0.5" is-array-buffer "^3.0.2" is-callable "^1.2.7" @@ -1974,45 +2038,69 @@ es-abstract@^1.19.0, es-abstract@^1.20.4, es-abstract@^1.21.2: is-regex "^1.1.4" is-shared-array-buffer "^1.0.2" is-string "^1.0.7" - is-typed-array "^1.1.10" + is-typed-array "^1.1.12" is-weakref "^1.0.2" - object-inspect "^1.12.3" + object-inspect "^1.13.1" object-keys "^1.1.1" object.assign "^4.1.4" - regexp.prototype.flags "^1.4.3" + regexp.prototype.flags "^1.5.1" + safe-array-concat "^1.0.1" safe-regex-test "^1.0.0" - string.prototype.trim "^1.2.7" - string.prototype.trimend "^1.0.6" - string.prototype.trimstart "^1.0.6" + string.prototype.trim "^1.2.8" + string.prototype.trimend "^1.0.7" + string.prototype.trimstart "^1.0.7" + typed-array-buffer "^1.0.0" + typed-array-byte-length "^1.0.0" + typed-array-byte-offset "^1.0.0" typed-array-length "^1.0.4" unbox-primitive "^1.0.2" - which-typed-array "^1.1.9" + which-typed-array "^1.1.13" es-array-method-boxes-properly@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e" integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA== +es-iterator-helpers@^1.0.12: + version "1.0.15" + resolved "https://registry.yarnpkg.com/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz#bd81d275ac766431d19305923707c3efd9f1ae40" + integrity sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g== + dependencies: + asynciterator.prototype "^1.0.0" + call-bind "^1.0.2" + define-properties "^1.2.1" + es-abstract "^1.22.1" + es-set-tostringtag "^2.0.1" + function-bind "^1.1.1" + get-intrinsic "^1.2.1" + globalthis "^1.0.3" + has-property-descriptors "^1.0.0" + has-proto "^1.0.1" + has-symbols "^1.0.3" + internal-slot "^1.0.5" + iterator.prototype "^1.1.2" + safe-array-concat "^1.0.1" + es-module-lexer@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.3.0.tgz#6be9c9e0b4543a60cd166ff6f8b4e9dae0b0c16f" - integrity sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA== + version "1.4.1" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.4.1.tgz#41ea21b43908fe6a287ffcbe4300f790555331f5" + integrity sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w== es-set-tostringtag@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" - integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg== + version "2.0.2" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz#11f7cc9f63376930a5f20be4915834f4bc74f9c9" + integrity sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q== dependencies: - get-intrinsic "^1.1.3" - has "^1.0.3" + get-intrinsic "^1.2.2" has-tostringtag "^1.0.0" + hasown "^2.0.0" es-shim-unscopables@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" - integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== + version "1.0.2" + resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763" + integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw== dependencies: - has "^1.0.3" + hasown "^2.0.0" es-to-primitive@^1.2.1: version "1.2.1" @@ -2053,16 +2141,16 @@ eslint-config-standard@17.1.0: resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz#40ffb8595d47a6b242e07cbfd49dc211ed128975" integrity sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q== -eslint-import-resolver-node@^0.3.7: - version "0.3.7" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz#83b375187d412324a1963d84fa664377a23eb4d7" - integrity sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA== +eslint-import-resolver-node@^0.3.9: + version "0.3.9" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" + integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== dependencies: debug "^3.2.7" - is-core-module "^2.11.0" - resolve "^1.22.1" + is-core-module "^2.13.0" + resolve "^1.22.4" -eslint-module-utils@^2.7.4: +eslint-module-utils@^2.8.0: version "2.8.0" resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz#e439fee65fc33f6bba630ff621efc38ec0375c49" integrity sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw== @@ -2078,25 +2166,27 @@ eslint-plugin-es@^4.1.0: regexpp "^3.0.0" eslint-plugin-import@^2.27.5: - version "2.27.5" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz#876a6d03f52608a3e5bb439c2550588e51dd6c65" - integrity sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow== + version "2.29.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.29.1.tgz#d45b37b5ef5901d639c15270d74d46d161150643" + integrity sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw== dependencies: - array-includes "^3.1.6" - array.prototype.flat "^1.3.1" - array.prototype.flatmap "^1.3.1" + array-includes "^3.1.7" + array.prototype.findlastindex "^1.2.3" + array.prototype.flat "^1.3.2" + array.prototype.flatmap "^1.3.2" debug "^3.2.7" doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.7" - eslint-module-utils "^2.7.4" - has "^1.0.3" - is-core-module "^2.11.0" + eslint-import-resolver-node "^0.3.9" + eslint-module-utils "^2.8.0" + hasown "^2.0.0" + is-core-module "^2.13.1" is-glob "^4.0.3" minimatch "^3.1.2" - object.values "^1.1.6" - resolve "^1.22.1" - semver "^6.3.0" - tsconfig-paths "^3.14.1" + object.fromentries "^2.0.7" + object.groupby "^1.0.1" + object.values "^1.1.7" + semver "^6.3.1" + tsconfig-paths "^3.15.0" eslint-plugin-n@^15.7.0: version "15.7.0" @@ -2118,14 +2208,15 @@ eslint-plugin-promise@^6.1.1: integrity sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig== eslint-plugin-react@^7.32.2: - version "7.32.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.32.2.tgz#e71f21c7c265ebce01bcbc9d0955170c55571f10" - integrity sha512-t2fBMa+XzonrrNkyVirzKlvn5RXzzPwRHtMvLAtVZrt8oxgnTQaYbU6SXTOO1mwQgp1y5+toMSKInnzGr0Knqg== + version "7.33.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.33.2.tgz#69ee09443ffc583927eafe86ffebb470ee737608" + integrity sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw== dependencies: array-includes "^3.1.6" array.prototype.flatmap "^1.3.1" array.prototype.tosorted "^1.1.1" doctrine "^2.1.0" + es-iterator-helpers "^1.0.12" estraverse "^5.3.0" jsx-ast-utils "^2.4.1 || ^3.0.0" minimatch "^3.1.2" @@ -2135,7 +2226,7 @@ eslint-plugin-react@^7.32.2: object.values "^1.1.6" prop-types "^15.8.1" resolve "^2.0.0-next.4" - semver "^6.3.0" + semver "^6.3.1" string.prototype.matchall "^4.0.8" eslint-scope@5.1.1: @@ -2146,10 +2237,10 @@ eslint-scope@5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-scope@^7.2.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.0.tgz#f21ebdafda02352f103634b96dd47d9f81ca117b" - integrity sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw== +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" @@ -2178,32 +2269,33 @@ eslint-visitor-keys@^2.0.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== -eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz#c22c48f48942d08ca824cc526211ae400478a994" - integrity sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA== +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== eslint@^8.41.0: - version "8.44.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.44.0.tgz#51246e3889b259bbcd1d7d736a0c10add4f0e500" - integrity sha512-0wpHoUbDUHgNCyvFB5aXLiQVfK9B0at6gUvzy83k4kAsQ/u769TQDX6iKC+aO4upIHO9WSaA3QoXYQDHbNwf1A== + version "8.56.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.56.0.tgz#4957ce8da409dc0809f99ab07a1b94832ab74b15" + integrity sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ== dependencies: "@eslint-community/eslint-utils" "^4.2.0" - "@eslint-community/regexpp" "^4.4.0" - "@eslint/eslintrc" "^2.1.0" - "@eslint/js" "8.44.0" - "@humanwhocodes/config-array" "^0.11.10" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.4" + "@eslint/js" "8.56.0" + "@humanwhocodes/config-array" "^0.11.13" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" - ajv "^6.10.0" + "@ungap/structured-clone" "^1.2.0" + ajv "^6.12.4" chalk "^4.0.0" cross-spawn "^7.0.2" debug "^4.3.2" doctrine "^3.0.0" escape-string-regexp "^4.0.0" - eslint-scope "^7.2.0" - eslint-visitor-keys "^3.4.1" - espree "^9.6.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" esquery "^1.4.2" esutils "^2.0.2" fast-deep-equal "^3.1.3" @@ -2213,7 +2305,6 @@ eslint@^8.41.0: globals "^13.19.0" graphemer "^1.4.0" ignore "^5.2.0" - import-fresh "^3.0.0" imurmurhash "^0.1.4" is-glob "^4.0.0" is-path-inside "^3.0.3" @@ -2225,18 +2316,17 @@ eslint@^8.41.0: natural-compare "^1.4.0" optionator "^0.9.3" strip-ansi "^6.0.1" - strip-json-comments "^3.1.0" text-table "^0.2.0" esmock@^2.5.0: - version "2.5.1" - resolved "https://registry.yarnpkg.com/esmock/-/esmock-2.5.1.tgz#cef05c9cd23c46edbfb2e0add34466f6c52e37f6" - integrity sha512-3pu+ri9kNrRjahR8c+FWXphK3xpKrgBwLHu+A+Xj3vw84fGsScWY3SWTH1v5nSiheYQAdlz5Ny+a319tlle1mA== + version "2.6.2" + resolved "https://registry.yarnpkg.com/esmock/-/esmock-2.6.2.tgz#a4b86c7c1fef626f725707aa56eec341a1f224f3" + integrity sha512-tcxyxE6l0TmyEwkybbVKqFwUpsWBrjVWXYiVc8PPnKC7q9RRYyRtK03z7MXaZimRAsLkEp7SQeHCQEnrD6z4Ww== -espree@^9.6.0: - version "9.6.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.0.tgz#80869754b1c6560f32e3b6929194a3fe07c5b82f" - integrity sha512-1FH/IiruXZ84tpUlm0aCUEwMl2Ho5ilqVh0VvQXw+byAz/4SAciyHLlfmL5WYqsvD38oymdUwBss0LtK8m4s/A== +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== dependencies: acorn "^8.9.0" acorn-jsx "^5.3.2" @@ -2287,9 +2377,9 @@ express-http-context2@^1.0.0: integrity sha512-xdukoNNpWcuMn5ZJcjDe/tA+2A96rQ1MyAB/oWUU7qP15Tkz3txQyFsw/QG8YgRzTJ1sNAA8Bdq0o5b/1Y4zLA== express-rate-limit@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-7.0.1.tgz#933af24166990ea4fc8004335e6cd6c86fd31562" - integrity sha512-oTIPm094gh8c7nbShl4TNLqnayzOcbDGY7dCRnFqUAvptyb0pp5231LaH34JtvVEbZlOJMiixikU5AVK8VN3FA== + version "7.1.5" + resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-7.1.5.tgz#af4c81143a945ea97f2599d13957440a0ddbfcfe" + integrity sha512-/iVogxu7ueadrepw1bS0X0kaRC/U0afwiYRSLg68Ts+p4Dc85Q5QKsOnPS/QUjPMHvOJQtBDrZgvkOzf8ejUYw== express-validator@^6.10.0, express-validator@^6.13.0: version "6.15.0" @@ -2342,9 +2432,9 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== fast-glob@^3.0.3, fast-glob@^3.2.11, fast-glob@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.0.tgz#7c40cb491e1e2ed5664749e87bfb516dbe8727c0" - integrity sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA== + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" @@ -2368,9 +2458,9 @@ fastest-levenshtein@^1.0.12: integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== fastq@^1.6.0: - version "1.15.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a" - integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== + version "1.16.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.16.0.tgz#83b9a9375692db77a822df081edb6a9cf6839320" + integrity sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA== dependencies: reusify "^1.0.4" @@ -2439,11 +2529,12 @@ find-up@^4.0.0, find-up@^4.1.0: path-exists "^4.0.0" flat-cache@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" - integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== + version "3.2.0" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee" + integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw== dependencies: - flatted "^3.1.0" + flatted "^3.2.9" + keyv "^4.5.3" rimraf "^3.0.2" flat@^5.0.2: @@ -2451,20 +2542,20 @@ flat@^5.0.2: resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== -flatted@^3.1.0: - version "3.2.7" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" - integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== +flatted@^3.2.9: + version "3.2.9" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" + integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== fn.name@1.x.x: version "1.1.0" resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" integrity sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw== -follow-redirects@^1.14.7: - version "1.15.2" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" - integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== +follow-redirects@^1.15.4: + version "1.15.5" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" + integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== for-each@^0.3.3: version "0.3.3" @@ -2473,6 +2564,15 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -2503,26 +2603,26 @@ fs.realpath@^1.0.0: integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function-bind@^1.1.1, function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== -function.prototype.name@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" - integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== +function.prototype.name@^1.1.5, function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== dependencies: call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.0" - functions-have-names "^1.2.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + functions-have-names "^1.2.3" -functions-have-names@^1.2.2, functions-have-names@^1.2.3: +functions-have-names@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== @@ -2537,20 +2637,20 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-func-name@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.0.tgz#ead774abee72e20409433a066366023dd6887a41" - integrity sha512-Hm0ixYtaSZ/V7C8FJrtZIuBBI+iSgL+1Aq82zSu8VQNB4S3Gk8e7Qs3VwBDJAhmRZcFqkl3tQu36g/Foh5I5ig== +get-func-name@^2.0.1, get-func-name@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/get-func-name/-/get-func-name-2.0.2.tgz#0d7cf20cd13fda808669ffa88f4ffc7a3943fc41" + integrity sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ== -get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82" - integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" + integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== dependencies: - function-bind "^1.1.1" - has "^1.0.3" + function-bind "^1.1.2" has-proto "^1.0.1" has-symbols "^1.0.3" + hasown "^2.0.0" get-stdin@^8.0.0: version "8.0.0" @@ -2626,9 +2726,9 @@ globals@^11.1.0: integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== globals@^13.19.0: - version "13.20.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82" - integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ== + version "13.24.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-13.24.0.tgz#8432a19d78ce0c1e833949c36adb345400bb1171" + integrity sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ== dependencies: type-fest "^0.20.2" @@ -2720,12 +2820,12 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-property-descriptors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" - integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== +has-property-descriptors@^1.0.0, has-property-descriptors@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340" + integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg== dependencies: - get-intrinsic "^1.1.1" + get-intrinsic "^1.2.2" has-proto@^1.0.1: version "1.0.1" @@ -2744,13 +2844,6 @@ has-tostringtag@^1.0.0: dependencies: has-symbols "^1.0.2" -has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" - hash-base@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" @@ -2761,9 +2854,16 @@ hash-base@^3.0.0: safe-buffer "^5.2.0" hash-wasm@^4.9.0: - version "4.9.0" - resolved "https://registry.yarnpkg.com/hash-wasm/-/hash-wasm-4.9.0.tgz#7e9dcc9f7d6bd0cc802f2a58f24edce999744206" - integrity sha512-7SW7ejyfnRxuOc7ptQHSf4LDoZaWOivfzqw+5rpcQku0nHfmicPKE51ra9BiRLAmT8+gGLestr1XroUkqdjL6w== + version "4.11.0" + resolved "https://registry.yarnpkg.com/hash-wasm/-/hash-wasm-4.11.0.tgz#7d1479b114c82e48498fdb1d2462a687d00386d5" + integrity sha512-HVusNXlVqHe0fzIzdQOGolnFN6mX/fqcrSAOcTBXdvzrXVHwTz11vXeKRmkR5gTuwVpvHZEIyKoePDvuAR+XwQ== + +hasown@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" + integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== + dependencies: + function-bind "^1.1.2" he@1.2.0: version "1.2.0" @@ -2782,6 +2882,11 @@ homedir-polyfill@^1.0.1: dependencies: parse-passwd "^1.0.0" +html-escaper@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" + integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== + http-cache-semantics@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" @@ -2831,16 +2936,16 @@ ignore-by-default@^1.0.1: integrity sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA== ignore@^5.1.1, ignore@^5.1.2, ignore@^5.2.0, ignore@^5.2.4: - version "5.2.4" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" - integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== + version "5.3.0" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.0.tgz#67418ae40d34d6999c95ff56016759c718c82f78" + integrity sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg== immutable@^4.0.0: version "4.3.4" resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.4.tgz#2e07b33837b4bb7662f288c244d1ced1ef65a78f" integrity sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA== -import-fresh@^3.0.0, import-fresh@^3.2.1: +import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== @@ -2874,13 +2979,13 @@ inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -internal-slot@^1.0.3, internal-slot@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986" - integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== +internal-slot@^1.0.5: + version "1.0.6" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.6.tgz#37e756098c4911c5e912b8edbf71ed3aa116f930" + integrity sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg== dependencies: - get-intrinsic "^1.2.0" - has "^1.0.3" + get-intrinsic "^1.2.2" + hasown "^2.0.0" side-channel "^1.0.4" interpret@^3.1.1: @@ -2927,6 +3032,13 @@ is-arrayish@^0.3.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== +is-async-function@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.0.0.tgz#8e4418efd3e5d3a6ebb0164c05ef5afb69aa9646" + integrity sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA== + dependencies: + has-tostringtag "^1.0.0" + is-bigint@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" @@ -2954,14 +3066,14 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-core-module@^2.11.0, is-core-module@^2.9.0: - version "2.12.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.1.tgz#0c0b6885b6f80011c71541ce15c8d66cf5a4f9fd" - integrity sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg== +is-core-module@^2.11.0, is-core-module@^2.13.0, is-core-module@^2.13.1: + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== dependencies: - has "^1.0.3" + hasown "^2.0.0" -is-date-object@^1.0.1: +is-date-object@^1.0.1, is-date-object@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== @@ -2981,6 +3093,13 @@ is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== +is-finalizationregistry@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz#c8749b65f17c133313e661b1289b95ad3dbd62e6" + integrity sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw== + dependencies: + call-bind "^1.0.2" + is-fullwidth-code-point@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" @@ -2991,6 +3110,13 @@ is-fullwidth-code-point@^3.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== +is-generator-function@^1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" @@ -2998,6 +3124,11 @@ is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" +is-map@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" + integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== + is-negative-zero@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" @@ -3032,6 +3163,11 @@ is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" +is-plain-object@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" + integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== + is-promise@^2.0.0: version "2.2.2" resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" @@ -3045,6 +3181,11 @@ is-regex@^1.0.3, is-regex@^1.1.4: call-bind "^1.0.2" has-tostringtag "^1.0.0" +is-set@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" + integrity sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g== + is-shared-array-buffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" @@ -3071,22 +3212,23 @@ is-symbol@^1.0.2, is-symbol@^1.0.3: dependencies: has-symbols "^1.0.2" -is-typed-array@^1.1.10, is-typed-array@^1.1.9: - version "1.1.10" - resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" - integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== +is-typed-array@^1.1.10, is-typed-array@^1.1.12, is-typed-array@^1.1.9: + version "1.1.12" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.12.tgz#d0bab5686ef4a76f7a73097b95470ab199c57d4a" + integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== dependencies: - available-typed-arrays "^1.0.5" - call-bind "^1.0.2" - for-each "^0.3.3" - gopd "^1.0.1" - has-tostringtag "^1.0.0" + which-typed-array "^1.1.11" is-unicode-supported@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== +is-weakmap@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" + integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== + is-weakref@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" @@ -3094,6 +3236,14 @@ is-weakref@^1.0.2: dependencies: call-bind "^1.0.2" +is-weakset@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.2.tgz#4569d67a747a1ce5a994dfd4ef6dcea76e7c0a1d" + integrity sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + isarray@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" @@ -3109,6 +3259,17 @@ isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== +iterator.prototype@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/iterator.prototype/-/iterator.prototype-1.1.2.tgz#5e29c8924f01916cb9335f1ff80619dcff22b0c0" + integrity sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w== + dependencies: + define-properties "^1.2.1" + get-intrinsic "^1.2.1" + has-symbols "^1.0.3" + reflect.getprototypeof "^1.0.4" + set-function-name "^2.0.1" + jest-worker@^27.4.5: version "27.5.1" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" @@ -3119,9 +3280,9 @@ jest-worker@^27.4.5: supports-color "^8.0.0" jose@^4.14.4: - version "4.14.4" - resolved "https://registry.yarnpkg.com/jose/-/jose-4.14.4.tgz#59e09204e2670c3164ee24cbfe7115c6f8bff9ca" - integrity sha512-j8GhLiKmUAh+dsFXlX1aJCbt5KMibuKb+d7j1JaOJG6s2UjX1PQlW+OKB/sD4a/5ZYF4RcmYmLSndOoU3Lt/3g== + version "4.15.4" + resolved "https://registry.yarnpkg.com/jose/-/jose-4.15.4.tgz#02a9a763803e3872cf55f29ecef0dfdcc218cc03" + integrity sha512-W+oqK4H+r5sITxfxpSU+MMdr/YSWGvgZMQDIsNoBDGGy4i7GBPTtvFKibQzW06n3U3TqHjhvBJsirShsEJ6eeQ== js-stringify@^1.0.2: version "1.0.2" @@ -3182,7 +3343,7 @@ json5@^1.0.2: dependencies: minimist "^1.2.0" -json5@^2.2.2: +json5@^2.2.3: version "2.2.3" resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== @@ -3210,19 +3371,19 @@ jstransformer@1.0.0: promise "^7.0.1" "jsx-ast-utils@^2.4.1 || ^3.0.0": - version "3.3.4" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.4.tgz#b896535fed5b867650acce5a9bd4135ffc7b3bf9" - integrity sha512-fX2TVdCViod6HwKEtSWGHs57oFhVfCMwieb9PuRDgjDPh5XeqJiHFFFJCHxU5cnTc3Bu/GRL+kPiFmw8XWOfKw== + version "3.3.5" + resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz#4766bd05a8e2a11af222becd19e15575e52a853a" + integrity sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ== dependencies: array-includes "^3.1.6" array.prototype.flat "^1.3.1" object.assign "^4.1.4" object.values "^1.1.6" -keyv@^4.0.0, keyv@^4.5.0: - version "4.5.2" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.2.tgz#0e310ce73bf7851ec702f2eaf46ec4e3805cce56" - integrity sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g== +keyv@^4.0.0, keyv@^4.5.0, keyv@^4.5.3: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== dependencies: json-buffer "3.0.1" @@ -3315,7 +3476,7 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash@^4.17.20, lodash@^4.17.21: +lodash@^4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -3329,11 +3490,11 @@ log-symbols@4.1.0: is-unicode-supported "^0.1.0" logform@^2.3.2, logform@^2.4.0: - version "2.5.1" - resolved "https://registry.yarnpkg.com/logform/-/logform-2.5.1.tgz#44c77c34becd71b3a42a3970c77929e52c6ed48b" - integrity sha512-9FyqAm9o9NKKfiAKfZoYo9bGXXuwMkxQiQttkT4YjjVtQVIQtK6LmVtlxmCaFswo6N4AfEkHqZTV0taDtPotNg== + version "2.6.0" + resolved "https://registry.yarnpkg.com/logform/-/logform-2.6.0.tgz#8c82a983f05d6eaeb2d75e3decae7a768b2bf9b5" + integrity sha512-1ulHeNPp6k/LD8H91o7VYFBng5i1BDE7HoKxVbZiGFidS1Rj65qcywLxX+pVfAPoQJEjRdvKcusKwOupHCVOVQ== dependencies: - "@colors/colors" "1.5.0" + "@colors/colors" "1.6.0" "@types/triple-beam" "^1.3.2" fecha "^4.2.0" ms "^2.1.1" @@ -3347,12 +3508,12 @@ loose-envify@^1.4.0: dependencies: js-tokens "^3.0.0 || ^4.0.0" -loupe@^2.3.1: - version "2.3.6" - resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.6.tgz#76e4af498103c532d1ecc9be102036a21f787b53" - integrity sha512-RaPMZKiMy8/JruncMU5Bt6na1eftNoo++R4Y+N2FrxkDVTrGvcyzFTsaGif4QTeKESheMGegbhw6iUAq+5A8zA== +loupe@^2.3.6: + version "2.3.7" + resolved "https://registry.yarnpkg.com/loupe/-/loupe-2.3.7.tgz#6e69b7d4db7d3ab436328013d37d1c8c3540c697" + integrity sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA== dependencies: - get-func-name "^2.0.0" + get-func-name "^2.0.1" lowercase-keys@^2.0.0: version "2.0.0" @@ -3387,9 +3548,9 @@ make-dir@^2.0.0, make-dir@^2.1.0: semver "^5.6.0" markdown-it@^13.0.1: - version "13.0.1" - resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-13.0.1.tgz#c6ecc431cacf1a5da531423fc6a42807814af430" - integrity sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q== + version "13.0.2" + resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-13.0.2.tgz#1bc22e23379a6952e5d56217fbed881e0c94d536" + integrity sha512-FtwnEuuK+2yVU7goGn/MJ0WBZMM9ZPgU9spqlFs7/A/pDIUNSOQZhUgOqYCficIuR2QaFnrt8LHqBWsbTAoI5w== dependencies: argparse "^2.0.1" entities "~3.0.1" @@ -3456,7 +3617,7 @@ mime-db@1.52.0: resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.27, mime-types@~2.1.24, mime-types@~2.1.34: +mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -3479,9 +3640,9 @@ mimic-response@^3.1.0: integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== mini-css-extract-plugin@^2.5.3: - version "2.7.6" - resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.6.tgz#282a3d38863fddcd2e0c220aaed5b90bc156564d" - integrity sha512-Qk7HcgaPkGG6eD77mLvZS1nmxlao3j+9PkrT9Uc7HAE1id3F41+DdBRYRYkbyfNRGzm8/YWtzhw7nVPmwhqTQw== + version "2.7.7" + resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-2.7.7.tgz#4acf02f362c641c38fb913bfcb7ca2fc4a7cf339" + integrity sha512-+0n11YGyRavUR3IlaOzJ0/4Il1avMvJ1VJfhWfCn24ITQXhRr1gghbhhrda6tgtNcpZaWKdSuwKq20Jb7fnlyw== dependencies: schema-utils "^4.0.0" @@ -3536,10 +3697,10 @@ mocha@^10.1.0: yargs-parser "20.2.4" yargs-unparser "2.0.0" -mrmime@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-1.0.1.tgz#5f90c825fad4bdd41dc914eff5d1a8cfdaf24f27" - integrity sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw== +mrmime@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-2.0.0.tgz#151082a6e06e59a9a39b46b3e14d5cfe92b3abb4" + integrity sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw== ms@2.0.0: version "2.0.0" @@ -3561,15 +3722,15 @@ nanoid@3.3.3: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== -nanoid@^3.3.6: - version "3.3.6" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" - integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA== +nanoid@^3.3.7: + version "3.3.7" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.7.tgz#d0c301a691bc8d54efa0a2226ccf3fe2fd656bd8" + integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== nanoid@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-5.0.1.tgz#3e95d775a8bc8a98afbf0a237e2bbc6a71b0662e" - integrity sha512-vWeVtV5Cw68aML/QaZvqN/3QQXc6fBfIieAlu05m7FZW2Dgb+3f0xc0TTxuJW+7u30t7iSDTV/j3kVI0oJqIfQ== + version "5.0.4" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-5.0.4.tgz#d2b608d8169d7da669279127615535705aa52edf" + integrity sha512-vAjmBf13gsmhXSgBrtIclinISzFFy22WwCYoyilZlsrRXNIHSwgFQ1bEdjRwMT3aoadeIF6HMuDRlOxzfXV8ig== natural-compare@^1.4.0: version "1.4.0" @@ -3595,29 +3756,29 @@ node-environment-flags@^1.0.5: semver "^5.7.0" node-fetch@^2.6.1, node-fetch@^2.6.6: - version "2.6.12" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.12.tgz#02eb8e22074018e3d5a83016649d04df0e348fba" - integrity sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g== + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== dependencies: whatwg-url "^5.0.0" -node-releases@^2.0.12: - version "2.0.12" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.12.tgz#35627cc224a23bfb06fb3380f2b3afaaa7eb1039" - integrity sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ== +node-releases@^2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" + integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== -nodemon@^2.0.20: - version "2.0.22" - resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-2.0.22.tgz#182c45c3a78da486f673d6c1702e00728daf5258" - integrity sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ== +nodemon@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/nodemon/-/nodemon-3.0.3.tgz#244a62d1c690eece3f6165c6cdb0db03ebd80b76" + integrity sha512-7jH/NXbFPxVaMwmBCC2B9F/V6X1VkEdNgx3iu9jji8WxWcvhMWkmhNWhI5077zknOnZnBzba9hZP6bCPJLSReQ== dependencies: chokidar "^3.5.2" - debug "^3.2.7" + debug "^4" ignore-by-default "^1.0.1" minimatch "^3.1.2" pstree.remy "^1.1.8" - semver "^5.7.1" - simple-update-notifier "^1.0.7" + semver "^7.5.3" + simple-update-notifier "^2.0.0" supports-color "^5.5.0" touch "^3.1.0" undefsafe "^2.0.5" @@ -3644,10 +3805,10 @@ object-assign@^4, object-assign@^4.1.1: resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== -object-inspect@^1.12.3, object-inspect@^1.9.0: - version "1.12.3" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" - integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== +object-inspect@^1.13.1, object-inspect@^1.9.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== object-keys@^1.1.1: version "1.1.1" @@ -3655,60 +3816,70 @@ object-keys@^1.1.1: integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== object.assign@^4.1.4: - version "4.1.4" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" - integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== + version "4.1.5" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" + integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" + call-bind "^1.0.5" + define-properties "^1.2.1" has-symbols "^1.0.3" object-keys "^1.1.1" object.entries@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.6.tgz#9737d0e5b8291edd340a3e3264bb8a3b00d5fa23" - integrity sha512-leTPzo4Zvg3pmbQ3rDK69Rl8GQvIqMWubrkxONG9/ojtFE2rD9fjMKfSI5BxW3osRH1m6VdzmqK8oAY9aT4x5w== + version "1.1.7" + resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.7.tgz#2b47760e2a2e3a752f39dd874655c61a7f03c131" + integrity sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA== dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - -object.fromentries@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.6.tgz#cdb04da08c539cffa912dcd368b886e0904bfa73" - integrity sha512-VciD13dswC4j1Xt5394WR4MzmAQmlgN72phd/riNp9vtD7tp4QQWJ0R4wvclXcafgcYK8veHRed2W6XeGBvcfg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - -object.getownpropertydescriptors@^2.0.3: - version "2.1.6" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.6.tgz#5e5c384dd209fa4efffead39e3a0512770ccc312" - integrity sha512-lq+61g26E/BgHv0ZTFgRvi7NMEPuAxLkFU7rukXjc/AlwH4Am5xXVnIXy3un1bg/JPbXHrixRkK1itUzzPiIjQ== - dependencies: - array.prototype.reduce "^1.0.5" call-bind "^1.0.2" define-properties "^1.2.0" - es-abstract "^1.21.2" - safe-array-concat "^1.0.0" + es-abstract "^1.22.1" -object.hasown@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.2.tgz#f919e21fad4eb38a57bc6345b3afd496515c3f92" - integrity sha512-B5UIT3J1W+WuWIU55h0mjlwaqxiE5vYENJXIXZ4VFe05pNYrkKuK0U/6aFcb0pKywYJh7IhfoqUfKVmrJJHZHw== - dependencies: - define-properties "^1.1.4" - es-abstract "^1.20.4" - -object.values@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.6.tgz#4abbaa71eba47d63589d402856f908243eea9b1d" - integrity sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw== +object.fromentries@^2.0.6, object.fromentries@^2.0.7: + version "2.0.7" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.7.tgz#71e95f441e9a0ea6baf682ecaaf37fa2a8d7e616" + integrity sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +object.getownpropertydescriptors@^2.0.3: + version "2.1.7" + resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.7.tgz#7a466a356cd7da4ba8b9e94ff6d35c3eeab5d56a" + integrity sha512-PrJz0C2xJ58FNn11XV2lr4Jt5Gzl94qpy9Lu0JlfEj14z88sqbSBJCBEzdlNUCzY2gburhbrwOZ5BHCmuNUy0g== + dependencies: + array.prototype.reduce "^1.0.6" + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + safe-array-concat "^1.0.0" + +object.groupby@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/object.groupby/-/object.groupby-1.0.1.tgz#d41d9f3c8d6c778d9cbac86b4ee9f5af103152ee" + integrity sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + +object.hasown@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.3.tgz#6a5f2897bb4d3668b8e79364f98ccf971bda55ae" + integrity sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA== + dependencies: + define-properties "^1.2.0" + es-abstract "^1.22.1" + +object.values@^1.1.6, object.values@^1.1.7: + version "1.1.7" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.7.tgz#617ed13272e7e1071b43973aa1655d9291b8442a" + integrity sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" on-finished@2.4.1: version "2.4.1" @@ -3737,9 +3908,9 @@ opener@^1.5.2: integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A== openpgp@^5.5.0: - version "5.9.0" - resolved "https://registry.yarnpkg.com/openpgp/-/openpgp-5.9.0.tgz#f7ebe7b1e228aebc494835509ec9239853faed61" - integrity sha512-wEI6TAinCAq8ZLZA4oZ3ZtJ2BhhHj+CiPCd8TzE7zCicr0V8tvG5UF76OtddLLOJcK63w3Aj3KiRd+VLMScirQ== + version "5.11.0" + resolved "https://registry.yarnpkg.com/openpgp/-/openpgp-5.11.0.tgz#cec5b285d188148f7b5201b9aceb53850cc286a2" + integrity sha512-hytHsxIPtRhuh6uAmoBUThHSwHSX3imLu7x4453T+xkVqIw49rl22MRD4KQIAQdCDoVdouejzYgcuLmMA/2OAA== dependencies: asn1.js "^5.0.0" @@ -3880,7 +4051,7 @@ pify@^4.0.1: resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== -pirates@^4.0.5: +pirates@^4.0.6: version "4.0.6" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== @@ -3917,19 +4088,19 @@ postcss-modules-extract-imports@^3.0.0: resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz#cda1f047c0ae80c97dbe28c3e76a43b88025741d" integrity sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw== -postcss-modules-local-by-default@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.3.tgz#b08eb4f083050708998ba2c6061b50c2870ca524" - integrity sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA== +postcss-modules-local-by-default@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.4.tgz#7cbed92abd312b94aaea85b68226d3dec39a14e6" + integrity sha512-L4QzMnOdVwRm1Qb8m4x8jsZzKAaPAgrUF1r/hjDR2Xj7R+8Zsf97jAlSQzWtKx5YNiNGN8QxmPFIc/sh+RQl+Q== dependencies: icss-utils "^5.0.0" postcss-selector-parser "^6.0.2" postcss-value-parser "^4.1.0" -postcss-modules-scope@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.0.0.tgz#9ef3151456d3bbfa120ca44898dfca6f2fa01f06" - integrity sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg== +postcss-modules-scope@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-3.1.1.tgz#32cfab55e84887c079a19bbb215e721d683ef134" + integrity sha512-uZgqzdTleelWjzJY+Fhti6F3C9iF1JR/dODLs/JDefozYcKTBCdD8BIl6nNPbTbcLnGrk56hzwZC2DaGNvYjzA== dependencies: postcss-selector-parser "^6.0.4" @@ -3941,9 +4112,9 @@ postcss-modules-values@^4.0.0: icss-utils "^5.0.0" postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: - version "6.0.13" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz#d05d8d76b1e8e173257ef9d60b706a8e5e99bf1b" - integrity sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ== + version "6.0.15" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz#11cc2b21eebc0b99ea374ffb9887174855a01535" + integrity sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw== dependencies: cssesc "^3.0.0" util-deprecate "^1.0.2" @@ -3953,12 +4124,12 @@ postcss-value-parser@^4.1.0, postcss-value-parser@^4.2.0: resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@^8.4.21: - version "8.4.24" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.24.tgz#f714dba9b2284be3cc07dbd2fc57ee4dc972d2df" - integrity sha512-M0RzbcI0sO/XJNucsGjvWU9ERWxb/ytp1w6dKtxTKgixdtQDq4rmx/g8W1hnaheq9jgwL/oyEdH5Bc4WwJKMqg== +postcss@^8.4.33: + version "8.4.33" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.33.tgz#1378e859c9f69bf6f638b990a0212f43e2aaa742" + integrity sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg== dependencies: - nanoid "^3.3.6" + nanoid "^3.3.7" picocolors "^1.0.0" source-map-js "^1.0.2" @@ -3991,6 +4162,11 @@ proxy-addr@~2.0.7: forwarded "0.2.0" ipaddr.js "1.9.1" +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + pstree.remy@^1.1.8: version "1.1.8" resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" @@ -4108,16 +4284,16 @@ pump@^3.0.0: once "^1.3.1" punycode@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f" - integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== -pvtsutils@^1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/pvtsutils/-/pvtsutils-1.3.2.tgz#9f8570d132cdd3c27ab7d51a2799239bf8d8d5de" - integrity sha512-+Ipe2iNUyrZz+8K/2IOo+kKikdtfhRKzNpQbruF2URmqPtoqAs8g3xS7TJvFF2GcPXjh7DkqMnpVveRFq4PgEQ== +pvtsutils@^1.3.2, pvtsutils@^1.3.5: + version "1.3.5" + resolved "https://registry.yarnpkg.com/pvtsutils/-/pvtsutils-1.3.5.tgz#b8705b437b7b134cd7fd858f025a23456f1ce910" + integrity sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA== dependencies: - tslib "^2.4.0" + tslib "^2.6.1" pvutils@^1.1.3: version "1.1.3" @@ -4211,19 +4387,31 @@ rechoir@^0.8.0: dependencies: resolve "^1.20.0" -regenerator-runtime@^0.13.11: - version "0.13.11" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" - integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== - -regexp.prototype.flags@^1.4.3: - version "1.5.0" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.0.tgz#fe7ce25e7e4cca8db37b6634c8a2c7009199b9cb" - integrity sha512-0SutC3pNudRKgquxGoRGIz946MZVHqbNfPjBdxeOhBrdgDKlRoXmYLQN9xRbrR09ZXWeGAdPuif7egofn6v5LA== +reflect.getprototypeof@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz#aaccbf41aca3821b87bb71d9dcbc7ad0ba50a3f3" + integrity sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw== dependencies: call-bind "^1.0.2" define-properties "^1.2.0" - functions-have-names "^1.2.3" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + globalthis "^1.0.3" + which-builtin-type "^1.1.3" + +regenerator-runtime@^0.14.0: + version "0.14.1" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" + integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== + +regexp.prototype.flags@^1.5.0, regexp.prototype.flags@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz#90ce989138db209f81492edd734183ce99f9677e" + integrity sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + set-function-name "^2.0.0" regexpp@^3.0.0: version "3.2.0" @@ -4267,21 +4455,21 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve@^1.15.1, resolve@^1.20.0, resolve@^1.22.1: - version "1.22.2" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" - integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== +resolve@^1.15.1, resolve@^1.20.0, resolve@^1.22.1, resolve@^1.22.4: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== dependencies: - is-core-module "^2.11.0" + is-core-module "^2.13.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" resolve@^2.0.0-next.4: - version "2.0.0-next.4" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" - integrity sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ== + version "2.0.0-next.5" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.5.tgz#6b0ec3107e671e52b68cd068ef327173b90dc03c" + integrity sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA== dependencies: - is-core-module "^2.9.0" + is-core-module "^2.13.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" @@ -4298,9 +4486,9 @@ reusify@^1.0.4: integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== rfc4648@^1.5.2: - version "1.5.2" - resolved "https://registry.yarnpkg.com/rfc4648/-/rfc4648-1.5.2.tgz#cf5dac417dd83e7f4debf52e3797a723c1373383" - integrity sha512-tLOizhR6YGovrEBLatX1sdcuhoSCXddw3mqNVAcKxGJ+J0hFeJ+SjeWCv5UPA/WU3YzWPPuCVYgXBKZUPGpKtg== + version "1.5.3" + resolved "https://registry.yarnpkg.com/rfc4648/-/rfc4648-1.5.3.tgz#e62b81736c10361ca614efe618a566e93d0b41c0" + integrity sha512-MjOWxM065+WswwnmNONOT+bD1nXzY9Km6u3kzvnx8F8/HXGZdz3T6e6vZJ8Q/RIMUSp/nxqjH3GwvJDy8ijeQQ== rimraf@^3.0.2: version "3.0.2" @@ -4324,13 +4512,13 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" -safe-array-concat@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.0.0.tgz#2064223cba3c08d2ee05148eedbc563cd6d84060" - integrity sha512-9dVEFruWIsnie89yym+xWTAYASdpw3CJV7Li/6zBewGf9z2i1j31rP6jnY0pHEO4QZh6N0K11bFjWmdR8UGdPQ== +safe-array-concat@^1.0.0, safe-array-concat@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.0.tgz#8d0cae9cb806d6d1c06e08ab13d847293ebe0692" + integrity sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg== dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.2.0" + call-bind "^1.0.5" + get-intrinsic "^1.2.2" has-symbols "^1.0.3" isarray "^2.0.5" @@ -4340,12 +4528,12 @@ safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, s integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== safe-regex-test@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" - integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== + version "1.0.2" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.2.tgz#3ba32bdb3ea35f940ee87e5087c60ee786c3f6c5" + integrity sha512-83S9w6eFq12BBIJYvjMux6/dkirb8+4zJRA9cxNBVb7Wq5fJBW+Xze48WqR8pxua7bDuAaaAxtVVd4Idjp1dBQ== dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.3" + call-bind "^1.0.5" + get-intrinsic "^1.2.2" is-regex "^1.1.4" safe-stable-stringify@^2.3.1: @@ -4384,16 +4572,16 @@ saslmechanisms@^0.1.1: integrity sha512-pVlvK5ysevz8MzybRnDIa2YMxn0OJ7b9lDiWhMoaKPoJ7YkAg/7YtNjUgaYzElkwHxsw8dBMhaEn7UP6zxEwPg== sass-loader@^13.3.2: - version "13.3.2" - resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-13.3.2.tgz#460022de27aec772480f03de17f5ba88fa7e18c6" - integrity sha512-CQbKl57kdEv+KDLquhC+gE3pXt74LEAzm+tzywcA0/aHZuub8wTErbjAoNI57rPUWRYRNC5WUnNl8eGJNbDdwg== + version "13.3.3" + resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-13.3.3.tgz#60df5e858788cffb1a3215e5b92e9cba61e7e133" + integrity sha512-mt5YN2F1MOZr3d/wBRcZxeFgwgkH44wVc2zohO2YF6JiOMkiXe4BYRZpSu2sO1g71mo/j16txzUhsKZlqjVGzA== dependencies: neo-async "^2.6.2" sass@^1.67.0: - version "1.67.0" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.67.0.tgz#fed84d74b9cd708db603b1380d6dc1f71bb24f6f" - integrity sha512-SVrO9ZeX/QQyEGtuZYCVxoeAL5vGlYjJ9p4i4HFuekWl8y/LtJ7tJc10Z+ck1c8xOuoBm2MYzcLfTAffD0pl/A== + version "1.70.0" + resolved "https://registry.yarnpkg.com/sass/-/sass-1.70.0.tgz#761197419d97b5358cb25f9dd38c176a8a270a75" + integrity sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ== dependencies: chokidar ">=3.0.0 <4.0.0" immutable "^4.0.0" @@ -4418,28 +4606,23 @@ schema-utils@^4.0.0: ajv-formats "^2.1.1" ajv-keywords "^5.1.0" -semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== +semver@^5.6.0, semver@^5.7.0: + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== +semver@^6.3.1: + version "6.3.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.0.0, semver@^7.3.8: - version "7.5.3" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.3.tgz#161ce8c2c6b4b3bdca6caadc9fa3317a4c4fe88e" - integrity sha512-QBlUtyVk/5EeHbi7X0fw6liDZc7BBmEaSYn01fMU1OUYbf6GPsbTtd8WmnqbI20SeycoHSeiybkE/q1Q+qlThQ== +semver@^7.0.0, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4: + version "7.5.4" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== dependencies: lru-cache "^6.0.0" -semver@~7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" - integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== - send@0.18.0: version "0.18.0" resolved "https://registry.yarnpkg.com/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" @@ -4467,9 +4650,9 @@ serialize-javascript@6.0.0: randombytes "^2.1.0" serialize-javascript@^6.0.0, serialize-javascript@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c" - integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w== + version "6.0.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== dependencies: randombytes "^2.1.0" @@ -4488,6 +4671,26 @@ set-blocking@^2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== +set-function-length@^1.1.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.0.tgz#2f81dc6c16c7059bda5ab7c82c11f03a515ed8e1" + integrity sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w== + dependencies: + define-data-property "^1.1.1" + function-bind "^1.1.2" + get-intrinsic "^1.2.2" + gopd "^1.0.1" + has-property-descriptors "^1.0.1" + +set-function-name@^2.0.0, set-function-name@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.1.tgz#12ce38b7954310b9f61faa12701620a0c882793a" + integrity sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA== + dependencies: + define-data-property "^1.0.1" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.0" + setprototypeof@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" @@ -4536,21 +4739,21 @@ simple-swizzle@^0.2.2: dependencies: is-arrayish "^0.3.1" -simple-update-notifier@^1.0.7: - version "1.1.0" - resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz#67694c121de354af592b347cdba798463ed49c82" - integrity sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg== +simple-update-notifier@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz#d70b92bdab7d6d90dfd73931195a30b6e3d7cebb" + integrity sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w== dependencies: - semver "~7.0.0" + semver "^7.5.3" -sirv@^1.0.7: - version "1.0.19" - resolved "https://registry.yarnpkg.com/sirv/-/sirv-1.0.19.tgz#1d73979b38c7fe91fcba49c85280daa9c2363b49" - integrity sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ== +sirv@^2.0.3: + version "2.0.4" + resolved "https://registry.yarnpkg.com/sirv/-/sirv-2.0.4.tgz#5dd9a725c578e34e449f332703eb2a74e46a29b0" + integrity sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ== dependencies: - "@polka/url" "^1.0.0-next.20" - mrmime "^1.0.0" - totalist "^1.0.0" + "@polka/url" "^1.0.0-next.24" + mrmime "^2.0.0" + totalist "^3.0.0" slash@^2.0.0: version "2.0.0" @@ -4621,9 +4824,9 @@ statuses@2.0.1: integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== string-replace-middleware@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-replace-middleware/-/string-replace-middleware-1.0.2.tgz#aa6a2f55322515d17e32b2f76327b777fb5c89f2" - integrity sha512-AZBPVPJ0A3DhBleAGjA8qAoL0hEKxb7C5QUYr9VnzCO+COEqI6tyGDe2a+A3odJc5uRDT/CPKdbH8oe5laCv/g== + version "1.1.0" + resolved "https://registry.yarnpkg.com/string-replace-middleware/-/string-replace-middleware-1.1.0.tgz#b6d2dede76a48a5e782231454ade1c40cbaa56ca" + integrity sha512-baLex/R7Hit/JBeFcEQa0deE74NWdqauvPbMTRBlJUNBAiEf4tsAFuD6PTr7Tt+QwTi5egPFaTW2ZWp8x/mxHQ== dependencies: escape-string-regexp "^4.0.0" hijackresponse "^4.0.0" @@ -4647,45 +4850,46 @@ string-width@^4.1.0, string-width@^4.2.0: strip-ansi "^6.0.1" string.prototype.matchall@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz#3bf85722021816dcd1bf38bb714915887ca79fd3" - integrity sha512-6zOCOcJ+RJAQshcTvXPHoxoQGONa3e/Lqx90wUA+wEzX78sg5Bo+1tQo4N0pohS0erG9qtCqJDjNCQBjeWVxyg== + version "4.0.10" + resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.10.tgz#a1553eb532221d4180c51581d6072cd65d1ee100" + integrity sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" - get-intrinsic "^1.1.3" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" has-symbols "^1.0.3" - internal-slot "^1.0.3" - regexp.prototype.flags "^1.4.3" + internal-slot "^1.0.5" + regexp.prototype.flags "^1.5.0" + set-function-name "^2.0.0" side-channel "^1.0.4" -string.prototype.trim@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533" - integrity sha512-p6TmeT1T3411M8Cgg9wBTMRtY2q9+PNy9EV1i2lIXUN/btt763oIfxwN3RR8VU6wHX8j/1CFy0L+YuThm6bgOg== +string.prototype.trim@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz#f9ac6f8af4bd55ddfa8895e6aea92a96395393bd" + integrity sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" -string.prototype.trimend@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" - integrity sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ== +string.prototype.trimend@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz#1bb3afc5008661d73e2dc015cd4853732d6c471e" + integrity sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" -string.prototype.trimstart@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" - integrity sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA== +string.prototype.trimstart@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz#d4cdb44b83a4737ffbac2d406e405d43d0184298" + integrity sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg== dependencies: call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.20.4" + define-properties "^1.2.0" + es-abstract "^1.22.1" string_decoder@^1.1.1: version "1.3.0" @@ -4713,15 +4917,15 @@ strip-bom@^3.0.0: resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== -strip-json-comments@3.1.1, strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: +strip-json-comments@3.1.1, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== style-loader@^3.3.1: - version "3.3.3" - resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.3.tgz#bba8daac19930169c0c9c96706749a597ae3acff" - integrity sha512-53BiGLXAcll9maCYtZi2RCQZKa8NQQai5C4horqKyRmHj9H7QmcUyucrH+4KW/gBQbXM2AsB0axoEcFZPlfPcw== + version "3.3.4" + resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.4.tgz#f30f786c36db03a45cbd55b6a70d930c479090e7" + integrity sha512-0WqXzrsMTyb8yjZJHDqwmnwRJvhALK9LfRtRc6B4UTWe8AijYLZYZ9thuJTZc2VfQWINADW/j+LiJnfy2RoC1w== supports-color@8.1.1, supports-color@^8.0.0: version "8.1.1" @@ -4755,20 +4959,20 @@ tapable@^2.1.1, tapable@^2.2.0: integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== terser-webpack-plugin@^5.3.7: - version "5.3.9" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.9.tgz#832536999c51b46d468067f9e37662a3b96adfe1" - integrity sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA== + version "5.3.10" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz#904f4c9193c6fd2a03f693a2150c62a92f40d199" + integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w== dependencies: - "@jridgewell/trace-mapping" "^0.3.17" + "@jridgewell/trace-mapping" "^0.3.20" jest-worker "^27.4.5" schema-utils "^3.1.1" serialize-javascript "^6.0.1" - terser "^5.16.8" + terser "^5.26.0" -terser@^5.16.8: - version "5.18.2" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.18.2.tgz#ff3072a0faf21ffd38f99acc9a0ddf7b5f07b948" - integrity sha512-Ah19JS86ypbJzTzvUCX7KOsEIhDaRONungA4aYBjEP3JZRf4ocuDzTg4QWZnPn9DEMiMYGJPiSOy7aykoCc70w== +terser@^5.26.0: + version "5.27.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.27.0.tgz#70108689d9ab25fef61c4e93e808e9fd092bf20c" + integrity sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A== dependencies: "@jridgewell/source-map" "^0.3.3" acorn "^8.8.2" @@ -4807,10 +5011,10 @@ token-stream@1.0.0: resolved "https://registry.yarnpkg.com/token-stream/-/token-stream-1.0.0.tgz#cc200eab2613f4166d27ff9afc7ca56d49df6eb4" integrity sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg== -totalist@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" - integrity sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g== +totalist@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/totalist/-/totalist-3.0.1.tgz#ba3a3d600c915b1a97872348f79c127475f6acf8" + integrity sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ== touch@^3.1.0: version "3.1.0" @@ -4825,24 +5029,24 @@ tr46@~0.0.3: integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== triple-beam@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9" - integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw== + version "1.4.1" + resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.4.1.tgz#6fde70271dc6e5d73ca0c3b24e2d92afb7441984" + integrity sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg== -tsconfig-paths@^3.14.1: - version "3.14.2" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz#6e32f1f79412decd261f92d633a9dc1cfa99f088" - integrity sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g== +tsconfig-paths@^3.15.0: + version "3.15.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" + integrity sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg== dependencies: "@types/json5" "^0.0.29" json5 "^1.0.2" minimist "^1.2.6" strip-bom "^3.0.0" -tslib@^2.0.0, tslib@^2.4.0, tslib@^2.5.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.0.tgz#b295854684dbda164e181d259a22cd779dcd7bc3" - integrity sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA== +tslib@^2.0.0, tslib@^2.4.0, tslib@^2.6.1, tslib@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== type-check@^0.4.0, type-check@~0.4.0: version "0.4.0" @@ -4851,7 +5055,7 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" -type-detect@^4.0.0, type-detect@^4.0.5: +type-detect@^4.0.0, type-detect@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== @@ -4874,6 +5078,36 @@ type-is@~1.6.18: media-typer "0.3.0" mime-types "~2.1.24" +typed-array-buffer@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz#18de3e7ed7974b0a729d3feecb94338d1472cd60" + integrity sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + is-typed-array "^1.1.10" + +typed-array-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz#d787a24a995711611fb2b87a4052799517b230d0" + integrity sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + +typed-array-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz#cbbe89b51fdef9cd6aaf07ad4707340abbc4ea0b" + integrity sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + typed-array-length@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" @@ -4903,6 +5137,11 @@ undefsafe@^2.0.5: resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" @@ -4913,10 +5152,10 @@ unpipe@1.0.0, unpipe@~1.0.0: resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== -update-browserslist-db@^1.0.11: - version "1.0.11" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz#9a2a641ad2907ae7b3616506f4b977851db5b940" - integrity sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA== +update-browserslist-db@^1.0.13: + version "1.0.13" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" + integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== dependencies: escalade "^3.1.1" picocolors "^1.0.0" @@ -4951,9 +5190,9 @@ valid-url@^1.0.9: integrity sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA== validator@^13.9.0: - version "13.9.0" - resolved "https://registry.yarnpkg.com/validator/-/validator-13.9.0.tgz#33e7b85b604f3bbce9bb1a05d5c3e22e1c2ff855" - integrity sha512-B+dGG8U3fdtM0/aNK4/X8CXq/EcxU2WPrPEkJGslb47qyHsxmbggTWK0yEA4qnYVNF+nxNlN88o14hIcPmSIEA== + version "13.11.0" + resolved "https://registry.yarnpkg.com/validator/-/validator-13.11.0.tgz#23ab3fd59290c61248364eabf4067f04955fbb1b" + integrity sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ== vary@^1, vary@~1.1.2: version "1.1.2" @@ -4978,16 +5217,16 @@ watchpack@^2.4.0: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" -webcrypto-core@^1.7.7: - version "1.7.7" - resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.7.7.tgz#06f24b3498463e570fed64d7cab149e5437b162c" - integrity sha512-7FjigXNsBfopEj+5DV2nhNpfic2vumtjjgPmeDKk45z+MJwXKKfhPB7118Pfzrmh4jqOMST6Ch37iPAHoImg5g== +webcrypto-core@^1.7.8: + version "1.7.8" + resolved "https://registry.yarnpkg.com/webcrypto-core/-/webcrypto-core-1.7.8.tgz#056918036e846c72cfebbb04052e283f57f1114a" + integrity sha512-eBR98r9nQXTqXt/yDRtInszPMjTaSAMJAFDg2AHsgrnczawT1asx9YNBX6k5p+MekbPF4+s/UJJrr88zsTqkSg== dependencies: - "@peculiar/asn1-schema" "^2.3.6" + "@peculiar/asn1-schema" "^2.3.8" "@peculiar/json-schema" "^1.1.12" asn1js "^3.0.1" - pvtsutils "^1.3.2" - tslib "^2.4.0" + pvtsutils "^1.3.5" + tslib "^2.6.2" webidl-conversions@^3.0.0: version "3.0.1" @@ -4995,19 +5234,22 @@ webidl-conversions@^3.0.0: integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== webpack-bundle-analyzer@^4.7.0: - version "4.9.0" - resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.9.0.tgz#fc093c4ab174fd3dcbd1c30b763f56d10141209d" - integrity sha512-+bXGmO1LyiNx0i9enBu3H8mv42sj/BJWhZNFwjz92tVnBa9J3JMGo2an2IXlEleoDOPn/Hofl5hr/xCpObUDtw== + version "4.10.1" + resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.10.1.tgz#84b7473b630a7b8c21c741f81d8fe4593208b454" + integrity sha512-s3P7pgexgT/HTUSYgxJyn28A+99mmLq4HsJepMPzu0R8ImJc52QNqaFYW1Z2z2uIb1/J3eYgaAWVpaC+v/1aAQ== dependencies: "@discoveryjs/json-ext" "0.5.7" acorn "^8.0.4" acorn-walk "^8.0.0" - chalk "^4.1.0" commander "^7.2.0" + debounce "^1.2.1" + escape-string-regexp "^4.0.0" gzip-size "^6.0.0" - lodash "^4.17.20" + html-escaper "^2.0.2" + is-plain-object "^5.0.0" opener "^1.5.2" - sirv "^1.0.7" + picocolors "^1.0.0" + sirv "^2.0.3" ws "^7.3.1" webpack-cli@^5.0.0: @@ -5030,11 +5272,12 @@ webpack-cli@^5.0.0: webpack-merge "^5.7.3" webpack-merge@^5.7.3: - version "5.9.0" - resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.9.0.tgz#dc160a1c4cf512ceca515cc231669e9ddb133826" - integrity sha512-6NbRQw4+Sy50vYNTw7EyOn41OZItPiXB8GNv3INSoe3PSFaHJEz3SHTrYVaRm2LilNGnFUzh0FAwqPEmU/CwDg== + version "5.10.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177" + integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA== dependencies: clone-deep "^4.0.1" + flat "^5.0.2" wildcard "^2.0.0" webpack-sources@^3.2.3: @@ -5043,9 +5286,9 @@ webpack-sources@^3.2.3: integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== webpack@^5.88.2: - version "5.88.2" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.88.2.tgz#f62b4b842f1c6ff580f3fcb2ed4f0b579f4c210e" - integrity sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ== + version "5.89.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.89.0.tgz#56b8bf9a34356e93a6625770006490bf3a7f32dc" + integrity sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw== dependencies: "@types/eslint-scope" "^3.7.3" "@types/estree" "^1.0.0" @@ -5091,22 +5334,49 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" +which-builtin-type@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/which-builtin-type/-/which-builtin-type-1.1.3.tgz#b1b8443707cc58b6e9bf98d32110ff0c2cbd029b" + integrity sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw== + dependencies: + function.prototype.name "^1.1.5" + has-tostringtag "^1.0.0" + is-async-function "^2.0.0" + is-date-object "^1.0.5" + is-finalizationregistry "^1.0.2" + is-generator-function "^1.0.10" + is-regex "^1.1.4" + is-weakref "^1.0.2" + isarray "^2.0.5" + which-boxed-primitive "^1.0.2" + which-collection "^1.0.1" + which-typed-array "^1.1.9" + +which-collection@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906" + integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== + dependencies: + is-map "^2.0.1" + is-set "^2.0.1" + is-weakmap "^2.0.1" + is-weakset "^2.0.1" + which-module@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.1.tgz#776b1fe35d90aebe99e8ac15eb24093389a4a409" integrity sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ== -which-typed-array@^1.1.9: - version "1.1.9" - resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" - integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== +which-typed-array@^1.1.11, which-typed-array@^1.1.13, which-typed-array@^1.1.9: + version "1.1.13" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.13.tgz#870cd5be06ddb616f504e7b039c4c24898184d36" + integrity sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow== dependencies: available-typed-arrays "^1.0.5" - call-bind "^1.0.2" + call-bind "^1.0.4" for-each "^0.3.3" gopd "^1.0.1" has-tostringtag "^1.0.0" - is-typed-array "^1.1.10" which@^2.0.1: version "2.0.2" @@ -5121,20 +5391,20 @@ wildcard@^2.0.0: integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== winston-transport@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.5.0.tgz#6e7b0dd04d393171ed5e4e4905db265f7ab384fa" - integrity sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q== + version "4.6.0" + resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.6.0.tgz#f1c1a665ad1b366df72199e27892721832a19e1b" + integrity sha512-wbBA9PbPAHxKiygo7ub7BYRiKxms0tpfU2ljtWzb3SjRjv5yl6Ozuy/TkXf00HTAt+Uylo3gSkNwzc4ME0wiIg== dependencies: logform "^2.3.2" readable-stream "^3.6.0" triple-beam "^1.3.0" winston@^3.8.2: - version "3.9.0" - resolved "https://registry.yarnpkg.com/winston/-/winston-3.9.0.tgz#2bbdeb8167a75fac6d9a0c6d002890cd908016c2" - integrity sha512-jW51iW/X95BCW6MMtZWr2jKQBP4hV5bIDq9QrIjfDk6Q9QuxvTKEAlpUNAzP+HYHFFCeENhph16s0zEunu4uuQ== + version "3.11.0" + resolved "https://registry.yarnpkg.com/winston/-/winston-3.11.0.tgz#2d50b0a695a2758bb1c95279f0a88e858163ed91" + integrity sha512-L3yR6/MzZAOl0DsysUXHVjOwv8mKZ71TrA/41EIduGpOOV5LQVodqN+QdQ6BS6PJ/RdIshZhq84P/fStEZkk7g== dependencies: - "@colors/colors" "1.5.0" + "@colors/colors" "^1.6.0" "@dabh/diagnostics" "^2.0.2" async "^3.2.3" is-stream "^2.0.0" @@ -5199,9 +5469,9 @@ ws@^7.3.1: integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== ws@^8.4.0: - version "8.13.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" - integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== + version "8.16.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4" + integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ== xdg-basedir@^4.0.0: version "4.0.0" From ee60af395aa62aa862a59d6c5058559a948d1ae1 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Tue, 23 Jan 2024 19:26:28 +0100 Subject: [PATCH 108/144] chore: bump deps --- package.json | 2 +- yarn.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 79c2e80..8b2d06a 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "libravatar": "^3.0.0", "nanoid": "^5.0.1", "openpgp": "^5.5.0", - "pug": "^3.0.0", + "pug": "^3.0.2", "qrcode": "^1.4.4", "string-replace-middleware": "^1.0.2", "winston": "^3.8.2" diff --git a/yarn.lock b/yarn.lock index 82895de..f84820c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4261,7 +4261,7 @@ pug-walk@^2.0.0: resolved "https://registry.yarnpkg.com/pug-walk/-/pug-walk-2.0.0.tgz#417aabc29232bb4499b5b5069a2b2d2a24d5f5fe" integrity sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ== -pug@^3.0.0: +pug@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/pug/-/pug-3.0.2.tgz#f35c7107343454e43bc27ae0ff76c731b78ea535" integrity sha512-bp0I/hiK1D1vChHh6EfDxtndHji55XP/ZJKwsRqrz6lRia6ZC2OZbdAymlxdVFwd1L70ebrVJw4/eZ79skrIaw== From e0d47e5247cb0f9090fb0a26b1deac121db21669 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Tue, 23 Jan 2024 19:34:17 +0100 Subject: [PATCH 109/144] feat: add proxy routes --- src/api/v3/proxy_get.js | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/api/v3/proxy_get.js b/src/api/v3/proxy_get.js index a766735..5addc17 100644 --- a/src/api/v3/proxy_get.js +++ b/src/api/v3/proxy_get.js @@ -92,6 +92,40 @@ router.get( } ) +// ASPE route +router.get('/aspe', query('aspeUri').isString(), (req, res) => { + const errors = validationResult(req) + if (!errors.isEmpty()) { + return res.status(400).json({ errors: errors.array() }) + } + + fetcher.aspe + .fn(req.query, opts) + .then((data) => { + return res.status(200).send(data) + }) + .catch((err) => { + return res.status(400).json({ errors: err.message ? err.message : err }) + }) +}) + +// OpenPGP route +router.get('/openpgp', query('url').isFQDN(), query('protocol').isString(), (req, res) => { + const errors = validationResult(req) + if (!errors.isEmpty()) { + return res.status(400).json({ errors: errors.array() }) + } + + fetcher.openpgp + .fn(req.query, opts) + .then((data) => { + return res.status(200).send(data) + }) + .catch((err) => { + return res.status(400).json({ errors: err.message ? err.message : err }) + }) +}) + // DNS route router.get('/dns', query('domain').isFQDN(), (req, res) => { const errors = validationResult(req) From ee55c50a1a636bb59b3518f34edfbacc90d8c4cb Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Tue, 23 Jan 2024 19:47:33 +0100 Subject: [PATCH 110/144] fix: URL query validation --- src/api/v3/proxy_get.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/v3/proxy_get.js b/src/api/v3/proxy_get.js index 5addc17..69e6e1b 100644 --- a/src/api/v3/proxy_get.js +++ b/src/api/v3/proxy_get.js @@ -110,7 +110,7 @@ router.get('/aspe', query('aspeUri').isString(), (req, res) => { }) // OpenPGP route -router.get('/openpgp', query('url').isFQDN(), query('protocol').isString(), (req, res) => { +router.get('/openpgp', query('url').isURL(), query('protocol').isString(), (req, res) => { const errors = validationResult(req) if (!errors.isEmpty()) { return res.status(400).json({ errors: errors.array() }) From a2857d1a3e3bae8aec867f18de19bc8b7efa67fc Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Tue, 23 Jan 2024 20:11:31 +0100 Subject: [PATCH 111/144] feat: update footer year --- views/partials/footer.pug | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/partials/footer.pug b/views/partials/footer.pug index d7b4241..47b3ab8 100644 --- a/views/partials/footer.pug +++ b/views/partials/footer.pug @@ -30,5 +30,5 @@ footer | -dev br - | © 2023 Keyoxide project contributors + | © 2024 Keyoxide project contributors \ No newline at end of file From 9be8cab4357014d3b3bd17a4a38e4aa26fb22748 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Tue, 23 Jan 2024 20:16:48 +0100 Subject: [PATCH 112/144] feat: support openpgp4fpr in URL --- src/server/index.js | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/server/index.js b/src/server/index.js index 3edd263..cb453f4 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -117,6 +117,7 @@ const generateAutoProfile = async (id) => { let result const aspeRe = /aspe:(.*):(.*)/ + const openpgpRe = /openpgp4fpr:(.*)/ if (aspeRe.test(id)) { result = await generateAspeProfile(id) @@ -126,6 +127,15 @@ const generateAutoProfile = async (id) => { } } + if (openpgpRe.test(id)) { + const match = id.match(openpgpRe) + result = await generateHKPProfile(match[1]) + + if (result && !('errors' in result)) { + return result + } + } + if (id.includes('@')) { result = await generateWKDProfile(id) From 607fee17f9871dc403d8728599336e10d6955bb6 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Wed, 24 Jan 2024 19:29:58 +0100 Subject: [PATCH 113/144] fix: remove obsolete config --- docker-compose.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index 4cc785f..cbfdc21 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -12,9 +12,6 @@ services: - 3000:3000 environment: - DOMAIN= - # - KX_HIGHLIGHTS_1_NAME= - # - KX_HIGHLIGHTS_1_DESCRIPTION= - # - KX_HIGHLIGHTS_1_FINGERPRINT= ## The hostname to reach the doip_proxy container below # - PROXY_HOSTNAME= ## The onion URL to advertise in the HTTP response headers From 1607ca684bda39ab61a20cefa65a6494ad249482 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Wed, 24 Jan 2024 19:32:59 +0100 Subject: [PATCH 114/144] chore: release 4.2.6 --- CHANGELOG.md | 9 +++++++++ package.json | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 54d49b1..a59aa63 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [4.2.6] - 2024-01-24 +### Added +- Support openpgp4fpr: queries in URL +- Proxy routes for openpgp and aspe +### Changed +- Update doipjs to 1.2.8 +### Notes +- This version notably adds support for OpenPGP and ASPE claim verification. + ## [4.2.5] - 2023-10-09 ### Changed - Update doipjs to 1.2.7 diff --git a/package.json b/package.json index 8b2d06a..f5ee0e2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keyoxide-web", - "version": "4.2.5", + "version": "4.2.6", "description": "Verifying online identity with cryptography", "main": "./src/index.js", "type": "module", From 875df78dfed5cdd12ccc724330bc00d664c52f90 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Fri, 26 Jan 2024 16:14:55 +0100 Subject: [PATCH 115/144] chore: change issue template [SKIP CI] --- .gitea/issue_template.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.gitea/issue_template.md b/.gitea/issue_template.md index e47965f..90beb7e 100644 --- a/.gitea/issue_template.md +++ b/.gitea/issue_template.md @@ -1,7 +1,9 @@ - + +Issues about claim verification should be opened at: +https://codeberg.org/keyoxide/doipjs/issues - +New suggestions for services to claim should also be opened at: +https://codeberg.org/keyoxide/doipjs/issues +Feel free to remove this comment --> From 4873d8bc8cacabbae6e033f83aa2f43d6dfb3f16 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Fri, 26 Jan 2024 20:52:25 +0100 Subject: [PATCH 116/144] chore: improve issue templates [SKIP CI] --- .gitea/issue_template.md | 9 --------- .gitea/issue_template/bug.md | 16 ++++++++++++++++ .gitea/issue_template/claim_verification_bug.yml | 8 ++++++++ .gitea/issue_template/enhancement.md | 12 ++++++++++++ .gitea/issue_template/new_claim.yml | 8 ++++++++ 5 files changed, 44 insertions(+), 9 deletions(-) delete mode 100644 .gitea/issue_template.md create mode 100644 .gitea/issue_template/bug.md create mode 100644 .gitea/issue_template/claim_verification_bug.yml create mode 100644 .gitea/issue_template/enhancement.md create mode 100644 .gitea/issue_template/new_claim.yml diff --git a/.gitea/issue_template.md b/.gitea/issue_template.md deleted file mode 100644 index 90beb7e..0000000 --- a/.gitea/issue_template.md +++ /dev/null @@ -1,9 +0,0 @@ - diff --git a/.gitea/issue_template/bug.md b/.gitea/issue_template/bug.md new file mode 100644 index 0000000..993a6f5 --- /dev/null +++ b/.gitea/issue_template/bug.md @@ -0,0 +1,16 @@ +--- +name: 'Bug' +about: 'Report a bug' +title: '[BUG] ' +ref: 'dev' +labels: + - Status/Backlog + - Type/Bug +--- + +### What happened + + + +### Proposed solutions + diff --git a/.gitea/issue_template/claim_verification_bug.yml b/.gitea/issue_template/claim_verification_bug.yml new file mode 100644 index 0000000..2b8f05f --- /dev/null +++ b/.gitea/issue_template/claim_verification_bug.yml @@ -0,0 +1,8 @@ +name: Claim verification bug +about: Report a claim no longer verifying, or not verifying as it should +title: '' +body: + - type: markdown + attributes: + value: | + Please open this issue in the [doip-js repo](https://codeberg.org/keyoxide/doipjs/issues/new/choose) instead. \ No newline at end of file diff --git a/.gitea/issue_template/enhancement.md b/.gitea/issue_template/enhancement.md new file mode 100644 index 0000000..2d2553c --- /dev/null +++ b/.gitea/issue_template/enhancement.md @@ -0,0 +1,12 @@ +--- +name: 'Enhancement' +about: 'Suggest a new feature or improve an existing one' +title: '' +ref: 'dev' +labels: + - Status/Backlog + - Type/Enhancement +--- + +### Proposal + diff --git a/.gitea/issue_template/new_claim.yml b/.gitea/issue_template/new_claim.yml new file mode 100644 index 0000000..731ae3b --- /dev/null +++ b/.gitea/issue_template/new_claim.yml @@ -0,0 +1,8 @@ +name: New claim +about: Suggest a new service provider or website for identity verification +title: '' +body: + - type: markdown + attributes: + value: | + Please open this issue in the [doip-js repo](https://codeberg.org/keyoxide/doipjs/issues/new/choose) instead. \ No newline at end of file From 0061149aa5b3b2a06706f7eccc45d7687e93ab2f Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Fri, 26 Jan 2024 23:10:03 +0100 Subject: [PATCH 117/144] fix: change default label to "Needs Triage" --- .gitea/issue_template/bug.md | 2 +- .gitea/issue_template/enhancement.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitea/issue_template/bug.md b/.gitea/issue_template/bug.md index 993a6f5..bb067d2 100644 --- a/.gitea/issue_template/bug.md +++ b/.gitea/issue_template/bug.md @@ -4,7 +4,7 @@ about: 'Report a bug' title: '[BUG] ' ref: 'dev' labels: - - Status/Backlog + - 'Status/Needs Triage' - Type/Bug --- diff --git a/.gitea/issue_template/enhancement.md b/.gitea/issue_template/enhancement.md index 2d2553c..db15500 100644 --- a/.gitea/issue_template/enhancement.md +++ b/.gitea/issue_template/enhancement.md @@ -4,7 +4,7 @@ about: 'Suggest a new feature or improve an existing one' title: '' ref: 'dev' labels: - - Status/Backlog + - 'Status/Needs Triage' - Type/Enhancement --- From e55a0b2b770797d97a9fd87a0673e0bea0dfb27b Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Sat, 27 Jan 2024 21:31:32 +0100 Subject: [PATCH 118/144] feat: add version API endpoint --- package.json | 1 + src/api/v3/index.js | 11 +++++++++++ src/index.js | 5 +++++ yarn.lock | 5 +++++ 4 files changed, 22 insertions(+) diff --git a/package.json b/package.json index f5ee0e2..2d17a20 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ }, "devDependencies": { "@biomejs/biome": "1.2.2", + "@richex/git-last-commit": "^2.1.1", "@vercel/ncc": "^0.34.0", "chai": "^4.3.6", "copy-webpack-plugin": "^11.0.0", diff --git a/src/api/v3/index.js b/src/api/v3/index.js index 80a0d28..73da0c2 100644 --- a/src/api/v3/index.js +++ b/src/api/v3/index.js @@ -30,9 +30,20 @@ more information on this, and how to apply and follow the GNU AGPL, see { + // TODO Support responding with JSON object when requested + + const versionDetails = (app.get('git_branch') && app.get('git_hash')) + ? `+${app.get('git_branch')}.${app.get('git_hash').substring(0, 10)}` + : '' + + return res.status(200).contentType('text/plain').send(`${app.get('keyoxide_name')}/${app.get('keyoxide_version')}${versionDetails}`) +}) + if ((process.env.ENABLE_MAIN_MODULE ?? 'true') === 'true') { router.use('/profile', keyoxideProfileApiRouter) } diff --git a/src/index.js b/src/index.js index f583006..8c88dad 100644 --- a/src/index.js +++ b/src/index.js @@ -27,6 +27,7 @@ You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see . */ +import git from '@richex/git-last-commit' import express from 'express' import * as httpContext from 'express-http-context2' import { nanoid } from 'nanoid' @@ -45,13 +46,17 @@ dotenv.config() const app = express() const packageData = JSON.parse(readFileSync('./package.json')) +const commit = await git.getLastCommitAsync() app.set('env', process.env.NODE_ENV || 'production') app.engine('pug', pug.__express).set('view engine', 'pug') app.set('port', process.env.PORT || 3000) app.set('domain', process.env.DOMAIN) app.set('scheme', process.env.SCHEME || 'https') +app.set('keyoxide_name', 'keyoxide-web') app.set('keyoxide_version', packageData.version) +app.set('git_branch', commit.branch ?? process.env.CI_COMMIT_BRANCH ?? process.env.COMMIT_BRANCH) +app.set('git_hash', commit.hash ?? process.env.CI_COMMIT_SHA ?? process.env.COMMIT_SHA) app.set('onion_url', process.env.ONION_URL) // Middlewares diff --git a/yarn.lock b/yarn.lock index f84820c..59cd22c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -488,6 +488,11 @@ resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.24.tgz#58601079e11784d20f82d0585865bb42305c4df3" integrity sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ== +"@richex/git-last-commit@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@richex/git-last-commit/-/git-last-commit-2.1.1.tgz#b6527ae4223af8143f13e39d8f0cdaeaed7f9ba6" + integrity sha512-rNa0hCMyKTJ+qPwyN54FlwTUhDBNNghiQWt0ES27g11iYzyzpLcFBGLKYhDaEEEUPZ7aawXOCEYAwCg5JZ1FAQ== + "@sindresorhus/is@^4.0.0": version "4.6.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" From de2a938cccc8938961f1bad55b21e08780d0cf93 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Sat, 27 Jan 2024 21:44:11 +0100 Subject: [PATCH 119/144] fix: simplify version API endpoint --- package.json | 1 - src/api/v3/index.js | 7 +++++-- src/index.js | 6 ++---- yarn.lock | 5 ----- 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 2d17a20..f5ee0e2 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,6 @@ }, "devDependencies": { "@biomejs/biome": "1.2.2", - "@richex/git-last-commit": "^2.1.1", "@vercel/ncc": "^0.34.0", "chai": "^4.3.6", "copy-webpack-plugin": "^11.0.0", diff --git a/src/api/v3/index.js b/src/api/v3/index.js index 73da0c2..5d30412 100644 --- a/src/api/v3/index.js +++ b/src/api/v3/index.js @@ -37,8 +37,11 @@ const router = express.Router() router.get('/version', async (req, res) => { // TODO Support responding with JSON object when requested - const versionDetails = (app.get('git_branch') && app.get('git_hash')) - ? `+${app.get('git_branch')}.${app.get('git_hash').substring(0, 10)}` + let versionDetails = app.get('git_branch') + ? `+${app.get('git_branch')}` + : '' + versionDetails += (app.get('git_branch') && app.get('git_hash')) + ? `.${app.get('git_hash').substring(0, 10)}` : '' return res.status(200).contentType('text/plain').send(`${app.get('keyoxide_name')}/${app.get('keyoxide_version')}${versionDetails}`) diff --git a/src/index.js b/src/index.js index 8c88dad..e49a099 100644 --- a/src/index.js +++ b/src/index.js @@ -27,7 +27,6 @@ You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see . */ -import git from '@richex/git-last-commit' import express from 'express' import * as httpContext from 'express-http-context2' import { nanoid } from 'nanoid' @@ -46,7 +45,6 @@ dotenv.config() const app = express() const packageData = JSON.parse(readFileSync('./package.json')) -const commit = await git.getLastCommitAsync() app.set('env', process.env.NODE_ENV || 'production') app.engine('pug', pug.__express).set('view engine', 'pug') @@ -55,8 +53,8 @@ app.set('domain', process.env.DOMAIN) app.set('scheme', process.env.SCHEME || 'https') app.set('keyoxide_name', 'keyoxide-web') app.set('keyoxide_version', packageData.version) -app.set('git_branch', commit.branch ?? process.env.CI_COMMIT_BRANCH ?? process.env.COMMIT_BRANCH) -app.set('git_hash', commit.hash ?? process.env.CI_COMMIT_SHA ?? process.env.COMMIT_SHA) +app.set('git_branch', process.env.CI_COMMIT_BRANCH ?? process.env.COMMIT_BRANCH) +app.set('git_hash', process.env.CI_COMMIT_SHA ?? process.env.COMMIT_SHA) app.set('onion_url', process.env.ONION_URL) // Middlewares diff --git a/yarn.lock b/yarn.lock index 59cd22c..f84820c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -488,11 +488,6 @@ resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.24.tgz#58601079e11784d20f82d0585865bb42305c4df3" integrity sha512-2LuNTFBIO0m7kKIQvvPHN6UE63VjpmL9rnEEaOOaiSPbZK+zUOYIzBAWcED+3XYzhYsd/0mD57VdxAEqqV52CQ== -"@richex/git-last-commit@^2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@richex/git-last-commit/-/git-last-commit-2.1.1.tgz#b6527ae4223af8143f13e39d8f0cdaeaed7f9ba6" - integrity sha512-rNa0hCMyKTJ+qPwyN54FlwTUhDBNNghiQWt0ES27g11iYzyzpLcFBGLKYhDaEEEUPZ7aawXOCEYAwCg5JZ1FAQ== - "@sindresorhus/is@^4.0.0": version "4.6.0" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" From 1f3c47af2bd56eb5bc3eb0f387402005cadd1576 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Sat, 27 Jan 2024 21:48:51 +0100 Subject: [PATCH 120/144] fix: add necessary env args to CI build --- .woodpecker.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.woodpecker.yml b/.woodpecker.yml index e61dbc4..773ad03 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -18,6 +18,9 @@ steps: from_secret: codeberg_password repo: codeberg.org/keyoxide/keyoxide-web tags: latest + build_args_from_env: + - CI_COMMIT_SHA + - CI_COMMIT_BRANCH build-tag-container: when: @@ -32,6 +35,9 @@ steps: from_secret: codeberg_password repo: codeberg.org/keyoxide/keyoxide-web auto_tag: true + build_args_from_env: + - CI_COMMIT_SHA + - CI_COMMIT_BRANCH build-dev-container: when: @@ -45,4 +51,7 @@ steps: password: from_secret: codeberg_password repo: codeberg.org/keyoxide/keyoxide-web - tags: dev \ No newline at end of file + tags: dev + build_args_from_env: + - CI_COMMIT_SHA + - CI_COMMIT_BRANCH \ No newline at end of file From 7416912197a4d4a00321630068639416a1dbfd9d Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Sat, 27 Jan 2024 21:51:59 +0100 Subject: [PATCH 121/144] fix: add env args to Dockerfile --- Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Dockerfile b/Dockerfile index 71c311d..12775b1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,6 +11,9 @@ RUN yarn run build:static FROM node:20-alpine +ARG CI_COMMIT_SHA +ARG CI_COMMIT_BRANCH + WORKDIR /app COPY --from=builder /app/package.json /app/package.json COPY --from=builder /app/dist /app/dist From 759f7bd79ef4162fe47d64b0a4d31f9df45bdaec Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Sat, 27 Jan 2024 21:57:06 +0100 Subject: [PATCH 122/144] fix: change arg to env --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 12775b1..c4e1291 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,8 +11,8 @@ RUN yarn run build:static FROM node:20-alpine -ARG CI_COMMIT_SHA -ARG CI_COMMIT_BRANCH +ENV CI_COMMIT_SHA +ENV CI_COMMIT_BRANCH WORKDIR /app COPY --from=builder /app/package.json /app/package.json From 6950170cc988162dda975e52fc7f6c0ad6512c4e Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Sat, 27 Jan 2024 22:00:06 +0100 Subject: [PATCH 123/144] fix: handling of arg and env --- Dockerfile | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index c4e1291..a1cb0cc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,8 +11,11 @@ RUN yarn run build:static FROM node:20-alpine -ENV CI_COMMIT_SHA -ENV CI_COMMIT_BRANCH +ARG CI_COMMIT_SHA +ARG CI_COMMIT_BRANCH + +ENV COMMIT_SHA=$CI_COMMIT_SHA +ENV COMMIT_BRANCH=$CI_COMMIT_BRANCH WORKDIR /app COPY --from=builder /app/package.json /app/package.json From e52d965d02621981a1fe1250e06437aef46a8198 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Sat, 27 Jan 2024 22:12:43 +0100 Subject: [PATCH 124/144] fix: move version endpoint --- src/api/v3/index.js | 14 -------------- src/routes/main.js | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/api/v3/index.js b/src/api/v3/index.js index 5d30412..80a0d28 100644 --- a/src/api/v3/index.js +++ b/src/api/v3/index.js @@ -30,23 +30,9 @@ more information on this, and how to apply and follow the GNU AGPL, see { - // TODO Support responding with JSON object when requested - - let versionDetails = app.get('git_branch') - ? `+${app.get('git_branch')}` - : '' - versionDetails += (app.get('git_branch') && app.get('git_hash')) - ? `.${app.get('git_hash').substring(0, 10)}` - : '' - - return res.status(200).contentType('text/plain').send(`${app.get('keyoxide_name')}/${app.get('keyoxide_version')}${versionDetails}`) -}) - if ((process.env.ENABLE_MAIN_MODULE ?? 'true') === 'true') { router.use('/profile', keyoxideProfileApiRouter) } diff --git a/src/routes/main.js b/src/routes/main.js index 9c1f3df..65ff1de 100644 --- a/src/routes/main.js +++ b/src/routes/main.js @@ -31,6 +31,7 @@ import express from 'express' import markdownImport from 'markdown-it' import { readFileSync } from 'fs' import { getMetaFromReq } from '../server/utils.js' +import app from '../index.js' const router = express.Router() const md = markdownImport({ typographer: true }) @@ -79,6 +80,19 @@ router.get('/.well-known/webfinger', (req, res) => { res.json(body) }) +router.get('/.well-known/keyoxide/version', async (req, res) => { + // TODO Support responding with JSON object when requested + + let versionDetails = app.get('git_branch') + ? `+${app.get('git_branch')}` + : '' + versionDetails += (app.get('git_branch') && app.get('git_hash')) + ? `.${app.get('git_hash').substring(0, 10)}` + : '' + + return res.status(200).contentType('text/plain').send(`${app.get('keyoxide_name')}/${app.get('keyoxide_version')}${versionDetails}`) +}) + router.get('/users/keyoxide', (req, res) => { if (!(process.env.DOMAIN && process.env.ACTIVITYPUB_PUBLIC_KEY)) { res.status(404).send('
Cannot GET /keyoxide
') From fe96ab2c352faca0497497d639552da3daad6c0d Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Sat, 27 Jan 2024 22:16:37 +0100 Subject: [PATCH 125/144] fix: remove obsolete references to highlights --- src/routes/main.js | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/routes/main.js b/src/routes/main.js index 65ff1de..5f19067 100644 --- a/src/routes/main.js +++ b/src/routes/main.js @@ -37,19 +37,7 @@ const router = express.Router() const md = markdownImport({ typographer: true }) router.get('/', (req, res) => { - const highlights = [] - for (let index = 1; index < 4; index++) { - if (process.env[`KX_HIGHLIGHTS_${index}_NAME`] && - process.env[`KX_HIGHLIGHTS_${index}_FINGERPRINT`]) { - highlights.push({ - name: process.env[`KX_HIGHLIGHTS_${index}_NAME`], - description: process.env[`KX_HIGHLIGHTS_${index}_DESCRIPTION`], - fingerprint: process.env[`KX_HIGHLIGHTS_${index}_FINGERPRINT`] - }) - } - } - - res.render('index', { highlights, meta: getMetaFromReq(req) }) + res.render('index', { meta: getMetaFromReq(req) }) }) router.get('/apps', (req, res) => { From 4b764583d98b04c2d2f7486687347785863179a5 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Sat, 27 Jan 2024 22:35:21 +0100 Subject: [PATCH 126/144] feat: optimize version number generation --- src/routes/main.js | 12 ++---------- src/server/utils.js | 22 +++++++++++++++++++++- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/routes/main.js b/src/routes/main.js index 5f19067..6e9f988 100644 --- a/src/routes/main.js +++ b/src/routes/main.js @@ -31,7 +31,6 @@ import express from 'express' import markdownImport from 'markdown-it' import { readFileSync } from 'fs' import { getMetaFromReq } from '../server/utils.js' -import app from '../index.js' const router = express.Router() const md = markdownImport({ typographer: true }) @@ -70,15 +69,8 @@ router.get('/.well-known/webfinger', (req, res) => { router.get('/.well-known/keyoxide/version', async (req, res) => { // TODO Support responding with JSON object when requested - - let versionDetails = app.get('git_branch') - ? `+${app.get('git_branch')}` - : '' - versionDetails += (app.get('git_branch') && app.get('git_hash')) - ? `.${app.get('git_hash').substring(0, 10)}` - : '' - - return res.status(200).contentType('text/plain').send(`${app.get('keyoxide_name')}/${app.get('keyoxide_version')}${versionDetails}`) + const meta = getMetaFromReq(req) + return res.status(200).contentType('text/plain').send(meta.keyoxide.semver) }) router.get('/users/keyoxide', (req, res) => { diff --git a/src/server/utils.js b/src/server/utils.js index a4583ac..852d989 100644 --- a/src/server/utils.js +++ b/src/server/utils.js @@ -82,10 +82,30 @@ export function encodeZBase32 (data) { } export function getMetaFromReq (req) { + let versionDetails = req.app.get('git_branch') + ? `+${req.app.get('git_branch')}` + : '' + versionDetails += (req.app.get('git_branch') && req.app.get('git_hash')) + ? `.${req.app.get('git_hash').substring(0, 10)}` + : '' + + const semver = `${req.app.get('keyoxide_name')}/${req.app.get('keyoxide_version')}${versionDetails}` + + const sourceUrl = req.app.get('git_hash') + ? `https://codeberg.org/keyoxide/keyoxide-web/src/commit/${req.app.get('git_hash')}` + : req.app.get('git_branch') + ? `https://codeberg.org/keyoxide/keyoxide-web/src/branch/${req.app.get('git_branch')}` + : 'https://codeberg.org/keyoxide/keyoxide-web' + return { env: req.app.get('env'), keyoxide: { - version: req.app.get('keyoxide_version') + name: req.app.get('keyoxide_name'), + version: req.app.get('keyoxide_version'), + branch: req.app.get('git_branch'), + hash: req.app.get('git_hash'), + semver, + sourceUrl } } } From 0d5f6aaf3e8faccfc90c3f010773698bfb6fdb9a Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Sat, 27 Jan 2024 22:35:51 +0100 Subject: [PATCH 127/144] feat: add source URL to footer --- views/partials/footer.pug | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/views/partials/footer.pug b/views/partials/footer.pug index 47b3ab8..e480807 100644 --- a/views/partials/footer.pug +++ b/views/partials/footer.pug @@ -25,10 +25,9 @@ footer a(href="https://ariadne.id") ariadne.id p - | Version #{meta.keyoxide.version} - if (meta.env === "development") - | -dev + | Version + a(href=meta.keyoxide.sourceUrl)= meta.keyoxide.semver br - | © 2024 Keyoxide project contributors + | © 2020-2024 Keyoxide project contributors \ No newline at end of file From 37266422119706befea07592fd97fdc080a9c8c6 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Sun, 28 Jan 2024 10:00:09 +0100 Subject: [PATCH 128/144] chore: update README [SKIP CI] --- README.md | 70 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 40 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index ebff545..fe8dd20 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,15 @@ -# Keyoxide +# keyoxide-web -[![Drone (self-hosted) with branch](https://img.shields.io/drone/build/keyoxide/keyoxide-web/main?server=https%3A%2F%2Fdrone.keyoxide.org&style=for-the-badge)](https://drone.keyoxide.org/keyoxide/keyoxide-web) -[![License](https://img.shields.io/badge/license-AGPL--v3-blue?style=for-the-badge)](https://codeberg.org/keyoxide/web/src/branch/main/LICENSE) -[![Docker Image Version (latest semver)](https://img.shields.io/docker/v/keyoxide/keyoxide?sort=semver&style=for-the-badge)](https://hub.docker.com/r/keyoxide/keyoxide) -[![Docker Pulls](https://img.shields.io/docker/pulls/keyoxide/keyoxide?style=for-the-badge)](https://hub.docker.com/r/keyoxide/keyoxide) -[![Mastodon Follow](https://img.shields.io/mastodon/follow/247838?domain=https%3A%2F%2Ffosstodon.org&style=for-the-badge)](https://fosstodon.org/@keyoxide) -[![Open Collective backers and sponsors](https://img.shields.io/opencollective/all/keyoxide?style=for-the-badge)](https://opencollective.com/keyoxide) +[![status-badge](https://ci.codeberg.org/api/badges/5919/status.svg)](https://ci.codeberg.org/repos/5919) +[![License](https://img.shields.io/badge/license-AGPL--v3-blue?style=flat)](https://codeberg.org/keyoxide/web/src/branch/main/LICENSE) +[![Docker Image Version (latest semver)](https://img.shields.io/docker/v/keyoxide/keyoxide?sort=semver&style=flat)](https://hub.docker.com/r/keyoxide/keyoxide) +[![Docker Pulls](https://img.shields.io/docker/pulls/keyoxide/keyoxide?style=flat)](https://hub.docker.com/r/keyoxide/keyoxide) +[![Mastodon Follow](https://img.shields.io/mastodon/follow/247838?domain=https%3A%2F%2Ffosstodon.org&style=flat)](https://fosstodon.org/@keyoxide) +[![Open Collective backers and sponsors](https://img.shields.io/opencollective/all/keyoxide?style=flat)](https://opencollective.com/keyoxide) -[Keyoxide](https://keyoxide.org) is a modern, secure and decentralized platform to prove your online identity. +`keyoxide-web` is the web client that powers [keyoxide.org](https://keyoxide.org) and which you can freely host on your own infrastructure. -## Self-hosting +## Hosting keyoxide-web Self-hosting Keyoxide is an important aspect of the project. Users need to trust the Keyoxide instance they're using to reliably verify identities. Making Keyoxide itself decentralized means no one needs to trust a central server. If a friend or family member is hosting a Keyoxide instance, it becomes much easier to trust the instance! @@ -23,34 +23,44 @@ docker run -d -p 3000:3000 codeberg.org/keyoxide/keyoxide-web:latest Keyoxide will now be available by visiting http://localhost:3000. -More information available in the [documentation](docs.keyoxide.org/self-hosting). +More information available in the [documentation](https://docs.keyoxide.org/guides/self-hosting/). + +## Local development + +Install `node` in one of the following ways: + + - [nix](https://nixos.org/guides/install-nix.html) with [direnv](https://direnv.net/) + - using [fnm](https://github.com/Schniz/fnm) + - using [nvm](https://github.com/nvm-sh/nvm) + - directly from their [website](https://nodejs.org/) + +Install dependencies with `npm install` or `yarn`. + +Run the server with `npm dev` or `yarn dev`. The Keyoxide web client will now be available at [https://localhost:3000](https://localhost:3000). ## Contributing -Anyone can contribute if they'd like! No need to be a programmer or technically-oriented for that matter. +Anyone can contribute! -Contributing to Keyoxide can happen in many forms: +Developers are invited to: -- Finding and reporting bugs -- Suggesting new features -- Improving documentation -- Writing code to fix bugs and features -- Promoting decentralized identity and web3.0 +- fork the repository and play around +- submit PRs to [implement new features or fix bugs](https://codeberg.org/keyoxide/keyoxide-web/issues) + +If you are new to contributing to open source software, we'd love to help you! To get started, here's a [list of "good first issues"](https://codeberg.org/keyoxide/keyoxide-web/issues?q=&type=all&state=open&labels=183598) that you could look into. + +Everyone is invited to: + +- find and [report bugs](https://codeberg.org/keyoxide/keyoxide-web/issues/new/choose) +- suggesting [new features](https://codeberg.org/keyoxide/keyoxide-web/issues/new/choose) +- [help with translations](https://translate.codeberg.org/projects/keyoxide/) +- [improve documentation](https://codeberg.org/keyoxide/keyoxide-docs) +- start using open source software and promote it Please note that this project has a [Code of Conduct](https://codeberg.org/keyoxide/web/src/branch/main/CODE_OF_CONDUCT.md) that all contributors agree to abide when participating. -### Local development +### About the Keyoxide project -To run Keyoxide locally on your machine for development: +The Keyoxide project strives for a healthier internet for all and has made all its efforts fully [open source](https://codeberg.org/keyoxide). Our [community](https://docs.keyoxide.org/community/) is open and welcoming, feel free to say hi! -- install either - - NodeJS - - directly from their [website](https://nodejs.org/en/), or - - using [nvm](https://github.com/nvm-sh/nvm): `nvm install --lts; nvm use --lts` - - [yarn](https://yarnpkg.com/) - - [nix](https://nixos.org/guides/install-nix.html) with - [direnv](https://direnv.net/) will install yarn and other dependencies. -- install dependencies with `npm install` or `yarn` -- run the server with `npm dev` or `yarn dev` - -Keyoxide will now be available at [https://localhost:3000](https://localhost:3000) +Funding for the project comes primarily from the [NLnet foundation](https://nlnet.nl/), [NGI0](https://www.ngi.eu/) and the people supporting our [OpenCollective](https://opencollective.com/keyoxide). The project is grateful for all your support. From 827969f9a1ee1124daf103d63708f072b56b5105 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Sun, 28 Jan 2024 10:01:06 +0100 Subject: [PATCH 129/144] chore: update README [SKIP CI] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fe8dd20..894b0e3 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ Everyone is invited to: Please note that this project has a [Code of Conduct](https://codeberg.org/keyoxide/web/src/branch/main/CODE_OF_CONDUCT.md) that all contributors agree to abide when participating. -### About the Keyoxide project +## About the Keyoxide project The Keyoxide project strives for a healthier internet for all and has made all its efforts fully [open source](https://codeberg.org/keyoxide). Our [community](https://docs.keyoxide.org/community/) is open and welcoming, feel free to say hi! From 18841c41afe4d73ef19675678c5afc2106d686b2 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Sun, 28 Jan 2024 10:02:05 +0100 Subject: [PATCH 130/144] chore: update README [SKIP CI] --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 894b0e3..5f3b7b0 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,6 @@ Please note that this project has a [Code of Conduct](https://codeberg.org/keyox ## About the Keyoxide project -The Keyoxide project strives for a healthier internet for all and has made all its efforts fully [open source](https://codeberg.org/keyoxide). Our [community](https://docs.keyoxide.org/community/) is open and welcoming, feel free to say hi! +The Keyoxide project strives for a healthier internet for all and has made its efforts fully [open source](https://codeberg.org/keyoxide). Our [community](https://docs.keyoxide.org/community/) is open and welcoming, feel free to say hi! -Funding for the project comes primarily from the [NLnet foundation](https://nlnet.nl/), [NGI0](https://www.ngi.eu/) and the people supporting our [OpenCollective](https://opencollective.com/keyoxide). The project is grateful for all your support. +Funding for the project comes from the [NLnet foundation](https://nlnet.nl/), [NGI0](https://www.ngi.eu/) and the people supporting our [OpenCollective](https://opencollective.com/keyoxide). The project is grateful for all your support. From 7f671e74b6837dd1d1be06233e5122ff2bf05ee7 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Sun, 28 Jan 2024 10:15:26 +0100 Subject: [PATCH 131/144] chore: update README [SKIP CI] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5f3b7b0..22b84ab 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # keyoxide-web [![status-badge](https://ci.codeberg.org/api/badges/5919/status.svg)](https://ci.codeberg.org/repos/5919) -[![License](https://img.shields.io/badge/license-AGPL--v3-blue?style=flat)](https://codeberg.org/keyoxide/web/src/branch/main/LICENSE) +[![License](https://img.shields.io/badge/license-AGPL--v3-blue?style=flat)](https://codeberg.org/keyoxide/keyoxide-web/src/branch/main/LICENSE) [![Docker Image Version (latest semver)](https://img.shields.io/docker/v/keyoxide/keyoxide?sort=semver&style=flat)](https://hub.docker.com/r/keyoxide/keyoxide) [![Docker Pulls](https://img.shields.io/docker/pulls/keyoxide/keyoxide?style=flat)](https://hub.docker.com/r/keyoxide/keyoxide) [![Mastodon Follow](https://img.shields.io/mastodon/follow/247838?domain=https%3A%2F%2Ffosstodon.org&style=flat)](https://fosstodon.org/@keyoxide) From 5f1b800a4206ed6ef695f98cfde32a21dcacfaae Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Sun, 28 Jan 2024 13:53:47 +0100 Subject: [PATCH 132/144] feat: get info about last git commit --- src/index.js | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/index.js b/src/index.js index e49a099..893c7f2 100644 --- a/src/index.js +++ b/src/index.js @@ -31,6 +31,7 @@ import express from 'express' import * as httpContext from 'express-http-context2' import { nanoid } from 'nanoid' import { readFileSync } from 'fs' +import { execSync } from 'child_process'; import { stringReplace } from 'string-replace-middleware' import * as pug from 'pug' import * as dotenv from 'dotenv' @@ -43,6 +44,20 @@ import staticRoute from './routes/static.js' import utilRoute from './routes/util.js' dotenv.config() +// Get information about the last git commit +let gitBranch = process.env.CI_COMMIT_BRANCH ?? process.env.COMMIT_BRANCH +if (!gitBranch) { + try { + gitBranch = execSync('git rev-parse --abbrev-ref HEAD').toString().trim() + } catch (_) {} +} +let gitHash = process.env.CI_COMMIT_SHA ?? process.env.COMMIT_SHA +if (!gitHash) { + try { + gitHash = execSync('git rev-parse HEAD').toString().trim() + } catch (_) {} +} + const app = express() const packageData = JSON.parse(readFileSync('./package.json')) @@ -53,8 +68,8 @@ app.set('domain', process.env.DOMAIN) app.set('scheme', process.env.SCHEME || 'https') app.set('keyoxide_name', 'keyoxide-web') app.set('keyoxide_version', packageData.version) -app.set('git_branch', process.env.CI_COMMIT_BRANCH ?? process.env.COMMIT_BRANCH) -app.set('git_hash', process.env.CI_COMMIT_SHA ?? process.env.COMMIT_SHA) +app.set('git_branch', gitBranch) +app.set('git_hash', gitHash) app.set('onion_url', process.env.ONION_URL) // Middlewares From 9e19a622d9ceaa17875b664d1220c8c627d2e6f8 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Sun, 28 Jan 2024 13:54:50 +0100 Subject: [PATCH 133/144] fix: typo --- src/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.js b/src/index.js index 893c7f2..faf331d 100644 --- a/src/index.js +++ b/src/index.js @@ -31,7 +31,7 @@ import express from 'express' import * as httpContext from 'express-http-context2' import { nanoid } from 'nanoid' import { readFileSync } from 'fs' -import { execSync } from 'child_process'; +import { execSync } from 'child_process' import { stringReplace } from 'string-replace-middleware' import * as pug from 'pug' import * as dotenv from 'dotenv' From 912f3619eb52631f81215b97328b6bcdd01842c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Jaenisch?= Date: Tue, 26 Sep 2023 16:17:00 +0200 Subject: [PATCH 134/144] fix: use PATH expansion for run-scripts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Yarn is including `./node_modules/.bin` in $PATH when evaluating run-commands. In other words, they don't have to be included for executables inside of `package.json`. Signed-off-by: André Jaenisch --- package.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index f5ee0e2..5e57d9f 100644 --- a/package.json +++ b/package.json @@ -50,19 +50,19 @@ "start": "node ./", "dev": "LOG_LEVEL=debug yarn run watch & yarn run build:static:dev", "test": "yarn run lint && mocha", - "watch": "./node_modules/.bin/nodemon --config nodemon.json ./", + "watch": "nodemon --config nodemon.json ./", "build": "yarn run build:server && yarn run build:static", "build:server": "ncc build ./src/index.js -o dist", "build:static": "webpack --config webpack.config.js --env static=true --env mode=production", "build:static:dev": "webpack --config webpack.config.js --env static=true --env mode=development", "lint": "yarn run standard:check && yarn run biome:check", - "standard:check": "./node_modules/.bin/standard ./src", - "standard:fix": "./node_modules/.bin/standard --fix ./src", "biome:check": "biome check ./src && biome lint ./src", "biome:fix": "biome check --apply ./src && biome lint --apply ./src", - "license:check": "./node_modules/.bin/license-check-and-add check", - "license:add": "./node_modules/.bin/license-check-and-add add", - "license:remove": "./node_modules/.bin/license-check-and-add remove" + "standard:check": "standard ./src", + "standard:fix": "standard --fix ./src", + "license:check": "license-check-and-add check", + "license:add": "license-check-and-add add", + "license:remove": "license-check-and-add remove" }, "repository": { "type": "git", From ed4c265dadac6ad1f94e7c27fa48fdd0f7479490 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Tue, 30 Jan 2024 01:21:13 +0100 Subject: [PATCH 135/144] fix: use only hash in semver build metadata --- src/server/utils.js | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/server/utils.js b/src/server/utils.js index 852d989..fb1cbc1 100644 --- a/src/server/utils.js +++ b/src/server/utils.js @@ -82,20 +82,15 @@ export function encodeZBase32 (data) { } export function getMetaFromReq (req) { - let versionDetails = req.app.get('git_branch') - ? `+${req.app.get('git_branch')}` - : '' - versionDetails += (req.app.get('git_branch') && req.app.get('git_hash')) - ? `.${req.app.get('git_hash').substring(0, 10)}` + let versionDetails = (req.app.get('git_hash')) + ? `+${req.app.get('git_hash').substring(0, 10)}` : '' const semver = `${req.app.get('keyoxide_name')}/${req.app.get('keyoxide_version')}${versionDetails}` const sourceUrl = req.app.get('git_hash') ? `https://codeberg.org/keyoxide/keyoxide-web/src/commit/${req.app.get('git_hash')}` - : req.app.get('git_branch') - ? `https://codeberg.org/keyoxide/keyoxide-web/src/branch/${req.app.get('git_branch')}` - : 'https://codeberg.org/keyoxide/keyoxide-web' + : 'https://codeberg.org/keyoxide/keyoxide-web' return { env: req.app.get('env'), From e7c1a878ff71a4b6d4cb5e575d0be6f5ec36ccee Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Tue, 30 Jan 2024 01:23:36 +0100 Subject: [PATCH 136/144] fix: use const --- src/server/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/utils.js b/src/server/utils.js index fb1cbc1..4c65b8f 100644 --- a/src/server/utils.js +++ b/src/server/utils.js @@ -82,7 +82,7 @@ export function encodeZBase32 (data) { } export function getMetaFromReq (req) { - let versionDetails = (req.app.get('git_hash')) + const versionDetails = (req.app.get('git_hash')) ? `+${req.app.get('git_hash').substring(0, 10)}` : '' From 3fb5fb860faccf6e211b1ba645ccac1828b66bbc Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Thu, 1 Feb 2024 17:17:16 +0100 Subject: [PATCH 137/144] chore: bump doipjs to 1.2.9 --- package.json | 2 +- yarn.lock | 25 +++++++------------------ 2 files changed, 8 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index 5e57d9f..024c0c4 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "bent": "^7.3.12", "body-parser": "^1.19.0", "colorjs.io": "^0.4.5", - "doipjs": "^1.2.8", + "doipjs": "^1.2.9", "dotenv": "^16.0.3", "express": "^4.17.1", "express-http-context2": "^1.0.0", diff --git a/yarn.lock b/yarn.lock index f84820c..aab8295 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1717,14 +1717,6 @@ core-js@^3.30.2: resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.35.1.tgz#9c28f8b7ccee482796f8590cc8d15739eaaf980c" integrity sha512-IgdsbxNyMskrTFxa9lWHyMwAJU5gXOPP+1yO+K59d50VLVAIDAbs7gIv705KzALModfK3ZrSZTPNpC0PQgIZuw== -cors@^2.8.5: - version "2.8.5" - resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" - integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== - dependencies: - object-assign "^4" - vary "^1" - create-hash@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" @@ -1905,10 +1897,10 @@ doctypes@^1.1.0: resolved "https://registry.yarnpkg.com/doctypes/-/doctypes-1.1.0.tgz#ea80b106a87538774e8a3a4a5afe293de489e0a9" integrity sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ== -doipjs@^1.2.8: - version "1.2.8" - resolved "https://registry.yarnpkg.com/doipjs/-/doipjs-1.2.8.tgz#7048da97fca4a7d2cf784a5c84ac31bf6020180d" - integrity sha512-VIyJ091pXZzA7E4IQuMF6qkPRX5f7hZ40ue65vXspGDCxyiCkeZY0sFjAAONQcZFhqPsdUZ/ypfqfIRhvV4Trw== +doipjs@^1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/doipjs/-/doipjs-1.2.9.tgz#a1dd34854a33b112a9f601c1d3c161406c449d00" + integrity sha512-t9HE54n+EgsDFhw3WBtFT7Hx7ydIMbIv0eTv4xBvw71G4i8AoRrQV91yGL0SA80rUnKrSGKyjVjGlhmzppDN2g== dependencies: "@openpgp/hkp-client" "^0.0.3" "@openpgp/wkd-client" "^0.0.4" @@ -1916,10 +1908,7 @@ doipjs@^1.2.8: "@xmpp/debug" "^0.13.0" axios "^1.6.5" browser-or-node "^1.3.0" - cors "^2.8.5" entities "^4.4.0" - express "^4.17.1" - express-validator "^6.10.0" hash-wasm "^4.9.0" irc-upd "^0.11.0" jose "^4.14.4" @@ -2381,7 +2370,7 @@ express-rate-limit@^7.0.1: resolved "https://registry.yarnpkg.com/express-rate-limit/-/express-rate-limit-7.1.5.tgz#af4c81143a945ea97f2599d13957440a0ddbfcfe" integrity sha512-/iVogxu7ueadrepw1bS0X0kaRC/U0afwiYRSLg68Ts+p4Dc85Q5QKsOnPS/QUjPMHvOJQtBDrZgvkOzf8ejUYw== -express-validator@^6.10.0, express-validator@^6.13.0: +express-validator@^6.13.0: version "6.15.0" resolved "https://registry.yarnpkg.com/express-validator/-/express-validator-6.15.0.tgz#5e4601428960b0d66f5f4ae09cb32ed2077374a4" integrity sha512-r05VYoBL3i2pswuehoFSy+uM8NBuVaY7avp5qrYjQBDzagx2Z5A77FZqPT8/gNLF3HopWkIzaTFaC4JysWXLqg== @@ -3800,7 +3789,7 @@ normalize-url@^6.0.1: resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a" integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A== -object-assign@^4, object-assign@^4.1.1: +object-assign@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== @@ -5194,7 +5183,7 @@ validator@^13.9.0: resolved "https://registry.yarnpkg.com/validator/-/validator-13.11.0.tgz#23ab3fd59290c61248364eabf4067f04955fbb1b" integrity sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ== -vary@^1, vary@~1.1.2: +vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== From 785647bbb8ab2fa1af3c706071f576317a5907ed Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Thu, 1 Feb 2024 18:02:04 +0100 Subject: [PATCH 138/144] chore: release 4.2.7 --- CHANGELOG.md | 11 +++++++++++ package.json | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a59aa63..3d286d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [4.2.7] - 2024-02-01 +### Added +- Server version HTTP endpoint +- Server version in footer +### Changed +- Update doipjs to 1.2.9 +### Notes +- The version of keyoxide-web that the server is running can now be requested at the `/.well-known/keyoxide/version` HTTP endpoint. It is also found in the footer of every page. +- Version 1.2.9 of doipjs notably adds support for ORCiD claim verification, as well as a couple of performance improvements. + + ## [4.2.6] - 2024-01-24 ### Added - Support openpgp4fpr: queries in URL diff --git a/package.json b/package.json index 024c0c4..1351d6c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "keyoxide-web", - "version": "4.2.6", + "version": "4.2.7", "description": "Verifying online identity with cryptography", "main": "./src/index.js", "type": "module", From 255e99af3965eff599cefe8dffcacfc4f83f79dc Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Mon, 12 Feb 2024 10:26:24 +0100 Subject: [PATCH 139/144] feat: escape parameters --- src/routes/profile.js | 171 ++++++++++++++++++++++++------------------ 1 file changed, 97 insertions(+), 74 deletions(-) diff --git a/src/routes/profile.js b/src/routes/profile.js index 431c125..11b5edb 100644 --- a/src/routes/profile.js +++ b/src/routes/profile.js @@ -28,6 +28,7 @@ if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see . */ import express from 'express' +import { param } from 'express-validator' import bodyParserImport from 'body-parser' import { rateLimit } from 'express-rate-limit' import { generateSignatureProfile, utils, generateWKDProfile, generateHKPProfile, generateAutoProfile, generateKeybaseProfile } from '../server/index.js' @@ -60,90 +61,112 @@ if (process.env.ENABLE_EXPERIMENTAL_RATE_LIMITER) { { component: 'profile_rate_limiter', action: 'start' }) } -router.get('/sig', profileRateLimiter, (req, res) => { - res.render('profile', { isSignature: true, signature: null, meta: getMetaFromReq(req) }) -}) - -router.post('/sig', profileRateLimiter, bodyParser, async (req, res) => { - const data = await generateSignatureProfile(req.body.signature) - const title = utils.generatePageTitle('profile', data) - res.set('ariadne-identity-proof', data.identifier) - res.render('profile', { - title, - data: data instanceof Profile ? data.toJSON() : data, - isSignature: true, - signature: req.body.signature, - enable_message_encryption: false, - enable_signature_verification: false, - meta: getMetaFromReq(req) +router.get('/sig', + profileRateLimiter, + (req, res) => { + res.render('profile', { isSignature: true, signature: null, meta: getMetaFromReq(req) }) }) -}) -router.get('/wkd/:id', profileRateLimiter, async (req, res) => { - const data = await generateWKDProfile(req.params.id) - const title = utils.generatePageTitle('profile', data) - res.set('ariadne-identity-proof', data.identifier) - res.render('profile', { - title, - data: data instanceof Profile ? data.toJSON() : data, - enable_message_encryption: false, - enable_signature_verification: false, - meta: getMetaFromReq(req) +router.post('/sig', + profileRateLimiter, + bodyParser, + async (req, res) => { + const data = await generateSignatureProfile(req.body.signature) + const title = utils.generatePageTitle('profile', data) + res.set('ariadne-identity-proof', data.identifier) + res.render('profile', { + title, + data: data instanceof Profile ? data.toJSON() : data, + isSignature: true, + signature: req.body.signature, + enable_message_encryption: false, + enable_signature_verification: false, + meta: getMetaFromReq(req) + }) }) -}) -router.get('/hkp/:id', profileRateLimiter, async (req, res) => { - const data = await generateHKPProfile(req.params.id) - const title = utils.generatePageTitle('profile', data) - res.set('ariadne-identity-proof', data.identifier) - res.render('profile', { - title, - data: data instanceof Profile ? data.toJSON() : data, - enable_message_encryption: false, - enable_signature_verification: false, - meta: getMetaFromReq(req) +router.get('/wkd/:id', + profileRateLimiter, + param('id').escape(), + async (req, res) => { + const data = await generateWKDProfile(req.params.id) + const title = utils.generatePageTitle('profile', data) + res.set('ariadne-identity-proof', data.identifier) + res.render('profile', { + title, + data: data instanceof Profile ? data.toJSON() : data, + enable_message_encryption: false, + enable_signature_verification: false, + meta: getMetaFromReq(req) + }) }) -}) -router.get('/hkp/:server/:id', profileRateLimiter, async (req, res) => { - const data = await generateHKPProfile(req.params.id, req.params.server) - const title = utils.generatePageTitle('profile', data) - res.set('ariadne-identity-proof', data.identifier) - res.render('profile', { - title, - data: data instanceof Profile ? data.toJSON() : data, - enable_message_encryption: false, - enable_signature_verification: false, - meta: getMetaFromReq(req) +router.get('/hkp/:id', + profileRateLimiter, + param('id').escape(), + async (req, res) => { + const data = await generateHKPProfile(req.params.id) + const title = utils.generatePageTitle('profile', data) + res.set('ariadne-identity-proof', data.identifier) + res.render('profile', { + title, + data: data instanceof Profile ? data.toJSON() : data, + enable_message_encryption: false, + enable_signature_verification: false, + meta: getMetaFromReq(req) + }) }) -}) -router.get('/keybase/:username/:fingerprint', profileRateLimiter, async (req, res) => { - const data = await generateKeybaseProfile(req.params.username, req.params.fingerprint) - const title = utils.generatePageTitle('profile', data) - res.set('ariadne-identity-proof', data.identifier) - res.render('profile', { - title, - data: data instanceof Profile ? data.toJSON() : data, - enable_message_encryption: false, - enable_signature_verification: false, - meta: getMetaFromReq(req) +router.get('/hkp/:server/:id', + profileRateLimiter, + param('server').escape(), + param('id').escape(), + async (req, res) => { + const data = await generateHKPProfile(req.params.id, req.params.server) + const title = utils.generatePageTitle('profile', data) + res.set('ariadne-identity-proof', data.identifier) + res.render('profile', { + title, + data: data instanceof Profile ? data.toJSON() : data, + enable_message_encryption: false, + enable_signature_verification: false, + meta: getMetaFromReq(req) + }) }) -}) -router.get('/:id', profileRateLimiter, async (req, res) => { - const data = await generateAutoProfile(req.params.id) - const theme = generateProfileTheme(data) - const title = utils.generatePageTitle('profile', data) - res.set('ariadne-identity-proof', data.identifier) - res.render('profile', { - title, - data: data instanceof Profile ? data.toJSON() : data, - enable_message_encryption: false, - enable_signature_verification: false, - theme, - meta: getMetaFromReq(req) +router.get('/keybase/:username/:fingerprint', + profileRateLimiter, + param('username').escape(), + param('fingerprint').escape(), + async (req, res) => { + const data = await generateKeybaseProfile(req.params.username, req.params.fingerprint) + const title = utils.generatePageTitle('profile', data) + res.set('ariadne-identity-proof', data.identifier) + res.render('profile', { + title, + data: data instanceof Profile ? data.toJSON() : data, + enable_message_encryption: false, + enable_signature_verification: false, + meta: getMetaFromReq(req) + }) + }) + +router.get('/:id', + profileRateLimiter, + param('id').escape(), + async (req, res) => { + const data = await generateAutoProfile(req.params.id) + const theme = generateProfileTheme(data) + const title = utils.generatePageTitle('profile', data) + res.set('ariadne-identity-proof', data.identifier) + res.render('profile', { + title, + data: data instanceof Profile ? data.toJSON() : data, + enable_message_encryption: false, + enable_signature_verification: false, + theme, + meta: getMetaFromReq(req) + }) }) -}) export default router From a57d24ad6aa19df2d2414f7e0e98c5383cae53bb Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Tue, 13 Feb 2024 09:55:30 +0100 Subject: [PATCH 140/144] feat: improve param escaping --- src/routes/profile.js | 17 +++++++-------- src/routes/util.js | 50 +++++++++++++++++++++++++++---------------- src/server/utils.js | 24 +++++++++++++++++++++ 3 files changed, 63 insertions(+), 28 deletions(-) diff --git a/src/routes/profile.js b/src/routes/profile.js index 11b5edb..49048c2 100644 --- a/src/routes/profile.js +++ b/src/routes/profile.js @@ -28,12 +28,11 @@ if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see . */ import express from 'express' -import { param } from 'express-validator' import bodyParserImport from 'body-parser' import { rateLimit } from 'express-rate-limit' import { generateSignatureProfile, utils, generateWKDProfile, generateHKPProfile, generateAutoProfile, generateKeybaseProfile } from '../server/index.js' import { Profile } from 'doipjs' -import { generateProfileTheme, getMetaFromReq } from '../server/utils.js' +import { generateProfileTheme, getMetaFromReq, escapedParam } from '../server/utils.js' import logger from '../log.js' const router = express.Router() @@ -87,7 +86,7 @@ router.post('/sig', router.get('/wkd/:id', profileRateLimiter, - param('id').escape(), + escapedParam('id'), async (req, res) => { const data = await generateWKDProfile(req.params.id) const title = utils.generatePageTitle('profile', data) @@ -103,7 +102,7 @@ router.get('/wkd/:id', router.get('/hkp/:id', profileRateLimiter, - param('id').escape(), + escapedParam('id'), async (req, res) => { const data = await generateHKPProfile(req.params.id) const title = utils.generatePageTitle('profile', data) @@ -119,8 +118,8 @@ router.get('/hkp/:id', router.get('/hkp/:server/:id', profileRateLimiter, - param('server').escape(), - param('id').escape(), + escapedParam('server'), + escapedParam('id'), async (req, res) => { const data = await generateHKPProfile(req.params.id, req.params.server) const title = utils.generatePageTitle('profile', data) @@ -136,8 +135,8 @@ router.get('/hkp/:server/:id', router.get('/keybase/:username/:fingerprint', profileRateLimiter, - param('username').escape(), - param('fingerprint').escape(), + escapedParam('username'), + escapedParam('fingerprint'), async (req, res) => { const data = await generateKeybaseProfile(req.params.username, req.params.fingerprint) const title = utils.generatePageTitle('profile', data) @@ -153,7 +152,7 @@ router.get('/keybase/:username/:fingerprint', router.get('/:id', profileRateLimiter, - param('id').escape(), + escapedParam('id'), async (req, res) => { const data = await generateAutoProfile(req.params.id) const theme = generateProfileTheme(data) diff --git a/src/routes/util.js b/src/routes/util.js index bc5d207..9cc3f3d 100644 --- a/src/routes/util.js +++ b/src/routes/util.js @@ -28,7 +28,7 @@ if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see . */ import express from 'express' -import { getMetaFromReq } from '../server/utils.js' +import { escapedParam, getMetaFromReq } from '../server/utils.js' const router = express.Router() @@ -38,43 +38,55 @@ router.get('/', function (req, res) { router.get('/profile-url', function (req, res) { res.render('util/profile-url', { meta: getMetaFromReq(req) }) }) -router.get('/profile-url/:input', function (req, res) { - res.render('util/profile-url', { input: req.params.input, meta: getMetaFromReq(req) }) -}) +router.get('/profile-url/:input', + escapedParam('input'), + function (req, res) { + res.render('util/profile-url', { input: req.params.input, meta: getMetaFromReq(req) }) + }) router.get('/qr', function (req, res) { res.render('util/qr', { meta: getMetaFromReq(req) }) }) -router.get('/qr/:input', function (req, res) { - res.render('util/qr', { input: req.params.input, meta: getMetaFromReq(req) }) -}) +router.get('/qr/:input', + escapedParam('input'), + function (req, res) { + res.render('util/qr', { input: req.params.input, meta: getMetaFromReq(req) }) + }) router.get('/qrfp', function (req, res) { res.render('util/qrfp', { meta: getMetaFromReq(req) }) }) -router.get('/qrfp/:input', function (req, res) { - res.render('util/qrfp', { input: req.params.input, meta: getMetaFromReq(req) }) -}) +router.get('/qrfp/:input', + escapedParam('input'), + function (req, res) { + res.render('util/qrfp', { input: req.params.input, meta: getMetaFromReq(req) }) + }) router.get('/wkd', function (req, res) { res.render('util/wkd', { meta: getMetaFromReq(req) }) }) -router.get('/wkd/:input', function (req, res) { - res.render('util/wkd', { input: req.params.input, meta: getMetaFromReq(req) }) -}) +router.get('/wkd/:input', + escapedParam('input'), + function (req, res) { + res.render('util/wkd', { input: req.params.input, meta: getMetaFromReq(req) }) + }) router.get('/argon2', function (req, res) { res.render('util/argon2', { meta: getMetaFromReq(req) }) }) -router.get('/argon2/:input', function (req, res) { - res.render('util/argon2', { input: req.params.input, meta: getMetaFromReq(req) }) -}) +router.get('/argon2/:input', + escapedParam('input'), + function (req, res) { + res.render('util/argon2', { input: req.params.input, meta: getMetaFromReq(req) }) + }) router.get('/bcrypt', function (req, res) { res.render('util/bcrypt', { meta: getMetaFromReq(req) }) }) -router.get('/bcrypt/:input', function (req, res) { - res.render('util/bcrypt', { input: req.params.input, meta: getMetaFromReq(req) }) -}) +router.get('/bcrypt/:input', + escapedParam('input'), + function (req, res) { + res.render('util/bcrypt', { input: req.params.input, meta: getMetaFromReq(req) }) + }) export default router diff --git a/src/server/utils.js b/src/server/utils.js index 4c65b8f..0475f5d 100644 --- a/src/server/utils.js +++ b/src/server/utils.js @@ -30,6 +30,7 @@ more information on this, and how to apply and follow the GNU AGPL, see ]+@[^\s@<>]+>)/ + +export function escapedParam(name) { + return param(name).customSanitizer(value => { + return value.split(reEmailLike).map(token => { + if (reEmailLike.test(token)) return token + return escape(token) + }).join('') + }) +} + +// Copied from https://github.com/validatorjs/validator.js/blob/b958bd7d1026a434ad3bf90064d3dcb8b775f1a9/src/lib/escape.js +function escape(input) { + return (input.replace(/&/g, '&') + .replace(/"/g, '"') + .replace(/'/g, ''') + .replace(//g, '>') + .replace(/\//g, '/') + .replace(/\\/g, '\') + .replace(/`/g, '`')) +} From 567130f634fea9520748c89f8ecd5baa85ec2638 Mon Sep 17 00:00:00 2001 From: Yarmo Mackenbach Date: Tue, 13 Feb 2024 10:05:27 +0100 Subject: [PATCH 141/144] fix: avoid shadowing escape --- src/routes/util.js | 2 +- src/server/utils.js | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/routes/util.js b/src/routes/util.js index 9cc3f3d..1d42175 100644 --- a/src/routes/util.js +++ b/src/routes/util.js @@ -38,7 +38,7 @@ router.get('/', function (req, res) { router.get('/profile-url', function (req, res) { res.render('util/profile-url', { meta: getMetaFromReq(req) }) }) -router.get('/profile-url/:input', +router.get('/profile-url/:input', escapedParam('input'), function (req, res) { res.render('util/profile-url', { input: req.params.input, meta: getMetaFromReq(req) }) diff --git a/src/server/utils.js b/src/server/utils.js index 0475f5d..894fa6d 100644 --- a/src/server/utils.js +++ b/src/server/utils.js @@ -156,17 +156,17 @@ export function generateProfileTheme (/** @type {Profile} */ profile) { const reEmailLike = /(<[^\s@<>]+@[^\s@<>]+>)/ -export function escapedParam(name) { +export function escapedParam (/** @type {String} */ name) { return param(name).customSanitizer(value => { return value.split(reEmailLike).map(token => { if (reEmailLike.test(token)) return token - return escape(token) + return escapeString(token) }).join('') }) } -// Copied from https://github.com/validatorjs/validator.js/blob/b958bd7d1026a434ad3bf90064d3dcb8b775f1a9/src/lib/escape.js -function escape(input) { +// Copied from https://github.com/validatorjs/validator.js/blob/b958bd7d1026a434ad3bf90064d3dcb8b775f1a9/src/lib/escapeString.js +function escapeString (/** @type {String} */ input) { return (input.replace(/&/g, '&') .replace(/"/g, '"') .replace(/'/g, ''') From ba532be4f39ce25a83420d32b1c1979694902b38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Jaenisch?= Date: Mon, 26 Jun 2023 15:26:08 +0200 Subject: [PATCH 142/144] feat: Add SystemD service file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I am running Keyoxide-Web on baremetal on my VPS. I cloned the repo into `/opt/` and `adduser` a dedicated user for it. Things you might want to adjust also is the `PORT` value. Fixes #48. Signed-off-by: André Jaenisch --- keyoxide-web.service | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 keyoxide-web.service diff --git a/keyoxide-web.service b/keyoxide-web.service new file mode 100644 index 0000000..d85fd44 --- /dev/null +++ b/keyoxide-web.service @@ -0,0 +1,16 @@ +[Unit] +Description=Keyoxide (Your online passport) +After=syslog.target +After=network.target + +[Service] +User=keyoxide +Group=www-data +WorkingDirectory=/opt/keyoxide-web/ +ExecStart=/usr/bin/node /opt/keyoxide-web/src/index.js +Restart=always +RestartSec=2s +Environment=PORT=5000 + +[Install] +WantedBy=multi-user.target From 9caa1f6795a07eff44d7f1c89f89c741c68a4cff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Jaenisch?= Date: Tue, 13 Feb 2024 17:26:22 +0100 Subject: [PATCH 143/144] feat: update service file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: André Jaenisch --- keyoxide-web.service | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/keyoxide-web.service b/keyoxide-web.service index d85fd44..1b2074a 100644 --- a/keyoxide-web.service +++ b/keyoxide-web.service @@ -1,5 +1,5 @@ [Unit] -Description=Keyoxide (Your online passport) +Description=Keyoxide (Online identity verification) After=syslog.target After=network.target @@ -7,10 +7,10 @@ After=network.target User=keyoxide Group=www-data WorkingDirectory=/opt/keyoxide-web/ -ExecStart=/usr/bin/node /opt/keyoxide-web/src/index.js +ExecStart=/usr/bin/node /opt/keyoxide-web/dist/index.js Restart=always RestartSec=2s -Environment=PORT=5000 +Environment=PORT=5000 DOMAIN=keyoxide.org PROXY_HOSTNAME=keyoxide.org [Install] WantedBy=multi-user.target From b8c94ebc0b9fffe1e2bd9dfbec62890ea24fbea7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Jaenisch?= Date: Tue, 13 Feb 2024 19:44:29 +0100 Subject: [PATCH 144/144] refactor: use domain.example for documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a reserved TLD for technical documentation. Signed-off-by: André Jaenisch --- keyoxide-web.service | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keyoxide-web.service b/keyoxide-web.service index 1b2074a..16c23e2 100644 --- a/keyoxide-web.service +++ b/keyoxide-web.service @@ -10,7 +10,7 @@ WorkingDirectory=/opt/keyoxide-web/ ExecStart=/usr/bin/node /opt/keyoxide-web/dist/index.js Restart=always RestartSec=2s -Environment=PORT=5000 DOMAIN=keyoxide.org PROXY_HOSTNAME=keyoxide.org +Environment=PORT=5000 DOMAIN=domain.example PROXY_HOSTNAME=domain.example [Install] WantedBy=multi-user.target